xref: /freebsd/sys/dev/usb/input/ums.c (revision d2b2128a286a00ee53d79cb88b4e59bf42525cf9)
1 /*-
2  * Copyright (c) 1998 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Lennart Augustsson (lennart@augustsson.net) at
7  * Carlstedt Research & Technology.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *        This product includes software developed by the NetBSD
20  *        Foundation, Inc. and its contributors.
21  * 4. Neither the name of The NetBSD Foundation nor the names of its
22  *    contributors may be used to endorse or promote products derived
23  *    from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD$");
40 
41 /*
42  * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
43  */
44 
45 #include "usbdevs.h"
46 #include <dev/usb/usb.h>
47 #include <dev/usb/usb_mfunc.h>
48 #include <dev/usb/usb_error.h>
49 #include <dev/usb/usbhid.h>
50 
51 #define	USB_DEBUG_VAR ums_debug
52 
53 #include <dev/usb/usb_core.h>
54 #include <dev/usb/usb_util.h>
55 #include <dev/usb/usb_debug.h>
56 #include <dev/usb/usb_busdma.h>
57 #include <dev/usb/usb_process.h>
58 #include <dev/usb/usb_transfer.h>
59 #include <dev/usb/usb_request.h>
60 #include <dev/usb/usb_dynamic.h>
61 #include <dev/usb/usb_mbuf.h>
62 #include <dev/usb/usb_dev.h>
63 #include <dev/usb/usb_hid.h>
64 
65 #include <dev/usb/quirk/usb_quirk.h>
66 
67 #include <sys/ioccom.h>
68 #include <sys/filio.h>
69 #include <sys/tty.h>
70 #include <sys/mouse.h>
71 
72 #if USB_DEBUG
73 static int ums_debug = 0;
74 
75 SYSCTL_NODE(_hw_usb2, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums");
76 SYSCTL_INT(_hw_usb2_ums, OID_AUTO, debug, CTLFLAG_RW,
77     &ums_debug, 0, "Debug level");
78 #endif
79 
80 #define	MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
81 #define	MOUSE_FLAGS (HIO_RELATIVE)
82 
83 #define	UMS_BUF_SIZE      8		/* bytes */
84 #define	UMS_IFQ_MAXLEN   50		/* units */
85 #define	UMS_BUTTON_MAX   31		/* exclusive, must be less than 32 */
86 #define	UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i))
87 
88 enum {
89 	UMS_INTR_DT,
90 	UMS_N_TRANSFER,
91 };
92 
93 struct ums_softc {
94 	struct usb2_fifo_sc sc_fifo;
95 	struct mtx sc_mtx;
96 	struct usb2_callout sc_callout;
97 	struct hid_location sc_loc_w;
98 	struct hid_location sc_loc_x;
99 	struct hid_location sc_loc_y;
100 	struct hid_location sc_loc_z;
101 	struct hid_location sc_loc_t;
102 	struct hid_location sc_loc_btn[UMS_BUTTON_MAX];
103 	mousehw_t sc_hw;
104 	mousemode_t sc_mode;
105 	mousestatus_t sc_status;
106 
107 	struct usb2_xfer *sc_xfer[UMS_N_TRANSFER];
108 
109 	uint32_t sc_flags;
110 #define	UMS_FLAG_X_AXIS     0x0001
111 #define	UMS_FLAG_Y_AXIS     0x0002
112 #define	UMS_FLAG_Z_AXIS     0x0004
113 #define	UMS_FLAG_T_AXIS     0x0008
114 #define	UMS_FLAG_SBU        0x0010	/* spurious button up events */
115 #define	UMS_FLAG_REVZ	    0x0020	/* Z-axis is reversed */
116 #define	UMS_FLAG_W_AXIS     0x0040
117 
118 	uint8_t	sc_buttons;
119 	uint8_t	sc_iid;
120 	uint8_t	sc_iid_w;
121 	uint8_t	sc_iid_x;
122 	uint8_t	sc_iid_y;
123 	uint8_t	sc_iid_z;
124 	uint8_t	sc_iid_t;
125 	uint8_t	sc_iid_btn[UMS_BUTTON_MAX];
126 	uint8_t	sc_temp[64];
127 };
128 
129 static void ums_put_queue_timeout(void *__sc);
130 
131 static usb2_callback_t ums_intr_callback;
132 
133 static device_probe_t ums_probe;
134 static device_attach_t ums_attach;
135 static device_detach_t ums_detach;
136 
137 static usb2_fifo_cmd_t ums_start_read;
138 static usb2_fifo_cmd_t ums_stop_read;
139 static usb2_fifo_open_t ums_open;
140 static usb2_fifo_close_t ums_close;
141 static usb2_fifo_ioctl_t ums_ioctl;
142 
143 static void ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, int32_t dz, int32_t dt, int32_t buttons);
144 
145 static struct usb2_fifo_methods ums_fifo_methods = {
146 	.f_open = &ums_open,
147 	.f_close = &ums_close,
148 	.f_ioctl = &ums_ioctl,
149 	.f_start_read = &ums_start_read,
150 	.f_stop_read = &ums_stop_read,
151 	.basename[0] = "ums",
152 };
153 
154 static void
155 ums_put_queue_timeout(void *__sc)
156 {
157 	struct ums_softc *sc = __sc;
158 
159 	mtx_assert(&sc->sc_mtx, MA_OWNED);
160 
161 	ums_put_queue(sc, 0, 0, 0, 0, 0);
162 }
163 
164 static void
165 ums_intr_callback(struct usb2_xfer *xfer)
166 {
167 	struct ums_softc *sc = xfer->priv_sc;
168 	uint8_t *buf = sc->sc_temp;
169 	uint16_t len = xfer->actlen;
170 	int32_t buttons = 0;
171 	int32_t dw;
172 	int32_t dx;
173 	int32_t dy;
174 	int32_t dz;
175 	int32_t dt;
176 	uint8_t i;
177 	uint8_t id;
178 
179 	switch (USB_GET_STATE(xfer)) {
180 	case USB_ST_TRANSFERRED:
181 		DPRINTFN(6, "sc=%p actlen=%d\n", sc, len);
182 
183 		if (len > sizeof(sc->sc_temp)) {
184 			DPRINTFN(6, "truncating large packet to %zu bytes\n",
185 			    sizeof(sc->sc_temp));
186 			len = sizeof(sc->sc_temp);
187 		}
188 		if (len == 0)
189 			goto tr_setup;
190 
191 		usb2_copy_out(xfer->frbuffers, 0, buf, len);
192 
193 		DPRINTFN(6, "data = %02x %02x %02x %02x "
194 		    "%02x %02x %02x %02x\n",
195 		    (len > 0) ? buf[0] : 0, (len > 1) ? buf[1] : 0,
196 		    (len > 2) ? buf[2] : 0, (len > 3) ? buf[3] : 0,
197 		    (len > 4) ? buf[4] : 0, (len > 5) ? buf[5] : 0,
198 		    (len > 6) ? buf[6] : 0, (len > 7) ? buf[7] : 0);
199 
200 		if (sc->sc_iid) {
201 			id = *buf;
202 
203 			len--;
204 			buf++;
205 
206 		} else {
207 			id = 0;
208 			if (sc->sc_flags & UMS_FLAG_SBU) {
209 				if ((*buf == 0x14) || (*buf == 0x15)) {
210 					goto tr_setup;
211 				}
212 			}
213 		}
214 
215 		if ((sc->sc_flags & UMS_FLAG_W_AXIS) && (id == sc->sc_iid_w))
216 			dw = hid_get_data(buf, len, &sc->sc_loc_w);
217 		else
218 			dw = 0;
219 
220 		if ((sc->sc_flags & UMS_FLAG_X_AXIS) && (id == sc->sc_iid_x))
221 			dx = hid_get_data(buf, len, &sc->sc_loc_x);
222 		else
223 			dx = 0;
224 
225 		if ((sc->sc_flags & UMS_FLAG_Y_AXIS) && (id == sc->sc_iid_y))
226 			dy = -hid_get_data(buf, len, &sc->sc_loc_y);
227 		else
228 			dy = 0;
229 
230 		if ((sc->sc_flags & UMS_FLAG_Z_AXIS) && (id == sc->sc_iid_z))
231 			dz = -hid_get_data(buf, len, &sc->sc_loc_z);
232 		else
233 			dz = 0;
234 
235 		if (sc->sc_flags & UMS_FLAG_REVZ)
236 			dz = -dz;
237 
238 		if ((sc->sc_flags & UMS_FLAG_T_AXIS) && (id == sc->sc_iid_t))
239 			dt = -hid_get_data(buf, len, &sc->sc_loc_t);
240 		else
241 			dt = 0;
242 
243 		for (i = 0; i < sc->sc_buttons; i++) {
244 			if (id != sc->sc_iid_btn[i])
245 				continue;
246 			if (hid_get_data(buf, len, &sc->sc_loc_btn[i])) {
247 				buttons |= (1 << UMS_BUT(i));
248 			}
249 		}
250 
251 		if (dx || dy || dz || dt || dw ||
252 		    (buttons != sc->sc_status.button)) {
253 
254 			DPRINTFN(6, "x:%d y:%d z:%d t:%d w:%d buttons:0x%08x\n",
255 			    dx, dy, dz, dt, dw, buttons);
256 
257 			sc->sc_status.button = buttons;
258 			sc->sc_status.dx += dx;
259 			sc->sc_status.dy += dy;
260 			sc->sc_status.dz += dz;
261 			/*
262 			 * sc->sc_status.dt += dt;
263 			 * no way to export this yet
264 			 */
265 
266 			/*
267 		         * The Qtronix keyboard has a built in PS/2
268 		         * port for a mouse.  The firmware once in a
269 		         * while posts a spurious button up
270 		         * event. This event we ignore by doing a
271 		         * timeout for 50 msecs.  If we receive
272 		         * dx=dy=dz=buttons=0 before we add the event
273 		         * to the queue.  In any other case we delete
274 		         * the timeout event.
275 		         */
276 			if ((sc->sc_flags & UMS_FLAG_SBU) &&
277 			    (dx == 0) && (dy == 0) && (dz == 0) && (dt == 0) &&
278 			    (dw == 0) && (buttons == 0)) {
279 
280 				usb2_callout_reset(&sc->sc_callout, hz / 20,
281 				    &ums_put_queue_timeout, sc);
282 			} else {
283 
284 				usb2_callout_stop(&sc->sc_callout);
285 
286 				ums_put_queue(sc, dx, dy, dz, dt, buttons);
287 			}
288 		}
289 	case USB_ST_SETUP:
290 tr_setup:
291 		/* check if we can put more data into the FIFO */
292 		if (usb2_fifo_put_bytes_max(
293 		    sc->sc_fifo.fp[USB_FIFO_RX]) != 0) {
294 			xfer->frlengths[0] = xfer->max_data_length;
295 			usb2_start_hardware(xfer);
296 		}
297 		break;
298 
299 	default:			/* Error */
300 		if (xfer->error != USB_ERR_CANCELLED) {
301 			/* try clear stall first */
302 			xfer->flags.stall_pipe = 1;
303 			goto tr_setup;
304 		}
305 		break;
306 	}
307 }
308 
309 static const struct usb2_config ums_config[UMS_N_TRANSFER] = {
310 
311 	[UMS_INTR_DT] = {
312 		.type = UE_INTERRUPT,
313 		.endpoint = UE_ADDR_ANY,
314 		.direction = UE_DIR_IN,
315 		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
316 		.mh.bufsize = 0,	/* use wMaxPacketSize */
317 		.mh.callback = &ums_intr_callback,
318 	},
319 };
320 
321 static int
322 ums_probe(device_t dev)
323 {
324 	struct usb2_attach_arg *uaa = device_get_ivars(dev);
325 	struct usb2_interface_descriptor *id;
326 	void *d_ptr;
327 	int error;
328 	uint16_t d_len;
329 
330 	DPRINTFN(11, "\n");
331 
332 	if (uaa->usb2_mode != USB_MODE_HOST)
333 		return (ENXIO);
334 
335 	id = usb2_get_interface_descriptor(uaa->iface);
336 
337 	if ((id == NULL) ||
338 	    (id->bInterfaceClass != UICLASS_HID))
339 		return (ENXIO);
340 
341 	error = usb2_req_get_hid_desc(uaa->device, NULL,
342 	    &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex);
343 
344 	if (error)
345 		return (ENXIO);
346 
347 	if (hid_is_collection(d_ptr, d_len,
348 	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
349 		error = 0;
350 	else if ((id->bInterfaceSubClass == UISUBCLASS_BOOT) &&
351 	    (id->bInterfaceProtocol == UIPROTO_MOUSE))
352 		error = 0;
353 	else
354 		error = ENXIO;
355 
356 	free(d_ptr, M_TEMP);
357 	return (error);
358 }
359 
360 static int
361 ums_attach(device_t dev)
362 {
363 	struct usb2_attach_arg *uaa = device_get_ivars(dev);
364 	struct ums_softc *sc = device_get_softc(dev);
365 	void *d_ptr = NULL;
366 	int unit = device_get_unit(dev);
367 	int isize;
368 	int isizebits;
369 	int err;
370 	uint32_t flags;
371 	uint16_t d_len;
372 	uint8_t i;
373 
374 	DPRINTFN(11, "sc=%p\n", sc);
375 
376 	device_set_usb2_desc(dev);
377 
378 	mtx_init(&sc->sc_mtx, "ums lock", NULL, MTX_DEF | MTX_RECURSE);
379 
380 	usb2_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
381 
382 	/*
383          * Force the report (non-boot) protocol.
384          *
385          * Mice without boot protocol support may choose not to implement
386          * Set_Protocol at all; Ignore any error.
387          */
388 	err = usb2_req_set_protocol(uaa->device, NULL, uaa->info.bIfaceIndex, 1);
389 
390 	err = usb2_transfer_setup(uaa->device,
391 	    &uaa->info.bIfaceIndex, sc->sc_xfer, ums_config,
392 	    UMS_N_TRANSFER, sc, &sc->sc_mtx);
393 
394 	if (err) {
395 		DPRINTF("error=%s\n", usb2_errstr(err));
396 		goto detach;
397 	}
398 	err = usb2_req_get_hid_desc(uaa->device, NULL, &d_ptr,
399 	    &d_len, M_TEMP, uaa->info.bIfaceIndex);
400 
401 	if (err) {
402 		device_printf(dev, "error reading report description\n");
403 		goto detach;
404 	}
405 	if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
406 	    hid_input, &sc->sc_loc_x, &flags, &sc->sc_iid_x)) {
407 
408 		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
409 			sc->sc_flags |= UMS_FLAG_X_AXIS;
410 		}
411 	}
412 	if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
413 	    hid_input, &sc->sc_loc_y, &flags, &sc->sc_iid_y)) {
414 
415 		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
416 			sc->sc_flags |= UMS_FLAG_Y_AXIS;
417 		}
418 	}
419 	/* Try the wheel first as the Z activator since it's tradition. */
420 	if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
421 	    HUG_WHEEL), hid_input, &sc->sc_loc_z, &flags, &sc->sc_iid_z) ||
422 	    hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
423 	    HUG_TWHEEL), hid_input, &sc->sc_loc_z, &flags, &sc->sc_iid_z)) {
424 		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
425 			sc->sc_flags |= UMS_FLAG_Z_AXIS;
426 		}
427 		/*
428 		 * We might have both a wheel and Z direction, if so put
429 		 * put the Z on the W coordinate.
430 		 */
431 		if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
432 		    HUG_Z), hid_input, &sc->sc_loc_w, &flags, &sc->sc_iid_w)) {
433 
434 			if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
435 				sc->sc_flags |= UMS_FLAG_W_AXIS;
436 			}
437 		}
438 	} else if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
439 	    HUG_Z), hid_input, &sc->sc_loc_z, &flags, &sc->sc_iid_z)) {
440 
441 		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
442 			sc->sc_flags |= UMS_FLAG_Z_AXIS;
443 		}
444 	}
445 	/*
446 	 * The Microsoft Wireless Intellimouse 2.0 reports it's wheel
447 	 * using 0x0048, which is HUG_TWHEEL, and seems to expect you
448 	 * to know that the byte after the wheel is the tilt axis.
449 	 * There are no other HID axis descriptors other than X,Y and
450 	 * TWHEEL
451 	 */
452 	if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL),
453 	    hid_input, &sc->sc_loc_t, &flags, &sc->sc_iid_t)) {
454 
455 		sc->sc_loc_t.pos += 8;
456 
457 		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
458 			sc->sc_flags |= UMS_FLAG_T_AXIS;
459 		}
460 	}
461 	/* figure out the number of buttons */
462 
463 	for (i = 0; i < UMS_BUTTON_MAX; i++) {
464 		if (!hid_locate(d_ptr, d_len, HID_USAGE2(HUP_BUTTON, (i + 1)),
465 			hid_input, &sc->sc_loc_btn[i], NULL, &sc->sc_iid_btn[i])) {
466 			break;
467 		}
468 	}
469 
470 	sc->sc_buttons = i;
471 
472 	isize = hid_report_size(d_ptr, d_len, hid_input, &sc->sc_iid);
473 
474 	/*
475 	 * The Microsoft Wireless Notebook Optical Mouse seems to be in worse
476 	 * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and
477 	 * all of its other button positions are all off. It also reports that
478 	 * it has two addional buttons and a tilt wheel.
479 	 */
480 	if (usb2_test_quirk(uaa, UQ_MS_BAD_CLASS)) {
481 		sc->sc_flags = (UMS_FLAG_X_AXIS |
482 		    UMS_FLAG_Y_AXIS |
483 		    UMS_FLAG_Z_AXIS |
484 		    UMS_FLAG_SBU);
485 		sc->sc_buttons = 3;
486 		isize = 5;
487 		sc->sc_iid = 0;
488 		sc->sc_iid_x = 0;
489 		sc->sc_iid_y = 0;
490 		sc->sc_iid_z = 0;
491 		sc->sc_iid_btn[0] = 0;
492 		sc->sc_iid_btn[1] = 0;
493 		sc->sc_iid_btn[2] = 0;
494 		/* 1st byte of descriptor report contains garbage */
495 		sc->sc_loc_x.pos = 16;
496 		sc->sc_loc_y.pos = 24;
497 		sc->sc_loc_z.pos = 32;
498 		sc->sc_loc_btn[0].pos = 8;
499 		sc->sc_loc_btn[1].pos = 9;
500 		sc->sc_loc_btn[2].pos = 10;
501 	}
502 
503 	/*
504 	 * Some Microsoft devices have incorrectly high location
505 	 * positions. Correct this:
506 	 */
507 	isizebits = isize * 8;
508 	if ((sc->sc_iid != 0) && (isizebits > 8)) {
509 		isizebits -= 8;	/* remove size of report ID */
510 		sc->sc_loc_w.pos %= isizebits;
511 		sc->sc_loc_x.pos %= isizebits;
512 		sc->sc_loc_y.pos %= isizebits;
513 		sc->sc_loc_z.pos %= isizebits;
514 		sc->sc_loc_t.pos %= isizebits;
515 		for (i = 0; i != UMS_BUTTON_MAX; i++)
516 			sc->sc_loc_btn[i].pos %= isizebits;
517 	}
518 
519 	if (usb2_test_quirk(uaa, UQ_MS_REVZ)) {
520 		/* Some wheels need the Z axis reversed. */
521 		sc->sc_flags |= UMS_FLAG_REVZ;
522 	}
523 	if (isize > sc->sc_xfer[UMS_INTR_DT]->max_frame_size) {
524 		DPRINTF("WARNING: report size, %d bytes, is larger "
525 		    "than interrupt size, %d bytes!\n",
526 		    isize, sc->sc_xfer[UMS_INTR_DT]->max_frame_size);
527 	}
528 	/* announce information about the mouse */
529 
530 	device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates\n",
531 	    (sc->sc_buttons),
532 	    (sc->sc_flags & UMS_FLAG_X_AXIS) ? "X" : "",
533 	    (sc->sc_flags & UMS_FLAG_Y_AXIS) ? "Y" : "",
534 	    (sc->sc_flags & UMS_FLAG_Z_AXIS) ? "Z" : "",
535 	    (sc->sc_flags & UMS_FLAG_T_AXIS) ? "T" : "",
536 	    (sc->sc_flags & UMS_FLAG_W_AXIS) ? "W" : "");
537 
538 	free(d_ptr, M_TEMP);
539 	d_ptr = NULL;
540 
541 #if USB_DEBUG
542 	DPRINTF("sc=%p\n", sc);
543 	DPRINTF("X\t%d/%d id=%d\n", sc->sc_loc_x.pos,
544 	    sc->sc_loc_x.size, sc->sc_iid_x);
545 	DPRINTF("Y\t%d/%d id=%d\n", sc->sc_loc_y.pos,
546 	    sc->sc_loc_y.size, sc->sc_iid_y);
547 	DPRINTF("Z\t%d/%d id=%d\n", sc->sc_loc_z.pos,
548 	    sc->sc_loc_z.size, sc->sc_iid_z);
549 	DPRINTF("T\t%d/%d id=%d\n", sc->sc_loc_t.pos,
550 	    sc->sc_loc_t.size, sc->sc_iid_t);
551 	DPRINTF("W\t%d/%d id=%d\n", sc->sc_loc_w.pos,
552 	    sc->sc_loc_w.size, sc->sc_iid_w);
553 
554 	for (i = 0; i < sc->sc_buttons; i++) {
555 		DPRINTF("B%d\t%d/%d id=%d\n",
556 		    i + 1, sc->sc_loc_btn[i].pos,
557 		    sc->sc_loc_btn[i].size, sc->sc_iid_btn[i]);
558 	}
559 	DPRINTF("size=%d, id=%d\n", isize, sc->sc_iid);
560 #endif
561 
562 	if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
563 		sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
564 	else
565 		sc->sc_hw.buttons = sc->sc_buttons;
566 
567 	sc->sc_hw.iftype = MOUSE_IF_USB;
568 	sc->sc_hw.type = MOUSE_MOUSE;
569 	sc->sc_hw.model = MOUSE_MODEL_GENERIC;
570 	sc->sc_hw.hwid = 0;
571 
572 	sc->sc_mode.protocol = MOUSE_PROTO_MSC;
573 	sc->sc_mode.rate = -1;
574 	sc->sc_mode.resolution = MOUSE_RES_UNKNOWN;
575 	sc->sc_mode.accelfactor = 0;
576 	sc->sc_mode.level = 0;
577 	sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
578 	sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
579 	sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
580 
581 	sc->sc_status.flags = 0;
582 	sc->sc_status.button = 0;
583 	sc->sc_status.obutton = 0;
584 	sc->sc_status.dx = 0;
585 	sc->sc_status.dy = 0;
586 	sc->sc_status.dz = 0;
587 
588 	err = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
589 	    &ums_fifo_methods, &sc->sc_fifo,
590 	    unit, 0 - 1, uaa->info.bIfaceIndex,
591   	    UID_ROOT, GID_OPERATOR, 0644);
592 	if (err) {
593 		goto detach;
594 	}
595 	return (0);
596 
597 detach:
598 	if (d_ptr) {
599 		free(d_ptr, M_TEMP);
600 	}
601 	ums_detach(dev);
602 	return (ENOMEM);
603 }
604 
605 static int
606 ums_detach(device_t self)
607 {
608 	struct ums_softc *sc = device_get_softc(self);
609 
610 	DPRINTF("sc=%p\n", sc);
611 
612 	usb2_fifo_detach(&sc->sc_fifo);
613 
614 	usb2_transfer_unsetup(sc->sc_xfer, UMS_N_TRANSFER);
615 
616 	usb2_callout_drain(&sc->sc_callout);
617 
618 	mtx_destroy(&sc->sc_mtx);
619 
620 	return (0);
621 }
622 
623 static void
624 ums_start_read(struct usb2_fifo *fifo)
625 {
626 	struct ums_softc *sc = fifo->priv_sc0;
627 
628 	usb2_transfer_start(sc->sc_xfer[UMS_INTR_DT]);
629 }
630 
631 static void
632 ums_stop_read(struct usb2_fifo *fifo)
633 {
634 	struct ums_softc *sc = fifo->priv_sc0;
635 
636 	usb2_transfer_stop(sc->sc_xfer[UMS_INTR_DT]);
637 	usb2_callout_stop(&sc->sc_callout);
638 }
639 
640 
641 #if ((MOUSE_SYS_PACKETSIZE != 8) || \
642      (MOUSE_MSC_PACKETSIZE != 5))
643 #error "Software assumptions are not met. Please update code."
644 #endif
645 
646 static void
647 ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy,
648     int32_t dz, int32_t dt, int32_t buttons)
649 {
650 	uint8_t buf[8];
651 
652 	if (1) {
653 
654 		if (dx > 254)
655 			dx = 254;
656 		if (dx < -256)
657 			dx = -256;
658 		if (dy > 254)
659 			dy = 254;
660 		if (dy < -256)
661 			dy = -256;
662 		if (dz > 126)
663 			dz = 126;
664 		if (dz < -128)
665 			dz = -128;
666 		if (dt > 126)
667 			dt = 126;
668 		if (dt < -128)
669 			dt = -128;
670 
671 		buf[0] = sc->sc_mode.syncmask[1];
672 		buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS;
673 		buf[1] = dx >> 1;
674 		buf[2] = dy >> 1;
675 		buf[3] = dx - (dx >> 1);
676 		buf[4] = dy - (dy >> 1);
677 
678 		if (sc->sc_mode.level == 1) {
679 			buf[5] = dz >> 1;
680 			buf[6] = dz - (dz >> 1);
681 			buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS);
682 		}
683 		usb2_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf,
684 		    sc->sc_mode.packetsize, 1);
685 
686 	} else {
687 		DPRINTF("Buffer full, discarded packet\n");
688 	}
689 }
690 
691 static void
692 ums_reset_buf(struct ums_softc *sc)
693 {
694 	/* reset read queue */
695 	usb2_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]);
696 }
697 
698 static int
699 ums_open(struct usb2_fifo *fifo, int fflags)
700 {
701 	struct ums_softc *sc = fifo->priv_sc0;
702 
703 	DPRINTFN(2, "\n");
704 
705 	if (fflags & FREAD) {
706 
707 		/* reset status */
708 
709 		sc->sc_status.flags = 0;
710 		sc->sc_status.button = 0;
711 		sc->sc_status.obutton = 0;
712 		sc->sc_status.dx = 0;
713 		sc->sc_status.dy = 0;
714 		sc->sc_status.dz = 0;
715 		/* sc->sc_status.dt = 0; */
716 
717 		if (usb2_fifo_alloc_buffer(fifo,
718 		    UMS_BUF_SIZE, UMS_IFQ_MAXLEN)) {
719 			return (ENOMEM);
720 		}
721 	}
722 	return (0);
723 }
724 
725 static void
726 ums_close(struct usb2_fifo *fifo, int fflags)
727 {
728 	if (fflags & FREAD) {
729 		usb2_fifo_free_buffer(fifo);
730 	}
731 }
732 
733 static int
734 ums_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr, int fflags)
735 {
736 	struct ums_softc *sc = fifo->priv_sc0;
737 	mousemode_t mode;
738 	int error = 0;
739 
740 	DPRINTFN(2, "\n");
741 
742 	mtx_lock(&sc->sc_mtx);
743 
744 	switch (cmd) {
745 	case MOUSE_GETHWINFO:
746 		*(mousehw_t *)addr = sc->sc_hw;
747 		break;
748 
749 	case MOUSE_GETMODE:
750 		*(mousemode_t *)addr = sc->sc_mode;
751 		break;
752 
753 	case MOUSE_SETMODE:
754 		mode = *(mousemode_t *)addr;
755 
756 		if (mode.level == -1) {
757 			/* don't change the current setting */
758 		} else if ((mode.level < 0) || (mode.level > 1)) {
759 			error = EINVAL;
760 			goto done;
761 		} else {
762 			sc->sc_mode.level = mode.level;
763 		}
764 
765 		if (sc->sc_mode.level == 0) {
766 			if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
767 				sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
768 			else
769 				sc->sc_hw.buttons = sc->sc_buttons;
770 			sc->sc_mode.protocol = MOUSE_PROTO_MSC;
771 			sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
772 			sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
773 			sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
774 		} else if (sc->sc_mode.level == 1) {
775 			if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
776 				sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
777 			else
778 				sc->sc_hw.buttons = sc->sc_buttons;
779 			sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
780 			sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
781 			sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
782 			sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
783 		}
784 		ums_reset_buf(sc);
785 		break;
786 
787 	case MOUSE_GETLEVEL:
788 		*(int *)addr = sc->sc_mode.level;
789 		break;
790 
791 	case MOUSE_SETLEVEL:
792 		if (*(int *)addr < 0 || *(int *)addr > 1) {
793 			error = EINVAL;
794 			goto done;
795 		}
796 		sc->sc_mode.level = *(int *)addr;
797 
798 		if (sc->sc_mode.level == 0) {
799 			if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
800 				sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
801 			else
802 				sc->sc_hw.buttons = sc->sc_buttons;
803 			sc->sc_mode.protocol = MOUSE_PROTO_MSC;
804 			sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
805 			sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
806 			sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
807 		} else if (sc->sc_mode.level == 1) {
808 			if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
809 				sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
810 			else
811 				sc->sc_hw.buttons = sc->sc_buttons;
812 			sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
813 			sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
814 			sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
815 			sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
816 		}
817 		ums_reset_buf(sc);
818 		break;
819 
820 	case MOUSE_GETSTATUS:{
821 			mousestatus_t *status = (mousestatus_t *)addr;
822 
823 			*status = sc->sc_status;
824 			sc->sc_status.obutton = sc->sc_status.button;
825 			sc->sc_status.button = 0;
826 			sc->sc_status.dx = 0;
827 			sc->sc_status.dy = 0;
828 			sc->sc_status.dz = 0;
829 			/* sc->sc_status.dt = 0; */
830 
831 			if (status->dx || status->dy || status->dz /* || status->dt */ ) {
832 				status->flags |= MOUSE_POSCHANGED;
833 			}
834 			if (status->button != status->obutton) {
835 				status->flags |= MOUSE_BUTTONSCHANGED;
836 			}
837 			break;
838 		}
839 	default:
840 		error = ENOTTY;
841 	}
842 
843 done:
844 	mtx_unlock(&sc->sc_mtx);
845 	return (error);
846 }
847 
848 static devclass_t ums_devclass;
849 
850 static device_method_t ums_methods[] = {
851 	DEVMETHOD(device_probe, ums_probe),
852 	DEVMETHOD(device_attach, ums_attach),
853 	DEVMETHOD(device_detach, ums_detach),
854 	{0, 0}
855 };
856 
857 static driver_t ums_driver = {
858 	.name = "ums",
859 	.methods = ums_methods,
860 	.size = sizeof(struct ums_softc),
861 };
862 
863 DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, NULL, 0);
864 MODULE_DEPEND(ums, usb, 1, 1, 1);
865