]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/usb/ums.c
Consolidate the device recognition code.
[FreeBSD/FreeBSD.git] / sys / dev / usb / ums.c
1 /*      $FreeBSD$       */
2
3 /*
4  * Copyright (c) 1998 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Lennart Augustsson (lennart@augustsson.net) at
9  * Carlstedt Research & Technology.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *        This product includes software developed by the NetBSD
22  *        Foundation, Inc. and its contributors.
23  * 4. Neither the name of The NetBSD Foundation nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39
40 /*
41  * HID spec: http://www.usb.org/developers/data/devclass/hid1_1.pdf
42  */
43
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/malloc.h>
48 #include <sys/module.h>
49 #include <sys/bus.h>
50 #include <sys/ioccom.h>
51 #include <sys/conf.h>
52 #include <sys/tty.h>
53 #include <sys/file.h>
54 #if __FreeBSD_version >= 500014
55 #include <sys/selinfo.h>
56 #else
57 #include <sys/select.h>
58 #endif
59 #include <sys/vnode.h>
60 #include <sys/poll.h>
61 #include <sys/sysctl.h>
62
63 #include <dev/usb/usb.h>
64 #include <dev/usb/usbhid.h>
65
66 #include <dev/usb/usbdi.h>
67 #include <dev/usb/usbdi_util.h>
68 #include <dev/usb/usbdevs.h>
69 #include <dev/usb/usb_quirks.h>
70 #include <dev/usb/hid.h>
71
72 #include <sys/mouse.h>
73
74 #ifdef USB_DEBUG
75 #define DPRINTF(x)      if (umsdebug) logprintf x
76 #define DPRINTFN(n,x)   if (umsdebug>(n)) logprintf x
77 int     umsdebug = 0;
78 SYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums");
79 SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW,
80            &umsdebug, 0, "ums debug level");
81 #else
82 #define DPRINTF(x)
83 #define DPRINTFN(n,x)
84 #endif
85
86 #define UMSUNIT(s)      (minor(s)&0x1f)
87
88 #define MS_TO_TICKS(ms) ((ms) * hz / 1000)                            
89
90 #define QUEUE_BUFSIZE   400     /* MUST be divisible by 5 _and_ 8 */
91
92 struct ums_softc {
93         device_t sc_dev;                /* base device */
94         usbd_interface_handle sc_iface; /* interface */
95         usbd_pipe_handle sc_intrpipe;   /* interrupt pipe */
96         int sc_ep_addr;
97
98         u_char *sc_ibuf;
99         u_int8_t sc_iid;
100         int sc_isize;
101         struct hid_location sc_loc_x, sc_loc_y, sc_loc_z;
102         struct hid_location *sc_loc_btn;
103
104         usb_callout_t callout_handle;   /* for spurious button ups */
105
106         int sc_enabled;
107         int sc_disconnected;    /* device is gone */
108
109         int flags;              /* device configuration */
110 #define UMS_Z           0x01    /* z direction available */
111 #define UMS_SPUR_BUT_UP 0x02    /* spurious button up events */
112         int nbuttons;
113 #define MAX_BUTTONS     7       /* chosen because sc_buttons is u_char */
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         dev_t           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 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 #define UMS_CDEV_MAJOR  111
150
151 Static struct cdevsw ums_cdevsw = {
152         /* open */      ums_open,
153         /* close */     ums_close,
154         /* read */      ums_read,
155         /* write */     nowrite,
156         /* ioctl */     ums_ioctl,
157         /* poll */      ums_poll,
158         /* mmap */      nommap,
159         /* strategy */  nostrategy,
160         /* name */      "ums",
161         /* maj */       UMS_CDEV_MAJOR,
162         /* dump */      nodump,
163         /* psize */     nopsize,
164         /* flags */     0,
165 #if __FreeBSD_version < 500014
166         /* bmaj */      -1
167 #endif
168 };
169
170 USB_DECLARE_DRIVER(ums);
171
172 USB_MATCH(ums)
173 {
174         USB_MATCH_START(ums, uaa);
175         usb_interface_descriptor_t *id;
176         int size, ret;
177         void *desc;
178         usbd_status err;
179         
180         if (!uaa->iface)
181                 return (UMATCH_NONE);
182         id = usbd_get_interface_descriptor(uaa->iface);
183         if (!id || id->bInterfaceClass != UICLASS_HID)
184                 return (UMATCH_NONE);
185
186         err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP);
187         if (err)
188                 return (UMATCH_NONE);
189
190         if (hid_is_collection(desc, size, 
191                               HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
192                 ret = UMATCH_IFACECLASS;
193         else
194                 ret = UMATCH_NONE;
195
196         free(desc, M_TEMP);
197         return (ret);
198 }
199
200 USB_ATTACH(ums)
201 {
202         USB_ATTACH_START(ums, sc, uaa);
203         usbd_interface_handle iface = uaa->iface;
204         usb_interface_descriptor_t *id;
205         usb_endpoint_descriptor_t *ed;
206         int size;
207         void *desc;
208         usbd_status err;
209         char devinfo[1024];
210         u_int32_t flags;
211         int i;
212         struct hid_location loc_btn;
213         
214         sc->sc_disconnected = 1;
215         sc->sc_iface = iface;
216         id = usbd_get_interface_descriptor(iface);
217         usbd_devinfo(uaa->device, 0, devinfo);
218         USB_ATTACH_SETUP;
219         printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev),
220                devinfo, id->bInterfaceClass, id->bInterfaceSubClass);
221         ed = usbd_interface2endpoint_descriptor(iface, 0);
222         if (!ed) {
223                 printf("%s: could not read endpoint descriptor\n",
224                        USBDEVNAME(sc->sc_dev));
225                 USB_ATTACH_ERROR_RETURN;
226         }
227
228         DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d "
229                      "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
230                      " bInterval=%d\n",
231                      ed->bLength, ed->bDescriptorType, 
232                      UE_GET_ADDR(ed->bEndpointAddress),
233                      UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in":"out",
234                      UE_GET_XFERTYPE(ed->bmAttributes),
235                      UGETW(ed->wMaxPacketSize), ed->bInterval));
236
237         if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN ||
238             UE_GET_XFERTYPE(ed->bmAttributes) != UE_INTERRUPT) {
239                 printf("%s: unexpected endpoint\n",
240                        USBDEVNAME(sc->sc_dev));
241                 USB_ATTACH_ERROR_RETURN;
242         }
243
244         err = usbd_read_report_desc(uaa->iface, &desc, &size, M_TEMP);
245         if (err)
246                 USB_ATTACH_ERROR_RETURN;
247
248         if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
249                        hid_input, &sc->sc_loc_x, &flags)) {
250                 printf("%s: mouse has no X report\n", USBDEVNAME(sc->sc_dev));
251                 USB_ATTACH_ERROR_RETURN;
252         }
253         if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
254                 printf("%s: X report 0x%04x not supported\n",
255                        USBDEVNAME(sc->sc_dev), flags);
256                 USB_ATTACH_ERROR_RETURN;
257         }
258
259         if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
260                        hid_input, &sc->sc_loc_y, &flags)) {
261                 printf("%s: mouse has no Y report\n", USBDEVNAME(sc->sc_dev));
262                 USB_ATTACH_ERROR_RETURN;
263         }
264         if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
265                 printf("%s: Y report 0x%04x not supported\n",
266                        USBDEVNAME(sc->sc_dev), flags);
267                 USB_ATTACH_ERROR_RETURN;
268         }
269
270         /* try to guess the Z activator: first check Z, then WHEEL */
271         if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z),
272                        hid_input, &sc->sc_loc_z, &flags) ||
273             hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL),
274                        hid_input, &sc->sc_loc_z, &flags)) {
275                 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
276                         sc->sc_loc_z.size = 0;  /* Bad Z coord, ignore it */
277                 } else {
278                         sc->flags |= UMS_Z;
279                 }
280         }
281
282         /* figure out the number of buttons */
283         for (i = 1; i <= MAX_BUTTONS; i++)
284                 if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
285                                 hid_input, &loc_btn, 0))
286                         break;
287         sc->nbuttons = i - 1;
288         sc->sc_loc_btn = malloc(sizeof(struct hid_location)*sc->nbuttons, 
289                                 M_USBDEV, M_NOWAIT);
290         if (!sc->sc_loc_btn) {
291                 printf("%s: no memory\n", USBDEVNAME(sc->sc_dev));
292                 USB_ATTACH_ERROR_RETURN;
293         }
294
295         printf("%s: %d buttons%s\n", USBDEVNAME(sc->sc_dev),
296                sc->nbuttons, sc->flags & UMS_Z? " and Z dir." : "");
297
298         for (i = 1; i <= sc->nbuttons; i++)
299                 hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
300                                 hid_input, &sc->sc_loc_btn[i-1], 0);
301
302         sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid);
303         sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_NOWAIT);
304         if (!sc->sc_ibuf) {
305                 printf("%s: no memory\n", USBDEVNAME(sc->sc_dev));
306                 free(sc->sc_loc_btn, M_USB);
307                 USB_ATTACH_ERROR_RETURN;
308         }
309
310         sc->sc_ep_addr = ed->bEndpointAddress;
311         sc->sc_disconnected = 0;
312         free(desc, M_TEMP);
313
314 #ifdef USB_DEBUG
315         DPRINTF(("ums_attach: sc=%p\n", sc));
316         DPRINTF(("ums_attach: X\t%d/%d\n", 
317                  sc->sc_loc_x.pos, sc->sc_loc_x.size));
318         DPRINTF(("ums_attach: Y\t%d/%d\n", 
319                  sc->sc_loc_y.pos, sc->sc_loc_y.size));
320         if (sc->flags & UMS_Z)
321                 DPRINTF(("ums_attach: Z\t%d/%d\n", 
322                          sc->sc_loc_z.pos, sc->sc_loc_z.size));
323         for (i = 1; i <= sc->nbuttons; i++) {
324                 DPRINTF(("ums_attach: B%d\t%d/%d\n",
325                          i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size));
326         }
327         DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid));
328 #endif
329
330         if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
331                 sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
332         else
333                 sc->hw.buttons = sc->nbuttons;
334         sc->hw.iftype = MOUSE_IF_USB;
335         sc->hw.type = MOUSE_MOUSE;
336         sc->hw.model = MOUSE_MODEL_GENERIC;
337         sc->hw.hwid = 0;
338         sc->mode.protocol = MOUSE_PROTO_MSC;
339         sc->mode.rate = -1;
340         sc->mode.resolution = MOUSE_RES_UNKNOWN;
341         sc->mode.accelfactor = 0;
342         sc->mode.level = 0;
343         sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
344         sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
345         sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
346
347         sc->status.flags = 0;
348         sc->status.button = sc->status.obutton = 0;
349         sc->status.dx = sc->status.dy = sc->status.dz = 0;
350
351 #ifndef __FreeBSD__
352         sc->rsel.si_flags = 0;
353         sc->rsel.si_pid = 0;
354 #endif
355
356         sc->dev = make_dev(&ums_cdevsw, device_get_unit(self),
357                         UID_ROOT, GID_OPERATOR,
358                         0644, "ums%d", device_get_unit(self));
359
360         if (usbd_get_quirks(uaa->device)->uq_flags & UQ_SPUR_BUT_UP) {
361                 DPRINTF(("%s: Spurious button up events\n",
362                         USBDEVNAME(sc->sc_dev)));
363                 sc->flags |= UMS_SPUR_BUT_UP;
364         }
365
366         USB_ATTACH_SUCCESS_RETURN;
367 }
368
369
370 Static int
371 ums_detach(device_t self)
372 {
373         struct ums_softc *sc = device_get_softc(self);
374         struct vnode *vp;
375
376         if (sc->sc_enabled)
377                 ums_disable(sc);
378
379         DPRINTF(("%s: disconnected\n", USBDEVNAME(self)));
380
381         free(sc->sc_loc_btn, M_USB);
382         free(sc->sc_ibuf, M_USB);
383
384         vp = SLIST_FIRST(&sc->dev->si_hlist);
385         if (vp)
386                 VOP_REVOKE(vp, REVOKEALL);
387
388         /* someone waiting for data */
389         /*
390          * XXX If we wakeup the process here, the device will be gone by
391          * the time the process gets a chance to notice. *_close and friends
392          * should be fixed to handle this case.
393          * Or we should do a delayed detach for this.
394          * Does this delay now force tsleep to exit with an error?
395          */
396         if (sc->state & UMS_ASLEEP) {
397                 sc->state &= ~UMS_ASLEEP;
398                 wakeup(sc);
399         }
400         if (sc->state & UMS_SELECT) {
401                 sc->state &= ~UMS_SELECT;
402                 selwakeup(&sc->rsel);
403         }
404
405         destroy_dev(sc->dev);
406
407         return 0;
408 }
409
410 void
411 ums_intr(xfer, addr, status)
412         usbd_xfer_handle xfer;
413         usbd_private_handle addr;
414         usbd_status status;
415 {
416         struct ums_softc *sc = addr;
417         u_char *ibuf;
418         int dx, dy, dz;
419         u_char buttons = 0;
420         int i;
421
422 #define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i))
423
424         DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status));
425         DPRINTFN(5, ("ums_intr: data = %02x %02x %02x\n",
426                      sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2]));
427
428         if (status == USBD_CANCELLED)
429                 return;
430
431         if (status != USBD_NORMAL_COMPLETION) {
432                 DPRINTF(("ums_intr: status=%d\n", status));
433                 if (status == USBD_STALLED)
434                     usbd_clear_endpoint_stall_async(sc->sc_intrpipe);
435                 return;
436         }
437
438         ibuf = sc->sc_ibuf;
439         if (sc->sc_iid) {
440                 if (*ibuf++ != sc->sc_iid)
441                         return;
442         }
443
444         dx =  hid_get_data(ibuf, &sc->sc_loc_x);
445         dy = -hid_get_data(ibuf, &sc->sc_loc_y);
446         dz = -hid_get_data(ibuf, &sc->sc_loc_z);
447         for (i = 0; i < sc->nbuttons; i++)
448                 if (hid_get_data(ibuf, &sc->sc_loc_btn[i]))
449                         buttons |= (1 << UMS_BUT(i));
450
451         if (dx || dy || dz || (sc->flags & UMS_Z)
452             || buttons != sc->status.button) {
453                 DPRINTFN(5, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n",
454                         dx, dy, dz, buttons));
455
456                 sc->status.button = buttons;
457                 sc->status.dx += dx;
458                 sc->status.dy += dy;
459                 sc->status.dz += dz;
460
461                 /* Discard data in case of full buffer */
462                 if (sc->qcount == sizeof(sc->qbuf)) {
463                         DPRINTF(("Buffer full, discarded packet"));
464                         return;
465                 }
466
467                 /*
468                  * The Qtronix keyboard has a built in PS/2 port for a mouse.
469                  * The firmware once in a while posts a spurious button up
470                  * event. This event we ignore by doing a timeout for 50 msecs.
471                  * If we receive dx=dy=dz=buttons=0 before we add the event to
472                  * the queue.
473                  * In any other case we delete the timeout event.
474                  */
475                 if (sc->flags & UMS_SPUR_BUT_UP &&
476                     dx == 0 && dy == 0 && dz == 0 && buttons == 0) {
477                         usb_callout(sc->callout_handle, MS_TO_TICKS(50 /*msecs*/),
478                                     ums_add_to_queue_timeout, (void *) sc);
479                 } else {
480                         usb_uncallout(sc->callout_handle,
481                                       ums_add_to_queue_timeout, (void *) sc);
482                         ums_add_to_queue(sc, dx, dy, dz, buttons);
483                 }
484         }
485 }
486
487 Static void
488 ums_add_to_queue_timeout(void *priv)
489 {
490         struct ums_softc *sc = priv;
491         int s;
492
493         s = splusb();
494         ums_add_to_queue(sc, 0, 0, 0, 0);
495         splx(s);
496 }
497
498 Static void
499 ums_add_to_queue(struct ums_softc *sc, int dx, int dy, int dz, int buttons)
500 {
501         /* Discard data in case of full buffer */
502         if (sc->qhead+sc->mode.packetsize > sizeof(sc->qbuf)) {
503                 DPRINTF(("Buffer full, discarded packet"));
504                 return;
505         }
506
507         if (dx >  254)          dx =  254;
508         if (dx < -256)          dx = -256;
509         if (dy >  254)          dy =  254;
510         if (dy < -256)          dy = -256;
511         if (dz >  126)          dz =  126;
512         if (dz < -128)          dz = -128;
513
514         sc->qbuf[sc->qhead] = sc->mode.syncmask[1];
515         sc->qbuf[sc->qhead] |= ~buttons & MOUSE_MSC_BUTTONS;
516         sc->qbuf[sc->qhead+1] = dx >> 1;
517         sc->qbuf[sc->qhead+2] = dy >> 1;
518         sc->qbuf[sc->qhead+3] = dx - (dx >> 1);
519         sc->qbuf[sc->qhead+4] = dy - (dy >> 1);
520
521         if (sc->mode.level == 1) {
522                 sc->qbuf[sc->qhead+5] = dz >> 1;
523                 sc->qbuf[sc->qhead+6] = dz - (dz >> 1);
524                 sc->qbuf[sc->qhead+7] = ((~buttons >> 3)
525                                          & MOUSE_SYS_EXTBUTTONS);
526         }
527
528         sc->qhead += sc->mode.packetsize;
529         sc->qcount += sc->mode.packetsize;
530         /* wrap round at end of buffer */
531         if (sc->qhead >= sizeof(sc->qbuf))
532                 sc->qhead = 0;
533
534         /* someone waiting for data */
535         if (sc->state & UMS_ASLEEP) {
536                 sc->state &= ~UMS_ASLEEP;
537                 wakeup(sc);
538         }
539         if (sc->state & UMS_SELECT) {
540                 sc->state &= ~UMS_SELECT;
541                 selwakeup(&sc->rsel);
542         }
543 }
544 Static int
545 ums_enable(v)
546         void *v;
547 {
548         struct ums_softc *sc = v;
549
550         usbd_status err;
551
552         if (sc->sc_enabled)
553                 return EBUSY;
554
555         sc->sc_enabled = 1;
556         sc->qcount = 0;
557         sc->qhead = sc->qtail = 0;
558         sc->status.flags = 0;
559         sc->status.button = sc->status.obutton = 0;
560         sc->status.dx = sc->status.dy = sc->status.dz = 0;
561
562         callout_handle_init((struct callout_handle *)&sc->callout_handle);
563
564         /* Set up interrupt pipe. */
565         err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, 
566                                 USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, 
567                                 sc->sc_ibuf, sc->sc_isize, ums_intr,
568                                 USBD_DEFAULT_INTERVAL);
569         if (err) {
570                 DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n",
571                          err));
572                 sc->sc_enabled = 0;
573                 return (EIO);
574         }
575         return (0);
576 }
577
578 Static void
579 ums_disable(priv)
580         void *priv;
581 {
582         struct ums_softc *sc = priv;
583
584         usb_uncallout(sc->callout_handle, ums_add_to_queue_timeout, sc);
585
586         /* Disable interrupts. */
587         usbd_abort_pipe(sc->sc_intrpipe);
588         usbd_close_pipe(sc->sc_intrpipe);
589
590         sc->sc_enabled = 0;
591
592         if (sc->qcount != 0)
593                 DPRINTF(("Discarded %d bytes in queue\n", sc->qcount));
594 }
595
596 Static int
597 ums_open(dev_t dev, int flag, int fmt, usb_proc_ptr p)
598 {
599         struct ums_softc *sc;
600
601         USB_GET_SC_OPEN(ums, UMSUNIT(dev), sc);
602
603         return ums_enable(sc);
604 }
605
606 Static int
607 ums_close(dev_t dev, int flag, int fmt, usb_proc_ptr p)
608 {
609         struct ums_softc *sc;
610
611         USB_GET_SC(ums, UMSUNIT(dev), sc);
612
613         if (!sc)
614                 return 0;
615
616         if (sc->sc_enabled)
617                 ums_disable(sc);
618
619         return 0;
620 }
621
622 Static int
623 ums_read(dev_t dev, struct uio *uio, int flag)
624 {
625         struct ums_softc *sc;
626         int s;
627         char buf[sizeof(sc->qbuf)];
628         int l = 0;
629         int error;
630
631         USB_GET_SC(ums, UMSUNIT(dev), sc);
632
633         s = splusb();
634         if (!sc) {
635                 splx(s);
636                 return EIO;
637         }
638
639         while (sc->qcount == 0 )  {
640                 if (flag & IO_NDELAY) {         /* non-blocking I/O */
641                         splx(s);
642                         return EWOULDBLOCK;
643                 }
644                 
645                 sc->state |= UMS_ASLEEP;        /* blocking I/O */
646                 error = tsleep(sc, PZERO | PCATCH, "umsrea", 0);
647                 if (error) {
648                         splx(s);
649                         return error;
650                 } else if (!sc->sc_enabled) {
651                         splx(s);
652                         return EINTR;
653                 }
654                 /* check whether the device is still there */
655
656                 sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
657                 if (!sc) {
658                         splx(s);
659                         return EIO;
660                 }
661         }
662
663         /*
664          * XXX we could optimise the use of splx/splusb somewhat. The writer
665          * process only extends qcount and qtail. We could copy them and use the copies
666          * to do the copying out of the queue.
667          */
668
669         while ((sc->qcount > 0) && (uio->uio_resid > 0)) {
670                 l = (sc->qcount < uio->uio_resid? sc->qcount:uio->uio_resid);
671                 if (l > sizeof(buf))
672                         l = sizeof(buf);
673                 if (l > sizeof(sc->qbuf) - sc->qtail)           /* transfer till end of buf */
674                         l = sizeof(sc->qbuf) - sc->qtail;
675
676                 splx(s);
677                 uiomove(&sc->qbuf[sc->qtail], l, uio);
678                 s = splusb();
679
680                 if ( sc->qcount - l < 0 ) {
681                         DPRINTF(("qcount below 0, count=%d l=%d\n", sc->qcount, l));
682                         sc->qcount = l;
683                 }
684                 sc->qcount -= l;        /* remove the bytes from the buffer */
685                 sc->qtail = (sc->qtail + l) % sizeof(sc->qbuf);
686         }
687         splx(s);
688
689         return 0;
690 }
691
692 Static int
693 ums_poll(dev_t dev, int events, usb_proc_ptr p)
694 {
695         struct ums_softc *sc;
696         int revents = 0;
697         int s;
698
699         USB_GET_SC(ums, UMSUNIT(dev), sc);
700
701         if (!sc)
702                 return 0;
703
704         s = splusb();
705         if (events & (POLLIN | POLLRDNORM)) {
706                 if (sc->qcount) {
707                         revents = events & (POLLIN | POLLRDNORM);
708                 } else {
709                         sc->state |= UMS_SELECT;
710                         selrecord(p, &sc->rsel);
711                 }
712         }
713         splx(s);
714
715         return revents;
716 }
717         
718 int
719 ums_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p)
720 {
721         struct ums_softc *sc;
722         int error = 0;
723         int s;
724         mousemode_t mode;
725
726         USB_GET_SC(ums, UMSUNIT(dev), sc);
727
728         if (!sc)
729                 return EIO;
730
731         switch(cmd) {
732         case MOUSE_GETHWINFO:
733                 *(mousehw_t *)addr = sc->hw;
734                 break;
735         case MOUSE_GETMODE:
736                 *(mousemode_t *)addr = sc->mode;
737                 break;
738         case MOUSE_SETMODE:
739                 mode = *(mousemode_t *)addr;
740
741                 if (mode.level == -1)
742                         /* don't change the current setting */
743                         ;
744                 else if ((mode.level < 0) || (mode.level > 1))
745                         return (EINVAL);
746
747                 s = splusb();
748                 sc->mode.level = mode.level;
749
750                 if (sc->mode.level == 0) {
751                         if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
752                                 sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
753                         else
754                                 sc->hw.buttons = sc->nbuttons;
755                         sc->mode.protocol = MOUSE_PROTO_MSC;
756                         sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
757                         sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
758                         sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
759                 } else if (sc->mode.level == 1) {
760                         if (sc->nbuttons > MOUSE_SYS_MAXBUTTON)
761                                 sc->hw.buttons = MOUSE_SYS_MAXBUTTON;
762                         else
763                                 sc->hw.buttons = sc->nbuttons;
764                         sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
765                         sc->mode.packetsize = MOUSE_SYS_PACKETSIZE;
766                         sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
767                         sc->mode.syncmask[1] = MOUSE_SYS_SYNC;
768                 }
769
770                 bzero(sc->qbuf, sizeof(sc->qbuf));
771                 sc->qhead = sc->qtail = sc->qcount = 0;
772                 splx(s);
773
774                 break;
775         case MOUSE_GETLEVEL:
776                 *(int *)addr = sc->mode.level;
777                 break;
778         case MOUSE_SETLEVEL:
779                 if (*(int *)addr < 0 || *(int *)addr > 1)
780                         return (EINVAL);
781
782                 s = splusb();
783                 sc->mode.level = *(int *)addr;
784
785                 if (sc->mode.level == 0) {
786                         if (sc->nbuttons > MOUSE_MSC_MAXBUTTON)
787                                 sc->hw.buttons = MOUSE_MSC_MAXBUTTON;
788                         else
789                                 sc->hw.buttons = sc->nbuttons;
790                         sc->mode.protocol = MOUSE_PROTO_MSC;
791                         sc->mode.packetsize = MOUSE_MSC_PACKETSIZE;
792                         sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
793                         sc->mode.syncmask[1] = MOUSE_MSC_SYNC;
794                 } else if (sc->mode.level == 1) {
795                         if (sc->nbuttons > MOUSE_SYS_MAXBUTTON)
796                                 sc->hw.buttons = MOUSE_SYS_MAXBUTTON;
797                         else
798                                 sc->hw.buttons = sc->nbuttons;
799                         sc->mode.protocol = MOUSE_PROTO_SYSMOUSE;
800                         sc->mode.packetsize = MOUSE_SYS_PACKETSIZE;
801                         sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
802                         sc->mode.syncmask[1] = MOUSE_SYS_SYNC;
803                 }
804
805                 bzero(sc->qbuf, sizeof(sc->qbuf));
806                 sc->qhead = sc->qtail = sc->qcount = 0;
807                 splx(s);
808
809                 break;
810         case MOUSE_GETSTATUS: {
811                 mousestatus_t *status = (mousestatus_t *) addr;
812
813                 s = splusb();
814                 *status = sc->status;
815                 sc->status.obutton = sc->status.button;
816                 sc->status.button = 0;
817                 sc->status.dx = sc->status.dy = sc->status.dz = 0;
818                 splx(s);
819
820                 if (status->dx || status->dy || status->dz)
821                         status->flags |= MOUSE_POSCHANGED;
822                 if (status->button != status->obutton)
823                         status->flags |= MOUSE_BUTTONSCHANGED;
824                 break;
825                 }
826         default:
827                 error = ENOTTY;
828         }
829
830         return error;
831 }
832
833 DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, usbd_driver_load, 0);