1 /* $OpenBSD: uhid.c,v 1.58 2014/07/12 18:48:52 tedu Exp $ */ 2 /* $NetBSD: uhid.c,v 1.57 2003/03/11 16:44:00 augustss 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 (lennart@augustsson.net) 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 * 21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf 36 */ 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/kernel.h> 41 #include <sys/malloc.h> 42 #include <sys/signalvar.h> 43 #include <sys/device.h> 44 #include <sys/ioctl.h> 45 #include <sys/conf.h> 46 #include <sys/tty.h> 47 #include <sys/file.h> 48 #include <sys/selinfo.h> 49 #include <sys/proc.h> 50 #include <sys/vnode.h> 51 #include <sys/poll.h> 52 53 #include <dev/usb/usb.h> 54 #include <dev/usb/usbhid.h> 55 56 #include <dev/usb/usbdevs.h> 57 #include <dev/usb/usbdi.h> 58 #include <dev/usb/usbdi_util.h> 59 #include <dev/usb/hid.h> 60 #include <dev/usb/usb_quirks.h> 61 62 #include <dev/usb/uhidev.h> 63 64 #ifdef UHID_DEBUG 65 #define DPRINTF(x) do { if (uhiddebug) printf x; } while (0) 66 #define DPRINTFN(n,x) do { if (uhiddebug>(n)) printf x; } while (0) 67 int uhiddebug = 0; 68 #else 69 #define DPRINTF(x) 70 #define DPRINTFN(n,x) 71 #endif 72 73 struct uhid_softc { 74 struct uhidev sc_hdev; 75 76 u_char *sc_obuf; 77 78 struct clist sc_q; 79 struct selinfo sc_rsel; 80 struct process *sc_async; /* process that wants SIGIO */ 81 u_char sc_state; /* driver state */ 82 #define UHID_ASLP 0x01 /* waiting for device data */ 83 #define UHID_IMMED 0x02 /* return read data immediately */ 84 85 int sc_refcnt; 86 }; 87 88 #define UHIDUNIT(dev) (minor(dev)) 89 #define UHID_CHUNK 128 /* chunk size for read */ 90 #define UHID_BSIZE 1020 /* buffer size */ 91 92 void uhid_intr(struct uhidev *, void *, u_int len); 93 94 int uhid_do_read(struct uhid_softc *, struct uio *uio, int); 95 int uhid_do_write(struct uhid_softc *, struct uio *uio, int); 96 int uhid_do_ioctl(struct uhid_softc*, u_long, caddr_t, int, 97 struct proc *); 98 99 int uhid_match(struct device *, void *, void *); 100 void uhid_attach(struct device *, struct device *, void *); 101 int uhid_detach(struct device *, int); 102 103 struct cfdriver uhid_cd = { 104 NULL, "uhid", DV_DULL 105 }; 106 107 const struct cfattach uhid_ca = { 108 sizeof(struct uhid_softc), 109 uhid_match, 110 uhid_attach, 111 uhid_detach, 112 }; 113 114 int 115 uhid_match(struct device *parent, void *match, void *aux) 116 { 117 struct uhidev_attach_arg *uha = aux; 118 119 if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID) 120 return (UMATCH_NONE); 121 122 return (UMATCH_IFACECLASS_GENERIC); 123 } 124 125 void 126 uhid_attach(struct device *parent, struct device *self, void *aux) 127 { 128 struct uhid_softc *sc = (struct uhid_softc *)self; 129 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 130 int size, repid; 131 void *desc; 132 133 sc->sc_hdev.sc_intr = uhid_intr; 134 sc->sc_hdev.sc_parent = uha->parent; 135 sc->sc_hdev.sc_udev = uha->uaa->device; 136 sc->sc_hdev.sc_report_id = uha->reportid; 137 138 uhidev_get_report_desc(uha->parent, &desc, &size); 139 repid = uha->reportid; 140 sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid); 141 sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid); 142 sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid); 143 144 printf(": input=%d, output=%d, feature=%d\n", 145 sc->sc_hdev.sc_isize, sc->sc_hdev.sc_osize, sc->sc_hdev.sc_fsize); 146 } 147 148 int 149 uhid_detach(struct device *self, int flags) 150 { 151 struct uhid_softc *sc = (struct uhid_softc *)self; 152 int s; 153 int maj, mn; 154 155 DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags)); 156 157 if (sc->sc_hdev.sc_state & UHIDEV_OPEN) { 158 s = splusb(); 159 if (--sc->sc_refcnt >= 0) { 160 /* Wake everyone */ 161 wakeup(&sc->sc_q); 162 /* Wait for processes to go away. */ 163 usb_detach_wait(&sc->sc_hdev.sc_dev); 164 } 165 splx(s); 166 } 167 168 /* locate the major number */ 169 for (maj = 0; maj < nchrdev; maj++) 170 if (cdevsw[maj].d_open == uhidopen) 171 break; 172 173 /* Nuke the vnodes for any open instances (calls close). */ 174 mn = self->dv_unit; 175 vdevgone(maj, mn, mn, VCHR); 176 177 return (0); 178 } 179 180 void 181 uhid_intr(struct uhidev *addr, void *data, u_int len) 182 { 183 struct uhid_softc *sc = (struct uhid_softc *)addr; 184 185 #ifdef UHID_DEBUG 186 if (uhiddebug > 5) { 187 u_int32_t i; 188 189 DPRINTF(("uhid_intr: data =")); 190 for (i = 0; i < len; i++) 191 DPRINTF((" %02x", ((u_char *)data)[i])); 192 DPRINTF(("\n")); 193 } 194 #endif 195 196 (void)b_to_q(data, len, &sc->sc_q); 197 198 if (sc->sc_state & UHID_ASLP) { 199 sc->sc_state &= ~UHID_ASLP; 200 DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q)); 201 wakeup(&sc->sc_q); 202 } 203 selwakeup(&sc->sc_rsel); 204 if (sc->sc_async != NULL) { 205 DPRINTFN(3, ("uhid_intr: sending SIGIO %p\n", sc->sc_async)); 206 prsignal(sc->sc_async, SIGIO); 207 } 208 } 209 210 int 211 uhidopen(dev_t dev, int flag, int mode, struct proc *p) 212 { 213 struct uhid_softc *sc; 214 int error; 215 216 if (UHIDUNIT(dev) >= uhid_cd.cd_ndevs) 217 return (ENXIO); 218 sc = uhid_cd.cd_devs[UHIDUNIT(dev)]; 219 if (sc == NULL) 220 return (ENXIO); 221 222 DPRINTF(("uhidopen: sc=%p\n", sc)); 223 224 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 225 return (ENXIO); 226 227 error = uhidev_open(&sc->sc_hdev); 228 if (error) 229 return (error); 230 231 clalloc(&sc->sc_q, UHID_BSIZE, 0); 232 233 sc->sc_obuf = malloc(sc->sc_hdev.sc_osize, M_USBDEV, M_WAITOK); 234 sc->sc_state &= ~UHID_IMMED; 235 sc->sc_async = NULL; 236 237 return (0); 238 } 239 240 int 241 uhidclose(dev_t dev, int flag, int mode, struct proc *p) 242 { 243 struct uhid_softc *sc; 244 245 sc = uhid_cd.cd_devs[UHIDUNIT(dev)]; 246 247 DPRINTF(("uhidclose: sc=%p\n", sc)); 248 249 clfree(&sc->sc_q); 250 free(sc->sc_obuf, M_USBDEV, 0); 251 sc->sc_async = NULL; 252 uhidev_close(&sc->sc_hdev); 253 254 return (0); 255 } 256 257 int 258 uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag) 259 { 260 int s; 261 int error = 0; 262 int extra; 263 size_t length; 264 u_char buffer[UHID_CHUNK]; 265 usbd_status err; 266 267 DPRINTFN(1, ("uhidread\n")); 268 if (sc->sc_state & UHID_IMMED) { 269 DPRINTFN(1, ("uhidread immed\n")); 270 extra = sc->sc_hdev.sc_report_id != 0; 271 err = uhidev_get_report(&sc->sc_hdev, UHID_INPUT_REPORT, 272 sc->sc_hdev.sc_report_id, buffer, 273 sc->sc_hdev.sc_isize + extra); 274 if (err) 275 return (EIO); 276 return (uiomove(buffer+extra, sc->sc_hdev.sc_isize, uio)); 277 } 278 279 s = splusb(); 280 while (sc->sc_q.c_cc == 0) { 281 if (flag & IO_NDELAY) { 282 splx(s); 283 return (EWOULDBLOCK); 284 } 285 sc->sc_state |= UHID_ASLP; 286 DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q)); 287 error = tsleep(&sc->sc_q, PZERO | PCATCH, "uhidrea", 0); 288 DPRINTFN(5, ("uhidread: woke, error=%d\n", error)); 289 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 290 error = EIO; 291 if (error) { 292 sc->sc_state &= ~UHID_ASLP; 293 break; 294 } 295 } 296 splx(s); 297 298 /* Transfer as many chunks as possible. */ 299 while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) { 300 length = min(sc->sc_q.c_cc, uio->uio_resid); 301 if (length > sizeof(buffer)) 302 length = sizeof(buffer); 303 304 /* Remove a small chunk from the input queue. */ 305 (void) q_to_b(&sc->sc_q, buffer, length); 306 DPRINTFN(5, ("uhidread: got %lu chars\n", (u_long)length)); 307 308 /* Copy the data to the user process. */ 309 if ((error = uiomove(buffer, length, uio)) != 0) 310 break; 311 } 312 313 return (error); 314 } 315 316 int 317 uhidread(dev_t dev, struct uio *uio, int flag) 318 { 319 struct uhid_softc *sc; 320 int error; 321 322 sc = uhid_cd.cd_devs[UHIDUNIT(dev)]; 323 324 sc->sc_refcnt++; 325 error = uhid_do_read(sc, uio, flag); 326 if (--sc->sc_refcnt < 0) 327 usb_detach_wakeup(&sc->sc_hdev.sc_dev); 328 return (error); 329 } 330 331 int 332 uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag) 333 { 334 int error; 335 int size; 336 usbd_status err; 337 338 DPRINTFN(1, ("uhidwrite\n")); 339 340 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 341 return (EIO); 342 343 size = sc->sc_hdev.sc_osize; 344 error = 0; 345 if (uio->uio_resid != size) 346 return (EINVAL); 347 error = uiomove(sc->sc_obuf, size, uio); 348 if (!error) { 349 err = uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT, 350 sc->sc_hdev.sc_report_id, sc->sc_obuf, size); 351 if (err) 352 error = EIO; 353 } 354 355 return (error); 356 } 357 358 int 359 uhidwrite(dev_t dev, struct uio *uio, int flag) 360 { 361 struct uhid_softc *sc; 362 int error; 363 364 sc = uhid_cd.cd_devs[UHIDUNIT(dev)]; 365 366 sc->sc_refcnt++; 367 error = uhid_do_write(sc, uio, flag); 368 if (--sc->sc_refcnt < 0) 369 usb_detach_wakeup(&sc->sc_hdev.sc_dev); 370 return (error); 371 } 372 373 int 374 uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, 375 int flag, struct proc *p) 376 { 377 u_char buffer[UHID_CHUNK]; 378 int size, extra; 379 usbd_status err; 380 int rc; 381 382 DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd)); 383 384 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 385 return (EIO); 386 387 switch (cmd) { 388 case FIONBIO: 389 /* All handled in the upper FS layer. */ 390 break; 391 392 case FIOASYNC: 393 if (*(int *)addr) { 394 if (sc->sc_async != NULL) 395 return (EBUSY); 396 sc->sc_async = p->p_p; 397 DPRINTF(("uhid_do_ioctl: FIOASYNC %p\n", p)); 398 } else 399 sc->sc_async = NULL; 400 break; 401 402 /* XXX this is not the most general solution. */ 403 case TIOCSPGRP: 404 if (sc->sc_async == NULL) 405 return (EINVAL); 406 if (*(int *)addr != sc->sc_async->ps_pgid) 407 return (EPERM); 408 break; 409 410 case USB_SET_IMMED: 411 if (*(int *)addr) { 412 extra = sc->sc_hdev.sc_report_id != 0; 413 err = uhidev_get_report(&sc->sc_hdev, UHID_INPUT_REPORT, 414 sc->sc_hdev.sc_report_id, buffer, 415 sc->sc_hdev.sc_isize + extra); 416 if (err) 417 return (EOPNOTSUPP); 418 419 sc->sc_state |= UHID_IMMED; 420 } else 421 sc->sc_state &= ~UHID_IMMED; 422 break; 423 424 case USB_GET_DEVICEINFO: 425 usbd_fill_deviceinfo(sc->sc_hdev.sc_udev, 426 (struct usb_device_info *)addr, 1); 427 break; 428 429 case USB_GET_STRING_DESC: 430 { 431 struct usb_string_desc *si = (struct usb_string_desc *)addr; 432 err = usbd_get_string_desc(sc->sc_hdev.sc_udev, 433 si->usd_string_index, 434 si->usd_language_id, &si->usd_desc, &size); 435 if (err) 436 return (EINVAL); 437 break; 438 } 439 440 case USB_GET_REPORT_DESC: 441 case USB_GET_REPORT: 442 case USB_SET_REPORT: 443 case USB_GET_REPORT_ID: 444 default: 445 rc = uhidev_ioctl(&sc->sc_hdev, cmd, addr, flag, p); 446 if (rc == -1) 447 rc = EINVAL; 448 return rc; 449 } 450 return (0); 451 } 452 453 int 454 uhidioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) 455 { 456 struct uhid_softc *sc; 457 int error; 458 459 sc = uhid_cd.cd_devs[UHIDUNIT(dev)]; 460 461 sc->sc_refcnt++; 462 error = uhid_do_ioctl(sc, cmd, addr, flag, p); 463 if (--sc->sc_refcnt < 0) 464 usb_detach_wakeup(&sc->sc_hdev.sc_dev); 465 return (error); 466 } 467 468 int 469 uhidpoll(dev_t dev, int events, struct proc *p) 470 { 471 struct uhid_softc *sc; 472 int revents = 0; 473 int s; 474 475 sc = uhid_cd.cd_devs[UHIDUNIT(dev)]; 476 477 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 478 return (POLLERR); 479 480 s = splusb(); 481 if (events & (POLLOUT | POLLWRNORM)) 482 revents |= events & (POLLOUT | POLLWRNORM); 483 if (events & (POLLIN | POLLRDNORM)) { 484 if (sc->sc_q.c_cc > 0) 485 revents |= events & (POLLIN | POLLRDNORM); 486 else 487 selrecord(p, &sc->sc_rsel); 488 } 489 490 splx(s); 491 return (revents); 492 } 493 494 void filt_uhidrdetach(struct knote *); 495 int filt_uhidread(struct knote *, long); 496 int uhidkqfilter(dev_t, struct knote *); 497 498 void 499 filt_uhidrdetach(struct knote *kn) 500 { 501 struct uhid_softc *sc = (void *)kn->kn_hook; 502 int s; 503 504 s = splusb(); 505 SLIST_REMOVE(&sc->sc_rsel.si_note, kn, knote, kn_selnext); 506 splx(s); 507 } 508 509 int 510 filt_uhidread(struct knote *kn, long hint) 511 { 512 struct uhid_softc *sc = (void *)kn->kn_hook; 513 514 kn->kn_data = sc->sc_q.c_cc; 515 return (kn->kn_data > 0); 516 } 517 518 struct filterops uhidread_filtops = 519 { 1, NULL, filt_uhidrdetach, filt_uhidread }; 520 521 struct filterops uhid_seltrue_filtops = 522 { 1, NULL, filt_uhidrdetach, filt_seltrue }; 523 524 int 525 uhidkqfilter(dev_t dev, struct knote *kn) 526 { 527 struct uhid_softc *sc; 528 struct klist *klist; 529 int s; 530 531 sc = uhid_cd.cd_devs[UHIDUNIT(dev)]; 532 533 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 534 return (EIO); 535 536 switch (kn->kn_filter) { 537 case EVFILT_READ: 538 klist = &sc->sc_rsel.si_note; 539 kn->kn_fop = &uhidread_filtops; 540 break; 541 542 case EVFILT_WRITE: 543 klist = &sc->sc_rsel.si_note; 544 kn->kn_fop = &uhid_seltrue_filtops; 545 break; 546 547 default: 548 return (EINVAL); 549 } 550 551 kn->kn_hook = (void *)sc; 552 553 s = splusb(); 554 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 555 splx(s); 556 557 return (0); 558 } 559