1 /* $OpenBSD: uhid.c,v 1.69 2017/09/23 06:12:14 mpi 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 60 #include <dev/usb/uhidev.h> 61 62 #ifdef UHID_DEBUG 63 #define DPRINTF(x) do { if (uhiddebug) printf x; } while (0) 64 #define DPRINTFN(n,x) do { if (uhiddebug>(n)) printf x; } while (0) 65 int uhiddebug = 0; 66 #else 67 #define DPRINTF(x) 68 #define DPRINTFN(n,x) 69 #endif 70 71 struct uhid_softc { 72 struct uhidev sc_hdev; 73 74 u_char *sc_obuf; 75 76 struct clist sc_q; 77 struct selinfo sc_rsel; 78 u_char sc_state; /* driver state */ 79 #define UHID_ASLP 0x01 /* waiting for device data */ 80 81 int sc_refcnt; 82 }; 83 84 #define UHIDUNIT(dev) (minor(dev)) 85 #define UHID_CHUNK 128 /* chunk size for read */ 86 #define UHID_BSIZE 1020 /* buffer size */ 87 88 void uhid_intr(struct uhidev *, void *, u_int len); 89 90 int uhid_do_read(struct uhid_softc *, struct uio *uio, int); 91 int uhid_do_write(struct uhid_softc *, struct uio *uio, int); 92 int uhid_do_ioctl(struct uhid_softc*, u_long, caddr_t, int, 93 struct proc *); 94 95 int uhid_match(struct device *, void *, void *); 96 void uhid_attach(struct device *, struct device *, void *); 97 int uhid_detach(struct device *, int); 98 99 struct cfdriver uhid_cd = { 100 NULL, "uhid", DV_DULL 101 }; 102 103 const struct cfattach uhid_ca = { 104 sizeof(struct uhid_softc), 105 uhid_match, 106 uhid_attach, 107 uhid_detach, 108 }; 109 110 int 111 uhid_match(struct device *parent, void *match, void *aux) 112 { 113 struct uhidev_attach_arg *uha = aux; 114 115 if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID) 116 return (UMATCH_NONE); 117 118 return (UMATCH_IFACECLASS_GENERIC); 119 } 120 121 void 122 uhid_attach(struct device *parent, struct device *self, void *aux) 123 { 124 struct uhid_softc *sc = (struct uhid_softc *)self; 125 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 126 int size, repid; 127 void *desc; 128 129 sc->sc_hdev.sc_intr = uhid_intr; 130 sc->sc_hdev.sc_parent = uha->parent; 131 sc->sc_hdev.sc_udev = uha->uaa->device; 132 sc->sc_hdev.sc_report_id = uha->reportid; 133 134 uhidev_get_report_desc(uha->parent, &desc, &size); 135 repid = uha->reportid; 136 sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid); 137 sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid); 138 sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid); 139 140 printf(": input=%d, output=%d, feature=%d\n", 141 sc->sc_hdev.sc_isize, sc->sc_hdev.sc_osize, sc->sc_hdev.sc_fsize); 142 } 143 144 int 145 uhid_detach(struct device *self, int flags) 146 { 147 struct uhid_softc *sc = (struct uhid_softc *)self; 148 int s; 149 int maj, mn; 150 151 DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags)); 152 153 if (sc->sc_hdev.sc_state & UHIDEV_OPEN) { 154 s = splusb(); 155 if (--sc->sc_refcnt >= 0) { 156 /* Wake everyone */ 157 wakeup(&sc->sc_q); 158 /* Wait for processes to go away. */ 159 usb_detach_wait(&sc->sc_hdev.sc_dev); 160 } 161 splx(s); 162 } 163 164 /* locate the major number */ 165 for (maj = 0; maj < nchrdev; maj++) 166 if (cdevsw[maj].d_open == uhidopen) 167 break; 168 169 /* Nuke the vnodes for any open instances (calls close). */ 170 mn = self->dv_unit; 171 vdevgone(maj, mn, mn, VCHR); 172 173 return (0); 174 } 175 176 void 177 uhid_intr(struct uhidev *addr, void *data, u_int len) 178 { 179 struct uhid_softc *sc = (struct uhid_softc *)addr; 180 181 #ifdef UHID_DEBUG 182 if (uhiddebug > 5) { 183 u_int32_t i; 184 185 DPRINTF(("uhid_intr: data =")); 186 for (i = 0; i < len; i++) 187 DPRINTF((" %02x", ((u_char *)data)[i])); 188 DPRINTF(("\n")); 189 } 190 #endif 191 192 (void)b_to_q(data, len, &sc->sc_q); 193 194 if (sc->sc_state & UHID_ASLP) { 195 sc->sc_state &= ~UHID_ASLP; 196 DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q)); 197 wakeup(&sc->sc_q); 198 } 199 selwakeup(&sc->sc_rsel); 200 } 201 202 int 203 uhidopen(dev_t dev, int flag, int mode, struct proc *p) 204 { 205 struct uhid_softc *sc; 206 int error; 207 208 if (UHIDUNIT(dev) >= uhid_cd.cd_ndevs) 209 return (ENXIO); 210 sc = uhid_cd.cd_devs[UHIDUNIT(dev)]; 211 if (sc == NULL) 212 return (ENXIO); 213 214 DPRINTF(("uhidopen: sc=%p\n", sc)); 215 216 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 217 return (ENXIO); 218 219 error = uhidev_open(&sc->sc_hdev); 220 if (error) 221 return (error); 222 223 clalloc(&sc->sc_q, UHID_BSIZE, 0); 224 225 sc->sc_obuf = malloc(sc->sc_hdev.sc_osize, M_USBDEV, M_WAITOK); 226 227 return (0); 228 } 229 230 int 231 uhidclose(dev_t dev, int flag, int mode, struct proc *p) 232 { 233 struct uhid_softc *sc; 234 235 sc = uhid_cd.cd_devs[UHIDUNIT(dev)]; 236 237 DPRINTF(("uhidclose: sc=%p\n", sc)); 238 239 clfree(&sc->sc_q); 240 free(sc->sc_obuf, M_USBDEV, sc->sc_hdev.sc_osize); 241 uhidev_close(&sc->sc_hdev); 242 243 return (0); 244 } 245 246 int 247 uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag) 248 { 249 int s; 250 int error = 0; 251 size_t length; 252 u_char buffer[UHID_CHUNK]; 253 254 DPRINTFN(1, ("uhidread\n")); 255 256 s = splusb(); 257 while (sc->sc_q.c_cc == 0) { 258 if (flag & IO_NDELAY) { 259 splx(s); 260 return (EWOULDBLOCK); 261 } 262 sc->sc_state |= UHID_ASLP; 263 DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q)); 264 error = tsleep(&sc->sc_q, PZERO | PCATCH, "uhidrea", 0); 265 DPRINTFN(5, ("uhidread: woke, error=%d\n", error)); 266 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 267 error = EIO; 268 if (error) { 269 sc->sc_state &= ~UHID_ASLP; 270 break; 271 } 272 } 273 splx(s); 274 275 /* Transfer as many chunks as possible. */ 276 while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) { 277 length = ulmin(sc->sc_q.c_cc, uio->uio_resid); 278 if (length > sizeof(buffer)) 279 length = sizeof(buffer); 280 281 /* Remove a small chunk from the input queue. */ 282 (void) q_to_b(&sc->sc_q, buffer, length); 283 DPRINTFN(5, ("uhidread: got %zu chars\n", length)); 284 285 /* Copy the data to the user process. */ 286 if ((error = uiomove(buffer, length, uio)) != 0) 287 break; 288 } 289 290 return (error); 291 } 292 293 int 294 uhidread(dev_t dev, struct uio *uio, int flag) 295 { 296 struct uhid_softc *sc; 297 int error; 298 299 sc = uhid_cd.cd_devs[UHIDUNIT(dev)]; 300 301 sc->sc_refcnt++; 302 error = uhid_do_read(sc, uio, flag); 303 if (--sc->sc_refcnt < 0) 304 usb_detach_wakeup(&sc->sc_hdev.sc_dev); 305 return (error); 306 } 307 308 int 309 uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag) 310 { 311 int error; 312 int size; 313 314 DPRINTFN(1, ("uhidwrite\n")); 315 316 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 317 return (EIO); 318 319 size = sc->sc_hdev.sc_osize; 320 error = 0; 321 if (uio->uio_resid != size) 322 return (EINVAL); 323 error = uiomove(sc->sc_obuf, size, uio); 324 if (!error) { 325 if (uhidev_set_report(sc->sc_hdev.sc_parent, 326 UHID_OUTPUT_REPORT, sc->sc_hdev.sc_report_id, sc->sc_obuf, 327 size) != size) 328 error = EIO; 329 } 330 331 return (error); 332 } 333 334 int 335 uhidwrite(dev_t dev, struct uio *uio, int flag) 336 { 337 struct uhid_softc *sc; 338 int error; 339 340 sc = uhid_cd.cd_devs[UHIDUNIT(dev)]; 341 342 sc->sc_refcnt++; 343 error = uhid_do_write(sc, uio, flag); 344 if (--sc->sc_refcnt < 0) 345 usb_detach_wakeup(&sc->sc_hdev.sc_dev); 346 return (error); 347 } 348 349 int 350 uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, 351 int flag, struct proc *p) 352 { 353 int rc; 354 355 DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd)); 356 357 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 358 return (EIO); 359 360 switch (cmd) { 361 case FIONBIO: 362 case FIOASYNC: 363 /* All handled in the upper FS layer. */ 364 break; 365 366 case USB_GET_DEVICEINFO: 367 usbd_fill_deviceinfo(sc->sc_hdev.sc_udev, 368 (struct usb_device_info *)addr, 1); 369 break; 370 371 case USB_GET_REPORT_DESC: 372 case USB_GET_REPORT: 373 case USB_SET_REPORT: 374 case USB_GET_REPORT_ID: 375 default: 376 rc = uhidev_ioctl(&sc->sc_hdev, cmd, addr, flag, p); 377 if (rc == -1) 378 rc = EINVAL; 379 return rc; 380 } 381 return (0); 382 } 383 384 int 385 uhidioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) 386 { 387 struct uhid_softc *sc; 388 int error; 389 390 sc = uhid_cd.cd_devs[UHIDUNIT(dev)]; 391 392 sc->sc_refcnt++; 393 error = uhid_do_ioctl(sc, cmd, addr, flag, p); 394 if (--sc->sc_refcnt < 0) 395 usb_detach_wakeup(&sc->sc_hdev.sc_dev); 396 return (error); 397 } 398 399 int 400 uhidpoll(dev_t dev, int events, struct proc *p) 401 { 402 struct uhid_softc *sc; 403 int revents = 0; 404 int s; 405 406 sc = uhid_cd.cd_devs[UHIDUNIT(dev)]; 407 408 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 409 return (POLLERR); 410 411 s = splusb(); 412 if (events & (POLLOUT | POLLWRNORM)) 413 revents |= events & (POLLOUT | POLLWRNORM); 414 if (events & (POLLIN | POLLRDNORM)) { 415 if (sc->sc_q.c_cc > 0) 416 revents |= events & (POLLIN | POLLRDNORM); 417 else 418 selrecord(p, &sc->sc_rsel); 419 } 420 421 splx(s); 422 return (revents); 423 } 424 425 void filt_uhidrdetach(struct knote *); 426 int filt_uhidread(struct knote *, long); 427 int uhidkqfilter(dev_t, struct knote *); 428 429 void 430 filt_uhidrdetach(struct knote *kn) 431 { 432 struct uhid_softc *sc = (void *)kn->kn_hook; 433 int s; 434 435 s = splusb(); 436 SLIST_REMOVE(&sc->sc_rsel.si_note, kn, knote, kn_selnext); 437 splx(s); 438 } 439 440 int 441 filt_uhidread(struct knote *kn, long hint) 442 { 443 struct uhid_softc *sc = (void *)kn->kn_hook; 444 445 kn->kn_data = sc->sc_q.c_cc; 446 return (kn->kn_data > 0); 447 } 448 449 struct filterops uhidread_filtops = 450 { 1, NULL, filt_uhidrdetach, filt_uhidread }; 451 452 struct filterops uhid_seltrue_filtops = 453 { 1, NULL, filt_uhidrdetach, filt_seltrue }; 454 455 int 456 uhidkqfilter(dev_t dev, struct knote *kn) 457 { 458 struct uhid_softc *sc; 459 struct klist *klist; 460 int s; 461 462 sc = uhid_cd.cd_devs[UHIDUNIT(dev)]; 463 464 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 465 return (EIO); 466 467 switch (kn->kn_filter) { 468 case EVFILT_READ: 469 klist = &sc->sc_rsel.si_note; 470 kn->kn_fop = &uhidread_filtops; 471 break; 472 473 case EVFILT_WRITE: 474 klist = &sc->sc_rsel.si_note; 475 kn->kn_fop = &uhid_seltrue_filtops; 476 break; 477 478 default: 479 return (EINVAL); 480 } 481 482 kn->kn_hook = (void *)sc; 483 484 s = splusb(); 485 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 486 splx(s); 487 488 return (0); 489 } 490