uhid.c revision 1.26.4.1 1 /* $NetBSD: uhid.c,v 1.26.4.1 1999/11/15 00:41:34 fvdl 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 /*
41 * HID spec: http://www.usb.org/developers/data/usbhid10.pdf
42 */
43
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/malloc.h>
48 #if defined(__NetBSD__) || defined(__OpenBSD__)
49 #include <sys/device.h>
50 #include <sys/ioctl.h>
51 #elif defined(__FreeBSD__)
52 #include <sys/ioccom.h>
53 #include <sys/filio.h>
54 #include <sys/module.h>
55 #include <sys/bus.h>
56 #include <sys/ioccom.h>
57 #endif
58 #include <sys/conf.h>
59 #include <sys/tty.h>
60 #include <sys/file.h>
61 #include <sys/select.h>
62 #include <sys/proc.h>
63 #include <sys/vnode.h>
64 #include <sys/poll.h>
65
66 #include <dev/usb/usb.h>
67 #include <dev/usb/usbhid.h>
68
69 #include <dev/usb/usbdi.h>
70 #include <dev/usb/usbdi_util.h>
71 #include <dev/usb/hid.h>
72 #include <dev/usb/usb_quirks.h>
73
74 #ifdef UHID_DEBUG
75 #define DPRINTF(x) if (uhiddebug) logprintf x
76 #define DPRINTFN(n,x) if (uhiddebug>(n)) logprintf x
77 int uhiddebug = 0;
78 #else
79 #define DPRINTF(x)
80 #define DPRINTFN(n,x)
81 #endif
82
83 struct uhid_softc {
84 USBBASEDEVICE sc_dev; /* base device */
85 usbd_interface_handle sc_iface; /* interface */
86 usbd_pipe_handle sc_intrpipe; /* interrupt pipe */
87 int sc_ep_addr;
88
89 int sc_isize;
90 int sc_osize;
91 int sc_fsize;
92 u_int8_t sc_iid;
93 u_int8_t sc_oid;
94 u_int8_t sc_fid;
95
96 char *sc_ibuf;
97 char *sc_obuf;
98
99 void *sc_repdesc;
100 int sc_repdesc_size;
101
102 struct clist sc_q;
103 struct selinfo sc_rsel;
104 u_char sc_state; /* driver state */
105 #define UHID_OPEN 0x01 /* device is open */
106 #define UHID_ASLP 0x02 /* waiting for device data */
107 #define UHID_NEEDCLEAR 0x04 /* needs clearing endpoint stall */
108 #define UHID_IMMED 0x08 /* return read data immediately */
109
110 int sc_refcnt;
111 u_char sc_dying;
112 };
113
114 #define UHIDUNIT(dev) (minor(dev))
115 #define UHID_CHUNK 128 /* chunk size for read */
116 #define UHID_BSIZE 1020 /* buffer size */
117
118 #if defined(__NetBSD__) || defined(__OpenBSD__)
119 cdev_decl(uhid);
120 #elif defined(__FreeBSD__)
121 d_open_t uhidopen;
122 d_close_t uhidclose;
123 d_read_t uhidread;
124 d_write_t uhidwrite;
125 d_ioctl_t uhidioctl;
126 d_poll_t uhidpoll;
127
128 #define UHID_CDEV_MAJOR 122
129
130 static struct cdevsw uhid_cdevsw = {
131 /* open */ uhidopen,
132 /* close */ uhidclose,
133 /* read */ uhidread,
134 /* write */ uhidwrite,
135 /* ioctl */ uhidioctl,
136 /* poll */ uhidpoll,
137 /* mmap */ nommap,
138 /* strategy */ nostrategy,
139 /* name */ "uhid",
140 /* maj */ UHID_CDEV_MAJOR,
141 /* dump */ nodump,
142 /* psize */ nopsize,
143 /* flags */ 0,
144 /* bmaj */ -1
145 };
146 #endif
147
148 static void uhid_intr __P((usbd_xfer_handle, usbd_private_handle,
149 usbd_status));
150
151 static int uhid_do_read __P((struct uhid_softc *, struct uio *uio, int));
152 static int uhid_do_write __P((struct uhid_softc *, struct uio *uio, int));
153 static int uhid_do_ioctl __P((struct uhid_softc *, u_long, caddr_t, int,
154 struct proc *));
155
156 USB_DECLARE_DRIVER(uhid);
157
158 USB_MATCH(uhid)
159 {
160 USB_MATCH_START(uhid, uaa);
161 usb_interface_descriptor_t *id;
162
163 if (!uaa->iface)
164 return (UMATCH_NONE);
165 id = usbd_get_interface_descriptor(uaa->iface);
166 if (!id || id->bInterfaceClass != UCLASS_HID)
167 return (UMATCH_NONE);
168 return (UMATCH_IFACECLASS_GENERIC);
169 }
170
171 USB_ATTACH(uhid)
172 {
173 USB_ATTACH_START(uhid, sc, uaa);
174 usbd_interface_handle iface = uaa->iface;
175 usb_interface_descriptor_t *id;
176 usb_endpoint_descriptor_t *ed;
177 int size;
178 void *desc;
179 usbd_status err;
180 char devinfo[1024];
181
182 sc->sc_iface = iface;
183 id = usbd_get_interface_descriptor(iface);
184 usbd_devinfo(uaa->device, 0, devinfo);
185 USB_ATTACH_SETUP;
186 printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev),
187 devinfo, id->bInterfaceClass, id->bInterfaceSubClass);
188
189 ed = usbd_interface2endpoint_descriptor(iface, 0);
190 if (ed == NULL) {
191 printf("%s: could not read endpoint descriptor\n",
192 USBDEVNAME(sc->sc_dev));
193 sc->sc_dying = 1;
194 USB_ATTACH_ERROR_RETURN;
195 }
196
197 DPRINTFN(10,("uhid_attach: bLength=%d bDescriptorType=%d "
198 "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
199 " bInterval=%d\n",
200 ed->bLength, ed->bDescriptorType,
201 ed->bEndpointAddress & UE_ADDR,
202 UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out",
203 ed->bmAttributes & UE_XFERTYPE,
204 UGETW(ed->wMaxPacketSize), ed->bInterval));
205
206 if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN ||
207 (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
208 printf("%s: unexpected endpoint\n", USBDEVNAME(sc->sc_dev));
209 sc->sc_dying = 1;
210 USB_ATTACH_ERROR_RETURN;
211 }
212
213 sc->sc_ep_addr = ed->bEndpointAddress;
214
215 desc = 0;
216 err = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_USBDEV);
217 if (err) {
218 printf("%s: no report descriptor\n", USBDEVNAME(sc->sc_dev));
219 sc->sc_dying = 1;
220 if (desc != NULL)
221 free(desc, M_USBDEV);
222 USB_ATTACH_ERROR_RETURN;
223 }
224
225 (void)usbd_set_idle(iface, 0, 0);
226
227 sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid);
228 sc->sc_osize = hid_report_size(desc, size, hid_output, &sc->sc_oid);
229 sc->sc_fsize = hid_report_size(desc, size, hid_feature, &sc->sc_fid);
230
231 sc->sc_repdesc = desc;
232 sc->sc_repdesc_size = size;
233
234 USB_ATTACH_SUCCESS_RETURN;
235 }
236
237 #if defined(__NetBSD__) || defined(__OpenBSD__)
238 int
239 uhid_activate(self, act)
240 device_ptr_t self;
241 enum devact act;
242 {
243 struct uhid_softc *sc = (struct uhid_softc *)self;
244
245 switch (act) {
246 case DVACT_ACTIVATE:
247 return (EOPNOTSUPP);
248 break;
249
250 case DVACT_DEACTIVATE:
251 sc->sc_dying = 1;
252 break;
253 }
254 return (0);
255 }
256 #endif
257
258 USB_DETACH(uhid)
259 {
260 USB_DETACH_START(uhid, sc);
261 int s;
262 #if defined(__NetBSD__) || defined(__OpenBSD__)
263 int maj, mn;
264
265 DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags));
266 #else
267 DPRINTF(("uhid_detach: sc=%p\n", sc));
268 #endif
269
270 sc->sc_dying = 1;
271 if (sc->sc_intrpipe != NULL)
272 usbd_abort_pipe(sc->sc_intrpipe);
273
274 if (sc->sc_state & UHID_OPEN) {
275 s = splusb();
276 if (--sc->sc_refcnt >= 0) {
277 /* Wake everyone */
278 wakeup(&sc->sc_q);
279 /* Wait for processes to go away. */
280 usb_detach_wait(USBDEV(sc->sc_dev));
281 }
282 splx(s);
283 }
284
285 #if defined(__NetBSD__) || defined(__OpenBSD__)
286 /* locate the major number */
287 for (maj = 0; maj < nchrdev; maj++)
288 if (cdevsw[maj].d_open == uhidopen)
289 break;
290
291 /* Nuke the vnodes for any open instances (calls close). */
292 mn = self->dv_unit;
293 vdevgone(maj, mn, mn, VCHR);
294 #elif defined(__FreeBSD__)
295 /* XXX not implemented yet */
296 #endif
297
298 free(sc->sc_repdesc, M_USBDEV);
299
300 return (0);
301 }
302
303 void
304 uhid_intr(xfer, addr, status)
305 usbd_xfer_handle xfer;
306 usbd_private_handle addr;
307 usbd_status status;
308 {
309 struct uhid_softc *sc = addr;
310
311 DPRINTFN(5, ("uhid_intr: status=%d\n", status));
312 DPRINTFN(5, ("uhid_intr: data = %02x %02x %02x\n",
313 sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2]));
314
315 if (status == USBD_CANCELLED)
316 return;
317
318 if (status != USBD_NORMAL_COMPLETION) {
319 DPRINTF(("uhid_intr: status=%d\n", status));
320 sc->sc_state |= UHID_NEEDCLEAR;
321 return;
322 }
323
324 (void) b_to_q(sc->sc_ibuf, sc->sc_isize, &sc->sc_q);
325
326 if (sc->sc_state & UHID_ASLP) {
327 sc->sc_state &= ~UHID_ASLP;
328 DPRINTFN(5, ("uhid_intr: waking %p\n", sc));
329 wakeup(&sc->sc_q);
330 }
331 selwakeup(&sc->sc_rsel);
332 }
333
334 int
335 uhidopen(dev, flag, mode, p)
336 dev_t dev;
337 int flag;
338 int mode;
339 struct proc *p;
340 {
341 struct uhid_softc *sc;
342 usbd_status err;
343
344 USB_GET_SC_OPEN(uhid, UHIDUNIT(dev), sc);
345
346 DPRINTF(("uhidopen: sc=%p\n", sc));
347
348 if (sc->sc_dying)
349 return (ENXIO);
350
351 if (sc->sc_state & UHID_OPEN)
352 return (EBUSY);
353 sc->sc_state |= UHID_OPEN;
354
355 if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1) {
356 sc->sc_state &= ~UHID_OPEN;
357 return (ENOMEM);
358 }
359
360 sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
361 sc->sc_obuf = malloc(sc->sc_osize, M_USBDEV, M_WAITOK);
362
363 /* Set up interrupt pipe. */
364 err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
365 USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, sc->sc_ibuf,
366 sc->sc_isize, uhid_intr);
367 if (err) {
368 DPRINTF(("uhidopen: usbd_open_pipe_intr failed, "
369 "error=%d\n",err));
370 free(sc->sc_ibuf, M_USBDEV);
371 free(sc->sc_obuf, M_USBDEV);
372 sc->sc_state &= ~UHID_OPEN;
373 return (EIO);
374 }
375
376 sc->sc_state &= ~UHID_IMMED;
377
378 return (0);
379 }
380
381 int
382 uhidclose(dev, flag, mode, p)
383 dev_t dev;
384 int flag;
385 int mode;
386 struct proc *p;
387 {
388 struct uhid_softc *sc;
389
390 USB_GET_SC(uhid, UHIDUNIT(dev), sc);
391
392 DPRINTF(("uhidclose: sc=%p\n", sc));
393
394 /* Disable interrupts. */
395 usbd_abort_pipe(sc->sc_intrpipe);
396 usbd_close_pipe(sc->sc_intrpipe);
397 sc->sc_intrpipe = 0;
398
399 clfree(&sc->sc_q);
400
401 free(sc->sc_ibuf, M_USBDEV);
402 free(sc->sc_obuf, M_USBDEV);
403
404 sc->sc_state &= ~UHID_OPEN;
405
406 return (0);
407 }
408
409 int
410 uhid_do_read(sc, uio, flag)
411 struct uhid_softc *sc;
412 struct uio *uio;
413 int flag;
414 {
415 int s;
416 int error = 0;
417 size_t length;
418 u_char buffer[UHID_CHUNK];
419 usbd_status err;
420
421 DPRINTFN(1, ("uhidread\n"));
422 if (sc->sc_state & UHID_IMMED) {
423 DPRINTFN(1, ("uhidread immed\n"));
424
425 err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
426 sc->sc_iid, buffer, sc->sc_isize);
427 if (err)
428 return (EIO);
429 return (uiomove(buffer, sc->sc_isize, uio));
430 }
431
432 s = splusb();
433 while (sc->sc_q.c_cc == 0) {
434 if (flag & IO_NDELAY) {
435 splx(s);
436 return (EWOULDBLOCK);
437 }
438 sc->sc_state |= UHID_ASLP;
439 DPRINTFN(5, ("uhidread: sleep on %p\n", sc));
440 error = tsleep(&sc->sc_q, PZERO | PCATCH, "uhidrea", 0);
441 DPRINTFN(5, ("uhidread: woke, error=%d\n", error));
442 if (sc->sc_dying)
443 error = EIO;
444 if (error) {
445 sc->sc_state &= ~UHID_ASLP;
446 break;
447 }
448 if (sc->sc_state & UHID_NEEDCLEAR) {
449 DPRINTFN(-1,("uhidread: clearing stall\n"));
450 sc->sc_state &= ~UHID_NEEDCLEAR;
451 usbd_clear_endpoint_stall(sc->sc_intrpipe);
452 }
453 }
454 splx(s);
455
456 /* Transfer as many chunks as possible. */
457 while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) {
458 length = min(sc->sc_q.c_cc, uio->uio_resid);
459 if (length > sizeof(buffer))
460 length = sizeof(buffer);
461
462 /* Remove a small chunk from the input queue. */
463 (void) q_to_b(&sc->sc_q, buffer, length);
464 DPRINTFN(5, ("uhidread: got %d chars\n", length));
465
466 /* Copy the data to the user process. */
467 if ((error = uiomove(buffer, length, uio)) != 0)
468 break;
469 }
470
471 return (error);
472 }
473
474 int
475 uhidread(dev, uio, flag)
476 dev_t dev;
477 struct uio *uio;
478 int flag;
479 {
480 struct uhid_softc *sc;
481 int error;
482
483 USB_GET_SC(uhid, UHIDUNIT(dev), sc);
484
485 sc->sc_refcnt++;
486 error = uhid_do_read(sc, uio, flag);
487 if (--sc->sc_refcnt < 0)
488 usb_detach_wakeup(USBDEV(sc->sc_dev));
489 return (error);
490 }
491
492 int
493 uhid_do_write(sc, uio, flag)
494 struct uhid_softc *sc;
495 struct uio *uio;
496 int flag;
497 {
498 int error;
499 int size;
500 usbd_status err;
501
502 DPRINTFN(1, ("uhidwrite\n"));
503
504 if (sc->sc_dying)
505 return (EIO);
506
507 size = sc->sc_osize;
508 error = 0;
509 if (uio->uio_resid != size)
510 return (EINVAL);
511 error = uiomove(sc->sc_obuf, size, uio);
512 if (!error) {
513 if (sc->sc_oid)
514 err = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
515 sc->sc_obuf[0], sc->sc_obuf+1, size-1);
516 else
517 err = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
518 0, sc->sc_obuf, size);
519 if (err)
520 error = EIO;
521 }
522
523 return (error);
524 }
525
526 int
527 uhidwrite(dev, uio, flag)
528 dev_t dev;
529 struct uio *uio;
530 int flag;
531 {
532 struct uhid_softc *sc;
533 int error;
534
535 USB_GET_SC(uhid, UHIDUNIT(dev), sc);
536
537 sc->sc_refcnt++;
538 error = uhid_do_write(sc, uio, flag);
539 if (--sc->sc_refcnt < 0)
540 usb_detach_wakeup(USBDEV(sc->sc_dev));
541 return (error);
542 }
543
544 int
545 uhid_do_ioctl(sc, cmd, addr, flag, p)
546 struct uhid_softc *sc;
547 u_long cmd;
548 caddr_t addr;
549 int flag;
550 struct proc *p;
551 {
552 struct usb_ctl_report_desc *rd;
553 struct usb_ctl_report *re;
554 int size, id;
555 usbd_status err;
556
557 DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd));
558
559 if (sc->sc_dying)
560 return (EIO);
561
562 switch (cmd) {
563 case FIONBIO:
564 /* All handled in the upper FS layer. */
565 break;
566
567 case USB_GET_REPORT_DESC:
568 rd = (struct usb_ctl_report_desc *)addr;
569 size = min(sc->sc_repdesc_size, sizeof rd->data);
570 rd->size = size;
571 memcpy(rd->data, sc->sc_repdesc, size);
572 break;
573
574 case USB_SET_IMMED:
575 if (*(int *)addr) {
576 /* XXX should read into ibuf, but does it matter? */
577 err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
578 sc->sc_iid, sc->sc_ibuf, sc->sc_isize);
579 if (err)
580 return (EOPNOTSUPP);
581
582 sc->sc_state |= UHID_IMMED;
583 } else
584 sc->sc_state &= ~UHID_IMMED;
585 break;
586
587 case USB_GET_REPORT:
588 re = (struct usb_ctl_report *)addr;
589 switch (re->report) {
590 case UHID_INPUT_REPORT:
591 size = sc->sc_isize;
592 id = sc->sc_iid;
593 break;
594 case UHID_OUTPUT_REPORT:
595 size = sc->sc_osize;
596 id = sc->sc_oid;
597 break;
598 case UHID_FEATURE_REPORT:
599 size = sc->sc_fsize;
600 id = sc->sc_fid;
601 break;
602 default:
603 return (EINVAL);
604 }
605 err = usbd_get_report(sc->sc_iface, re->report, id, re->data,
606 size);
607 if (err)
608 return (EIO);
609 break;
610
611 default:
612 return (EINVAL);
613 }
614 return (0);
615 }
616
617 int
618 uhidioctl(dev, cmd, addr, flag, p)
619 dev_t dev;
620 u_long cmd;
621 caddr_t addr;
622 int flag;
623 struct proc *p;
624 {
625 struct uhid_softc *sc;
626 int error;
627
628 USB_GET_SC(uhid, UHIDUNIT(dev), sc);
629
630 sc->sc_refcnt++;
631 error = uhid_do_ioctl(sc, cmd, addr, flag, p);
632 if (--sc->sc_refcnt < 0)
633 usb_detach_wakeup(USBDEV(sc->sc_dev));
634 return (error);
635 }
636
637 int
638 uhidpoll(dev, events, p)
639 dev_t dev;
640 int events;
641 struct proc *p;
642 {
643 struct uhid_softc *sc;
644 int revents = 0;
645 int s;
646
647 USB_GET_SC(uhid, UHIDUNIT(dev), sc);
648
649 if (sc->sc_dying)
650 return (EIO);
651
652 s = splusb();
653 if (events & (POLLOUT | POLLWRNORM))
654 revents |= events & (POLLOUT | POLLWRNORM);
655 if (events & (POLLIN | POLLRDNORM)) {
656 if (sc->sc_q.c_cc > 0)
657 revents |= events & (POLLIN | POLLRDNORM);
658 else
659 selrecord(p, &sc->sc_rsel);
660 }
661
662 splx(s);
663 return (revents);
664 }
665
666 #if defined(__FreeBSD__)
667 DEV_DRIVER_MODULE(uhid, uhub, uhid_driver, uhid_devclass,
668 uhid_cdevsw, usbd_driver_load, 0);
669 #endif
670