1 /* $OpenBSD: uhidev.c,v 1.6 2002/11/11 02:32:32 nate Exp $ */ 2 /* $NetBSD: uhidev.c,v 1.10 2002/10/09 06:27:09 fair Exp $ */ 3 4 /* 5 * Copyright (c) 2001 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 * 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/devclass/hid1_1.pdf 43 */ 44 45 #include <sys/param.h> 46 #include <sys/systm.h> 47 #include <sys/kernel.h> 48 #include <sys/malloc.h> 49 #include <sys/signalvar.h> 50 #include <sys/device.h> 51 #include <sys/ioctl.h> 52 #include <sys/conf.h> 53 54 #include <dev/usb/usb.h> 55 #include <dev/usb/usbhid.h> 56 57 #include <dev/usb/usbdevs.h> 58 #include <dev/usb/usbdi.h> 59 #include <dev/usb/usbdi_util.h> 60 #include <dev/usb/hid.h> 61 #include <dev/usb/usb_quirks.h> 62 63 #include <dev/usb/uhidev.h> 64 65 /* Report descriptor for broken Wacom Graphire */ 66 #include <dev/usb/ugraphire_rdesc.h> 67 68 #ifdef UHIDEV_DEBUG 69 #define DPRINTF(x) if (uhidevdebug) logprintf x 70 #define DPRINTFN(n,x) if (uhidevdebug>(n)) logprintf x 71 int uhidevdebug = 0; 72 #else 73 #define DPRINTF(x) 74 #define DPRINTFN(n,x) 75 #endif 76 77 Static void uhidev_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); 78 79 Static int uhidev_maxrepid(void *buf, int len); 80 Static int uhidevprint(void *aux, const char *pnp); 81 #if defined(__NetBSD__) 82 Static int uhidevsubmatch(struct device *parent, struct cfdata *cf, void *aux); 83 #else 84 Static int uhidevsubmatch(struct device *parent, void *cf, void *aux); 85 #endif 86 87 USB_DECLARE_DRIVER(uhidev); 88 89 USB_MATCH(uhidev) 90 { 91 USB_MATCH_START(uhidev, uaa); 92 usb_interface_descriptor_t *id; 93 94 if (uaa->iface == NULL) 95 return (UMATCH_NONE); 96 id = usbd_get_interface_descriptor(uaa->iface); 97 if (id == NULL || id->bInterfaceClass != UICLASS_HID) 98 return (UMATCH_NONE); 99 if (uaa->matchlvl) 100 return (uaa->matchlvl); 101 return (UMATCH_IFACECLASS_GENERIC); 102 } 103 104 int repproto = 1; 105 106 USB_ATTACH(uhidev) 107 { 108 USB_ATTACH_START(uhidev, sc, uaa); 109 usbd_interface_handle iface = uaa->iface; 110 usb_interface_descriptor_t *id; 111 usb_endpoint_descriptor_t *ed; 112 struct uhidev_attach_arg uha; 113 struct uhidev *dev; 114 int size, nrepid, repid, repsz; 115 int repsizes[256]; 116 void *desc; 117 usbd_status err; 118 char devinfo[1024]; 119 120 sc->sc_udev = uaa->device; 121 sc->sc_iface = iface; 122 id = usbd_get_interface_descriptor(iface); 123 usbd_devinfo(uaa->device, 0, devinfo); 124 USB_ATTACH_SETUP; 125 printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), 126 devinfo, id->bInterfaceClass, id->bInterfaceSubClass); 127 128 (void)usbd_set_idle(iface, 0, 0); 129 #if 0 130 131 qflags = usbd_get_quirks(sc->sc_udev)->uq_flags; 132 if ((qflags & UQ_NO_SET_PROTO) == 0 && 133 id->bInterfaceSubClass != UISUBCLASS_BOOT) 134 (void)usbd_set_protocol(iface, 1); 135 #endif 136 137 ed = usbd_interface2endpoint_descriptor(iface, 0); 138 if (ed == NULL) { 139 printf("%s: could not read endpoint descriptor\n", 140 USBDEVNAME(sc->sc_dev)); 141 sc->sc_dying = 1; 142 USB_ATTACH_ERROR_RETURN; 143 } 144 145 DPRINTFN(10,("uhidev_attach: bLength=%d bDescriptorType=%d " 146 "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" 147 " bInterval=%d\n", 148 ed->bLength, ed->bDescriptorType, 149 ed->bEndpointAddress & UE_ADDR, 150 UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out", 151 ed->bmAttributes & UE_XFERTYPE, 152 UGETW(ed->wMaxPacketSize), ed->bInterval)); 153 154 if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN || 155 (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { 156 printf("%s: unexpected endpoint\n", USBDEVNAME(sc->sc_dev)); 157 sc->sc_dying = 1; 158 USB_ATTACH_ERROR_RETURN; 159 } 160 161 sc->sc_ep_addr = ed->bEndpointAddress; 162 163 /* XXX need to extend this */ 164 if (uaa->vendor == USB_VENDOR_WACOM && 165 uaa->product == USB_PRODUCT_WACOM_GRAPHIRE /* && 166 uaa->revision == 0x???? */) { /* XXX should use revision */ 167 /* The report descriptor for the Wacom Graphire is broken. */ 168 size = sizeof uhid_graphire_report_descr; 169 desc = malloc(size, M_USBDEV, M_NOWAIT); 170 if (desc == NULL) 171 err = USBD_NOMEM; 172 else { 173 err = USBD_NORMAL_COMPLETION; 174 memcpy(desc, uhid_graphire_report_descr, size); 175 } 176 } else { 177 desc = NULL; 178 err = usbd_read_report_desc(uaa->iface, &desc, &size, M_USBDEV); 179 } 180 if (err) { 181 printf("%s: no report descriptor\n", USBDEVNAME(sc->sc_dev)); 182 sc->sc_dying = 1; 183 USB_ATTACH_ERROR_RETURN; 184 } 185 186 sc->sc_repdesc = desc; 187 sc->sc_repdesc_size = size; 188 189 uha.uaa = uaa; 190 nrepid = uhidev_maxrepid(desc, size); 191 if (nrepid < 0) 192 USB_ATTACH_SUCCESS_RETURN; 193 if (nrepid > 0) 194 printf("%s: %d report ids\n", USBDEVNAME(sc->sc_dev), nrepid); 195 nrepid++; 196 sc->sc_subdevs = malloc(nrepid * sizeof(device_ptr_t), 197 M_USBDEV, M_NOWAIT); 198 bzero(sc->sc_subdevs, nrepid * sizeof(device_ptr_t)); 199 if (sc->sc_subdevs == NULL) { 200 printf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); 201 USB_ATTACH_ERROR_RETURN; 202 } 203 sc->sc_nrepid = nrepid; 204 sc->sc_isize = 0; 205 206 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, 207 USBDEV(sc->sc_dev)); 208 209 for (repid = 0; repid < nrepid; repid++) { 210 repsz = hid_report_size(desc, size, hid_input, repid); 211 DPRINTF(("uhidev_match: repid=%d, repsz=%d\n", repid, repsz)); 212 repsizes[repid] = repsz; 213 if (repsz > 0) { 214 if (repsz > sc->sc_isize) 215 sc->sc_isize = repsz; 216 } 217 } 218 sc->sc_isize += nrepid != 1; /* space for report ID */ 219 DPRINTF(("uhidev_attach: isize=%d\n", sc->sc_isize)); 220 221 uha.parent = sc; 222 for (repid = 0; repid < nrepid; repid++) { 223 DPRINTF(("uhidev_match: try repid=%d\n", repid)); 224 if (hid_report_size(desc, size, hid_input, repid) == 0 && 225 hid_report_size(desc, size, hid_output, repid) == 0 && 226 hid_report_size(desc, size, hid_feature, repid) == 0) { 227 ; /* already NULL in sc->sc_subdevs[repid] */ 228 } else { 229 uha.reportid = repid; 230 dev = (struct uhidev *)config_found_sm(self, &uha, 231 uhidevprint, uhidevsubmatch); 232 sc->sc_subdevs[repid] = dev; 233 if (dev != NULL) { 234 dev->sc_in_rep_size = repsizes[repid]; 235 #ifdef DIAGNOSTIC 236 DPRINTF(("uhidev_match: repid=%d dev=%p\n", 237 repid, dev)); 238 if (dev->sc_intr == NULL) { 239 printf("%s: sc_intr == NULL\n", 240 USBDEVNAME(sc->sc_dev)); 241 USB_ATTACH_ERROR_RETURN; 242 } 243 #endif 244 #if defined(__NetBSD__) && NRND > 0 245 rnd_attach_source(&dev->rnd_source, 246 USBDEVNAME(dev->sc_dev), 247 RND_TYPE_TTY, 0); 248 #endif 249 } 250 } 251 } 252 253 USB_ATTACH_SUCCESS_RETURN; 254 } 255 256 int 257 uhidev_maxrepid(void *buf, int len) 258 { 259 struct hid_data *d; 260 struct hid_item h; 261 int maxid; 262 263 maxid = -1; 264 h.report_ID = 0; 265 for (d = hid_start_parse(buf, len, hid_none); hid_get_item(d, &h); ) 266 if (h.report_ID > maxid) 267 maxid = h.report_ID; 268 hid_end_parse(d); 269 return (maxid); 270 } 271 272 int 273 uhidevprint(void *aux, const char *pnp) 274 { 275 struct uhidev_attach_arg *uha = aux; 276 277 if (pnp) 278 printf("uhid at %s", pnp); 279 if (uha->reportid != 0) 280 printf(" reportid %d", uha->reportid); 281 return (UNCONF); 282 } 283 284 #if defined(__NetBSD__) 285 Static int uhidevsubmatch(struct device *parent, struct cfdata *cf, void *aux) 286 #else 287 Static int uhidevsubmatch(struct device *parent, void *match, void *aux) 288 #endif 289 { 290 struct uhidev_attach_arg *uha = aux; 291 #if defined(__OpenBSD__) 292 struct cfdata *cf = match; 293 #endif 294 295 if (cf->uhidevcf_reportid != UHIDEV_UNK_REPORTID && 296 cf->uhidevcf_reportid != uha->reportid) 297 return (0); 298 if (cf->uhidevcf_reportid == uha->reportid) 299 uha->matchlvl = UMATCH_VENDOR_PRODUCT; 300 else 301 uha->matchlvl = 0; 302 return ((*cf->cf_attach->ca_match)(parent, cf, aux)); 303 } 304 305 int 306 uhidev_activate(device_ptr_t self, enum devact act) 307 { 308 struct uhidev_softc *sc = (struct uhidev_softc *)self; 309 int i, rv; 310 311 switch (act) { 312 case DVACT_ACTIVATE: 313 return (EOPNOTSUPP); 314 315 case DVACT_DEACTIVATE: 316 rv = 0; 317 for (i = 0; i < sc->sc_nrepid; i++) 318 if (sc->sc_subdevs[i] != NULL) 319 rv |= config_deactivate( 320 &sc->sc_subdevs[i]->sc_dev); 321 sc->sc_dying = 1; 322 break; 323 } 324 return (rv); 325 } 326 327 USB_DETACH(uhidev) 328 { 329 USB_DETACH_START(uhidev, sc); 330 int i, rv; 331 332 DPRINTF(("uhidev_detach: sc=%p flags=%d\n", sc, flags)); 333 334 sc->sc_dying = 1; 335 if (sc->sc_intrpipe != NULL) 336 usbd_abort_pipe(sc->sc_intrpipe); 337 338 if (sc->sc_repdesc != NULL) 339 free(sc->sc_repdesc, M_USBDEV); 340 341 rv = 0; 342 for (i = 0; i < sc->sc_nrepid; i++) { 343 if (sc->sc_subdevs[i] != NULL) { 344 #if defined(__NetBSD__) && NRND > 0 345 rnd_detach_source(&sc->sc_subdevs[i]->rnd_source); 346 #endif 347 rv |= config_detach(&sc->sc_subdevs[i]->sc_dev, flags); 348 sc->sc_subdevs[i] = NULL; 349 } 350 } 351 352 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, 353 USBDEV(sc->sc_dev)); 354 355 return (rv); 356 } 357 358 void 359 uhidev_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) 360 { 361 struct uhidev_softc *sc = addr; 362 struct uhidev *scd; 363 u_char *p; 364 u_int rep; 365 u_int32_t cc; 366 367 usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL); 368 369 #ifdef UHIDEV_DEBUG 370 if (uhidevdebug > 5) { 371 u_int32_t i; 372 373 DPRINTF(("uhidev_intr: status=%d cc=%d\n", status, cc)); 374 DPRINTF(("uhidev_intr: data =")); 375 for (i = 0; i < cc; i++) 376 DPRINTF((" %02x", sc->sc_ibuf[i])); 377 DPRINTF(("\n")); 378 } 379 #endif 380 381 if (status == USBD_CANCELLED) 382 return; 383 384 if (status != USBD_NORMAL_COMPLETION) { 385 DPRINTF(("%s: interrupt status=%d\n", USBDEVNAME(sc->sc_dev), 386 status)); 387 usbd_clear_endpoint_stall_async(sc->sc_intrpipe); 388 return; 389 } 390 391 p = sc->sc_ibuf; 392 if (sc->sc_nrepid != 1) 393 rep = *p++, cc--; 394 else 395 rep = 0; 396 if (rep >= sc->sc_nrepid) { 397 printf("uhidev_intr: bad repid %d\n", rep); 398 return; 399 } 400 scd = sc->sc_subdevs[rep]; 401 DPRINTFN(5,("uhidev_intr: rep=%d, scd=%p state=0x%x\n", 402 rep, scd, scd ? scd->sc_state : 0)); 403 if (scd == NULL || !(scd->sc_state & UHIDEV_OPEN)) 404 return; 405 #ifdef DIAGNOSTIC 406 if (scd->sc_in_rep_size != cc) 407 printf("%s: bad input length %d != %d\n",USBDEVNAME(sc->sc_dev), 408 scd->sc_in_rep_size, cc); 409 #endif 410 #if defined(__NetBSD__) && NRND > 0 411 rnd_add_uint32(&scd->rnd_source, (uintptr_t)(sc->sc_ibuf)); 412 #endif 413 scd->sc_intr(scd, p, cc); 414 } 415 416 void 417 uhidev_get_report_desc(struct uhidev_softc *sc, void **desc, int *size) 418 { 419 *desc = sc->sc_repdesc; 420 *size = sc->sc_repdesc_size; 421 } 422 423 int 424 uhidev_open(struct uhidev *scd) 425 { 426 struct uhidev_softc *sc = scd->sc_parent; 427 usbd_status err; 428 429 DPRINTF(("uhidev_open: open pipe, state=%d refcnt=%d\n", 430 scd->sc_state, sc->sc_refcnt)); 431 432 if (scd->sc_state & UHIDEV_OPEN) 433 return (EBUSY); 434 scd->sc_state |= UHIDEV_OPEN; 435 if (sc->sc_refcnt++) 436 return (0); 437 438 if (sc->sc_isize == 0) 439 return (0); 440 441 sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK); 442 443 /* Set up interrupt pipe. */ 444 DPRINTF(("uhidev_open: isize=%d, ep=0x%02x\n", sc->sc_isize, 445 sc->sc_ep_addr)); 446 err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, 447 USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, sc->sc_ibuf, 448 sc->sc_isize, uhidev_intr, USBD_DEFAULT_INTERVAL); 449 if (err) { 450 DPRINTF(("uhidopen: usbd_open_pipe_intr failed, " 451 "error=%d\n",err)); 452 free(sc->sc_ibuf, M_USBDEV); 453 scd->sc_state &= ~UHIDEV_OPEN; 454 sc->sc_refcnt = 0; 455 sc->sc_intrpipe = NULL; 456 return (EIO); 457 } 458 return (0); 459 } 460 461 void 462 uhidev_close(struct uhidev *scd) 463 { 464 struct uhidev_softc *sc = scd->sc_parent; 465 466 if (!(scd->sc_state & UHIDEV_OPEN)) 467 return; 468 scd->sc_state &= ~UHIDEV_OPEN; 469 if (--sc->sc_refcnt) 470 return; 471 DPRINTF(("uhidev_close: close pipe\n")); 472 473 /* Disable interrupts. */ 474 if (sc->sc_intrpipe != NULL) { 475 usbd_abort_pipe(sc->sc_intrpipe); 476 usbd_close_pipe(sc->sc_intrpipe); 477 sc->sc_intrpipe = NULL; 478 } 479 480 if (sc->sc_ibuf != NULL) { 481 free(sc->sc_ibuf, M_USBDEV); 482 sc->sc_ibuf = NULL; 483 } 484 } 485 486 usbd_status 487 uhidev_set_report(struct uhidev *scd, int type, void *data, int len) 488 { 489 /* XXX */ 490 char buf[100]; 491 if (scd->sc_report_id) { 492 buf[0] = scd->sc_report_id; 493 memcpy(buf+1, data, len); 494 len++; 495 data = buf; 496 } 497 498 return usbd_set_report(scd->sc_parent->sc_iface, type, 499 scd->sc_report_id, data, len); 500 } 501 502 void 503 uhidev_set_report_async(struct uhidev *scd, int type, void *data, int len) 504 { 505 /* XXX */ 506 char buf[100]; 507 if (scd->sc_report_id) { 508 buf[0] = scd->sc_report_id; 509 memcpy(buf+1, data, len); 510 len++; 511 data = buf; 512 } 513 514 usbd_set_report_async(scd->sc_parent->sc_iface, type, 515 scd->sc_report_id, data, len); 516 } 517 518 usbd_status 519 uhidev_get_report(struct uhidev *scd, int type, void *data, int len) 520 { 521 return usbd_get_report(scd->sc_parent->sc_iface, type, 522 scd->sc_report_id, data, len); 523 } 524