xref: /freebsd/sys/dev/usb/input/ums.c (revision b3aaa0cc21c63d388230c7ef2a80abd631ff20d5)
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_temp[64];
121 };
122 
123 static void ums_put_queue_timeout(void *__sc);
124 
125 static usb2_callback_t ums_intr_callback;
126 
127 static device_probe_t ums_probe;
128 static device_attach_t ums_attach;
129 static device_detach_t ums_detach;
130 
131 static usb2_fifo_cmd_t ums_start_read;
132 static usb2_fifo_cmd_t ums_stop_read;
133 static usb2_fifo_open_t ums_open;
134 static usb2_fifo_close_t ums_close;
135 static usb2_fifo_ioctl_t ums_ioctl;
136 
137 static void ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, int32_t dz, int32_t dt, int32_t buttons);
138 
139 static struct usb2_fifo_methods ums_fifo_methods = {
140 	.f_open = &ums_open,
141 	.f_close = &ums_close,
142 	.f_ioctl = &ums_ioctl,
143 	.f_start_read = &ums_start_read,
144 	.f_stop_read = &ums_stop_read,
145 	.basename[0] = "ums",
146 };
147 
148 static void
149 ums_put_queue_timeout(void *__sc)
150 {
151 	struct ums_softc *sc = __sc;
152 
153 	mtx_assert(&sc->sc_mtx, MA_OWNED);
154 
155 	ums_put_queue(sc, 0, 0, 0, 0, 0);
156 }
157 
158 static void
159 ums_intr_callback(struct usb2_xfer *xfer)
160 {
161 	struct ums_softc *sc = xfer->priv_sc;
162 	uint8_t *buf = sc->sc_temp;
163 	uint16_t len = xfer->actlen;
164 	int32_t buttons = 0;
165 	int32_t dw;
166 	int32_t dx;
167 	int32_t dy;
168 	int32_t dz;
169 	int32_t dt;
170 	uint8_t i;
171 
172 	switch (USB_GET_STATE(xfer)) {
173 	case USB_ST_TRANSFERRED:
174 		DPRINTFN(6, "sc=%p actlen=%d\n", sc, len);
175 
176 		if (len > sizeof(sc->sc_temp)) {
177 			DPRINTFN(6, "truncating large packet to %zu bytes\n",
178 			    sizeof(sc->sc_temp));
179 			len = sizeof(sc->sc_temp);
180 		}
181 		if (len == 0)
182 			goto tr_setup;
183 
184 		usb2_copy_out(xfer->frbuffers, 0, buf, len);
185 
186 		DPRINTFN(6, "data = %02x %02x %02x %02x "
187 		    "%02x %02x %02x %02x\n",
188 		    (len > 0) ? buf[0] : 0, (len > 1) ? buf[1] : 0,
189 		    (len > 2) ? buf[2] : 0, (len > 3) ? buf[3] : 0,
190 		    (len > 4) ? buf[4] : 0, (len > 5) ? buf[5] : 0,
191 		    (len > 6) ? buf[6] : 0, (len > 7) ? buf[7] : 0);
192 
193 		/*
194 		 * The M$ Wireless Intellimouse 2.0 sends 1 extra
195 		 * leading byte of data compared to most USB
196 		 * mice. This byte frequently switches from 0x01
197 		 * (usual state) to 0x02. I assume it is to allow
198 		 * extra, non-standard, reporting (say battery-life).
199 		 *
200 		 * However at the same time it generates a left-click
201 		 * message on the button byte which causes spurious
202 		 * left-click's where there shouldn't be.  This should
203 		 * sort that.  Currently it's the only user of
204 		 * UMS_FLAG_T_AXIS so use it as an identifier.
205 		 *
206 		 *
207 		 * UPDATE: This problem affects the M$ Wireless
208 		 * Notebook Optical Mouse, too. However, the leading
209 		 * byte for this mouse is normally 0x11, and the
210 		 * phantom mouse click occurs when its 0x14.
211 		 *
212 		 * We probably should switch to some more official quirk.
213 		 */
214 		if (sc->sc_iid) {
215 			if (sc->sc_flags & UMS_FLAG_T_AXIS) {
216 				if (*buf == 0x02) {
217 					goto tr_setup;
218 				}
219 			} else {
220 				if (*buf != sc->sc_iid) {
221 					goto tr_setup;
222 				}
223 			}
224 
225 			len--;
226 			buf++;
227 
228 		} else {
229 			if (sc->sc_flags & UMS_FLAG_SBU) {
230 				if ((*buf == 0x14) || (*buf == 0x15)) {
231 					goto tr_setup;
232 				}
233 			}
234 		}
235 
236 		dw = (sc->sc_flags & UMS_FLAG_W_AXIS) ?
237 		    hid_get_data(buf, len, &sc->sc_loc_w) : 0;
238 
239 		dx = (sc->sc_flags & UMS_FLAG_X_AXIS) ?
240 		    hid_get_data(buf, len, &sc->sc_loc_x) : 0;
241 
242 		dy = (sc->sc_flags & UMS_FLAG_Y_AXIS) ?
243 		    -hid_get_data(buf, len, &sc->sc_loc_y) : 0;
244 
245 		dz = (sc->sc_flags & UMS_FLAG_Z_AXIS) ?
246 		    -hid_get_data(buf, len, &sc->sc_loc_z) : 0;
247 
248 		if (sc->sc_flags & UMS_FLAG_REVZ) {
249 			dz = -dz;
250 		}
251 		dt = (sc->sc_flags & UMS_FLAG_T_AXIS) ?
252 		    -hid_get_data(buf, len, &sc->sc_loc_t): 0;
253 
254 		for (i = 0; i < sc->sc_buttons; i++) {
255 			if (hid_get_data(buf, len, &sc->sc_loc_btn[i])) {
256 				buttons |= (1 << UMS_BUT(i));
257 			}
258 		}
259 
260 		if (dx || dy || dz || dt || dw ||
261 		    (buttons != sc->sc_status.button)) {
262 
263 			DPRINTFN(6, "x:%d y:%d z:%d t:%d w:%d buttons:0x%08x\n",
264 			    dx, dy, dz, dt, dw, buttons);
265 
266 			sc->sc_status.button = buttons;
267 			sc->sc_status.dx += dx;
268 			sc->sc_status.dy += dy;
269 			sc->sc_status.dz += dz;
270 			/*
271 			 * sc->sc_status.dt += dt;
272 			 * no way to export this yet
273 			 */
274 
275 			/*
276 		         * The Qtronix keyboard has a built in PS/2
277 		         * port for a mouse.  The firmware once in a
278 		         * while posts a spurious button up
279 		         * event. This event we ignore by doing a
280 		         * timeout for 50 msecs.  If we receive
281 		         * dx=dy=dz=buttons=0 before we add the event
282 		         * to the queue.  In any other case we delete
283 		         * the timeout event.
284 		         */
285 			if ((sc->sc_flags & UMS_FLAG_SBU) &&
286 			    (dx == 0) && (dy == 0) && (dz == 0) && (dt == 0) &&
287 			    (dw == 0) && (buttons == 0)) {
288 
289 				usb2_callout_reset(&sc->sc_callout, hz / 20,
290 				    &ums_put_queue_timeout, sc);
291 			} else {
292 
293 				usb2_callout_stop(&sc->sc_callout);
294 
295 				ums_put_queue(sc, dx, dy, dz, dt, buttons);
296 			}
297 		}
298 	case USB_ST_SETUP:
299 tr_setup:
300 		/* check if we can put more data into the FIFO */
301 		if (usb2_fifo_put_bytes_max(
302 		    sc->sc_fifo.fp[USB_FIFO_RX]) != 0) {
303 			xfer->frlengths[0] = xfer->max_data_length;
304 			usb2_start_hardware(xfer);
305 		}
306 		break;
307 
308 	default:			/* Error */
309 		if (xfer->error != USB_ERR_CANCELLED) {
310 			/* try clear stall first */
311 			xfer->flags.stall_pipe = 1;
312 			goto tr_setup;
313 		}
314 		break;
315 	}
316 }
317 
318 static const struct usb2_config ums_config[UMS_N_TRANSFER] = {
319 
320 	[UMS_INTR_DT] = {
321 		.type = UE_INTERRUPT,
322 		.endpoint = UE_ADDR_ANY,
323 		.direction = UE_DIR_IN,
324 		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
325 		.mh.bufsize = 0,	/* use wMaxPacketSize */
326 		.mh.callback = &ums_intr_callback,
327 	},
328 };
329 
330 static int
331 ums_probe(device_t dev)
332 {
333 	struct usb2_attach_arg *uaa = device_get_ivars(dev);
334 	struct usb2_interface_descriptor *id;
335 	void *d_ptr;
336 	int error;
337 	uint16_t d_len;
338 
339 	DPRINTFN(11, "\n");
340 
341 	if (uaa->usb2_mode != USB_MODE_HOST)
342 		return (ENXIO);
343 
344 	id = usb2_get_interface_descriptor(uaa->iface);
345 
346 	if ((id == NULL) ||
347 	    (id->bInterfaceClass != UICLASS_HID))
348 		return (ENXIO);
349 
350 	error = usb2_req_get_hid_desc(uaa->device, &Giant,
351 	    &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex);
352 
353 	if (error)
354 		return (ENXIO);
355 
356 	if (hid_is_collection(d_ptr, d_len,
357 	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
358 		error = 0;
359 	else if ((id->bInterfaceSubClass == UISUBCLASS_BOOT) &&
360 	    (id->bInterfaceProtocol == UIPROTO_MOUSE))
361 		error = 0;
362 	else
363 		error = ENXIO;
364 
365 	free(d_ptr, M_TEMP);
366 	return (error);
367 }
368 
369 static int
370 ums_attach(device_t dev)
371 {
372 	struct usb2_attach_arg *uaa = device_get_ivars(dev);
373 	struct ums_softc *sc = device_get_softc(dev);
374 	void *d_ptr = NULL;
375 	int unit = device_get_unit(dev);
376 	int isize;
377 	int isizebits;
378 	int err;
379 	uint32_t flags;
380 	uint16_t d_len;
381 	uint8_t i;
382 
383 	DPRINTFN(11, "sc=%p\n", sc);
384 
385 	device_set_usb2_desc(dev);
386 
387 	mtx_init(&sc->sc_mtx, "ums lock", NULL, MTX_DEF | MTX_RECURSE);
388 
389 	usb2_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
390 
391 	/*
392          * Force the report (non-boot) protocol.
393          *
394          * Mice without boot protocol support may choose not to implement
395          * Set_Protocol at all; Ignore any error.
396          */
397 	err = usb2_req_set_protocol(uaa->device, NULL, uaa->info.bIfaceIndex, 1);
398 
399 	err = usb2_transfer_setup(uaa->device,
400 	    &uaa->info.bIfaceIndex, sc->sc_xfer, ums_config,
401 	    UMS_N_TRANSFER, sc, &sc->sc_mtx);
402 
403 	if (err) {
404 		DPRINTF("error=%s\n", usb2_errstr(err));
405 		goto detach;
406 	}
407 	err = usb2_req_get_hid_desc
408 	    (uaa->device, &Giant, &d_ptr,
409 	    &d_len, M_TEMP, uaa->info.bIfaceIndex);
410 
411 	if (err) {
412 		device_printf(dev, "error reading report description\n");
413 		goto detach;
414 	}
415 	if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
416 	    hid_input, &sc->sc_loc_x, &flags, &sc->sc_iid)) {
417 
418 		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
419 			sc->sc_flags |= UMS_FLAG_X_AXIS;
420 		}
421 	}
422 	if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
423 	    hid_input, &sc->sc_loc_y, &flags, &sc->sc_iid)) {
424 
425 		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
426 			sc->sc_flags |= UMS_FLAG_Y_AXIS;
427 		}
428 	}
429 	/* Try the wheel first as the Z activator since it's tradition. */
430 	if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
431 	    HUG_WHEEL), hid_input, &sc->sc_loc_z, &flags, &sc->sc_iid) ||
432 	    hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
433 	    HUG_TWHEEL), hid_input, &sc->sc_loc_z, &flags, &sc->sc_iid)) {
434 		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
435 			sc->sc_flags |= UMS_FLAG_Z_AXIS;
436 		}
437 		/*
438 		 * We might have both a wheel and Z direction, if so put
439 		 * put the Z on the W coordinate.
440 		 */
441 		if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
442 		    HUG_Z), hid_input, &sc->sc_loc_w, &flags, &sc->sc_iid)) {
443 
444 			if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
445 				sc->sc_flags |= UMS_FLAG_W_AXIS;
446 			}
447 		}
448 	} else if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
449 	    HUG_Z), hid_input, &sc->sc_loc_z, &flags, &sc->sc_iid)) {
450 
451 		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
452 			sc->sc_flags |= UMS_FLAG_Z_AXIS;
453 		}
454 	}
455 	/*
456 	 * The Microsoft Wireless Intellimouse 2.0 reports it's wheel
457 	 * using 0x0048, which is HUG_TWHEEL, and seems to expect you
458 	 * to know that the byte after the wheel is the tilt axis.
459 	 * There are no other HID axis descriptors other than X,Y and
460 	 * TWHEEL
461 	 */
462 	if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL),
463 	    hid_input, &sc->sc_loc_t, &flags, &sc->sc_iid)) {
464 
465 		sc->sc_loc_t.pos += 8;
466 
467 		if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
468 			sc->sc_flags |= UMS_FLAG_T_AXIS;
469 		}
470 	}
471 	/* figure out the number of buttons */
472 
473 	for (i = 0; i < UMS_BUTTON_MAX; i++) {
474 		if (!hid_locate(d_ptr, d_len, HID_USAGE2(HUP_BUTTON, (i + 1)),
475 			hid_input, &sc->sc_loc_btn[i], NULL, &sc->sc_iid)) {
476 			break;
477 		}
478 	}
479 
480 	sc->sc_buttons = i;
481 
482 	isize = hid_report_size(d_ptr, d_len, hid_input, NULL);
483 
484 	/*
485 	 * The Microsoft Wireless Notebook Optical Mouse seems to be in worse
486 	 * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and
487 	 * all of its other button positions are all off. It also reports that
488 	 * it has two addional buttons and a tilt wheel.
489 	 */
490 	if (usb2_test_quirk(uaa, UQ_MS_BAD_CLASS)) {
491 		sc->sc_flags = (UMS_FLAG_X_AXIS |
492 		    UMS_FLAG_Y_AXIS |
493 		    UMS_FLAG_Z_AXIS |
494 		    UMS_FLAG_SBU);
495 		sc->sc_buttons = 3;
496 		isize = 5;
497 		sc->sc_iid = 0;
498 		/* 1st byte of descriptor report contains garbage */
499 		sc->sc_loc_x.pos = 16;
500 		sc->sc_loc_y.pos = 24;
501 		sc->sc_loc_z.pos = 32;
502 		sc->sc_loc_btn[0].pos = 8;
503 		sc->sc_loc_btn[1].pos = 9;
504 		sc->sc_loc_btn[2].pos = 10;
505 	}
506 
507 	/*
508 	 * Some Microsoft devices have incorrectly high location
509 	 * positions. Correct this:
510 	 */
511 	isizebits = isize * 8;
512 	if ((sc->sc_iid != 0) && (isizebits > 8)) {
513 		isizebits -= 8;	/* remove size of report ID */
514 		sc->sc_loc_w.pos %= isizebits;
515 		sc->sc_loc_x.pos %= isizebits;
516 		sc->sc_loc_y.pos %= isizebits;
517 		sc->sc_loc_z.pos %= isizebits;
518 		sc->sc_loc_t.pos %= isizebits;
519 		for (i = 0; i != UMS_BUTTON_MAX; i++)
520 			sc->sc_loc_btn[i].pos %= isizebits;
521 	}
522 
523 	if (usb2_test_quirk(uaa, UQ_MS_REVZ)) {
524 		/* Some wheels need the Z axis reversed. */
525 		sc->sc_flags |= UMS_FLAG_REVZ;
526 	}
527 	if (isize > sc->sc_xfer[UMS_INTR_DT]->max_frame_size) {
528 		DPRINTF("WARNING: report size, %d bytes, is larger "
529 		    "than interrupt size, %d bytes!\n",
530 		    isize, sc->sc_xfer[UMS_INTR_DT]->max_frame_size);
531 	}
532 	/* announce information about the mouse */
533 
534 	device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates\n",
535 	    (sc->sc_buttons),
536 	    (sc->sc_flags & UMS_FLAG_X_AXIS) ? "X" : "",
537 	    (sc->sc_flags & UMS_FLAG_Y_AXIS) ? "Y" : "",
538 	    (sc->sc_flags & UMS_FLAG_Z_AXIS) ? "Z" : "",
539 	    (sc->sc_flags & UMS_FLAG_T_AXIS) ? "T" : "",
540 	    (sc->sc_flags & UMS_FLAG_W_AXIS) ? "W" : "");
541 
542 	free(d_ptr, M_TEMP);
543 	d_ptr = NULL;
544 
545 #if USB_DEBUG
546 	DPRINTF("sc=%p\n", sc);
547 	DPRINTF("X\t%d/%d\n", sc->sc_loc_x.pos, sc->sc_loc_x.size);
548 	DPRINTF("Y\t%d/%d\n", sc->sc_loc_y.pos, sc->sc_loc_y.size);
549 	DPRINTF("Z\t%d/%d\n", sc->sc_loc_z.pos, sc->sc_loc_z.size);
550 	DPRINTF("T\t%d/%d\n", sc->sc_loc_t.pos, sc->sc_loc_t.size);
551 	DPRINTF("W\t%d/%d\n", sc->sc_loc_w.pos, sc->sc_loc_w.size);
552 
553 	for (i = 0; i < sc->sc_buttons; i++) {
554 		DPRINTF("B%d\t%d/%d\n",
555 		    i + 1, sc->sc_loc_btn[i].pos, sc->sc_loc_btn[i].size);
556 	}
557 	DPRINTF("size=%d, id=%d\n", isize, sc->sc_iid);
558 #endif
559 
560 	if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
561 		sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
562 	else
563 		sc->sc_hw.buttons = sc->sc_buttons;
564 
565 	sc->sc_hw.iftype = MOUSE_IF_USB;
566 	sc->sc_hw.type = MOUSE_MOUSE;
567 	sc->sc_hw.model = MOUSE_MODEL_GENERIC;
568 	sc->sc_hw.hwid = 0;
569 
570 	sc->sc_mode.protocol = MOUSE_PROTO_MSC;
571 	sc->sc_mode.rate = -1;
572 	sc->sc_mode.resolution = MOUSE_RES_UNKNOWN;
573 	sc->sc_mode.accelfactor = 0;
574 	sc->sc_mode.level = 0;
575 	sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
576 	sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
577 	sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
578 
579 	sc->sc_status.flags = 0;
580 	sc->sc_status.button = 0;
581 	sc->sc_status.obutton = 0;
582 	sc->sc_status.dx = 0;
583 	sc->sc_status.dy = 0;
584 	sc->sc_status.dz = 0;
585 
586 	/* set interface permissions */
587 	usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex,
588 	    UID_ROOT, GID_OPERATOR, 0644);
589 
590 	err = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
591 	    &ums_fifo_methods, &sc->sc_fifo,
592 	    unit, 0 - 1, uaa->info.bIfaceIndex);
593 	if (err) {
594 		goto detach;
595 	}
596 	return (0);
597 
598 detach:
599 	if (d_ptr) {
600 		free(d_ptr, M_TEMP);
601 	}
602 	ums_detach(dev);
603 	return (ENOMEM);
604 }
605 
606 static int
607 ums_detach(device_t self)
608 {
609 	struct ums_softc *sc = device_get_softc(self);
610 
611 	DPRINTF("sc=%p\n", sc);
612 
613 	usb2_fifo_detach(&sc->sc_fifo);
614 
615 	usb2_transfer_unsetup(sc->sc_xfer, UMS_N_TRANSFER);
616 
617 	usb2_callout_drain(&sc->sc_callout);
618 
619 	mtx_destroy(&sc->sc_mtx);
620 
621 	return (0);
622 }
623 
624 static void
625 ums_start_read(struct usb2_fifo *fifo)
626 {
627 	struct ums_softc *sc = fifo->priv_sc0;
628 
629 	usb2_transfer_start(sc->sc_xfer[UMS_INTR_DT]);
630 }
631 
632 static void
633 ums_stop_read(struct usb2_fifo *fifo)
634 {
635 	struct ums_softc *sc = fifo->priv_sc0;
636 
637 	usb2_transfer_stop(sc->sc_xfer[UMS_INTR_DT]);
638 	usb2_callout_stop(&sc->sc_callout);
639 }
640 
641 
642 #if ((MOUSE_SYS_PACKETSIZE != 8) || \
643      (MOUSE_MSC_PACKETSIZE != 5))
644 #error "Software assumptions are not met. Please update code."
645 #endif
646 
647 static void
648 ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy,
649     int32_t dz, int32_t dt, int32_t buttons)
650 {
651 	uint8_t buf[8];
652 
653 	if (1) {
654 
655 		if (dx > 254)
656 			dx = 254;
657 		if (dx < -256)
658 			dx = -256;
659 		if (dy > 254)
660 			dy = 254;
661 		if (dy < -256)
662 			dy = -256;
663 		if (dz > 126)
664 			dz = 126;
665 		if (dz < -128)
666 			dz = -128;
667 		if (dt > 126)
668 			dt = 126;
669 		if (dt < -128)
670 			dt = -128;
671 
672 		buf[0] = sc->sc_mode.syncmask[1];
673 		buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS;
674 		buf[1] = dx >> 1;
675 		buf[2] = dy >> 1;
676 		buf[3] = dx - (dx >> 1);
677 		buf[4] = dy - (dy >> 1);
678 
679 		if (sc->sc_mode.level == 1) {
680 			buf[5] = dz >> 1;
681 			buf[6] = dz - (dz >> 1);
682 			buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS);
683 		}
684 		usb2_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf,
685 		    sc->sc_mode.packetsize, 1);
686 
687 	} else {
688 		DPRINTF("Buffer full, discarded packet\n");
689 	}
690 }
691 
692 static void
693 ums_reset_buf(struct ums_softc *sc)
694 {
695 	/* reset read queue */
696 	usb2_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]);
697 }
698 
699 static int
700 ums_open(struct usb2_fifo *fifo, int fflags, struct thread *td)
701 {
702 	struct ums_softc *sc = fifo->priv_sc0;
703 
704 	DPRINTFN(2, "\n");
705 
706 	if (fflags & FREAD) {
707 
708 		/* reset status */
709 
710 		sc->sc_status.flags = 0;
711 		sc->sc_status.button = 0;
712 		sc->sc_status.obutton = 0;
713 		sc->sc_status.dx = 0;
714 		sc->sc_status.dy = 0;
715 		sc->sc_status.dz = 0;
716 		/* sc->sc_status.dt = 0; */
717 
718 		if (usb2_fifo_alloc_buffer(fifo,
719 		    UMS_BUF_SIZE, UMS_IFQ_MAXLEN)) {
720 			return (ENOMEM);
721 		}
722 	}
723 	return (0);
724 }
725 
726 static void
727 ums_close(struct usb2_fifo *fifo, int fflags, struct thread *td)
728 {
729 	if (fflags & FREAD) {
730 		usb2_fifo_free_buffer(fifo);
731 	}
732 }
733 
734 static int
735 ums_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr,
736     int fflags, struct thread *td)
737 {
738 	struct ums_softc *sc = fifo->priv_sc0;
739 	mousemode_t mode;
740 	int error = 0;
741 
742 	DPRINTFN(2, "\n");
743 
744 	mtx_lock(&sc->sc_mtx);
745 
746 	switch (cmd) {
747 	case MOUSE_GETHWINFO:
748 		*(mousehw_t *)addr = sc->sc_hw;
749 		break;
750 
751 	case MOUSE_GETMODE:
752 		*(mousemode_t *)addr = sc->sc_mode;
753 		break;
754 
755 	case MOUSE_SETMODE:
756 		mode = *(mousemode_t *)addr;
757 
758 		if (mode.level == -1) {
759 			/* don't change the current setting */
760 		} else if ((mode.level < 0) || (mode.level > 1)) {
761 			error = EINVAL;
762 			goto done;
763 		} else {
764 			sc->sc_mode.level = mode.level;
765 		}
766 
767 		if (sc->sc_mode.level == 0) {
768 			if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
769 				sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
770 			else
771 				sc->sc_hw.buttons = sc->sc_buttons;
772 			sc->sc_mode.protocol = MOUSE_PROTO_MSC;
773 			sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
774 			sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
775 			sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
776 		} else if (sc->sc_mode.level == 1) {
777 			if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
778 				sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
779 			else
780 				sc->sc_hw.buttons = sc->sc_buttons;
781 			sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
782 			sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
783 			sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
784 			sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
785 		}
786 		ums_reset_buf(sc);
787 		break;
788 
789 	case MOUSE_GETLEVEL:
790 		*(int *)addr = sc->sc_mode.level;
791 		break;
792 
793 	case MOUSE_SETLEVEL:
794 		if (*(int *)addr < 0 || *(int *)addr > 1) {
795 			error = EINVAL;
796 			goto done;
797 		}
798 		sc->sc_mode.level = *(int *)addr;
799 
800 		if (sc->sc_mode.level == 0) {
801 			if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
802 				sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
803 			else
804 				sc->sc_hw.buttons = sc->sc_buttons;
805 			sc->sc_mode.protocol = MOUSE_PROTO_MSC;
806 			sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
807 			sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
808 			sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
809 		} else if (sc->sc_mode.level == 1) {
810 			if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
811 				sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
812 			else
813 				sc->sc_hw.buttons = sc->sc_buttons;
814 			sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
815 			sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
816 			sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
817 			sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
818 		}
819 		ums_reset_buf(sc);
820 		break;
821 
822 	case MOUSE_GETSTATUS:{
823 			mousestatus_t *status = (mousestatus_t *)addr;
824 
825 			*status = sc->sc_status;
826 			sc->sc_status.obutton = sc->sc_status.button;
827 			sc->sc_status.button = 0;
828 			sc->sc_status.dx = 0;
829 			sc->sc_status.dy = 0;
830 			sc->sc_status.dz = 0;
831 			/* sc->sc_status.dt = 0; */
832 
833 			if (status->dx || status->dy || status->dz /* || status->dt */ ) {
834 				status->flags |= MOUSE_POSCHANGED;
835 			}
836 			if (status->button != status->obutton) {
837 				status->flags |= MOUSE_BUTTONSCHANGED;
838 			}
839 			break;
840 		}
841 	default:
842 		error = ENOTTY;
843 	}
844 
845 done:
846 	mtx_unlock(&sc->sc_mtx);
847 	return (error);
848 }
849 
850 static devclass_t ums_devclass;
851 
852 static device_method_t ums_methods[] = {
853 	DEVMETHOD(device_probe, ums_probe),
854 	DEVMETHOD(device_attach, ums_attach),
855 	DEVMETHOD(device_detach, ums_detach),
856 	{0, 0}
857 };
858 
859 static driver_t ums_driver = {
860 	.name = "ums",
861 	.methods = ums_methods,
862 	.size = sizeof(struct ums_softc),
863 };
864 
865 DRIVER_MODULE(ums, ushub, ums_driver, ums_devclass, NULL, 0);
866 MODULE_DEPEND(ums, usb, 1, 1, 1);
867