1 /* $NetBSD: uhid.c,v 1.29 1999/12/18 22:47:11 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@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 %lu chars\n", (u_long)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