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