ums.c revision 1.18 1 /* $NetBSD: ums.c,v 1.18 1998/12/30 17:46:20 augustss Exp $ */
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 (augustss (at) carlstedt.se) 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 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/malloc.h>
44 #if defined(__NetBSD__)
45 #include <sys/device.h>
46 #include <sys/ioctl.h>
47 #elif defined(__FreeBSD__)
48 #include <sys/module.h>
49 #include <sys/bus.h>
50 #include <sys/ioccom.h>
51 #include <sys/conf.h>
52 #endif
53 #include <sys/tty.h>
54 #include <sys/file.h>
55 #include <sys/select.h>
56 #include <sys/proc.h>
57 #include <sys/vnode.h>
58 #include <sys/poll.h>
59
60 #include <dev/usb/usb.h>
61 #include <dev/usb/usbhid.h>
62
63 #include <dev/usb/usbdi.h>
64 #include <dev/usb/usbdi_util.h>
65 #include <dev/usb/usbdevs.h>
66 #include <dev/usb/usb_quirks.h>
67 #include <dev/usb/hid.h>
68
69 #if defined(__NetBSD__)
70 #include <dev/wscons/wsconsio.h>
71 #include <dev/wscons/wsmousevar.h>
72 #elif defined(__FreeBSD__)
73 #include <machine/mouse.h>
74 #endif
75
76 #ifdef USB_DEBUG
77 #define DPRINTF(x) if (umsdebug) printf x
78 #define DPRINTFN(n,x) if (umsdebug>(n)) printf x
79 int umsdebug = 0;
80 #else
81 #define DPRINTF(x)
82 #define DPRINTFN(n,x)
83 #endif
84
85 #define UMSUNIT(s) (minor(s)&0x1f)
86
87 #define PS2LBUTMASK x01
88 #define PS2RBUTMASK x02
89 #define PS2MBUTMASK x04
90 #define PS2BUTMASK 0x0f
91
92 #define QUEUE_BUFSIZE 240 /* MUST be divisible by 3 _and_ 4 */
93
94 struct ums_softc {
95 bdevice sc_dev; /* base device */
96 usbd_interface_handle sc_iface; /* interface */
97 usbd_pipe_handle sc_intrpipe; /* interrupt pipe */
98 int sc_ep_addr;
99
100 u_char *sc_ibuf;
101 u_int8_t sc_iid;
102 int sc_isize;
103 struct hid_location sc_loc_x, sc_loc_y, sc_loc_z;
104 struct hid_location *sc_loc_btn;
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 int nbuttons;
112
113 #if defined(__NetBSD__)
114 u_char sc_buttons; /* mouse button status */
115 struct device *sc_wsmousedev;
116 #elif defined(__FreeBSD__)
117 u_char qbuf[QUEUE_BUFSIZE];
118 u_char dummy[100]; /* just for safety and for now */
119 int qcount, qhead, qtail;
120 mousehw_t hw;
121 mousemode_t mode;
122 mousestatus_t status;
123
124 int state;
125 # define UMS_ASLEEP 0x01 /* readFromDevice is waiting */
126 # define UMS_SELECT 0x02 /* select is waiting */
127 struct selinfo rsel; /* process waiting in select */
128 #endif
129 };
130
131 #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
132 #define MOUSE_FLAGS (HIO_RELATIVE)
133
134 void ums_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
135 void ums_disco __P((void *));
136
137 static int ums_enable __P((void *));
138 static void ums_disable __P((void *));
139
140 #if defined(__NetBSD__)
141 static int ums_ioctl __P((void *, u_long, caddr_t, int, struct proc *));
142
143 const struct wsmouse_accessops ums_accessops = {
144 ums_enable,
145 ums_ioctl,
146 ums_disable,
147 };
148
149 #elif defined(__FreeBSD__)
150 static d_open_t ums_open;
151 static d_close_t ums_close;
152 static d_read_t ums_read;
153 static d_ioctl_t ums_ioctl;
154 static d_poll_t ums_poll;
155
156 #define UMS_CDEV_MAJOR 138 /* XXX NWH should be requested */
157
158 static struct cdevsw ums_cdevsw = {
159 ums_open, ums_close, ums_read, nowrite,
160 ums_ioctl, nostop, nullreset, nodevtotty,
161 ums_poll, nommap,
162 NULL, "ums_", NULL, -1
163 };
164 #endif
165
166 USB_DECLARE_DRIVER(ums);
167
168 USB_MATCH(ums)
169 {
170 USB_MATCH_START(ums, uaa);
171 usb_interface_descriptor_t *id;
172 int size, ret;
173 void *desc;
174 usbd_status r;
175
176 if (!uaa->iface)
177 return (UMATCH_NONE);
178 id = usbd_get_interface_descriptor(uaa->iface);
179 if (!id || id->bInterfaceClass != UCLASS_HID)
180 return (UMATCH_NONE);
181
182 r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP);
183 if (r != USBD_NORMAL_COMPLETION)
184 return (UMATCH_NONE);
185
186 if (hid_is_collection(desc, size,
187 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE)))
188 ret = UMATCH_IFACECLASS;
189 else
190 ret = UMATCH_NONE;
191
192 free(desc, M_TEMP);
193 return (ret);
194 }
195
196 USB_ATTACH(ums)
197 {
198 USB_ATTACH_START(ums, sc, uaa);
199 usbd_interface_handle iface = uaa->iface;
200 usb_interface_descriptor_t *id;
201 usb_endpoint_descriptor_t *ed;
202 struct wsmousedev_attach_args a;
203 int size;
204 void *desc;
205 usbd_status r;
206 char devinfo[1024];
207 u_int32_t flags;
208 int i;
209 struct hid_location loc_btn;
210
211 sc->sc_disconnected = 1;
212 sc->sc_iface = iface;
213 id = usbd_get_interface_descriptor(iface);
214 usbd_devinfo(uaa->device, 0, devinfo);
215 USB_ATTACH_SETUP;
216 printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev),
217 devinfo, id->bInterfaceClass, id->bInterfaceSubClass);
218 ed = usbd_interface2endpoint_descriptor(iface, 0);
219 if (!ed) {
220 printf("%s: could not read endpoint descriptor\n",
221 USBDEVNAME(sc->sc_dev));
222 USB_ATTACH_ERROR_RETURN;
223 }
224
225 DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d "
226 "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
227 " bInterval=%d\n",
228 ed->bLength, ed->bDescriptorType,
229 ed->bEndpointAddress & UE_ADDR,
230 ed->bEndpointAddress & UE_IN ? "in" : "out",
231 ed->bmAttributes & UE_XFERTYPE,
232 UGETW(ed->wMaxPacketSize), ed->bInterval));
233
234 if ((ed->bEndpointAddress & UE_IN) != UE_IN ||
235 (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
236 printf("%s: unexpected endpoint\n",
237 USBDEVNAME(sc->sc_dev));
238 USB_ATTACH_ERROR_RETURN;
239 }
240
241 r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP);
242 if (r != USBD_NORMAL_COMPLETION)
243 USB_ATTACH_ERROR_RETURN;
244
245 if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
246 hid_input, &sc->sc_loc_x, &flags)) {
247 printf("%s: mouse has no X report\n", USBDEVNAME(sc->sc_dev));
248 USB_ATTACH_ERROR_RETURN;
249 }
250 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
251 printf("%s: X report 0x%04x not supported\n",
252 USBDEVNAME(sc->sc_dev), flags);
253 USB_ATTACH_ERROR_RETURN;
254 }
255
256 if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
257 hid_input, &sc->sc_loc_y, &flags)) {
258 printf("%s: mouse has no Y report\n", USBDEVNAME(sc->sc_dev));
259 USB_ATTACH_ERROR_RETURN;
260 }
261 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
262 printf("%s: Y report 0x%04x not supported\n",
263 USBDEVNAME(sc->sc_dev), flags);
264 USB_ATTACH_ERROR_RETURN;
265 }
266
267 /* try to guess the Z activator: first check Z, then WHEEL */
268 if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z),
269 hid_input, &sc->sc_loc_z, &flags) ||
270 hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL),
271 hid_input, &sc->sc_loc_z, &flags)) {
272 if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) {
273 sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */
274 } else {
275 sc->flags |= UMS_Z;
276 }
277 }
278
279 /* figure out the number of buttons, 7 is an arbitrary limit */
280 for (i = 1; i <= 7; i++)
281 if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
282 hid_input, &loc_btn, 0))
283 break;
284 sc->nbuttons = i - 1;
285 sc->sc_loc_btn = malloc(sizeof(struct hid_location)*sc->nbuttons,
286 M_USBDEV, M_NOWAIT);
287 if (!sc->sc_loc_btn)
288 USB_ATTACH_ERROR_RETURN;
289
290 printf("%s: %d buttons%s\n", USBDEVNAME(sc->sc_dev),
291 sc->nbuttons, (sc->flags & UMS_Z? " and Z dir." : ""));
292
293 for (i = 1; i <= sc->nbuttons; i++)
294 hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i),
295 hid_input, &sc->sc_loc_btn[i-1], 0);
296
297 sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid);
298 sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_NOWAIT);
299 if (!sc->sc_ibuf) {
300 free(sc->sc_loc_btn, M_USB);
301 USB_ATTACH_ERROR_RETURN;
302 }
303
304 sc->sc_ep_addr = ed->bEndpointAddress;
305 sc->sc_disconnected = 0;
306 free(desc, M_TEMP);
307
308 #ifdef USB_DEBUG
309 DPRINTF(("ums_attach: sc=%p\n", sc));
310 DPRINTF(("ums_attach: X\t%d/%d\n",
311 sc->sc_loc_x.pos, sc->sc_loc_x.size));
312 DPRINTF(("ums_attach: Y\t%d/%d\n",
313 sc->sc_loc_x.pos, sc->sc_loc_x.size));
314 if (sc->flags & UMS_Z)
315 DPRINTF(("ums_attach: Z\t%d/%d\n",
316 sc->sc_loc_z.pos, sc->sc_loc_z.size));
317 for (i = 1; i <= sc->nbuttons; i++) {
318 DPRINTF(("ums_attach: B%d\t%d/%d\n",
319 i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size));
320 }
321 DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid));
322 #endif
323
324 #if defined(__NetBSD__)
325 a.accessops = &ums_accessops;
326 a.accesscookie = sc;
327
328 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
329 #elif defined(__FreeBSD__)
330 sc->hw.buttons = 2; /* XXX hw&mode values are bogus */
331 sc->hw.iftype = MOUSE_IF_PS2;
332 sc->hw.type = MOUSE_MOUSE;
333 if (sc->flags & UMS_Z)
334 sc->hw.model = MOUSE_MODEL_INTELLI;
335 else
336 sc->hw.model = MOUSE_MODEL_GENERIC;
337 sc->hw.hwid = 0;
338 sc->mode.protocol = MOUSE_PROTO_PS2;
339 sc->mode.rate = -1;
340 sc->mode.resolution = MOUSE_RES_DEFAULT;
341 sc->mode.accelfactor = 1;
342 sc->mode.level = 0;
343 if (sc->flags & UMS_Z) {
344 sc->mode.packetsize = MOUSE_INTELLI_PACKETSIZE;
345 sc->mode.syncmask[0] = 0xc8;
346 } else {
347 sc->mode.packetsize = MOUSE_PS2_PACKETSIZE;
348 sc->mode.syncmask[0] = 0xc0;
349 }
350 sc->mode.syncmask[1] = 0;
351
352 sc->status.flags = 0;
353 sc->status.button = sc->status.obutton = 0;
354 sc->status.dx = sc->status.dy = sc->status.dz = 0;
355
356 sc->rsel.si_flags = 0;
357 sc->rsel.si_pid = 0;
358 #endif
359
360 USB_ATTACH_SUCCESS_RETURN;
361 }
362
363
364 #if defined(__FreeBSD__)
365 static int
366 ums_detach(device_t self)
367 {
368 struct ums_softc *sc = device_get_softc(self);
369 char *devinfo = (char *) device_get_desc(self);
370
371 if (devinfo) {
372 device_set_desc(self, NULL);
373 free(devinfo, M_USB);
374 }
375 free(sc->sc_loc_btn, M_USB);
376 free(sc->sc_ibuf, M_USB);
377
378 return 0;
379 }
380 #endif
381
382 void
383 ums_disco(p)
384 void *p;
385 {
386 struct ums_softc *sc = p;
387
388 DPRINTF(("ums_disco: sc=%p\n", sc));
389 usbd_abort_pipe(sc->sc_intrpipe);
390 sc->sc_disconnected = 1;
391 }
392
393 void
394 ums_intr(reqh, addr, status)
395 usbd_request_handle reqh;
396 usbd_private_handle addr;
397 usbd_status status;
398 {
399 struct ums_softc *sc = addr;
400 u_char *ibuf;
401 int dx, dy, dz;
402 u_char buttons = 0;
403 int i;
404 #if defined(__NetBSD__)
405 #define UMS_BUT(i) ((i) == 1 || (i) == 2 ? 3 - (i) : i)
406 #elif defined(__FreeBSD__)
407 #define UMS_BUT(i) (i)
408 #endif
409
410 DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status));
411 DPRINTFN(5, ("ums_intr: data = %02x %02x %02x\n",
412 sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2]));
413
414 if (status == USBD_CANCELLED)
415 return;
416
417 if (status != USBD_NORMAL_COMPLETION) {
418 DPRINTF(("ums_intr: status=%d\n", status));
419 usbd_clear_endpoint_stall_async(sc->sc_intrpipe);
420 return;
421 }
422
423 ibuf = sc->sc_ibuf;
424 if (sc->sc_iid) {
425 if (*ibuf++ != sc->sc_iid)
426 return;
427 }
428 dx = hid_get_data(ibuf, &sc->sc_loc_x);
429 dy = -hid_get_data(ibuf, &sc->sc_loc_y);
430 dz = hid_get_data(ibuf, &sc->sc_loc_z);
431 /* NWH Why are you modifying the button assignments here?
432 * That's the purpose of a high level mouse driver
433 */
434 for (i = 0; i < sc->nbuttons; i++)
435 if (hid_get_data(ibuf, &sc->sc_loc_btn[i]))
436 buttons |= (1 << UMS_BUT(i));
437
438 #if defined(__NetBSD__)
439 if (dx || dy || buttons != sc->sc_buttons) {
440 DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n",
441 dx, dy, dz, buttons));
442 sc->sc_buttons = buttons;
443 if (sc->sc_wsmousedev)
444 wsmouse_input(sc->sc_wsmousedev, buttons, dx, dy, dz);
445 #elif defined(__FreeBSD__)
446 if (dx || dy || buttons != sc->status.button) {
447 DPRINTFN(10, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n",
448 dx, dy, dz, buttons));
449
450 sc->status.button = buttons;
451 sc->status.dx += dx;
452 sc->status.dy += dy;
453 sc->status.dz += dz;
454
455 /* Discard data in case of full buffer */
456 if (sc->qcount == sizeof(sc->qbuf)) {
457 DPRINTF(("Buffer full, discarded packet"));
458 return;
459 }
460
461 sc->qbuf[sc->qhead] = MOUSE_PS2_SYNC;
462 if (dx < 0)
463 sc->qbuf[sc->qhead] |= MOUSE_PS2_XNEG;
464 if (dx > 255 || dx < -255)
465 sc->qbuf[sc->qhead] |= MOUSE_PS2_XOVERFLOW;
466 if (dy < 0)
467 sc->qbuf[sc->qhead] |= MOUSE_PS2_YNEG;
468 if (dy > 255 || dy < -255)
469 sc->qbuf[sc->qhead] |= MOUSE_PS2_YOVERFLOW;
470 sc->qbuf[sc->qhead++] |= buttons;
471 sc->qbuf[sc->qhead++] = dx;
472 sc->qbuf[sc->qhead++] = dy;
473 sc->qcount += 3;
474 if (sc->flags & UMS_Z) {
475 sc->qbuf[sc->qhead++] = dz;
476 sc->qcount++;
477 }
478 #ifdef USB_DEBUG
479 if (sc->qhead > sizeof(sc->qbuf))
480 DPRINTF(("Buffer overrun! %d %d\n",
481 sc->qhead, sizeof(sc->qbuf)));
482 #endif
483 /* wrap round at end of buffer */
484 if (sc->qhead >= sizeof(sc->qbuf))
485 sc->qhead = 0;
486
487 /* someone waiting for data */
488 if (sc->state & UMS_ASLEEP)
489 wakeup(sc);
490 /* wake up any pending selects */
491 selwakeup(&sc->rsel);
492 sc->state &= ~UMS_SELECT;
493 #endif
494 }
495 }
496
497
498 static int
499 ums_enable(v)
500 void *v;
501 {
502 struct ums_softc *sc = v;
503
504 usbd_status r;
505
506 if (sc->sc_enabled)
507 return EBUSY;
508
509 sc->sc_enabled = 1;
510 #if defined(__NetBSD__)
511 sc->sc_buttons = 0;
512 #elif defined(__FreeBSD__)
513 sc->qcount = 0;
514 sc->qhead = sc->qtail = 0;
515 #ifdef USB_DEBUG
516 if (sizeof(sc->qbuf) % 4 || sizeof(sc->qbuf) % 3) {
517 DPRINTF(("Buffer size not divisible by 3 or 4\n"));
518 return ENXIO;
519 }
520 #endif
521 sc->status.flags = 0;
522 sc->status.button = sc->status.obutton = 0;
523 sc->status.dx = sc->status.dy = sc->status.dz = 0;
524 #endif
525
526 /* Set up interrupt pipe. */
527 r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
528 USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc,
529 sc->sc_ibuf, sc->sc_isize, ums_intr);
530 if (r != USBD_NORMAL_COMPLETION) {
531 DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n",
532 r));
533 sc->sc_enabled = 0;
534 return (EIO);
535 }
536 usbd_set_disco(sc->sc_intrpipe, ums_disco, sc);
537 return (0);
538 }
539
540 static void
541 ums_disable(v)
542 void *v;
543 {
544 struct ums_softc *sc = v;
545
546 /* Disable interrupts. */
547 usbd_abort_pipe(sc->sc_intrpipe);
548 usbd_close_pipe(sc->sc_intrpipe);
549
550 sc->sc_enabled = 0;
551
552 #if defined(USBVERBOSE) && defined(__FreeBSD__)
553 if (sc->qcount != 0)
554 DPRINTF(("Discarded %d bytes in queue\n", sc->qcount));
555 #endif
556 }
557
558 #if defined(__NetBSD__)
559 static int
560 ums_ioctl(v, cmd, data, flag, p)
561 void *v;
562 u_long cmd;
563 caddr_t data;
564 int flag;
565 struct proc *p;
566
567 {
568 switch (cmd) {
569 case WSMOUSEIO_GTYPE:
570 *(u_int *)data = WSMOUSE_TYPE_USB;
571 return (0);
572 }
573
574 return (-1);
575 }
576
577 #elif defined(__FreeBSD__)
578 static int
579 ums_open(dev_t dev, int flag, int fmt, struct proc *p)
580 {
581 struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
582
583 if (!sc) {
584 DPRINTF(("sc not found at open"));
585 return EINVAL;
586 }
587
588 return ums_enable(sc);
589 }
590
591 static int
592 ums_close(dev_t dev, int flag, int fmt, struct proc *p)
593 {
594 struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
595
596 if (!sc) {
597 DPRINTF(("sc not found at close"));
598 return EINVAL;
599 }
600
601 if (sc->sc_enabled)
602 ums_disable(sc);
603 return 0;
604 }
605
606 static int
607 ums_read(dev_t dev, struct uio *uio, int flag)
608 {
609 struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
610 int s;
611 char buf[sizeof(sc->qbuf)];
612 int l = 0;
613 int error;
614
615 if (!sc || !sc->sc_enabled) {
616 DPRINTF(("sc not found at read"));
617 return EINVAL;
618 }
619
620 s = splusb();
621 while (sc->qcount == 0 ) {
622 /* NWH XXX non blocking I/O ??
623 if (non blocking I/O ) {
624 splx(s);
625 return EWOULDBLOCK;
626 } else {
627 */
628 sc->state |= UMS_ASLEEP;
629 error = tsleep(sc, PZERO | PCATCH, "umsrea", 0);
630 sc->state &= ~UMS_ASLEEP;
631 if (error) {
632 splx(s);
633 return error;
634 }
635 }
636
637 while ((sc->qcount > 0) && (uio->uio_resid > 0)) {
638 l = (sc->qcount < uio->uio_resid? sc->qcount:uio->uio_resid);
639 if (l > sizeof(buf))
640 l = sizeof(buf);
641 if (l > sizeof(sc->qbuf) - sc->qtail) /* transfer till end of buf */
642 l = sizeof(sc->qbuf) - sc->qtail;
643
644 splx(s);
645 uiomove(&sc->qbuf[sc->qtail], l, uio);
646 s = splusb();
647
648 if ( sc->qcount - l < 0 ) {
649 DPRINTF(("qcount below 0, count=%d l=%d\n", sc->qcount, l));
650 sc->qcount = l;
651 }
652 sc->qcount -= l; /* remove the bytes from the buffer */
653 sc->qtail = (sc->qtail + l) % sizeof(sc->qbuf);
654 }
655 splx(s);
656
657 return 0;
658 }
659
660 static int
661 ums_poll(dev_t dev, int events, struct proc *p)
662 {
663 struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
664 int revents = 0;
665 int s;
666
667 if (!sc) {
668 DPRINTF(("sc not found at poll"));
669 return 0; /* just to make sure */
670 }
671
672 s = splusb();
673 if (events & (POLLIN | POLLRDNORM))
674 if (sc->qcount) {
675 revents = events & (POLLIN | POLLRDNORM);
676 } else {
677 sc->state |= UMS_SELECT;
678 selrecord(p, &sc->rsel);
679 }
680 splx(s);
681
682 return revents;
683 }
684
685 int
686 ums_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
687 {
688 struct ums_softc *sc = devclass_get_softc(ums_devclass, UMSUNIT(dev));
689 int error = 0;
690 int s;
691
692 if (!sc) {
693 DPRINTF(("sc not found at ioctl"));
694 return ENOENT;
695 }
696
697 switch(cmd) {
698 case MOUSE_GETHWINFO:
699 *(mousehw_t *)addr = sc->hw;
700 break;
701 case MOUSE_GETMODE:
702 *(mousemode_t *)addr = sc->mode;
703 break;
704 case MOUSE_GETLEVEL:
705 *(int *)addr = sc->mode.level;
706 break;
707 case MOUSE_GETSTATUS: {
708 mousestatus_t *status = (mousestatus_t *) addr;
709
710 s = splusb();
711 *status = sc->status;
712 sc->status.obutton = sc->status.button;
713 sc->status.button = 0;
714 sc->status.dx = sc->status.dy = sc->status.dz = 0;
715 splx(s);
716
717 if (status->dx || status->dy || status->dz)
718 status->flags |= MOUSE_POSCHANGED;
719 if (status->button != status->obutton)
720 status->flags |= MOUSE_BUTTONSCHANGED;
721 break;
722 }
723 default:
724 error = ENOTTY;
725 }
726
727 return error;
728 }
729 #endif
730
731 #if defined(__FreeBSD__)
732 CDEV_DRIVER_MODULE(ums, usb, ums_driver, ums_devclass,
733 UMS_CDEV_MAJOR, ums_cdevsw, usb_driver_load, 0);
734 #endif
735
736