ums.c revision 190734
1184610Salfred/*-
2184610Salfred * Copyright (c) 1998 The NetBSD Foundation, Inc.
3184610Salfred * All rights reserved.
4184610Salfred *
5184610Salfred * This code is derived from software contributed to The NetBSD Foundation
6184610Salfred * by Lennart Augustsson (lennart@augustsson.net) at
7184610Salfred * Carlstedt Research & Technology.
8184610Salfred *
9184610Salfred * Redistribution and use in source and binary forms, with or without
10184610Salfred * modification, are permitted provided that the following conditions
11184610Salfred * are met:
12184610Salfred * 1. Redistributions of source code must retain the above copyright
13184610Salfred *    notice, this list of conditions and the following disclaimer.
14184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
15184610Salfred *    notice, this list of conditions and the following disclaimer in the
16184610Salfred *    documentation and/or other materials provided with the distribution.
17184610Salfred * 3. All advertising materials mentioning features or use of this software
18184610Salfred *    must display the following acknowledgement:
19184610Salfred *        This product includes software developed by the NetBSD
20184610Salfred *        Foundation, Inc. and its contributors.
21184610Salfred * 4. Neither the name of The NetBSD Foundation nor the names of its
22184610Salfred *    contributors may be used to endorse or promote products derived
23184610Salfred *    from this software without specific prior written permission.
24184610Salfred *
25184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28184610Salfred * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35184610Salfred * POSSIBILITY OF SUCH DAMAGE.
36184610Salfred */
37184610Salfred
38184610Salfred#include <sys/cdefs.h>
39184610Salfred__FBSDID("$FreeBSD: head/sys/dev/usb/input/ums.c 190734 2009-04-05 18:20:38Z thompsa $");
40184610Salfred
41184610Salfred/*
42184610Salfred * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
43184610Salfred */
44184610Salfred
45188746Sthompsa#include "usbdevs.h"
46188942Sthompsa#include <dev/usb/usb.h>
47188942Sthompsa#include <dev/usb/usb_mfunc.h>
48188942Sthompsa#include <dev/usb/usb_error.h>
49188942Sthompsa#include <dev/usb/usbhid.h>
50184610Salfred
51184610Salfred#define	USB_DEBUG_VAR ums_debug
52184610Salfred
53188942Sthompsa#include <dev/usb/usb_core.h>
54188942Sthompsa#include <dev/usb/usb_util.h>
55188942Sthompsa#include <dev/usb/usb_debug.h>
56188942Sthompsa#include <dev/usb/usb_busdma.h>
57188942Sthompsa#include <dev/usb/usb_process.h>
58188942Sthompsa#include <dev/usb/usb_transfer.h>
59188942Sthompsa#include <dev/usb/usb_request.h>
60188942Sthompsa#include <dev/usb/usb_dynamic.h>
61188942Sthompsa#include <dev/usb/usb_mbuf.h>
62188942Sthompsa#include <dev/usb/usb_dev.h>
63188942Sthompsa#include <dev/usb/usb_hid.h>
64184610Salfred
65188942Sthompsa#include <dev/usb/quirk/usb_quirk.h>
66184610Salfred
67184610Salfred#include <sys/ioccom.h>
68184610Salfred#include <sys/filio.h>
69184610Salfred#include <sys/tty.h>
70184610Salfred#include <sys/mouse.h>
71184610Salfred
72184610Salfred#if USB_DEBUG
73184610Salfredstatic int ums_debug = 0;
74184610Salfred
75184610SalfredSYSCTL_NODE(_hw_usb2, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums");
76184610SalfredSYSCTL_INT(_hw_usb2_ums, OID_AUTO, debug, CTLFLAG_RW,
77184610Salfred    &ums_debug, 0, "Debug level");
78184610Salfred#endif
79184610Salfred
80184610Salfred#define	MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
81184610Salfred#define	MOUSE_FLAGS (HIO_RELATIVE)
82184610Salfred
83184610Salfred#define	UMS_BUF_SIZE      8		/* bytes */
84184610Salfred#define	UMS_IFQ_MAXLEN   50		/* units */
85184610Salfred#define	UMS_BUTTON_MAX   31		/* exclusive, must be less than 32 */
86184610Salfred#define	UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i))
87184610Salfred
88187259Sthompsaenum {
89187259Sthompsa	UMS_INTR_DT,
90188981Sthompsa	UMS_N_TRANSFER,
91187259Sthompsa};
92187259Sthompsa
93184610Salfredstruct ums_softc {
94184610Salfred	struct usb2_fifo_sc sc_fifo;
95184610Salfred	struct mtx sc_mtx;
96184610Salfred	struct usb2_callout sc_callout;
97184610Salfred	struct hid_location sc_loc_w;
98184610Salfred	struct hid_location sc_loc_x;
99184610Salfred	struct hid_location sc_loc_y;
100184610Salfred	struct hid_location sc_loc_z;
101184610Salfred	struct hid_location sc_loc_t;
102184610Salfred	struct hid_location sc_loc_btn[UMS_BUTTON_MAX];
103184610Salfred	mousehw_t sc_hw;
104184610Salfred	mousemode_t sc_mode;
105184610Salfred	mousestatus_t sc_status;
106184610Salfred
107184610Salfred	struct usb2_xfer *sc_xfer[UMS_N_TRANSFER];
108184610Salfred
109184610Salfred	uint32_t sc_flags;
110184610Salfred#define	UMS_FLAG_X_AXIS     0x0001
111184610Salfred#define	UMS_FLAG_Y_AXIS     0x0002
112184610Salfred#define	UMS_FLAG_Z_AXIS     0x0004
113184610Salfred#define	UMS_FLAG_T_AXIS     0x0008
114184610Salfred#define	UMS_FLAG_SBU        0x0010	/* spurious button up events */
115188981Sthompsa#define	UMS_FLAG_REVZ	    0x0020	/* Z-axis is reversed */
116188981Sthompsa#define	UMS_FLAG_W_AXIS     0x0040
117184610Salfred
118184610Salfred	uint8_t	sc_buttons;
119184610Salfred	uint8_t	sc_iid;
120189583Sthompsa	uint8_t	sc_iid_w;
121189583Sthompsa	uint8_t	sc_iid_x;
122189583Sthompsa	uint8_t	sc_iid_y;
123189583Sthompsa	uint8_t	sc_iid_z;
124189583Sthompsa	uint8_t	sc_iid_t;
125189583Sthompsa	uint8_t	sc_iid_btn[UMS_BUTTON_MAX];
126184610Salfred	uint8_t	sc_temp[64];
127184610Salfred};
128184610Salfred
129184610Salfredstatic void ums_put_queue_timeout(void *__sc);
130184610Salfred
131184610Salfredstatic usb2_callback_t ums_intr_callback;
132184610Salfred
133184610Salfredstatic device_probe_t ums_probe;
134184610Salfredstatic device_attach_t ums_attach;
135184610Salfredstatic device_detach_t ums_detach;
136184610Salfred
137184610Salfredstatic usb2_fifo_cmd_t ums_start_read;
138184610Salfredstatic usb2_fifo_cmd_t ums_stop_read;
139184610Salfredstatic usb2_fifo_open_t ums_open;
140184610Salfredstatic usb2_fifo_close_t ums_close;
141184610Salfredstatic usb2_fifo_ioctl_t ums_ioctl;
142184610Salfred
143184610Salfredstatic void ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, int32_t dz, int32_t dt, int32_t buttons);
144184610Salfred
145184610Salfredstatic struct usb2_fifo_methods ums_fifo_methods = {
146184610Salfred	.f_open = &ums_open,
147184610Salfred	.f_close = &ums_close,
148184610Salfred	.f_ioctl = &ums_ioctl,
149184610Salfred	.f_start_read = &ums_start_read,
150184610Salfred	.f_stop_read = &ums_stop_read,
151184610Salfred	.basename[0] = "ums",
152184610Salfred};
153184610Salfred
154184610Salfredstatic void
155184610Salfredums_put_queue_timeout(void *__sc)
156184610Salfred{
157184610Salfred	struct ums_softc *sc = __sc;
158184610Salfred
159184610Salfred	mtx_assert(&sc->sc_mtx, MA_OWNED);
160184610Salfred
161184610Salfred	ums_put_queue(sc, 0, 0, 0, 0, 0);
162184610Salfred}
163184610Salfred
164184610Salfredstatic void
165184610Salfredums_intr_callback(struct usb2_xfer *xfer)
166184610Salfred{
167184610Salfred	struct ums_softc *sc = xfer->priv_sc;
168184610Salfred	uint8_t *buf = sc->sc_temp;
169184610Salfred	uint16_t len = xfer->actlen;
170184610Salfred	int32_t buttons = 0;
171184610Salfred	int32_t dw;
172184610Salfred	int32_t dx;
173184610Salfred	int32_t dy;
174184610Salfred	int32_t dz;
175184610Salfred	int32_t dt;
176184610Salfred	uint8_t i;
177189583Sthompsa	uint8_t id;
178184610Salfred
179184610Salfred	switch (USB_GET_STATE(xfer)) {
180184610Salfred	case USB_ST_TRANSFERRED:
181184610Salfred		DPRINTFN(6, "sc=%p actlen=%d\n", sc, len);
182184610Salfred
183184610Salfred		if (len > sizeof(sc->sc_temp)) {
184184610Salfred			DPRINTFN(6, "truncating large packet to %zu bytes\n",
185184610Salfred			    sizeof(sc->sc_temp));
186184610Salfred			len = sizeof(sc->sc_temp);
187184610Salfred		}
188188981Sthompsa		if (len == 0)
189184610Salfred			goto tr_setup;
190188981Sthompsa
191184610Salfred		usb2_copy_out(xfer->frbuffers, 0, buf, len);
192184610Salfred
193184610Salfred		DPRINTFN(6, "data = %02x %02x %02x %02x "
194184610Salfred		    "%02x %02x %02x %02x\n",
195184610Salfred		    (len > 0) ? buf[0] : 0, (len > 1) ? buf[1] : 0,
196184610Salfred		    (len > 2) ? buf[2] : 0, (len > 3) ? buf[3] : 0,
197184610Salfred		    (len > 4) ? buf[4] : 0, (len > 5) ? buf[5] : 0,
198184610Salfred		    (len > 6) ? buf[6] : 0, (len > 7) ? buf[7] : 0);
199184610Salfred
200184610Salfred		if (sc->sc_iid) {
201189583Sthompsa			id = *buf;
202184610Salfred
203184610Salfred			len--;
204184610Salfred			buf++;
205184610Salfred
206184610Salfred		} else {
207189583Sthompsa			id = 0;
208184610Salfred			if (sc->sc_flags & UMS_FLAG_SBU) {
209184610Salfred				if ((*buf == 0x14) || (*buf == 0x15)) {
210184610Salfred					goto tr_setup;
211184610Salfred				}
212184610Salfred			}
213184610Salfred		}
214184610Salfred
215189583Sthompsa		if ((sc->sc_flags & UMS_FLAG_W_AXIS) && (id == sc->sc_iid_w))
216189583Sthompsa			dw = hid_get_data(buf, len, &sc->sc_loc_w);
217189583Sthompsa		else
218189583Sthompsa			dw = 0;
219184610Salfred
220189583Sthompsa		if ((sc->sc_flags & UMS_FLAG_X_AXIS) && (id == sc->sc_iid_x))
221189583Sthompsa			dx = hid_get_data(buf, len, &sc->sc_loc_x);
222189583Sthompsa		else
223189583Sthompsa			dx = 0;
224184610Salfred
225189583Sthompsa		if ((sc->sc_flags & UMS_FLAG_Y_AXIS) && (id == sc->sc_iid_y))
226189583Sthompsa			dy = -hid_get_data(buf, len, &sc->sc_loc_y);
227189583Sthompsa		else
228189583Sthompsa			dy = 0;
229184610Salfred
230189583Sthompsa		if ((sc->sc_flags & UMS_FLAG_Z_AXIS) && (id == sc->sc_iid_z))
231189583Sthompsa			dz = -hid_get_data(buf, len, &sc->sc_loc_z);
232189583Sthompsa		else
233189583Sthompsa			dz = 0;
234184610Salfred
235189583Sthompsa		if (sc->sc_flags & UMS_FLAG_REVZ)
236184610Salfred			dz = -dz;
237184610Salfred
238189583Sthompsa		if ((sc->sc_flags & UMS_FLAG_T_AXIS) && (id == sc->sc_iid_t))
239189583Sthompsa			dt = -hid_get_data(buf, len, &sc->sc_loc_t);
240189583Sthompsa		else
241189583Sthompsa			dt = 0;
242189583Sthompsa
243184610Salfred		for (i = 0; i < sc->sc_buttons; i++) {
244189583Sthompsa			if (id != sc->sc_iid_btn[i])
245189583Sthompsa				continue;
246184610Salfred			if (hid_get_data(buf, len, &sc->sc_loc_btn[i])) {
247184610Salfred				buttons |= (1 << UMS_BUT(i));
248184610Salfred			}
249184610Salfred		}
250184610Salfred
251184610Salfred		if (dx || dy || dz || dt || dw ||
252184610Salfred		    (buttons != sc->sc_status.button)) {
253184610Salfred
254184610Salfred			DPRINTFN(6, "x:%d y:%d z:%d t:%d w:%d buttons:0x%08x\n",
255184610Salfred			    dx, dy, dz, dt, dw, buttons);
256184610Salfred
257184610Salfred			sc->sc_status.button = buttons;
258184610Salfred			sc->sc_status.dx += dx;
259184610Salfred			sc->sc_status.dy += dy;
260184610Salfred			sc->sc_status.dz += dz;
261184610Salfred			/*
262184610Salfred			 * sc->sc_status.dt += dt;
263184610Salfred			 * no way to export this yet
264184610Salfred			 */
265184610Salfred
266184610Salfred			/*
267188981Sthompsa		         * The Qtronix keyboard has a built in PS/2
268188981Sthompsa		         * port for a mouse.  The firmware once in a
269188981Sthompsa		         * while posts a spurious button up
270188981Sthompsa		         * event. This event we ignore by doing a
271188981Sthompsa		         * timeout for 50 msecs.  If we receive
272188981Sthompsa		         * dx=dy=dz=buttons=0 before we add the event
273188981Sthompsa		         * to the queue.  In any other case we delete
274188981Sthompsa		         * the timeout event.
275184610Salfred		         */
276184610Salfred			if ((sc->sc_flags & UMS_FLAG_SBU) &&
277184610Salfred			    (dx == 0) && (dy == 0) && (dz == 0) && (dt == 0) &&
278184610Salfred			    (dw == 0) && (buttons == 0)) {
279184610Salfred
280184610Salfred				usb2_callout_reset(&sc->sc_callout, hz / 20,
281184610Salfred				    &ums_put_queue_timeout, sc);
282184610Salfred			} else {
283184610Salfred
284184610Salfred				usb2_callout_stop(&sc->sc_callout);
285184610Salfred
286184610Salfred				ums_put_queue(sc, dx, dy, dz, dt, buttons);
287184610Salfred			}
288184610Salfred		}
289184610Salfred	case USB_ST_SETUP:
290184610Salfredtr_setup:
291188981Sthompsa		/* check if we can put more data into the FIFO */
292188981Sthompsa		if (usb2_fifo_put_bytes_max(
293188981Sthompsa		    sc->sc_fifo.fp[USB_FIFO_RX]) != 0) {
294188981Sthompsa			xfer->frlengths[0] = xfer->max_data_length;
295188981Sthompsa			usb2_start_hardware(xfer);
296184610Salfred		}
297188981Sthompsa		break;
298184610Salfred
299184610Salfred	default:			/* Error */
300184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
301188981Sthompsa			/* try clear stall first */
302188981Sthompsa			xfer->flags.stall_pipe = 1;
303188981Sthompsa			goto tr_setup;
304184610Salfred		}
305188981Sthompsa		break;
306184610Salfred	}
307184610Salfred}
308184610Salfred
309184610Salfredstatic const struct usb2_config ums_config[UMS_N_TRANSFER] = {
310184610Salfred
311187259Sthompsa	[UMS_INTR_DT] = {
312184610Salfred		.type = UE_INTERRUPT,
313184610Salfred		.endpoint = UE_ADDR_ANY,
314184610Salfred		.direction = UE_DIR_IN,
315190734Sthompsa		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
316190734Sthompsa		.bufsize = 0,	/* use wMaxPacketSize */
317190734Sthompsa		.callback = &ums_intr_callback,
318184610Salfred	},
319184610Salfred};
320184610Salfred
321184610Salfredstatic int
322184610Salfredums_probe(device_t dev)
323184610Salfred{
324184610Salfred	struct usb2_attach_arg *uaa = device_get_ivars(dev);
325184610Salfred	struct usb2_interface_descriptor *id;
326184610Salfred	void *d_ptr;
327188981Sthompsa	int error;
328184610Salfred	uint16_t d_len;
329184610Salfred
330184610Salfred	DPRINTFN(11, "\n");
331184610Salfred
332188981Sthompsa	if (uaa->usb2_mode != USB_MODE_HOST)
333184610Salfred		return (ENXIO);
334188981Sthompsa
335184610Salfred	id = usb2_get_interface_descriptor(uaa->iface);
336184610Salfred
337184610Salfred	if ((id == NULL) ||
338188981Sthompsa	    (id->bInterfaceClass != UICLASS_HID))
339184610Salfred		return (ENXIO);
340188981Sthompsa
341190172Sthompsa	error = usb2_req_get_hid_desc(uaa->device, NULL,
342184610Salfred	    &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex);
343184610Salfred
344188981Sthompsa	if (error)
345184610Salfred		return (ENXIO);
346188981Sthompsa
347184610Salfred	if (hid_is_collection(d_ptr, d_len,
348188981Sthompsa	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
349184610Salfred		error = 0;
350188981Sthompsa	else if ((id->bInterfaceSubClass == UISUBCLASS_BOOT) &&
351188981Sthompsa	    (id->bInterfaceProtocol == UIPROTO_MOUSE))
352184610Salfred		error = 0;
353188981Sthompsa	else
354184610Salfred		error = ENXIO;
355184610Salfred
356184610Salfred	free(d_ptr, M_TEMP);
357184610Salfred	return (error);
358184610Salfred}
359184610Salfred
360184610Salfredstatic int
361184610Salfredums_attach(device_t dev)
362184610Salfred{
363184610Salfred	struct usb2_attach_arg *uaa = device_get_ivars(dev);
364184610Salfred	struct ums_softc *sc = device_get_softc(dev);
365184610Salfred	void *d_ptr = NULL;
366184610Salfred	int unit = device_get_unit(dev);
367188981Sthompsa	int isize;
368188981Sthompsa	int isizebits;
369188981Sthompsa	int err;
370184610Salfred	uint32_t flags;
371184610Salfred	uint16_t d_len;
372184610Salfred	uint8_t i;
373184610Salfred
374184610Salfred	DPRINTFN(11, "sc=%p\n", sc);
375184610Salfred
376184610Salfred	device_set_usb2_desc(dev);
377184610Salfred
378184610Salfred	mtx_init(&sc->sc_mtx, "ums lock", NULL, MTX_DEF | MTX_RECURSE);
379184610Salfred
380186454Sthompsa	usb2_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
381184610Salfred
382184610Salfred	/*
383184610Salfred         * Force the report (non-boot) protocol.
384184610Salfred         *
385184610Salfred         * Mice without boot protocol support may choose not to implement
386184610Salfred         * Set_Protocol at all; Ignore any error.
387184610Salfred         */
388184610Salfred	err = usb2_req_set_protocol(uaa->device, NULL, uaa->info.bIfaceIndex, 1);
389184610Salfred
390184610Salfred	err = usb2_transfer_setup(uaa->device,
391184610Salfred	    &uaa->info.bIfaceIndex, sc->sc_xfer, ums_config,
392184610Salfred	    UMS_N_TRANSFER, sc, &sc->sc_mtx);
393184610Salfred
394184610Salfred	if (err) {
395184610Salfred		DPRINTF("error=%s\n", usb2_errstr(err));
396184610Salfred		goto detach;
397184610Salfred	}
398190172Sthompsa	err = usb2_req_get_hid_desc(uaa->device, NULL, &d_ptr,
399184610Salfred	    &d_len, M_TEMP, uaa->info.bIfaceIndex);
400184610Salfred
401184610Salfred	if (err) {
402184610Salfred		device_printf(dev, "error reading report description\n");
403184610Salfred		goto detach;
404184610Salfred	}
405184610Salfred	if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
406189583Sthompsa	    hid_input, &sc->sc_loc_x, &flags, &sc->sc_iid_x)) {
407184610Salfred
408184610Salfred		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
409184610Salfred			sc->sc_flags |= UMS_FLAG_X_AXIS;
410184610Salfred		}
411184610Salfred	}
412184610Salfred	if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
413189583Sthompsa	    hid_input, &sc->sc_loc_y, &flags, &sc->sc_iid_y)) {
414184610Salfred
415184610Salfred		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
416184610Salfred			sc->sc_flags |= UMS_FLAG_Y_AXIS;
417184610Salfred		}
418184610Salfred	}
419184610Salfred	/* Try the wheel first as the Z activator since it's tradition. */
420184610Salfred	if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
421189583Sthompsa	    HUG_WHEEL), hid_input, &sc->sc_loc_z, &flags, &sc->sc_iid_z) ||
422184610Salfred	    hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
423189583Sthompsa	    HUG_TWHEEL), hid_input, &sc->sc_loc_z, &flags, &sc->sc_iid_z)) {
424184610Salfred		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
425184610Salfred			sc->sc_flags |= UMS_FLAG_Z_AXIS;
426184610Salfred		}
427184610Salfred		/*
428184610Salfred		 * We might have both a wheel and Z direction, if so put
429184610Salfred		 * put the Z on the W coordinate.
430184610Salfred		 */
431184610Salfred		if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
432189583Sthompsa		    HUG_Z), hid_input, &sc->sc_loc_w, &flags, &sc->sc_iid_w)) {
433184610Salfred
434184610Salfred			if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
435184610Salfred				sc->sc_flags |= UMS_FLAG_W_AXIS;
436184610Salfred			}
437184610Salfred		}
438184610Salfred	} else if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
439189583Sthompsa	    HUG_Z), hid_input, &sc->sc_loc_z, &flags, &sc->sc_iid_z)) {
440184610Salfred
441184610Salfred		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
442184610Salfred			sc->sc_flags |= UMS_FLAG_Z_AXIS;
443184610Salfred		}
444184610Salfred	}
445184610Salfred	/*
446184610Salfred	 * The Microsoft Wireless Intellimouse 2.0 reports it's wheel
447184610Salfred	 * using 0x0048, which is HUG_TWHEEL, and seems to expect you
448184610Salfred	 * to know that the byte after the wheel is the tilt axis.
449184610Salfred	 * There are no other HID axis descriptors other than X,Y and
450184610Salfred	 * TWHEEL
451184610Salfred	 */
452184610Salfred	if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL),
453189583Sthompsa	    hid_input, &sc->sc_loc_t, &flags, &sc->sc_iid_t)) {
454184610Salfred
455184610Salfred		sc->sc_loc_t.pos += 8;
456184610Salfred
457184610Salfred		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
458184610Salfred			sc->sc_flags |= UMS_FLAG_T_AXIS;
459184610Salfred		}
460184610Salfred	}
461184610Salfred	/* figure out the number of buttons */
462184610Salfred
463184610Salfred	for (i = 0; i < UMS_BUTTON_MAX; i++) {
464184610Salfred		if (!hid_locate(d_ptr, d_len, HID_USAGE2(HUP_BUTTON, (i + 1)),
465189583Sthompsa			hid_input, &sc->sc_loc_btn[i], NULL, &sc->sc_iid_btn[i])) {
466184610Salfred			break;
467184610Salfred		}
468184610Salfred	}
469184610Salfred
470184610Salfred	sc->sc_buttons = i;
471184610Salfred
472189583Sthompsa	isize = hid_report_size(d_ptr, d_len, hid_input, &sc->sc_iid);
473184610Salfred
474184610Salfred	/*
475184610Salfred	 * The Microsoft Wireless Notebook Optical Mouse seems to be in worse
476184610Salfred	 * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and
477184610Salfred	 * all of its other button positions are all off. It also reports that
478184610Salfred	 * it has two addional buttons and a tilt wheel.
479184610Salfred	 */
480184610Salfred	if (usb2_test_quirk(uaa, UQ_MS_BAD_CLASS)) {
481184610Salfred		sc->sc_flags = (UMS_FLAG_X_AXIS |
482184610Salfred		    UMS_FLAG_Y_AXIS |
483184610Salfred		    UMS_FLAG_Z_AXIS |
484184610Salfred		    UMS_FLAG_SBU);
485184610Salfred		sc->sc_buttons = 3;
486184610Salfred		isize = 5;
487184610Salfred		sc->sc_iid = 0;
488189583Sthompsa		sc->sc_iid_x = 0;
489189583Sthompsa		sc->sc_iid_y = 0;
490189583Sthompsa		sc->sc_iid_z = 0;
491189583Sthompsa		sc->sc_iid_btn[0] = 0;
492189583Sthompsa		sc->sc_iid_btn[1] = 0;
493189583Sthompsa		sc->sc_iid_btn[2] = 0;
494184610Salfred		/* 1st byte of descriptor report contains garbage */
495184610Salfred		sc->sc_loc_x.pos = 16;
496184610Salfred		sc->sc_loc_y.pos = 24;
497184610Salfred		sc->sc_loc_z.pos = 32;
498184610Salfred		sc->sc_loc_btn[0].pos = 8;
499184610Salfred		sc->sc_loc_btn[1].pos = 9;
500184610Salfred		sc->sc_loc_btn[2].pos = 10;
501184610Salfred	}
502188981Sthompsa
503184610Salfred	/*
504188981Sthompsa	 * Some Microsoft devices have incorrectly high location
505188981Sthompsa	 * positions. Correct this:
506184610Salfred	 */
507188981Sthompsa	isizebits = isize * 8;
508188981Sthompsa	if ((sc->sc_iid != 0) && (isizebits > 8)) {
509188981Sthompsa		isizebits -= 8;	/* remove size of report ID */
510188981Sthompsa		sc->sc_loc_w.pos %= isizebits;
511188981Sthompsa		sc->sc_loc_x.pos %= isizebits;
512188981Sthompsa		sc->sc_loc_y.pos %= isizebits;
513188981Sthompsa		sc->sc_loc_z.pos %= isizebits;
514188981Sthompsa		sc->sc_loc_t.pos %= isizebits;
515188981Sthompsa		for (i = 0; i != UMS_BUTTON_MAX; i++)
516188981Sthompsa			sc->sc_loc_btn[i].pos %= isizebits;
517184610Salfred	}
518188981Sthompsa
519184610Salfred	if (usb2_test_quirk(uaa, UQ_MS_REVZ)) {
520184610Salfred		/* Some wheels need the Z axis reversed. */
521184610Salfred		sc->sc_flags |= UMS_FLAG_REVZ;
522184610Salfred	}
523187259Sthompsa	if (isize > sc->sc_xfer[UMS_INTR_DT]->max_frame_size) {
524184610Salfred		DPRINTF("WARNING: report size, %d bytes, is larger "
525184610Salfred		    "than interrupt size, %d bytes!\n",
526187259Sthompsa		    isize, sc->sc_xfer[UMS_INTR_DT]->max_frame_size);
527184610Salfred	}
528184610Salfred	/* announce information about the mouse */
529184610Salfred
530184610Salfred	device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates\n",
531184610Salfred	    (sc->sc_buttons),
532184610Salfred	    (sc->sc_flags & UMS_FLAG_X_AXIS) ? "X" : "",
533184610Salfred	    (sc->sc_flags & UMS_FLAG_Y_AXIS) ? "Y" : "",
534184610Salfred	    (sc->sc_flags & UMS_FLAG_Z_AXIS) ? "Z" : "",
535184610Salfred	    (sc->sc_flags & UMS_FLAG_T_AXIS) ? "T" : "",
536184610Salfred	    (sc->sc_flags & UMS_FLAG_W_AXIS) ? "W" : "");
537184610Salfred
538184610Salfred	free(d_ptr, M_TEMP);
539184610Salfred	d_ptr = NULL;
540184610Salfred
541184610Salfred#if USB_DEBUG
542184610Salfred	DPRINTF("sc=%p\n", sc);
543189583Sthompsa	DPRINTF("X\t%d/%d id=%d\n", sc->sc_loc_x.pos,
544189583Sthompsa	    sc->sc_loc_x.size, sc->sc_iid_x);
545189583Sthompsa	DPRINTF("Y\t%d/%d id=%d\n", sc->sc_loc_y.pos,
546189583Sthompsa	    sc->sc_loc_y.size, sc->sc_iid_y);
547189583Sthompsa	DPRINTF("Z\t%d/%d id=%d\n", sc->sc_loc_z.pos,
548189583Sthompsa	    sc->sc_loc_z.size, sc->sc_iid_z);
549189583Sthompsa	DPRINTF("T\t%d/%d id=%d\n", sc->sc_loc_t.pos,
550189583Sthompsa	    sc->sc_loc_t.size, sc->sc_iid_t);
551189583Sthompsa	DPRINTF("W\t%d/%d id=%d\n", sc->sc_loc_w.pos,
552189583Sthompsa	    sc->sc_loc_w.size, sc->sc_iid_w);
553184610Salfred
554184610Salfred	for (i = 0; i < sc->sc_buttons; i++) {
555189583Sthompsa		DPRINTF("B%d\t%d/%d id=%d\n",
556189583Sthompsa		    i + 1, sc->sc_loc_btn[i].pos,
557189583Sthompsa		    sc->sc_loc_btn[i].size, sc->sc_iid_btn[i]);
558184610Salfred	}
559184610Salfred	DPRINTF("size=%d, id=%d\n", isize, sc->sc_iid);
560184610Salfred#endif
561184610Salfred
562184610Salfred	if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
563184610Salfred		sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
564184610Salfred	else
565184610Salfred		sc->sc_hw.buttons = sc->sc_buttons;
566184610Salfred
567184610Salfred	sc->sc_hw.iftype = MOUSE_IF_USB;
568184610Salfred	sc->sc_hw.type = MOUSE_MOUSE;
569184610Salfred	sc->sc_hw.model = MOUSE_MODEL_GENERIC;
570184610Salfred	sc->sc_hw.hwid = 0;
571184610Salfred
572184610Salfred	sc->sc_mode.protocol = MOUSE_PROTO_MSC;
573184610Salfred	sc->sc_mode.rate = -1;
574184610Salfred	sc->sc_mode.resolution = MOUSE_RES_UNKNOWN;
575184610Salfred	sc->sc_mode.accelfactor = 0;
576184610Salfred	sc->sc_mode.level = 0;
577184610Salfred	sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
578184610Salfred	sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
579184610Salfred	sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
580184610Salfred
581184610Salfred	sc->sc_status.flags = 0;
582184610Salfred	sc->sc_status.button = 0;
583184610Salfred	sc->sc_status.obutton = 0;
584184610Salfred	sc->sc_status.dx = 0;
585184610Salfred	sc->sc_status.dy = 0;
586184610Salfred	sc->sc_status.dz = 0;
587184610Salfred
588184610Salfred	err = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
589184610Salfred	    &ums_fifo_methods, &sc->sc_fifo,
590189110Sthompsa	    unit, 0 - 1, uaa->info.bIfaceIndex,
591189110Sthompsa  	    UID_ROOT, GID_OPERATOR, 0644);
592184610Salfred	if (err) {
593184610Salfred		goto detach;
594184610Salfred	}
595184610Salfred	return (0);
596184610Salfred
597184610Salfreddetach:
598184610Salfred	if (d_ptr) {
599184610Salfred		free(d_ptr, M_TEMP);
600184610Salfred	}
601184610Salfred	ums_detach(dev);
602184610Salfred	return (ENOMEM);
603184610Salfred}
604184610Salfred
605184610Salfredstatic int
606184610Salfredums_detach(device_t self)
607184610Salfred{
608184610Salfred	struct ums_softc *sc = device_get_softc(self);
609184610Salfred
610184610Salfred	DPRINTF("sc=%p\n", sc);
611184610Salfred
612184610Salfred	usb2_fifo_detach(&sc->sc_fifo);
613184610Salfred
614184610Salfred	usb2_transfer_unsetup(sc->sc_xfer, UMS_N_TRANSFER);
615184610Salfred
616184610Salfred	usb2_callout_drain(&sc->sc_callout);
617184610Salfred
618184610Salfred	mtx_destroy(&sc->sc_mtx);
619184610Salfred
620184610Salfred	return (0);
621184610Salfred}
622184610Salfred
623184610Salfredstatic void
624184610Salfredums_start_read(struct usb2_fifo *fifo)
625184610Salfred{
626184610Salfred	struct ums_softc *sc = fifo->priv_sc0;
627184610Salfred
628187259Sthompsa	usb2_transfer_start(sc->sc_xfer[UMS_INTR_DT]);
629184610Salfred}
630184610Salfred
631184610Salfredstatic void
632184610Salfredums_stop_read(struct usb2_fifo *fifo)
633184610Salfred{
634184610Salfred	struct ums_softc *sc = fifo->priv_sc0;
635184610Salfred
636187259Sthompsa	usb2_transfer_stop(sc->sc_xfer[UMS_INTR_DT]);
637184610Salfred	usb2_callout_stop(&sc->sc_callout);
638184610Salfred}
639184610Salfred
640184610Salfred
641184610Salfred#if ((MOUSE_SYS_PACKETSIZE != 8) || \
642184610Salfred     (MOUSE_MSC_PACKETSIZE != 5))
643184610Salfred#error "Software assumptions are not met. Please update code."
644184610Salfred#endif
645184610Salfred
646184610Salfredstatic void
647184610Salfredums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy,
648184610Salfred    int32_t dz, int32_t dt, int32_t buttons)
649184610Salfred{
650184610Salfred	uint8_t buf[8];
651184610Salfred
652184610Salfred	if (1) {
653184610Salfred
654184610Salfred		if (dx > 254)
655184610Salfred			dx = 254;
656184610Salfred		if (dx < -256)
657184610Salfred			dx = -256;
658184610Salfred		if (dy > 254)
659184610Salfred			dy = 254;
660184610Salfred		if (dy < -256)
661184610Salfred			dy = -256;
662184610Salfred		if (dz > 126)
663184610Salfred			dz = 126;
664184610Salfred		if (dz < -128)
665184610Salfred			dz = -128;
666184610Salfred		if (dt > 126)
667184610Salfred			dt = 126;
668184610Salfred		if (dt < -128)
669184610Salfred			dt = -128;
670184610Salfred
671184610Salfred		buf[0] = sc->sc_mode.syncmask[1];
672184610Salfred		buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS;
673184610Salfred		buf[1] = dx >> 1;
674184610Salfred		buf[2] = dy >> 1;
675184610Salfred		buf[3] = dx - (dx >> 1);
676184610Salfred		buf[4] = dy - (dy >> 1);
677184610Salfred
678184610Salfred		if (sc->sc_mode.level == 1) {
679184610Salfred			buf[5] = dz >> 1;
680184610Salfred			buf[6] = dz - (dz >> 1);
681184610Salfred			buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS);
682184610Salfred		}
683184610Salfred		usb2_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf,
684184610Salfred		    sc->sc_mode.packetsize, 1);
685184610Salfred
686184610Salfred	} else {
687184610Salfred		DPRINTF("Buffer full, discarded packet\n");
688184610Salfred	}
689184610Salfred}
690184610Salfred
691184610Salfredstatic void
692184610Salfredums_reset_buf(struct ums_softc *sc)
693184610Salfred{
694184610Salfred	/* reset read queue */
695184610Salfred	usb2_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]);
696184610Salfred}
697184610Salfred
698184610Salfredstatic int
699189110Sthompsaums_open(struct usb2_fifo *fifo, int fflags)
700184610Salfred{
701184610Salfred	struct ums_softc *sc = fifo->priv_sc0;
702184610Salfred
703184610Salfred	DPRINTFN(2, "\n");
704184610Salfred
705184610Salfred	if (fflags & FREAD) {
706184610Salfred
707184610Salfred		/* reset status */
708184610Salfred
709184610Salfred		sc->sc_status.flags = 0;
710184610Salfred		sc->sc_status.button = 0;
711184610Salfred		sc->sc_status.obutton = 0;
712184610Salfred		sc->sc_status.dx = 0;
713184610Salfred		sc->sc_status.dy = 0;
714184610Salfred		sc->sc_status.dz = 0;
715184610Salfred		/* sc->sc_status.dt = 0; */
716184610Salfred
717184610Salfred		if (usb2_fifo_alloc_buffer(fifo,
718184610Salfred		    UMS_BUF_SIZE, UMS_IFQ_MAXLEN)) {
719184610Salfred			return (ENOMEM);
720184610Salfred		}
721184610Salfred	}
722184610Salfred	return (0);
723184610Salfred}
724184610Salfred
725184610Salfredstatic void
726189110Sthompsaums_close(struct usb2_fifo *fifo, int fflags)
727184610Salfred{
728184610Salfred	if (fflags & FREAD) {
729184610Salfred		usb2_fifo_free_buffer(fifo);
730184610Salfred	}
731184610Salfred}
732184610Salfred
733184610Salfredstatic int
734189110Sthompsaums_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr, int fflags)
735184610Salfred{
736184610Salfred	struct ums_softc *sc = fifo->priv_sc0;
737184610Salfred	mousemode_t mode;
738184610Salfred	int error = 0;
739184610Salfred
740184610Salfred	DPRINTFN(2, "\n");
741184610Salfred
742184610Salfred	mtx_lock(&sc->sc_mtx);
743184610Salfred
744184610Salfred	switch (cmd) {
745184610Salfred	case MOUSE_GETHWINFO:
746184610Salfred		*(mousehw_t *)addr = sc->sc_hw;
747184610Salfred		break;
748184610Salfred
749184610Salfred	case MOUSE_GETMODE:
750184610Salfred		*(mousemode_t *)addr = sc->sc_mode;
751184610Salfred		break;
752184610Salfred
753184610Salfred	case MOUSE_SETMODE:
754184610Salfred		mode = *(mousemode_t *)addr;
755184610Salfred
756184610Salfred		if (mode.level == -1) {
757184610Salfred			/* don't change the current setting */
758184610Salfred		} else if ((mode.level < 0) || (mode.level > 1)) {
759184610Salfred			error = EINVAL;
760184610Salfred			goto done;
761184610Salfred		} else {
762184610Salfred			sc->sc_mode.level = mode.level;
763184610Salfred		}
764184610Salfred
765184610Salfred		if (sc->sc_mode.level == 0) {
766184610Salfred			if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
767184610Salfred				sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
768184610Salfred			else
769184610Salfred				sc->sc_hw.buttons = sc->sc_buttons;
770184610Salfred			sc->sc_mode.protocol = MOUSE_PROTO_MSC;
771184610Salfred			sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
772184610Salfred			sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
773184610Salfred			sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
774184610Salfred		} else if (sc->sc_mode.level == 1) {
775184610Salfred			if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
776184610Salfred				sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
777184610Salfred			else
778184610Salfred				sc->sc_hw.buttons = sc->sc_buttons;
779184610Salfred			sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
780184610Salfred			sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
781184610Salfred			sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
782184610Salfred			sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
783184610Salfred		}
784184610Salfred		ums_reset_buf(sc);
785184610Salfred		break;
786184610Salfred
787184610Salfred	case MOUSE_GETLEVEL:
788184610Salfred		*(int *)addr = sc->sc_mode.level;
789184610Salfred		break;
790184610Salfred
791184610Salfred	case MOUSE_SETLEVEL:
792184610Salfred		if (*(int *)addr < 0 || *(int *)addr > 1) {
793184610Salfred			error = EINVAL;
794184610Salfred			goto done;
795184610Salfred		}
796184610Salfred		sc->sc_mode.level = *(int *)addr;
797184610Salfred
798184610Salfred		if (sc->sc_mode.level == 0) {
799184610Salfred			if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
800184610Salfred				sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
801184610Salfred			else
802184610Salfred				sc->sc_hw.buttons = sc->sc_buttons;
803184610Salfred			sc->sc_mode.protocol = MOUSE_PROTO_MSC;
804184610Salfred			sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
805184610Salfred			sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
806184610Salfred			sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
807184610Salfred		} else if (sc->sc_mode.level == 1) {
808184610Salfred			if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
809184610Salfred				sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
810184610Salfred			else
811184610Salfred				sc->sc_hw.buttons = sc->sc_buttons;
812184610Salfred			sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
813184610Salfred			sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
814184610Salfred			sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
815184610Salfred			sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
816184610Salfred		}
817184610Salfred		ums_reset_buf(sc);
818184610Salfred		break;
819184610Salfred
820184610Salfred	case MOUSE_GETSTATUS:{
821184610Salfred			mousestatus_t *status = (mousestatus_t *)addr;
822184610Salfred
823184610Salfred			*status = sc->sc_status;
824184610Salfred			sc->sc_status.obutton = sc->sc_status.button;
825184610Salfred			sc->sc_status.button = 0;
826184610Salfred			sc->sc_status.dx = 0;
827184610Salfred			sc->sc_status.dy = 0;
828184610Salfred			sc->sc_status.dz = 0;
829184610Salfred			/* sc->sc_status.dt = 0; */
830184610Salfred
831184610Salfred			if (status->dx || status->dy || status->dz /* || status->dt */ ) {
832184610Salfred				status->flags |= MOUSE_POSCHANGED;
833184610Salfred			}
834184610Salfred			if (status->button != status->obutton) {
835184610Salfred				status->flags |= MOUSE_BUTTONSCHANGED;
836184610Salfred			}
837184610Salfred			break;
838184610Salfred		}
839184610Salfred	default:
840184610Salfred		error = ENOTTY;
841184610Salfred	}
842184610Salfred
843184610Salfreddone:
844184610Salfred	mtx_unlock(&sc->sc_mtx);
845184610Salfred	return (error);
846184610Salfred}
847184610Salfred
848184610Salfredstatic devclass_t ums_devclass;
849184610Salfred
850184610Salfredstatic device_method_t ums_methods[] = {
851184610Salfred	DEVMETHOD(device_probe, ums_probe),
852184610Salfred	DEVMETHOD(device_attach, ums_attach),
853184610Salfred	DEVMETHOD(device_detach, ums_detach),
854184610Salfred	{0, 0}
855184610Salfred};
856184610Salfred
857184610Salfredstatic driver_t ums_driver = {
858184610Salfred	.name = "ums",
859184610Salfred	.methods = ums_methods,
860184610Salfred	.size = sizeof(struct ums_softc),
861184610Salfred};
862184610Salfred
863189275SthompsaDRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, NULL, 0);
864188942SthompsaMODULE_DEPEND(ums, usb, 1, 1, 1);
865