]> CyberLeo.Net >> Repos - FreeBSD/releng/8.0.git/blob - sys/dev/usb/input/ums.c
Adjust to reflect 8.0-RELEASE.
[FreeBSD/releng/8.0.git] / sys / dev / usb / input / ums.c
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 <sys/stdint.h>
46 #include <sys/stddef.h>
47 #include <sys/param.h>
48 #include <sys/queue.h>
49 #include <sys/types.h>
50 #include <sys/systm.h>
51 #include <sys/kernel.h>
52 #include <sys/bus.h>
53 #include <sys/linker_set.h>
54 #include <sys/module.h>
55 #include <sys/lock.h>
56 #include <sys/mutex.h>
57 #include <sys/condvar.h>
58 #include <sys/sysctl.h>
59 #include <sys/sx.h>
60 #include <sys/unistd.h>
61 #include <sys/callout.h>
62 #include <sys/malloc.h>
63 #include <sys/priv.h>
64 #include <sys/conf.h>
65 #include <sys/fcntl.h>
66
67 #include <dev/usb/usb.h>
68 #include <dev/usb/usbdi.h>
69 #include <dev/usb/usbdi_util.h>
70 #include <dev/usb/usbhid.h>
71 #include "usbdevs.h"
72
73 #define USB_DEBUG_VAR ums_debug
74 #include <dev/usb/usb_debug.h>
75
76 #include <dev/usb/quirk/usb_quirk.h>
77
78 #include <sys/ioccom.h>
79 #include <sys/filio.h>
80 #include <sys/tty.h>
81 #include <sys/mouse.h>
82
83 #if USB_DEBUG
84 static int ums_debug = 0;
85
86 SYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums");
87 SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW,
88     &ums_debug, 0, "Debug level");
89 #endif
90
91 #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
92 #define MOUSE_FLAGS (HIO_RELATIVE)
93
94 #define UMS_BUF_SIZE      8             /* bytes */
95 #define UMS_IFQ_MAXLEN   50             /* units */
96 #define UMS_BUTTON_MAX   31             /* exclusive, must be less than 32 */
97 #define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i))
98 #define UMS_INFO_MAX      2             /* maximum number of HID sets */
99
100 enum {
101         UMS_INTR_DT,
102         UMS_N_TRANSFER,
103 };
104
105 struct ums_info {
106         struct hid_location sc_loc_w;
107         struct hid_location sc_loc_x;
108         struct hid_location sc_loc_y;
109         struct hid_location sc_loc_z;
110         struct hid_location sc_loc_t;
111         struct hid_location sc_loc_btn[UMS_BUTTON_MAX];
112
113         uint32_t sc_flags;
114 #define UMS_FLAG_X_AXIS     0x0001
115 #define UMS_FLAG_Y_AXIS     0x0002
116 #define UMS_FLAG_Z_AXIS     0x0004
117 #define UMS_FLAG_T_AXIS     0x0008
118 #define UMS_FLAG_SBU        0x0010      /* spurious button up events */
119 #define UMS_FLAG_REVZ       0x0020      /* Z-axis is reversed */
120 #define UMS_FLAG_W_AXIS     0x0040
121
122         uint8_t sc_iid_w;
123         uint8_t sc_iid_x;
124         uint8_t sc_iid_y;
125         uint8_t sc_iid_z;
126         uint8_t sc_iid_t;
127         uint8_t sc_iid_btn[UMS_BUTTON_MAX];
128         uint8_t sc_buttons;
129 };
130
131 struct ums_softc {
132         struct usb_fifo_sc sc_fifo;
133         struct mtx sc_mtx;
134         struct usb_callout sc_callout;
135         struct ums_info sc_info[UMS_INFO_MAX];
136
137         mousehw_t sc_hw;
138         mousemode_t sc_mode;
139         mousestatus_t sc_status;
140
141         struct usb_xfer *sc_xfer[UMS_N_TRANSFER];
142
143         int sc_pollrate;
144
145         uint8_t sc_buttons;
146         uint8_t sc_iid;
147         uint8_t sc_temp[64];
148 };
149
150 static void ums_put_queue_timeout(void *__sc);
151
152 static usb_callback_t ums_intr_callback;
153
154 static device_probe_t ums_probe;
155 static device_attach_t ums_attach;
156 static device_detach_t ums_detach;
157
158 static usb_fifo_cmd_t ums_start_read;
159 static usb_fifo_cmd_t ums_stop_read;
160 static usb_fifo_open_t ums_open;
161 static usb_fifo_close_t ums_close;
162 static usb_fifo_ioctl_t ums_ioctl;
163
164 static void ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, int32_t dz, int32_t dt, int32_t buttons);
165
166 static struct usb_fifo_methods ums_fifo_methods = {
167         .f_open = &ums_open,
168         .f_close = &ums_close,
169         .f_ioctl = &ums_ioctl,
170         .f_start_read = &ums_start_read,
171         .f_stop_read = &ums_stop_read,
172         .basename[0] = "ums",
173 };
174
175 static void
176 ums_put_queue_timeout(void *__sc)
177 {
178         struct ums_softc *sc = __sc;
179
180         mtx_assert(&sc->sc_mtx, MA_OWNED);
181
182         ums_put_queue(sc, 0, 0, 0, 0, 0);
183 }
184
185 static void
186 ums_intr_callback(struct usb_xfer *xfer, usb_error_t error)
187 {
188         struct ums_softc *sc = usbd_xfer_softc(xfer);
189         struct ums_info *info = &sc->sc_info[0];
190         struct usb_page_cache *pc;
191         uint8_t *buf = sc->sc_temp;
192         int32_t buttons = 0;
193         int32_t buttons_found = 0;
194         int32_t dw = 0;
195         int32_t dx = 0;
196         int32_t dy = 0;
197         int32_t dz = 0;
198         int32_t dt = 0;
199         uint8_t i;
200         uint8_t id;
201         int len;
202
203         usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
204
205         switch (USB_GET_STATE(xfer)) {
206         case USB_ST_TRANSFERRED:
207                 DPRINTFN(6, "sc=%p actlen=%d\n", sc, len);
208
209                 if (len > sizeof(sc->sc_temp)) {
210                         DPRINTFN(6, "truncating large packet to %zu bytes\n",
211                             sizeof(sc->sc_temp));
212                         len = sizeof(sc->sc_temp);
213                 }
214                 if (len == 0)
215                         goto tr_setup;
216
217                 pc = usbd_xfer_get_frame(xfer, 0);
218                 usbd_copy_out(pc, 0, buf, len);
219
220                 DPRINTFN(6, "data = %02x %02x %02x %02x "
221                     "%02x %02x %02x %02x\n",
222                     (len > 0) ? buf[0] : 0, (len > 1) ? buf[1] : 0,
223                     (len > 2) ? buf[2] : 0, (len > 3) ? buf[3] : 0,
224                     (len > 4) ? buf[4] : 0, (len > 5) ? buf[5] : 0,
225                     (len > 6) ? buf[6] : 0, (len > 7) ? buf[7] : 0);
226
227                 if (sc->sc_iid) {
228                         id = *buf;
229
230                         len--;
231                         buf++;
232
233                 } else {
234                         id = 0;
235                         if (sc->sc_info[0].sc_flags & UMS_FLAG_SBU) {
236                                 if ((*buf == 0x14) || (*buf == 0x15)) {
237                                         goto tr_setup;
238                                 }
239                         }
240                 }
241
242         repeat:
243                 if ((info->sc_flags & UMS_FLAG_W_AXIS) &&
244                     (id == info->sc_iid_w))
245                         dw += hid_get_data(buf, len, &info->sc_loc_w);
246
247                 if ((info->sc_flags & UMS_FLAG_X_AXIS) && 
248                     (id == info->sc_iid_x))
249                         dx += hid_get_data(buf, len, &info->sc_loc_x);
250
251                 if ((info->sc_flags & UMS_FLAG_Y_AXIS) &&
252                     (id == info->sc_iid_y))
253                         dy = -hid_get_data(buf, len, &info->sc_loc_y);
254
255                 if ((info->sc_flags & UMS_FLAG_Z_AXIS) &&
256                     (id == info->sc_iid_z)) {
257                         int32_t temp;
258                         temp = hid_get_data(buf, len, &info->sc_loc_z);
259                         if (info->sc_flags & UMS_FLAG_REVZ)
260                                 temp = -temp;
261                         dz -= temp;
262                 }
263
264                 if ((info->sc_flags & UMS_FLAG_T_AXIS) &&
265                     (id == info->sc_iid_t))
266                         dt -= hid_get_data(buf, len, &info->sc_loc_t);
267
268                 for (i = 0; i < info->sc_buttons; i++) {
269                         uint32_t mask;
270                         mask = 1UL << UMS_BUT(i);
271                         /* check for correct button ID */
272                         if (id != info->sc_iid_btn[i])
273                                 continue;
274                         /* check for button pressed */
275                         if (hid_get_data(buf, len, &info->sc_loc_btn[i]))
276                                 buttons |= mask;
277                         /* register button mask */
278                         buttons_found |= mask;
279                 }
280
281                 if (++info != &sc->sc_info[UMS_INFO_MAX])
282                         goto repeat;
283
284                 /* keep old button value(s) for non-detected buttons */
285                 buttons |= sc->sc_status.button & ~buttons_found;
286
287                 if (dx || dy || dz || dt || dw ||
288                     (buttons != sc->sc_status.button)) {
289
290                         DPRINTFN(6, "x:%d y:%d z:%d t:%d w:%d buttons:0x%08x\n",
291                             dx, dy, dz, dt, dw, buttons);
292
293                         sc->sc_status.button = buttons;
294                         sc->sc_status.dx += dx;
295                         sc->sc_status.dy += dy;
296                         sc->sc_status.dz += dz;
297                         /*
298                          * sc->sc_status.dt += dt;
299                          * no way to export this yet
300                          */
301
302                         /*
303                          * The Qtronix keyboard has a built in PS/2
304                          * port for a mouse.  The firmware once in a
305                          * while posts a spurious button up
306                          * event. This event we ignore by doing a
307                          * timeout for 50 msecs.  If we receive
308                          * dx=dy=dz=buttons=0 before we add the event
309                          * to the queue.  In any other case we delete
310                          * the timeout event.
311                          */
312                         if ((sc->sc_info[0].sc_flags & UMS_FLAG_SBU) &&
313                             (dx == 0) && (dy == 0) && (dz == 0) && (dt == 0) &&
314                             (dw == 0) && (buttons == 0)) {
315
316                                 usb_callout_reset(&sc->sc_callout, hz / 20,
317                                     &ums_put_queue_timeout, sc);
318                         } else {
319
320                                 usb_callout_stop(&sc->sc_callout);
321
322                                 ums_put_queue(sc, dx, dy, dz, dt, buttons);
323                         }
324                 }
325         case USB_ST_SETUP:
326 tr_setup:
327                 /* check if we can put more data into the FIFO */
328                 if (usb_fifo_put_bytes_max(
329                     sc->sc_fifo.fp[USB_FIFO_RX]) != 0) {
330                         usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
331                         usbd_transfer_submit(xfer);
332                 }
333                 break;
334
335         default:                        /* Error */
336                 if (error != USB_ERR_CANCELLED) {
337                         /* try clear stall first */
338                         usbd_xfer_set_stall(xfer);
339                         goto tr_setup;
340                 }
341                 break;
342         }
343 }
344
345 static const struct usb_config ums_config[UMS_N_TRANSFER] = {
346
347         [UMS_INTR_DT] = {
348                 .type = UE_INTERRUPT,
349                 .endpoint = UE_ADDR_ANY,
350                 .direction = UE_DIR_IN,
351                 .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
352                 .bufsize = 0,   /* use wMaxPacketSize */
353                 .callback = &ums_intr_callback,
354         },
355 };
356
357 static int
358 ums_probe(device_t dev)
359 {
360         struct usb_attach_arg *uaa = device_get_ivars(dev);
361         void *d_ptr;
362         int error;
363         uint16_t d_len;
364
365         DPRINTFN(11, "\n");
366
367         if (uaa->usb_mode != USB_MODE_HOST)
368                 return (ENXIO);
369
370         if (uaa->info.bInterfaceClass != UICLASS_HID)
371                 return (ENXIO);
372
373         if ((uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT) &&
374             (uaa->info.bInterfaceProtocol == UIPROTO_MOUSE))
375                 return (0);
376
377         error = usbd_req_get_hid_desc(uaa->device, NULL,
378             &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex);
379
380         if (error)
381                 return (ENXIO);
382
383         if (hid_is_collection(d_ptr, d_len,
384             HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
385                 error = 0;
386         else
387                 error = ENXIO;
388
389         free(d_ptr, M_TEMP);
390         return (error);
391 }
392
393 static void
394 ums_hid_parse(struct ums_softc *sc, device_t dev, const uint8_t *buf,
395     uint16_t len, uint8_t index)
396 {
397         struct ums_info *info = &sc->sc_info[index];
398         uint32_t flags;
399         uint8_t i;
400
401         if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
402             hid_input, index, &info->sc_loc_x, &flags, &info->sc_iid_x)) {
403
404                 if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
405                         info->sc_flags |= UMS_FLAG_X_AXIS;
406                 }
407         }
408         if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
409             hid_input, index, &info->sc_loc_y, &flags, &info->sc_iid_y)) {
410
411                 if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
412                         info->sc_flags |= UMS_FLAG_Y_AXIS;
413                 }
414         }
415         /* Try the wheel first as the Z activator since it's tradition. */
416         if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
417             HUG_WHEEL), hid_input, index, &info->sc_loc_z, &flags,
418             &info->sc_iid_z) ||
419             hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
420             HUG_TWHEEL), hid_input, index, &info->sc_loc_z, &flags,
421             &info->sc_iid_z)) {
422                 if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
423                         info->sc_flags |= UMS_FLAG_Z_AXIS;
424                 }
425                 /*
426                  * We might have both a wheel and Z direction, if so put
427                  * put the Z on the W coordinate.
428                  */
429                 if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
430                     HUG_Z), hid_input, index, &info->sc_loc_w, &flags,
431                     &info->sc_iid_w)) {
432
433                         if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
434                                 info->sc_flags |= UMS_FLAG_W_AXIS;
435                         }
436                 }
437         } else if (hid_locate(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
438             HUG_Z), hid_input, index, &info->sc_loc_z, &flags, 
439             &info->sc_iid_z)) {
440
441                 if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
442                         info->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(buf, len, HID_USAGE2(HUP_GENERIC_DESKTOP,
453             HUG_TWHEEL), hid_input, index, &info->sc_loc_t, 
454             &flags, &info->sc_iid_t)) {
455
456                 info->sc_loc_t.pos += 8;
457
458                 if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
459                         info->sc_flags |= UMS_FLAG_T_AXIS;
460                 }
461         }
462         /* figure out the number of buttons */
463
464         for (i = 0; i < UMS_BUTTON_MAX; i++) {
465                 if (!hid_locate(buf, len, HID_USAGE2(HUP_BUTTON, (i + 1)),
466                     hid_input, index, &info->sc_loc_btn[i], NULL, 
467                     &info->sc_iid_btn[i])) {
468                         break;
469                 }
470         }
471         info->sc_buttons = i;
472
473         if (i > sc->sc_buttons)
474                 sc->sc_buttons = i;
475
476         if (info->sc_flags == 0)
477                 return;
478
479         /* announce information about the mouse */
480         device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates ID=%u\n",
481             (info->sc_buttons),
482             (info->sc_flags & UMS_FLAG_X_AXIS) ? "X" : "",
483             (info->sc_flags & UMS_FLAG_Y_AXIS) ? "Y" : "",
484             (info->sc_flags & UMS_FLAG_Z_AXIS) ? "Z" : "",
485             (info->sc_flags & UMS_FLAG_T_AXIS) ? "T" : "",
486             (info->sc_flags & UMS_FLAG_W_AXIS) ? "W" : "",
487             info->sc_iid_x);
488 }
489
490 static int
491 ums_attach(device_t dev)
492 {
493         struct usb_attach_arg *uaa = device_get_ivars(dev);
494         struct ums_softc *sc = device_get_softc(dev);
495         struct ums_info *info;
496         void *d_ptr = NULL;
497         int isize;
498         int err;
499         uint16_t d_len;
500         uint8_t i;
501         uint8_t j;
502
503         DPRINTFN(11, "sc=%p\n", sc);
504
505         device_set_usb_desc(dev);
506
507         mtx_init(&sc->sc_mtx, "ums lock", NULL, MTX_DEF | MTX_RECURSE);
508
509         usb_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
510
511         /*
512          * Force the report (non-boot) protocol.
513          *
514          * Mice without boot protocol support may choose not to implement
515          * Set_Protocol at all; Ignore any error.
516          */
517         err = usbd_req_set_protocol(uaa->device, NULL,
518             uaa->info.bIfaceIndex, 1);
519
520         err = usbd_transfer_setup(uaa->device,
521             &uaa->info.bIfaceIndex, sc->sc_xfer, ums_config,
522             UMS_N_TRANSFER, sc, &sc->sc_mtx);
523
524         if (err) {
525                 DPRINTF("error=%s\n", usbd_errstr(err));
526                 goto detach;
527         }
528
529         /* Get HID descriptor */
530         err = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr,
531             &d_len, M_TEMP, uaa->info.bIfaceIndex);
532
533         if (err) {
534                 device_printf(dev, "error reading report description\n");
535                 goto detach;
536         }
537
538         isize = hid_report_size(d_ptr, d_len, hid_input, &sc->sc_iid);
539
540         /*
541          * The Microsoft Wireless Notebook Optical Mouse seems to be in worse
542          * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and
543          * all of its other button positions are all off. It also reports that
544          * it has two addional buttons and a tilt wheel.
545          */
546         if (usb_test_quirk(uaa, UQ_MS_BAD_CLASS)) {
547
548                 sc->sc_iid = 0;
549
550                 info = &sc->sc_info[0];
551                 info->sc_flags = (UMS_FLAG_X_AXIS |
552                     UMS_FLAG_Y_AXIS |
553                     UMS_FLAG_Z_AXIS |
554                     UMS_FLAG_SBU);
555                 info->sc_buttons = 3;
556                 isize = 5;
557                 /* 1st byte of descriptor report contains garbage */
558                 info->sc_loc_x.pos = 16;
559                 info->sc_loc_x.size = 8;
560                 info->sc_loc_y.pos = 24;
561                 info->sc_loc_y.size = 8;
562                 info->sc_loc_z.pos = 32;
563                 info->sc_loc_z.size = 8;
564                 info->sc_loc_btn[0].pos = 8;
565                 info->sc_loc_btn[0].size = 1;
566                 info->sc_loc_btn[1].pos = 9;
567                 info->sc_loc_btn[1].size = 1;
568                 info->sc_loc_btn[2].pos = 10;
569                 info->sc_loc_btn[2].size = 1;
570
571                 /* Announce device */
572                 device_printf(dev, "3 buttons and [XYZ] "
573                     "coordinates ID=0\n");
574
575         } else {
576                 /* Search the HID descriptor and announce device */
577                 for (i = 0; i < UMS_INFO_MAX; i++) {
578                         ums_hid_parse(sc, dev, d_ptr, d_len, i);
579                 }
580         }
581
582         if (usb_test_quirk(uaa, UQ_MS_REVZ)) {
583                 info = &sc->sc_info[0];
584                 /* Some wheels need the Z axis reversed. */
585                 info->sc_flags |= UMS_FLAG_REVZ;
586         }
587         if (isize > usbd_xfer_max_framelen(sc->sc_xfer[UMS_INTR_DT])) {
588                 DPRINTF("WARNING: report size, %d bytes, is larger "
589                     "than interrupt size, %d bytes!\n", isize,
590                     usbd_xfer_max_framelen(sc->sc_xfer[UMS_INTR_DT]));
591         }
592         free(d_ptr, M_TEMP);
593         d_ptr = NULL;
594
595 #if USB_DEBUG
596         for (j = 0; j < UMS_INFO_MAX; j++) {
597                 info = &sc->sc_info[j];
598
599                 DPRINTF("sc=%p, index=%d\n", sc, j);
600                 DPRINTF("X\t%d/%d id=%d\n", info->sc_loc_x.pos,
601                     info->sc_loc_x.size, info->sc_iid_x);
602                 DPRINTF("Y\t%d/%d id=%d\n", info->sc_loc_y.pos,
603                     info->sc_loc_y.size, info->sc_iid_y);
604                 DPRINTF("Z\t%d/%d id=%d\n", info->sc_loc_z.pos,
605                     info->sc_loc_z.size, info->sc_iid_z);
606                 DPRINTF("T\t%d/%d id=%d\n", info->sc_loc_t.pos,
607                     info->sc_loc_t.size, info->sc_iid_t);
608                 DPRINTF("W\t%d/%d id=%d\n", info->sc_loc_w.pos,
609                     info->sc_loc_w.size, info->sc_iid_w);
610
611                 for (i = 0; i < info->sc_buttons; i++) {
612                         DPRINTF("B%d\t%d/%d id=%d\n",
613                             i + 1, info->sc_loc_btn[i].pos,
614                             info->sc_loc_btn[i].size, info->sc_iid_btn[i]);
615                 }
616         }
617         DPRINTF("size=%d, id=%d\n", isize, sc->sc_iid);
618 #endif
619
620         if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
621                 sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
622         else
623                 sc->sc_hw.buttons = sc->sc_buttons;
624
625         sc->sc_hw.iftype = MOUSE_IF_USB;
626         sc->sc_hw.type = MOUSE_MOUSE;
627         sc->sc_hw.model = MOUSE_MODEL_GENERIC;
628         sc->sc_hw.hwid = 0;
629
630         sc->sc_mode.protocol = MOUSE_PROTO_MSC;
631         sc->sc_mode.rate = -1;
632         sc->sc_mode.resolution = MOUSE_RES_UNKNOWN;
633         sc->sc_mode.accelfactor = 0;
634         sc->sc_mode.level = 0;
635         sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
636         sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
637         sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
638
639         err = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx,
640             &ums_fifo_methods, &sc->sc_fifo,
641             device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex,
642             UID_ROOT, GID_OPERATOR, 0644);
643         if (err) {
644                 goto detach;
645         }
646         return (0);
647
648 detach:
649         if (d_ptr) {
650                 free(d_ptr, M_TEMP);
651         }
652         ums_detach(dev);
653         return (ENOMEM);
654 }
655
656 static int
657 ums_detach(device_t self)
658 {
659         struct ums_softc *sc = device_get_softc(self);
660
661         DPRINTF("sc=%p\n", sc);
662
663         usb_fifo_detach(&sc->sc_fifo);
664
665         usbd_transfer_unsetup(sc->sc_xfer, UMS_N_TRANSFER);
666
667         usb_callout_drain(&sc->sc_callout);
668
669         mtx_destroy(&sc->sc_mtx);
670
671         return (0);
672 }
673
674 static void
675 ums_start_read(struct usb_fifo *fifo)
676 {
677         struct ums_softc *sc = usb_fifo_softc(fifo);
678         int rate;
679
680         /* Check if we should override the default polling interval */
681         rate = sc->sc_pollrate;
682         /* Range check rate */
683         if (rate > 1000)
684                 rate = 1000;
685         /* Check for set rate */
686         if ((rate > 0) && (sc->sc_xfer[UMS_INTR_DT] != NULL)) {
687                 DPRINTF("Setting pollrate = %d\n", rate);
688                 /* Stop current transfer, if any */
689                 usbd_transfer_stop(sc->sc_xfer[UMS_INTR_DT]);
690                 /* Set new interval */
691                 usbd_xfer_set_interval(sc->sc_xfer[UMS_INTR_DT], 1000 / rate);
692                 /* Only set pollrate once */
693                 sc->sc_pollrate = 0;
694         }
695
696         usbd_transfer_start(sc->sc_xfer[UMS_INTR_DT]);
697 }
698
699 static void
700 ums_stop_read(struct usb_fifo *fifo)
701 {
702         struct ums_softc *sc = usb_fifo_softc(fifo);
703
704         usbd_transfer_stop(sc->sc_xfer[UMS_INTR_DT]);
705         usb_callout_stop(&sc->sc_callout);
706 }
707
708
709 #if ((MOUSE_SYS_PACKETSIZE != 8) || \
710      (MOUSE_MSC_PACKETSIZE != 5))
711 #error "Software assumptions are not met. Please update code."
712 #endif
713
714 static void
715 ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy,
716     int32_t dz, int32_t dt, int32_t buttons)
717 {
718         uint8_t buf[8];
719
720         if (1) {
721
722                 if (dx > 254)
723                         dx = 254;
724                 if (dx < -256)
725                         dx = -256;
726                 if (dy > 254)
727                         dy = 254;
728                 if (dy < -256)
729                         dy = -256;
730                 if (dz > 126)
731                         dz = 126;
732                 if (dz < -128)
733                         dz = -128;
734                 if (dt > 126)
735                         dt = 126;
736                 if (dt < -128)
737                         dt = -128;
738
739                 buf[0] = sc->sc_mode.syncmask[1];
740                 buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS;
741                 buf[1] = dx >> 1;
742                 buf[2] = dy >> 1;
743                 buf[3] = dx - (dx >> 1);
744                 buf[4] = dy - (dy >> 1);
745
746                 if (sc->sc_mode.level == 1) {
747                         buf[5] = dz >> 1;
748                         buf[6] = dz - (dz >> 1);
749                         buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS);
750                 }
751                 usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf,
752                     sc->sc_mode.packetsize, 1);
753
754         } else {
755                 DPRINTF("Buffer full, discarded packet\n");
756         }
757 }
758
759 static void
760 ums_reset_buf(struct ums_softc *sc)
761 {
762         /* reset read queue */
763         usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]);
764 }
765
766 static int
767 ums_open(struct usb_fifo *fifo, int fflags)
768 {
769         struct ums_softc *sc = usb_fifo_softc(fifo);
770
771         DPRINTFN(2, "\n");
772
773         if (fflags & FREAD) {
774
775                 /* reset status */
776
777                 sc->sc_status.flags = 0;
778                 sc->sc_status.button = 0;
779                 sc->sc_status.obutton = 0;
780                 sc->sc_status.dx = 0;
781                 sc->sc_status.dy = 0;
782                 sc->sc_status.dz = 0;
783                 /* sc->sc_status.dt = 0; */
784
785                 if (usb_fifo_alloc_buffer(fifo,
786                     UMS_BUF_SIZE, UMS_IFQ_MAXLEN)) {
787                         return (ENOMEM);
788                 }
789         }
790         return (0);
791 }
792
793 static void
794 ums_close(struct usb_fifo *fifo, int fflags)
795 {
796         if (fflags & FREAD) {
797                 usb_fifo_free_buffer(fifo);
798         }
799 }
800
801 static int
802 ums_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
803 {
804         struct ums_softc *sc = usb_fifo_softc(fifo);
805         mousemode_t mode;
806         int error = 0;
807
808         DPRINTFN(2, "\n");
809
810         mtx_lock(&sc->sc_mtx);
811
812         switch (cmd) {
813         case MOUSE_GETHWINFO:
814                 *(mousehw_t *)addr = sc->sc_hw;
815                 break;
816
817         case MOUSE_GETMODE:
818                 *(mousemode_t *)addr = sc->sc_mode;
819                 break;
820
821         case MOUSE_SETMODE:
822                 mode = *(mousemode_t *)addr;
823
824                 if (mode.level == -1) {
825                         /* don't change the current setting */
826                 } else if ((mode.level < 0) || (mode.level > 1)) {
827                         error = EINVAL;
828                         goto done;
829                 } else {
830                         sc->sc_mode.level = mode.level;
831                 }
832
833                 /* store polling rate */
834                 sc->sc_pollrate = mode.rate;
835
836                 if (sc->sc_mode.level == 0) {
837                         if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
838                                 sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
839                         else
840                                 sc->sc_hw.buttons = sc->sc_buttons;
841                         sc->sc_mode.protocol = MOUSE_PROTO_MSC;
842                         sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
843                         sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
844                         sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
845                 } else if (sc->sc_mode.level == 1) {
846                         if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
847                                 sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
848                         else
849                                 sc->sc_hw.buttons = sc->sc_buttons;
850                         sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
851                         sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
852                         sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
853                         sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
854                 }
855                 ums_reset_buf(sc);
856                 break;
857
858         case MOUSE_GETLEVEL:
859                 *(int *)addr = sc->sc_mode.level;
860                 break;
861
862         case MOUSE_SETLEVEL:
863                 if (*(int *)addr < 0 || *(int *)addr > 1) {
864                         error = EINVAL;
865                         goto done;
866                 }
867                 sc->sc_mode.level = *(int *)addr;
868
869                 if (sc->sc_mode.level == 0) {
870                         if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
871                                 sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
872                         else
873                                 sc->sc_hw.buttons = sc->sc_buttons;
874                         sc->sc_mode.protocol = MOUSE_PROTO_MSC;
875                         sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
876                         sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
877                         sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
878                 } else if (sc->sc_mode.level == 1) {
879                         if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
880                                 sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
881                         else
882                                 sc->sc_hw.buttons = sc->sc_buttons;
883                         sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
884                         sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
885                         sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
886                         sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
887                 }
888                 ums_reset_buf(sc);
889                 break;
890
891         case MOUSE_GETSTATUS:{
892                         mousestatus_t *status = (mousestatus_t *)addr;
893
894                         *status = sc->sc_status;
895                         sc->sc_status.obutton = sc->sc_status.button;
896                         sc->sc_status.button = 0;
897                         sc->sc_status.dx = 0;
898                         sc->sc_status.dy = 0;
899                         sc->sc_status.dz = 0;
900                         /* sc->sc_status.dt = 0; */
901
902                         if (status->dx || status->dy || status->dz /* || status->dt */ ) {
903                                 status->flags |= MOUSE_POSCHANGED;
904                         }
905                         if (status->button != status->obutton) {
906                                 status->flags |= MOUSE_BUTTONSCHANGED;
907                         }
908                         break;
909                 }
910         default:
911                 error = ENOTTY;
912         }
913
914 done:
915         mtx_unlock(&sc->sc_mtx);
916         return (error);
917 }
918
919 static devclass_t ums_devclass;
920
921 static device_method_t ums_methods[] = {
922         DEVMETHOD(device_probe, ums_probe),
923         DEVMETHOD(device_attach, ums_attach),
924         DEVMETHOD(device_detach, ums_detach),
925         {0, 0}
926 };
927
928 static driver_t ums_driver = {
929         .name = "ums",
930         .methods = ums_methods,
931         .size = sizeof(struct ums_softc),
932 };
933
934 DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, NULL, 0);
935 MODULE_DEPEND(ums, usb, 1, 1, 1);