]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/usb/ums.c
Add support for the Apple Big Mac (BMAC) Ethernet controller,
[FreeBSD/FreeBSD.git] / sys / dev / usb / 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/param.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/malloc.h>
49 #include <sys/module.h>
50 #include <sys/bus.h>
51 #include <sys/ioccom.h>
52 #include <sys/conf.h>
53 #include <sys/fcntl.h>
54 #include <sys/tty.h>
55 #include <sys/file.h>
56 #include <sys/selinfo.h>
57 #include <sys/poll.h>
58 #include <sys/sysctl.h>
59 #include <sys/uio.h>
60
61 #include <dev/usb/usb.h>
62 #include <dev/usb/usbhid.h>
63
64 #include <dev/usb/usbdi.h>
65 #include <dev/usb/usbdi_util.h>
66 #include "usbdevs.h"
67 #include <dev/usb/usb_quirks.h>
68 #include <dev/usb/hid.h>
69
70 #include <sys/mouse.h>
71
72 #ifdef USB_DEBUG
73 #define DPRINTF(x)      if (umsdebug) printf x
74 #define DPRINTFN(n,x)   if (umsdebug>(n)) printf x
75 int     umsdebug = 0;
76 SYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums");
77 SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW,
78            &umsdebug, 0, "ums debug level");
79 #else
80 #define DPRINTF(x)
81 #define DPRINTFN(n,x)
82 #endif
83
84 #define UMSUNIT(s)      (minor(s)&0x1f)
85
86 #define MS_TO_TICKS(ms) ((ms) * hz / 1000)
87
88 #define QUEUE_BUFSIZE   400     /* MUST be divisible by 5 _and_ 8 */
89
90 struct ums_softc {
91         device_t sc_dev;                /* base device */
92         usbd_interface_handle sc_iface; /* interface */
93         usbd_pipe_handle sc_intrpipe;   /* interrupt pipe */
94         int sc_ep_addr;
95
96         u_char *sc_ibuf;
97         u_int8_t sc_iid;
98         int sc_isize;
99         struct hid_location sc_loc_x, sc_loc_y, sc_loc_z, sc_loc_t, sc_loc_w;
100         struct hid_location *sc_loc_btn;
101
102         struct callout callout_handle;  /* for spurious button ups */
103
104         int sc_enabled;
105         int sc_disconnected;    /* device is gone */
106
107         int flags;              /* device configuration */
108 #define UMS_Z           0x01    /* z direction available */
109 #define UMS_SPUR_BUT_UP 0x02    /* spurious button up events */
110 #define UMS_T           0x04    /* aa direction available (tilt) */
111 #define UMS_REVZ        0x08    /* Z-axis is reversed */
112         int nbuttons;
113 #define MAX_BUTTONS     31      /* chosen because sc_buttons is int */
114
115         u_char          qbuf[QUEUE_BUFSIZE];    /* must be divisable by 3&4 */
116         u_char          dummy[100];     /* XXX just for safety and for now */
117         int             qcount, qhead, qtail;
118         mousehw_t       hw;
119         mousemode_t     mode;
120         mousestatus_t   status;
121
122         int             state;
123 #         define        UMS_ASLEEP      0x01    /* readFromDevice is waiting */
124 #         define        UMS_SELECT      0x02    /* select is waiting */
125         struct selinfo  rsel;           /* process waiting in select */
126
127         struct cdev *dev;               /* specfs */
128 };
129
130 #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
131 #define MOUSE_FLAGS (HIO_RELATIVE)
132
133 static void ums_intr(usbd_xfer_handle xfer,
134                           usbd_private_handle priv, usbd_status status);
135
136 static void ums_add_to_queue(struct ums_softc *sc,
137                                 int dx, int dy, int dz, int dt, int buttons);
138 static void ums_add_to_queue_timeout(void *priv);
139
140 static int  ums_enable(void *);
141 static void ums_disable(void *);
142
143 static d_open_t  ums_open;
144 static d_close_t ums_close;
145 static d_read_t  ums_read;
146 static d_ioctl_t ums_ioctl;
147 static d_poll_t  ums_poll;
148
149
150 static struct cdevsw ums_cdevsw = {
151         .d_version =    D_VERSION,
152         .d_flags =      D_NEEDGIANT,
153         .d_open =       ums_open,
154         .d_close =      ums_close,
155         .d_read =       ums_read,
156         .d_ioctl =      ums_ioctl,
157         .d_poll =       ums_poll,
158         .d_name =       "ums",
159 };
160
161 static device_probe_t ums_match;
162 static device_attach_t ums_attach;
163 static device_detach_t ums_detach;
164
165 static device_method_t ums_methods[] = {
166         /* Device interface */
167         DEVMETHOD(device_probe,         ums_match),
168         DEVMETHOD(device_attach,        ums_attach),
169         DEVMETHOD(device_detach,        ums_detach),
170
171         { 0, 0 }
172 };
173
174 static driver_t ums_driver = {
175         "ums",
176         ums_methods,
177         sizeof(struct ums_softc)
178 };
179
180 static devclass_t ums_devclass;
181
182 static int
183 ums_match(device_t self)
184 {
185         struct usb_attach_arg *uaa = device_get_ivars(self);
186         usb_interface_descriptor_t *id;
187         int size, ret;
188         void *desc;
189         usbd_status err;
190
191         if (!uaa->iface)
192                 return (UMATCH_NONE);
193         id = usbd_get_interface_descriptor(uaa->iface);
194         if (!id || id->bInterfaceClass != UICLASS_HID)
195                 return (UMATCH_NONE);
196
197         err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP);
198         if (err)
199                 return (UMATCH_NONE);
200
201         if (hid_is_collection(desc, size,
202                               HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
203                 ret = UMATCH_IFACECLASS;
204         else if (id->bInterfaceClass == UICLASS_HID &&
205             id->bInterfaceSubClass == UISUBCLASS_BOOT &&
206             id->bInterfaceProtocol == UIPROTO_MOUSE)
207                 ret = UMATCH_IFACECLASS;
208         else
209                 ret = UMATCH_NONE;
210
211         free(desc, M_TEMP);
212         return (ret);
213 }
214
215 static int
216 ums_attach(device_t self)
217 {
218         struct ums_softc *sc = device_get_softc(self);
219         struct usb_attach_arg *uaa = device_get_ivars(self);
220         usbd_interface_handle iface = uaa->iface;
221         usb_interface_descriptor_t *id;
222         usb_endpoint_descriptor_t *ed;
223         int size;
224         void *desc;
225         usbd_status err;
226         u_int32_t flags;
227         int i, wheel;
228         struct hid_location loc_btn;
229
230         sc->sc_disconnected = 1;
231         sc->sc_iface = iface;
232         id = usbd_get_interface_descriptor(iface);
233         sc->sc_dev = self;
234         ed = usbd_interface2endpoint_descriptor(iface, 0);
235         if (!ed) {
236                 printf("%s: could not read endpoint descriptor\n",
237                        device_get_nameunit(sc->sc_dev));
238                 return ENXIO;
239         }
240
241         DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d "
242                      "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
243                      " bInterval=%d\n",
244                      ed->bLength, ed->bDescriptorType,
245                      UE_GET_ADDR(ed->bEndpointAddress),
246                      UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in":"out",
247                      UE_GET_XFERTYPE(ed->bmAttributes),
248                      UGETW(ed->wMaxPacketSize), ed->bInterval));
249
250         if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN ||
251             UE_GET_XFERTYPE(ed->bmAttributes) != UE_INTERRUPT) {
252                 printf("%s: unexpected endpoint\n",
253                        device_get_nameunit(sc->sc_dev));
254                 return ENXIO;
255         }
256
257         err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP);
258         if (err)
259                 return ENXIO;
260
261         if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
262                        hid_input, &sc->sc_loc_x, &flags)) {
263                 printf("%s: mouse has no X report\n", device_get_nameunit(sc->sc_dev));
264                 return ENXIO;
265         }
266         if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
267                 printf("%s: X report 0x%04x not supported\n",
268                        device_get_nameunit(sc->sc_dev), flags);
269                 return ENXIO;
270         }
271
272         if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
273                        hid_input, &sc->sc_loc_y, &flags)) {
274                 printf("%s: mouse has no Y report\n", device_get_nameunit(sc->sc_dev));
275                 return ENXIO;
276         }
277         if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
278                 printf("%s: Y report 0x%04x not supported\n",
279                        device_get_nameunit(sc->sc_dev), flags);
280                 return ENXIO;
281         }
282
283         /* Try the wheel first as the Z activator since it's tradition. */
284         wheel = hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP,
285                                                   HUG_WHEEL),
286                             hid_input, &sc->sc_loc_z, &flags);
287
288         if (wheel) {
289                 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
290                         printf("\n%s: Wheel report 0x%04x not supported\n",
291                                device_get_nameunit(sc->sc_dev), flags);
292                         sc->sc_loc_z.size = 0;  /* Bad Z coord, ignore it */
293                 } else {
294                         sc->flags |= UMS_Z;
295                         if (usbd_get_quirks(uaa->device)->uq_flags &
296                             UQ_MS_REVZ) {
297                                 /* Some wheels need the Z axis reversed. */
298                                 sc->flags |= UMS_REVZ;
299                         }
300
301                 }
302                 /*
303                  * We might have both a wheel and Z direction, if so put
304                  * put the Z on the W coordinate.
305                  */
306                 if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP,
307                                                       HUG_Z),
308                                 hid_input, &sc->sc_loc_w, &flags)) {
309                         if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
310                                 printf("\n%s: Z report 0x%04x not supported\n",
311                                        device_get_nameunit(sc->sc_dev), flags);
312                                 sc->sc_loc_w.size = 0;  /* Bad Z, ignore */
313                         }
314                 }
315         } else if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP,
316                                                      HUG_Z),
317                                hid_input, &sc->sc_loc_z, &flags)) {
318                 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
319                         printf("\n%s: Z report 0x%04x not supported\n",
320                                device_get_nameunit(sc->sc_dev), flags);
321                         sc->sc_loc_z.size = 0;  /* Bad Z coord, ignore it */
322                 } else {
323                         sc->flags |= UMS_Z;
324                 }
325         }
326
327         /*
328          * The Microsoft Wireless Intellimouse 2.0 reports it's wheel
329          * using 0x0048 (i've called it HUG_TWHEEL) and seems to expect
330          * you to know that the byte after the wheel is the tilt axis.
331          * There are no other HID axis descriptors other than X,Y and 
332          * TWHEEL
333          */
334         if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL),
335                         hid_input, &sc->sc_loc_t, &flags)) {
336                         sc->sc_loc_t.pos = sc->sc_loc_t.pos + 8;
337                         sc->flags |= UMS_T;
338         }
339
340         /* figure out the number of buttons */
341         for (i = 1; i <= MAX_BUTTONS; i++)
342                 if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
343                                 hid_input, &loc_btn, 0))
344                         break;
345         sc->nbuttons = i - 1;
346         sc->sc_loc_btn = malloc(sizeof(struct hid_location)*sc->nbuttons,
347                                 M_USBDEV, M_NOWAIT);
348         if (!sc->sc_loc_btn) {
349                 printf("%s: no memory\n", device_get_nameunit(sc->sc_dev));
350                 return ENXIO;
351         }
352
353         printf("%s: %d buttons%s%s.\n", device_get_nameunit(sc->sc_dev),
354                sc->nbuttons, sc->flags & UMS_Z? " and Z dir" : "", 
355                sc->flags & UMS_T?" and a TILT dir": "");
356
357         for (i = 1; i <= sc->nbuttons; i++)
358                 hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
359                                 hid_input, &sc->sc_loc_btn[i-1], 0);
360
361         sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid);
362         sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_NOWAIT);
363         if (!sc->sc_ibuf) {
364                 printf("%s: no memory\n", device_get_nameunit(sc->sc_dev));
365                 free(sc->sc_loc_btn, M_USB);
366                 return ENXIO;
367         }
368
369         /*
370          * The Microsoft Wireless Notebook Optical Mouse seems to be in worse
371          * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and
372          * all of its other button positions are all off. It also reports that
373          * it has two addional buttons and a tilt wheel.
374          */
375         if (usbd_get_quirks(uaa->device)->uq_flags & UQ_MS_BAD_CLASS) {
376                 sc->flags = UMS_Z;
377                 sc->flags |= UMS_SPUR_BUT_UP;
378                 sc->nbuttons = 3;
379                 sc->sc_isize = 5;
380                 sc->sc_iid = 0;
381                 /* 1st byte of descriptor report contains garbage */
382                 sc->sc_loc_x.pos = 16;
383                 sc->sc_loc_y.pos = 24;
384                 sc->sc_loc_z.pos = 32;
385                 sc->sc_loc_btn[0].pos = 8;
386                 sc->sc_loc_btn[1].pos = 9;
387                 sc->sc_loc_btn[2].pos = 10;
388         }
389
390         /*
391          * The Microsoft Wireless Notebook Optical Mouse 3000 Model 1049 has
392          * five Report IDs: 19 23 24 17 18 (in the order they appear in report
393          * descriptor), it seems that report id 17 contains the necessary
394          * mouse information(3-buttons,X,Y,wheel) so we specify it manually.
395          */
396         if (uaa->vendor == USB_VENDOR_MICROSOFT &&
397             uaa->product == USB_PRODUCT_MICROSOFT_WLNOTEBOOK3) {
398                 sc->flags = UMS_Z;
399                 sc->nbuttons = 3;
400                 sc->sc_isize = 5;
401                 sc->sc_iid = 17;
402                 sc->sc_loc_x.pos = 8;
403                 sc->sc_loc_y.pos = 16;
404                 sc->sc_loc_z.pos = 24;
405                 sc->sc_loc_btn[0].pos = 0;
406                 sc->sc_loc_btn[1].pos = 1;
407                 sc->sc_loc_btn[2].pos = 2;
408         }
409
410         sc->sc_ep_addr = ed->bEndpointAddress;
411         sc->sc_disconnected = 0;
412         free(desc, M_TEMP);
413
414 #ifdef USB_DEBUG
415         DPRINTF(("ums_attach: sc=%p\n", sc));
416         DPRINTF(("ums_attach: X\t%d/%d\n",
417                  sc->sc_loc_x.pos, sc->sc_loc_x.size));
418         DPRINTF(("ums_attach: Y\t%d/%d\n",
419                  sc->sc_loc_y.pos, sc->sc_loc_y.size));
420         if (sc->flags & UMS_Z)
421                 DPRINTF(("ums_attach: Z\t%d/%d\n",
422                          sc->sc_loc_z.pos, sc->sc_loc_z.size));
423         for (i = 1; i <= sc->nbuttons; i++) {
424                 DPRINTF(("ums_attach: B%d\t%d/%d\n",
425                          i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size));
426         }
427         DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid));
428 #endif
429
430         if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
431                 sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
432         else
433                 sc->hw.buttons = sc->nbuttons;
434         sc->hw.iftype = MOUSE_IF_USB;
435         sc->hw.type = MOUSE_MOUSE;
436         sc->hw.model = MOUSE_MODEL_GENERIC;
437         sc->hw.hwid = 0;
438         sc->mode.protocol = MOUSE_PROTO_MSC;
439         sc->mode.rate = -1;
440         sc->mode.resolution = MOUSE_RES_UNKNOWN;
441         sc->mode.accelfactor = 0;
442         sc->mode.level = 0;
443         sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
444         sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
445         sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
446
447         sc->status.flags = 0;
448         sc->status.button = sc->status.obutton = 0;
449         sc->status.dx = sc->status.dy = sc->status.dz = 0;
450
451         sc->dev = make_dev(&ums_cdevsw, device_get_unit(self),
452                         UID_ROOT, GID_OPERATOR,
453                         0644, "ums%d", device_get_unit(self));
454
455         callout_init(&sc->callout_handle, 0);
456         if (usbd_get_quirks(uaa->device)->uq_flags & UQ_SPUR_BUT_UP) {
457                 DPRINTF(("%s: Spurious button up events\n",
458                         device_get_nameunit(sc->sc_dev)));
459                 sc->flags |= UMS_SPUR_BUT_UP;
460         }
461
462         return 0;
463 }
464
465
466 static int
467 ums_detach(device_t self)
468 {
469         struct ums_softc *sc = device_get_softc(self);
470
471         if (sc->sc_enabled)
472                 ums_disable(sc);
473
474         DPRINTF(("%s: disconnected\n", device_get_nameunit(self)));
475
476         free(sc->sc_loc_btn, M_USB);
477         free(sc->sc_ibuf, M_USB);
478
479         /* someone waiting for data */
480         /*
481          * XXX If we wakeup the process here, the device will be gone by
482          * the time the process gets a chance to notice. *_close and friends
483          * should be fixed to handle this case.
484          * Or we should do a delayed detach for this.
485          * Does this delay now force tsleep to exit with an error?
486          */
487         if (sc->state & UMS_ASLEEP) {
488                 sc->state &= ~UMS_ASLEEP;
489                 wakeup(sc);
490         }
491         if (sc->state & UMS_SELECT) {
492                 sc->state &= ~UMS_SELECT;
493                 selwakeuppri(&sc->rsel, PZERO);
494         }
495
496         destroy_dev(sc->dev);
497
498         return 0;
499 }
500
501 void
502 ums_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
503 {
504         struct ums_softc *sc = addr;
505         u_char *ibuf;
506         int dx, dy, dz, dw, dt;
507         int buttons = 0;
508         int i;
509
510 #define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i))
511
512         DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status));
513         DPRINTFN(5, ("ums_intr: data ="));
514         for (i = 0; i < sc->sc_isize; i++)
515                 DPRINTFN(5, (" %02x", sc->sc_ibuf[i]));
516         DPRINTFN(5, ("\n"));
517
518         if (status == USBD_CANCELLED)
519                 return;
520
521         if (status != USBD_NORMAL_COMPLETION) {
522                 DPRINTF(("ums_intr: status=%d\n", status));
523                 if (status == USBD_STALLED)
524                     usbd_clear_endpoint_stall_async(sc->sc_intrpipe);
525                 if(status != USBD_IOERROR)
526                         return;
527         }
528
529         ibuf = sc->sc_ibuf;
530         /*
531          * The M$ Wireless Intellimouse 2.0 sends 1 extra leading byte of
532          * data compared to most USB mice. This byte frequently switches
533          * from 0x01 (usual state) to 0x02. I assume it is to allow
534          * extra, non-standard, reporting (say battery-life). However
535          * at the same time it generates a left-click message on the button
536          * byte which causes spurious left-click's where there shouldn't be.
537          * This should sort that.
538          * Currently it's the only user of UMS_T so use it as an identifier.
539          * We probably should switch to some more official quirk.
540          *
541          * UPDATE: This problem affects the M$ Wireless Notebook Optical Mouse,
542          * too. However, the leading byte for this mouse is normally 0x11,
543          * and the phantom mouse click occurs when its 0x14.
544          */
545         if (sc->flags & UMS_T) {
546                 if (sc->sc_iid) {
547                         if (*ibuf++ == 0x02)
548                                 return;
549                 }
550         } else if (sc->flags & UMS_SPUR_BUT_UP) {
551                 DPRINTFN(5, ("ums_intr: #### ibuf[0] =3D %d ####\n", *ibuf));
552                 if (*ibuf == 0x14 || *ibuf == 0x15)
553                         return;
554         } else {
555                 if (sc->sc_iid) {
556                         if (*ibuf++ != sc->sc_iid)
557                                 return;
558                 }
559         }
560
561         dx =  hid_get_data(ibuf, &sc->sc_loc_x);
562         dy = -hid_get_data(ibuf, &sc->sc_loc_y);
563         dz = -hid_get_data(ibuf, &sc->sc_loc_z);
564         dw =  hid_get_data(ibuf, &sc->sc_loc_w);
565         if (sc->flags & UMS_REVZ)
566                 dz = -dz;
567         if (sc->flags & UMS_T)
568                 dt = -hid_get_data(ibuf, &sc->sc_loc_t);
569         else
570                 dt = 0;
571         for (i = 0; i < sc->nbuttons; i++)
572                 if (hid_get_data(ibuf, &sc->sc_loc_btn[i]))
573                         buttons |= (1 << UMS_BUT(i));
574
575         if (dx || dy || dz || dt || dw || (sc->flags & UMS_Z)
576             || buttons != sc->status.button) {
577                 DPRINTFN(5, ("ums_intr: x:%d y:%d z:%d w:%d t:%d buttons:0x%x\n",
578                         dx, dy, dz, dw, dt, buttons));
579
580                 sc->status.button = buttons;
581                 sc->status.dx += dx;
582                 sc->status.dy += dy;
583                 sc->status.dz += dz;
584                 /* sc->status.dt += dt; */ /* no way to export this yet */
585                 /* sc->status.dw += dw; */ /* idem */
586                 
587                 /* Discard data in case of full buffer */
588                 if (sc->qcount == sizeof(sc->qbuf)) {
589                         DPRINTF(("Buffer full, discarded packet"));
590                         return;
591                 }
592
593                 /*
594                  * The Qtronix keyboard has a built in PS/2 port for a mouse.
595                  * The firmware once in a while posts a spurious button up
596                  * event. This event we ignore by doing a timeout for 50 msecs.
597                  * If we receive dx=dy=dz=buttons=0 before we add the event to
598                  * the queue.
599                  * In any other case we delete the timeout event.
600                  */
601                 if (sc->flags & UMS_SPUR_BUT_UP &&
602                     dx == 0 && dy == 0 && dz == 0 && dt == 0 && buttons == 0) {
603                         callout_reset(&sc->callout_handle, MS_TO_TICKS(50),
604                             ums_add_to_queue_timeout, (void *) sc);
605                 } else {
606                         callout_stop(&sc->callout_handle);
607                         ums_add_to_queue(sc, dx, dy, dz, dt, buttons);
608                 }
609         }
610 }
611
612 static void
613 ums_add_to_queue_timeout(void *priv)
614 {
615         struct ums_softc *sc = priv;
616         int s;
617
618         s = splusb();
619         ums_add_to_queue(sc, 0, 0, 0, 0, 0);
620         splx(s);
621 }
622
623 static void
624 ums_add_to_queue(struct ums_softc *sc, int dx, int dy, int dz, int dt, int buttons)
625 {
626         /* Discard data in case of full buffer */
627         if (sc->qhead+sc->mode.packetsize > sizeof(sc->qbuf)) {
628                 DPRINTF(("Buffer full, discarded packet"));
629                 return;
630         }
631
632         if (dx >  254)          dx =  254;
633         if (dx < -256)          dx = -256;
634         if (dy >  254)          dy =  254;
635         if (dy < -256)          dy = -256;
636         if (dz >  126)          dz =  126;
637         if (dz < -128)          dz = -128;
638         if (dt >  126)          dt =  126;
639         if (dt < -128)          dt = -128;
640
641         sc->qbuf[sc->qhead] = sc->mode.syncmask[1];
642         sc->qbuf[sc->qhead] |= ~buttons & MOUSE_MSC_BUTTONS;
643         sc->qbuf[sc->qhead+1] = dx >> 1;
644         sc->qbuf[sc->qhead+2] = dy >> 1;
645         sc->qbuf[sc->qhead+3] = dx - (dx >> 1);
646         sc->qbuf[sc->qhead+4] = dy - (dy >> 1);
647
648         if (sc->mode.level == 1) {
649                 sc->qbuf[sc->qhead+5] = dz >> 1;
650                 sc->qbuf[sc->qhead+6] = dz - (dz >> 1);
651                 sc->qbuf[sc->qhead+7] = ((~buttons >> 3)
652                                          & MOUSE_SYS_EXTBUTTONS);
653         }
654
655         sc->qhead += sc->mode.packetsize;
656         sc->qcount += sc->mode.packetsize;
657         /* wrap round at end of buffer */
658         if (sc->qhead >= sizeof(sc->qbuf))
659                 sc->qhead = 0;
660
661         /* someone waiting for data */
662         if (sc->state & UMS_ASLEEP) {
663                 sc->state &= ~UMS_ASLEEP;
664                 wakeup(sc);
665         }
666         if (sc->state & UMS_SELECT) {
667                 sc->state &= ~UMS_SELECT;
668                 selwakeuppri(&sc->rsel, PZERO);
669         }
670 }
671 static int
672 ums_enable(v)
673         void *v;
674 {
675         struct ums_softc *sc = v;
676
677         usbd_status err;
678
679         if (sc->sc_enabled)
680                 return EBUSY;
681
682         sc->sc_enabled = 1;
683         sc->qcount = 0;
684         sc->qhead = sc->qtail = 0;
685         sc->status.flags = 0;
686         sc->status.button = sc->status.obutton = 0;
687         sc->status.dx = sc->status.dy = sc->status.dz /* = sc->status.dt */ = 0;
688
689         callout_handle_init((struct callout_handle *)&sc->callout_handle);
690
691         /*
692          * Force the report (non-boot) protocol.
693          *
694          * Mice without boot protocol support may choose not to implement
695          * Set_Protocol at all; do not check for error.
696          */
697         usbd_set_protocol(sc->sc_iface, 1);
698
699         /* Set up interrupt pipe. */
700         err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
701                                 USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc,
702                                 sc->sc_ibuf, sc->sc_isize, ums_intr,
703                                 USBD_DEFAULT_INTERVAL);
704         if (err) {
705                 DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n",
706                          err));
707                 sc->sc_enabled = 0;
708                 return (EIO);
709         }
710         return (0);
711 }
712
713 static void
714 ums_disable(priv)
715         void *priv;
716 {
717         struct ums_softc *sc = priv;
718
719         callout_stop(&sc->callout_handle);
720
721         /* Disable interrupts. */
722         usbd_abort_pipe(sc->sc_intrpipe);
723         usbd_close_pipe(sc->sc_intrpipe);
724
725         sc->sc_enabled = 0;
726
727         if (sc->qcount != 0)
728                 DPRINTF(("Discarded %d bytes in queue\n", sc->qcount));
729 }
730
731 static int
732 ums_open(struct cdev *dev, int flag, int fmt, struct thread *p)
733 {
734         struct ums_softc *sc;
735
736         sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
737         if (sc == NULL)
738                 return (ENXIO);
739
740         return ums_enable(sc);
741 }
742
743 static int
744 ums_close(struct cdev *dev, int flag, int fmt, struct thread *p)
745 {
746         struct ums_softc *sc;
747
748         sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
749         if (!sc)
750                 return 0;
751
752         if (sc->sc_enabled)
753                 ums_disable(sc);
754
755         return 0;
756 }
757
758 static int
759 ums_read(struct cdev *dev, struct uio *uio, int flag)
760 {
761         struct ums_softc *sc;
762         int s;
763         char buf[sizeof(sc->qbuf)];
764         int l = 0;
765         int error;
766
767         sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
768         s = splusb();
769         if (!sc) {
770                 splx(s);
771                 return EIO;
772         }
773
774         while (sc->qcount == 0 )  {
775                 if (flag & O_NONBLOCK) {                /* non-blocking I/O */
776                         splx(s);
777                         return EWOULDBLOCK;
778                 }
779
780                 sc->state |= UMS_ASLEEP;        /* blocking I/O */
781                 error = tsleep(sc, PZERO | PCATCH, "umsrea", 0);
782                 if (error) {
783                         splx(s);
784                         return error;
785                 } else if (!sc->sc_enabled) {
786                         splx(s);
787                         return EINTR;
788                 }
789                 /* check whether the device is still there */
790
791                 sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
792                 if (!sc) {
793                         splx(s);
794                         return EIO;
795                 }
796         }
797
798         /*
799          * XXX we could optimise the use of splx/splusb somewhat. The writer
800          * process only extends qcount and qtail. We could copy them and use the copies
801          * to do the copying out of the queue.
802          */
803
804         while ((sc->qcount > 0) && (uio->uio_resid > 0)) {
805                 l = (sc->qcount < uio->uio_resid? sc->qcount:uio->uio_resid);
806                 if (l > sizeof(buf))
807                         l = sizeof(buf);
808                 if (l > sizeof(sc->qbuf) - sc->qtail)           /* transfer till end of buf */
809                         l = sizeof(sc->qbuf) - sc->qtail;
810
811                 splx(s);
812                 uiomove(&sc->qbuf[sc->qtail], l, uio);
813                 s = splusb();
814
815                 if ( sc->qcount - l < 0 ) {
816                         DPRINTF(("qcount below 0, count=%d l=%d\n", sc->qcount, l));
817                         sc->qcount = l;
818                 }
819                 sc->qcount -= l;        /* remove the bytes from the buffer */
820                 sc->qtail = (sc->qtail + l) % sizeof(sc->qbuf);
821         }
822         splx(s);
823
824         return 0;
825 }
826
827 static int
828 ums_poll(struct cdev *dev, int events, struct thread *p)
829 {
830         struct ums_softc *sc;
831         int revents = 0;
832         int s;
833
834         sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
835         if (!sc)
836                 return 0;
837
838         s = splusb();
839         if (events & (POLLIN | POLLRDNORM)) {
840                 if (sc->qcount) {
841                         revents = events & (POLLIN | POLLRDNORM);
842                 } else {
843                         sc->state |= UMS_SELECT;
844                         selrecord(p, &sc->rsel);
845                 }
846         }
847         splx(s);
848
849         return revents;
850 }
851
852 int
853 ums_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *p)
854 {
855         struct ums_softc *sc;
856         int error = 0;
857         int s;
858         mousemode_t mode;
859
860         sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
861         if (!sc)
862                 return EIO;
863
864         switch(cmd) {
865         case MOUSE_GETHWINFO:
866                 *(mousehw_t *)addr = sc->hw;
867                 break;
868         case MOUSE_GETMODE:
869                 *(mousemode_t *)addr = sc->mode;
870                 break;
871         case MOUSE_SETMODE:
872                 mode = *(mousemode_t *)addr;
873
874                 if (mode.level == -1)
875                         /* don't change the current setting */
876                         ;
877                 else if ((mode.level < 0) || (mode.level > 1))
878                         return (EINVAL);
879
880                 s = splusb();
881                 sc->mode.level = mode.level;
882
883                 if (sc->mode.level == 0) {
884                         if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
885                                 sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
886                         else
887                                 sc->hw.buttons = sc->nbuttons;
888                         sc->mode.protocol = MOUSE_PROTO_MSC;
889                         sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
890                         sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
891                         sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
892                 } else if (sc->mode.level == 1) {
893                         if (sc->nbuttons > MOUSE_SYS_MAXBUTTON)
894                                 sc->hw.buttons = MOUSE_SYS_MAXBUTTON;
895                         else
896                                 sc->hw.buttons = sc->nbuttons;
897                         sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
898                         sc->mode.packetsize = MOUSE_SYS_PACKETSIZE;
899                         sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
900                         sc->mode.syncmask[1] = MOUSE_SYS_SYNC;
901                 }
902
903                 bzero(sc->qbuf, sizeof(sc->qbuf));
904                 sc->qhead = sc->qtail = sc->qcount = 0;
905                 splx(s);
906
907                 break;
908         case MOUSE_GETLEVEL:
909                 *(int *)addr = sc->mode.level;
910                 break;
911         case MOUSE_SETLEVEL:
912                 if (*(int *)addr < 0 || *(int *)addr > 1)
913                         return (EINVAL);
914
915                 s = splusb();
916                 sc->mode.level = *(int *)addr;
917
918                 if (sc->mode.level == 0) {
919                         if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
920                                 sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
921                         else
922                                 sc->hw.buttons = sc->nbuttons;
923                         sc->mode.protocol = MOUSE_PROTO_MSC;
924                         sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
925                         sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
926                         sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
927                 } else if (sc->mode.level == 1) {
928                         if (sc->nbuttons > MOUSE_SYS_MAXBUTTON)
929                                 sc->hw.buttons = MOUSE_SYS_MAXBUTTON;
930                         else
931                                 sc->hw.buttons = sc->nbuttons;
932                         sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
933                         sc->mode.packetsize = MOUSE_SYS_PACKETSIZE;
934                         sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
935                         sc->mode.syncmask[1] = MOUSE_SYS_SYNC;
936                 }
937
938                 bzero(sc->qbuf, sizeof(sc->qbuf));
939                 sc->qhead = sc->qtail = sc->qcount = 0;
940                 splx(s);
941
942                 break;
943         case MOUSE_GETSTATUS: {
944                 mousestatus_t *status = (mousestatus_t *) addr;
945
946                 s = splusb();
947                 *status = sc->status;
948                 sc->status.obutton = sc->status.button;
949                 sc->status.button = 0;
950                 sc->status.dx = sc->status.dy
951                     = sc->status.dz = /* sc->status.dt = */ 0;
952                 splx(s);
953
954                 if (status->dx || status->dy || status->dz /* || status->dt */)
955                         status->flags |= MOUSE_POSCHANGED;
956                 if (status->button != status->obutton)
957                         status->flags |= MOUSE_BUTTONSCHANGED;
958                 break;
959                 }
960         default:
961                 error = ENOTTY;
962         }
963
964         return error;
965 }
966
967 MODULE_DEPEND(ums, usb, 1, 1, 1);
968 DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, usbd_driver_load, 0);