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