1 /* $NetBSD: usbdi_util.c,v 1.50 2008/04/28 20:24:01 martin Exp $ */ 2 3 /* 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Lennart Augustsson (lennart@augustsson.net) at 9 * Carlstedt Research & Technology. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: usbdi_util.c,v 1.50 2008/04/28 20:24:01 martin Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/kernel.h> 39 #include <sys/malloc.h> 40 #if defined(__NetBSD__) || defined(__OpenBSD__) 41 #include <sys/proc.h> 42 #include <sys/device.h> 43 #elif defined(__FreeBSD__) 44 #include <sys/bus.h> 45 #endif 46 47 #include <dev/usb/usb.h> 48 #include <dev/usb/usbhid.h> 49 50 #include <dev/usb/usbdi.h> 51 #include <dev/usb/usbdi_util.h> 52 53 #ifdef USB_DEBUG 54 #define DPRINTF(x) if (usbdebug) logprintf x 55 #define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x 56 extern int usbdebug; 57 #else 58 #define DPRINTF(x) 59 #define DPRINTFN(n,x) 60 #endif 61 62 usbd_status 63 usbd_get_desc(usbd_device_handle dev, int type, int index, int len, void *desc) 64 { 65 usb_device_request_t req; 66 67 DPRINTFN(3,("usbd_get_desc: type=%d, index=%d, len=%d\n", 68 type, index, len)); 69 70 req.bmRequestType = UT_READ_DEVICE; 71 req.bRequest = UR_GET_DESCRIPTOR; 72 USETW2(req.wValue, type, index); 73 USETW(req.wIndex, 0); 74 USETW(req.wLength, len); 75 return (usbd_do_request(dev, &req, desc)); 76 } 77 78 usbd_status 79 usbd_get_config_desc(usbd_device_handle dev, int confidx, 80 usb_config_descriptor_t *d) 81 { 82 usbd_status err; 83 84 DPRINTFN(3,("usbd_get_config_desc: confidx=%d\n", confidx)); 85 err = usbd_get_desc(dev, UDESC_CONFIG, confidx, 86 USB_CONFIG_DESCRIPTOR_SIZE, d); 87 if (err) 88 return (err); 89 if (d->bDescriptorType != UDESC_CONFIG) { 90 DPRINTFN(-1,("usbd_get_config_desc: confidx=%d, bad desc " 91 "len=%d type=%d\n", 92 confidx, d->bLength, d->bDescriptorType)); 93 return (USBD_INVAL); 94 } 95 return (USBD_NORMAL_COMPLETION); 96 } 97 98 usbd_status 99 usbd_get_config_desc_full(usbd_device_handle dev, int conf, void *d, int size) 100 { 101 DPRINTFN(3,("usbd_get_config_desc_full: conf=%d\n", conf)); 102 return (usbd_get_desc(dev, UDESC_CONFIG, conf, size, d)); 103 } 104 105 usbd_status 106 usbd_get_device_desc(usbd_device_handle dev, usb_device_descriptor_t *d) 107 { 108 DPRINTFN(3,("usbd_get_device_desc:\n")); 109 return (usbd_get_desc(dev, UDESC_DEVICE, 110 0, USB_DEVICE_DESCRIPTOR_SIZE, d)); 111 } 112 113 usbd_status 114 usbd_get_device_status(usbd_device_handle dev, usb_status_t *st) 115 { 116 usb_device_request_t req; 117 118 req.bmRequestType = UT_READ_DEVICE; 119 req.bRequest = UR_GET_STATUS; 120 USETW(req.wValue, 0); 121 USETW(req.wIndex, 0); 122 USETW(req.wLength, sizeof(usb_status_t)); 123 return (usbd_do_request(dev, &req, st)); 124 } 125 126 usbd_status 127 usbd_get_hub_status(usbd_device_handle dev, usb_hub_status_t *st) 128 { 129 usb_device_request_t req; 130 131 req.bmRequestType = UT_READ_CLASS_DEVICE; 132 req.bRequest = UR_GET_STATUS; 133 USETW(req.wValue, 0); 134 USETW(req.wIndex, 0); 135 USETW(req.wLength, sizeof(usb_hub_status_t)); 136 return (usbd_do_request(dev, &req, st)); 137 } 138 139 usbd_status 140 usbd_set_address(usbd_device_handle dev, int addr) 141 { 142 usb_device_request_t req; 143 144 req.bmRequestType = UT_WRITE_DEVICE; 145 req.bRequest = UR_SET_ADDRESS; 146 USETW(req.wValue, addr); 147 USETW(req.wIndex, 0); 148 USETW(req.wLength, 0); 149 return usbd_do_request(dev, &req, 0); 150 } 151 152 usbd_status 153 usbd_get_port_status(usbd_device_handle dev, int port, usb_port_status_t *ps) 154 { 155 usb_device_request_t req; 156 157 req.bmRequestType = UT_READ_CLASS_OTHER; 158 req.bRequest = UR_GET_STATUS; 159 USETW(req.wValue, 0); 160 USETW(req.wIndex, port); 161 USETW(req.wLength, sizeof *ps); 162 return (usbd_do_request(dev, &req, ps)); 163 } 164 165 usbd_status 166 usbd_clear_hub_feature(usbd_device_handle dev, int sel) 167 { 168 usb_device_request_t req; 169 170 req.bmRequestType = UT_WRITE_CLASS_DEVICE; 171 req.bRequest = UR_CLEAR_FEATURE; 172 USETW(req.wValue, sel); 173 USETW(req.wIndex, 0); 174 USETW(req.wLength, 0); 175 return (usbd_do_request(dev, &req, 0)); 176 } 177 178 usbd_status 179 usbd_set_hub_feature(usbd_device_handle dev, int sel) 180 { 181 usb_device_request_t req; 182 183 req.bmRequestType = UT_WRITE_CLASS_DEVICE; 184 req.bRequest = UR_SET_FEATURE; 185 USETW(req.wValue, sel); 186 USETW(req.wIndex, 0); 187 USETW(req.wLength, 0); 188 return (usbd_do_request(dev, &req, 0)); 189 } 190 191 usbd_status 192 usbd_clear_port_feature(usbd_device_handle dev, int port, int sel) 193 { 194 usb_device_request_t req; 195 196 req.bmRequestType = UT_WRITE_CLASS_OTHER; 197 req.bRequest = UR_CLEAR_FEATURE; 198 USETW(req.wValue, sel); 199 USETW(req.wIndex, port); 200 USETW(req.wLength, 0); 201 return (usbd_do_request(dev, &req, 0)); 202 } 203 204 usbd_status 205 usbd_set_port_feature(usbd_device_handle dev, int port, int sel) 206 { 207 usb_device_request_t req; 208 209 req.bmRequestType = UT_WRITE_CLASS_OTHER; 210 req.bRequest = UR_SET_FEATURE; 211 USETW(req.wValue, sel); 212 USETW(req.wIndex, port); 213 USETW(req.wLength, 0); 214 return (usbd_do_request(dev, &req, 0)); 215 } 216 217 usbd_status 218 usbd_get_protocol(usbd_interface_handle iface, u_int8_t *report) 219 { 220 usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface); 221 usbd_device_handle dev; 222 usb_device_request_t req; 223 224 DPRINTFN(4, ("usbd_get_protocol: iface=%p, endpt=%d\n", 225 iface, id->bInterfaceNumber)); 226 if (id == NULL) 227 return (USBD_IOERROR); 228 usbd_interface2device_handle(iface, &dev); 229 req.bmRequestType = UT_READ_CLASS_INTERFACE; 230 req.bRequest = UR_GET_PROTOCOL; 231 USETW(req.wValue, 0); 232 USETW(req.wIndex, id->bInterfaceNumber); 233 USETW(req.wLength, 1); 234 return (usbd_do_request(dev, &req, report)); 235 } 236 237 usbd_status 238 usbd_set_protocol(usbd_interface_handle iface, int report) 239 { 240 usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface); 241 usbd_device_handle dev; 242 usb_device_request_t req; 243 244 DPRINTFN(4, ("usbd_set_protocol: iface=%p, report=%d, endpt=%d\n", 245 iface, report, id->bInterfaceNumber)); 246 if (id == NULL) 247 return (USBD_IOERROR); 248 usbd_interface2device_handle(iface, &dev); 249 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 250 req.bRequest = UR_SET_PROTOCOL; 251 USETW(req.wValue, report); 252 USETW(req.wIndex, id->bInterfaceNumber); 253 USETW(req.wLength, 0); 254 return (usbd_do_request(dev, &req, 0)); 255 } 256 257 usbd_status 258 usbd_set_report(usbd_interface_handle iface, int type, int id, void *data, 259 int len) 260 { 261 usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface); 262 usbd_device_handle dev; 263 usb_device_request_t req; 264 265 DPRINTFN(4, ("usbd_set_report: len=%d\n", len)); 266 if (ifd == NULL) 267 return (USBD_IOERROR); 268 usbd_interface2device_handle(iface, &dev); 269 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 270 req.bRequest = UR_SET_REPORT; 271 USETW2(req.wValue, type, id); 272 USETW(req.wIndex, ifd->bInterfaceNumber); 273 USETW(req.wLength, len); 274 return (usbd_do_request(dev, &req, data)); 275 } 276 277 usbd_status 278 usbd_set_report_async(usbd_interface_handle iface, int type, int id, void *data, 279 int len) 280 { 281 usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface); 282 usbd_device_handle dev; 283 usb_device_request_t req; 284 285 DPRINTFN(4, ("usbd_set_report_async: len=%d\n", len)); 286 if (ifd == NULL) 287 return (USBD_IOERROR); 288 usbd_interface2device_handle(iface, &dev); 289 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 290 req.bRequest = UR_SET_REPORT; 291 USETW2(req.wValue, type, id); 292 USETW(req.wIndex, ifd->bInterfaceNumber); 293 USETW(req.wLength, len); 294 return (usbd_do_request_async(dev, &req, data)); 295 } 296 297 usbd_status 298 usbd_get_report(usbd_interface_handle iface, int type, int id, void *data, 299 int len) 300 { 301 usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface); 302 usbd_device_handle dev; 303 usb_device_request_t req; 304 305 DPRINTFN(4, ("usbd_get_report: len=%d\n", len)); 306 if (ifd == NULL) 307 return (USBD_IOERROR); 308 usbd_interface2device_handle(iface, &dev); 309 req.bmRequestType = UT_READ_CLASS_INTERFACE; 310 req.bRequest = UR_GET_REPORT; 311 USETW2(req.wValue, type, id); 312 USETW(req.wIndex, ifd->bInterfaceNumber); 313 USETW(req.wLength, len); 314 return (usbd_do_request(dev, &req, data)); 315 } 316 317 usbd_status 318 usbd_set_idle(usbd_interface_handle iface, int duration, int id) 319 { 320 usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface); 321 usbd_device_handle dev; 322 usb_device_request_t req; 323 324 DPRINTFN(4, ("usbd_set_idle: %d %d\n", duration, id)); 325 if (ifd == NULL) 326 return (USBD_IOERROR); 327 usbd_interface2device_handle(iface, &dev); 328 req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 329 req.bRequest = UR_SET_IDLE; 330 USETW2(req.wValue, duration, id); 331 USETW(req.wIndex, ifd->bInterfaceNumber); 332 USETW(req.wLength, 0); 333 return (usbd_do_request(dev, &req, 0)); 334 } 335 336 usbd_status 337 usbd_get_report_descriptor(usbd_device_handle dev, int ifcno, 338 int size, void *d) 339 { 340 usb_device_request_t req; 341 342 req.bmRequestType = UT_READ_INTERFACE; 343 req.bRequest = UR_GET_DESCRIPTOR; 344 USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */ 345 USETW(req.wIndex, ifcno); 346 USETW(req.wLength, size); 347 return (usbd_do_request(dev, &req, d)); 348 } 349 350 usb_hid_descriptor_t * 351 usbd_get_hid_descriptor(usbd_interface_handle ifc) 352 { 353 usb_interface_descriptor_t *idesc = usbd_get_interface_descriptor(ifc); 354 usbd_device_handle dev; 355 usb_config_descriptor_t *cdesc; 356 usb_hid_descriptor_t *hd; 357 char *p, *end; 358 359 if (idesc == NULL) 360 return (NULL); 361 usbd_interface2device_handle(ifc, &dev); 362 cdesc = usbd_get_config_descriptor(dev); 363 364 p = (char *)idesc + idesc->bLength; 365 end = (char *)cdesc + UGETW(cdesc->wTotalLength); 366 367 for (; p < end; p += hd->bLength) { 368 hd = (usb_hid_descriptor_t *)p; 369 if (p + hd->bLength <= end && hd->bDescriptorType == UDESC_HID) 370 return (hd); 371 if (hd->bDescriptorType == UDESC_INTERFACE) 372 break; 373 } 374 return (NULL); 375 } 376 377 usbd_status 378 usbd_read_report_desc(usbd_interface_handle ifc, void **descp, int *sizep, 379 usb_malloc_type mem) 380 { 381 usb_interface_descriptor_t *id; 382 usb_hid_descriptor_t *hid; 383 usbd_device_handle dev; 384 usbd_status err; 385 386 usbd_interface2device_handle(ifc, &dev); 387 id = usbd_get_interface_descriptor(ifc); 388 if (id == NULL) 389 return (USBD_INVAL); 390 hid = usbd_get_hid_descriptor(ifc); 391 if (hid == NULL) 392 return (USBD_IOERROR); 393 *sizep = UGETW(hid->descrs[0].wDescriptorLength); 394 *descp = malloc(*sizep, mem, M_NOWAIT); 395 if (*descp == NULL) 396 return (USBD_NOMEM); 397 err = usbd_get_report_descriptor(dev, id->bInterfaceNumber, 398 *sizep, *descp); 399 if (err) { 400 free(*descp, mem); 401 *descp = NULL; 402 return (err); 403 } 404 return (USBD_NORMAL_COMPLETION); 405 } 406 407 usbd_status 408 usbd_get_config(usbd_device_handle dev, u_int8_t *conf) 409 { 410 usb_device_request_t req; 411 412 req.bmRequestType = UT_READ_DEVICE; 413 req.bRequest = UR_GET_CONFIG; 414 USETW(req.wValue, 0); 415 USETW(req.wIndex, 0); 416 USETW(req.wLength, 1); 417 return (usbd_do_request(dev, &req, conf)); 418 } 419 420 Static void usbd_bulk_transfer_cb(usbd_xfer_handle xfer, 421 usbd_private_handle priv, usbd_status status); 422 Static void 423 usbd_bulk_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv, 424 usbd_status status) 425 { 426 wakeup(xfer); 427 } 428 429 usbd_status 430 usbd_bulk_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe, 431 u_int16_t flags, u_int32_t timeout, void *buf, 432 u_int32_t *size, const char *lbl) 433 { 434 usbd_status err; 435 int s, error; 436 437 usbd_setup_xfer(xfer, pipe, 0, buf, *size, 438 flags, timeout, usbd_bulk_transfer_cb); 439 DPRINTFN(1, ("usbd_bulk_transfer: start transfer %d bytes\n", *size)); 440 s = splusb(); /* don't want callback until tsleep() */ 441 err = usbd_transfer(xfer); 442 if (err != USBD_IN_PROGRESS) { 443 splx(s); 444 return (err); 445 } 446 error = tsleep((void *)xfer, PZERO | PCATCH, lbl, 0); 447 splx(s); 448 if (error) { 449 DPRINTF(("usbd_bulk_transfer: tsleep=%d\n", error)); 450 usbd_abort_pipe(pipe); 451 return (USBD_INTERRUPTED); 452 } 453 usbd_get_xfer_status(xfer, NULL, NULL, size, &err); 454 DPRINTFN(1,("usbd_bulk_transfer: transferred %d\n", *size)); 455 if (err) { 456 DPRINTF(("usbd_bulk_transfer: error=%d\n", err)); 457 usbd_clear_endpoint_stall(pipe); 458 } 459 return (err); 460 } 461 462 Static void usbd_intr_transfer_cb(usbd_xfer_handle xfer, 463 usbd_private_handle priv, usbd_status status); 464 Static void 465 usbd_intr_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv, 466 usbd_status status) 467 { 468 wakeup(xfer); 469 } 470 471 usbd_status 472 usbd_intr_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe, 473 u_int16_t flags, u_int32_t timeout, void *buf, 474 u_int32_t *size, const char *lbl) 475 { 476 usbd_status err; 477 int s, error; 478 479 usbd_setup_xfer(xfer, pipe, 0, buf, *size, 480 flags, timeout, usbd_intr_transfer_cb); 481 DPRINTFN(1, ("usbd_intr_transfer: start transfer %d bytes\n", *size)); 482 s = splusb(); /* don't want callback until tsleep() */ 483 err = usbd_transfer(xfer); 484 if (err != USBD_IN_PROGRESS) { 485 splx(s); 486 return (err); 487 } 488 error = tsleep(xfer, PZERO | PCATCH, lbl, 0); 489 splx(s); 490 if (error) { 491 DPRINTF(("usbd_intr_transfer: tsleep=%d\n", error)); 492 usbd_abort_pipe(pipe); 493 return (USBD_INTERRUPTED); 494 } 495 usbd_get_xfer_status(xfer, NULL, NULL, size, &err); 496 DPRINTFN(1,("usbd_intr_transfer: transferred %d\n", *size)); 497 if (err) { 498 DPRINTF(("usbd_intr_transfer: error=%d\n", err)); 499 usbd_clear_endpoint_stall(pipe); 500 } 501 return (err); 502 } 503 504 void 505 usb_detach_wait(device_ptr_t dv) 506 { 507 DPRINTF(("usb_detach_wait: waiting for %s\n", USBDEVPTRNAME(dv))); 508 if (tsleep(dv, PZERO, "usbdet", hz * 60)) 509 printf("usb_detach_wait: %s didn't detach\n", 510 USBDEVPTRNAME(dv)); 511 DPRINTF(("usb_detach_wait: %s done\n", USBDEVPTRNAME(dv))); 512 } 513 514 void 515 usb_detach_wakeup(device_ptr_t dv) 516 { 517 DPRINTF(("usb_detach_wakeup: for %s\n", USBDEVPTRNAME(dv))); 518 wakeup(dv); 519 } 520 521 const usb_cdc_descriptor_t * 522 usb_find_desc(usbd_device_handle dev, int type, int subtype) 523 { 524 usbd_desc_iter_t iter; 525 const usb_cdc_descriptor_t *desc; 526 527 usb_desc_iter_init(dev, &iter); 528 for (;;) { 529 desc = (const usb_cdc_descriptor_t *)usb_desc_iter_next(&iter); 530 if (!desc || (desc->bDescriptorType == type && 531 (subtype == USBD_CDCSUBTYPE_ANY || 532 subtype == desc->bDescriptorSubtype))) 533 break; 534 } 535 return desc; 536 } 537 538 /* same as usb_find_desc(), but searches only in the specified interface. */ 539 const usb_cdc_descriptor_t * 540 usb_find_desc_if(usbd_device_handle dev, int type, int subtype, 541 usb_interface_descriptor_t *id) 542 { 543 usbd_desc_iter_t iter; 544 const usb_cdc_descriptor_t *desc; 545 546 usb_desc_iter_init(dev, &iter); 547 548 iter.cur = (void *)id; /* start from the interface desc */ 549 usb_desc_iter_next(&iter); /* and skip it */ 550 551 while ((desc = (const usb_cdc_descriptor_t *)usb_desc_iter_next(&iter)) 552 != NULL) { 553 if (desc->bDescriptorType == UDESC_INTERFACE) { 554 /* we ran into the next interface --- not found */ 555 return NULL; 556 } 557 if (desc->bDescriptorType == type && 558 (subtype == USBD_CDCSUBTYPE_ANY || 559 subtype == desc->bDescriptorSubtype)) 560 break; 561 } 562 return desc; 563 } 564