|
|
1.1 ! root 1: /* ! 2: * BSD host USB redirector ! 3: * ! 4: * Copyright (c) 2006 Lonnie Mendez ! 5: * Portions of code and concepts borrowed from ! 6: * usb-linux.c and libusb's bsd.c and are copyright their respective owners. ! 7: * ! 8: * Permission is hereby granted, free of charge, to any person obtaining a copy ! 9: * of this software and associated documentation files (the "Software"), to deal ! 10: * in the Software without restriction, including without limitation the rights ! 11: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ! 12: * copies of the Software, and to permit persons to whom the Software is ! 13: * furnished to do so, subject to the following conditions: ! 14: * ! 15: * The above copyright notice and this permission notice shall be included in ! 16: * all copies or substantial portions of the Software. ! 17: * ! 18: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ! 19: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ! 20: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ! 21: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ! 22: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ! 23: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ! 24: * THE SOFTWARE. ! 25: */ ! 26: ! 27: #include "qemu-common.h" ! 28: #include "monitor.h" ! 29: #include "hw/usb.h" ! 30: ! 31: /* usb.h declares these */ ! 32: #undef USB_SPEED_HIGH ! 33: #undef USB_SPEED_FULL ! 34: #undef USB_SPEED_LOW ! 35: ! 36: #include <sys/ioctl.h> ! 37: #ifndef __DragonFly__ ! 38: #include <dev/usb/usb.h> ! 39: #else ! 40: #include <bus/usb/usb.h> ! 41: #endif ! 42: ! 43: /* This value has maximum potential at 16. ! 44: * You should also set hw.usb.debug to gain ! 45: * more detailed view. ! 46: */ ! 47: //#define DEBUG ! 48: #define UGEN_DEBUG_LEVEL 0 ! 49: ! 50: ! 51: typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id, ! 52: int vendor_id, int product_id, ! 53: const char *product_name, int speed); ! 54: static int usb_host_find_device(int *pbus_num, int *paddr, ! 55: const char *devname); ! 56: ! 57: typedef struct USBHostDevice { ! 58: USBDevice dev; ! 59: int ep_fd[USB_MAX_ENDPOINTS]; ! 60: int devfd; ! 61: char devpath[32]; ! 62: } USBHostDevice; ! 63: ! 64: ! 65: static int ensure_ep_open(USBHostDevice *dev, int ep, int mode) ! 66: { ! 67: char buf[32]; ! 68: int fd; ! 69: ! 70: /* Get the address for this endpoint */ ! 71: ep = UE_GET_ADDR(ep); ! 72: ! 73: if (dev->ep_fd[ep] < 0) { ! 74: #if defined(__FreeBSD__) || defined(__DragonFly__) ! 75: snprintf(buf, sizeof(buf) - 1, "%s.%d", dev->devpath, ep); ! 76: #else ! 77: snprintf(buf, sizeof(buf) - 1, "%s.%02d", dev->devpath, ep); ! 78: #endif ! 79: /* Try to open it O_RDWR first for those devices which have in and out ! 80: * endpoints with the same address (eg 0x02 and 0x82) ! 81: */ ! 82: fd = open(buf, O_RDWR); ! 83: if (fd < 0 && errno == ENXIO) ! 84: fd = open(buf, mode); ! 85: if (fd < 0) { ! 86: #ifdef DEBUG ! 87: printf("ensure_ep_open: failed to open device endpoint %s: %s\n", ! 88: buf, strerror(errno)); ! 89: #endif ! 90: } ! 91: dev->ep_fd[ep] = fd; ! 92: } ! 93: ! 94: return dev->ep_fd[ep]; ! 95: } ! 96: ! 97: static void ensure_eps_closed(USBHostDevice *dev) ! 98: { ! 99: int epnum = 1; ! 100: ! 101: if (!dev) ! 102: return; ! 103: ! 104: while (epnum < USB_MAX_ENDPOINTS) { ! 105: if (dev->ep_fd[epnum] >= 0) { ! 106: close(dev->ep_fd[epnum]); ! 107: dev->ep_fd[epnum] = -1; ! 108: } ! 109: epnum++; ! 110: } ! 111: } ! 112: ! 113: static void usb_host_handle_reset(USBDevice *dev) ! 114: { ! 115: #if 0 ! 116: USBHostDevice *s = (USBHostDevice *)dev; ! 117: #endif ! 118: } ! 119: ! 120: /* XXX: ! 121: * -check device states against transfer requests ! 122: * and return appropriate response ! 123: */ ! 124: static int usb_host_handle_control(USBDevice *dev, ! 125: USBPacket *p, ! 126: int request, ! 127: int value, ! 128: int index, ! 129: int length, ! 130: uint8_t *data) ! 131: { ! 132: USBHostDevice *s = (USBHostDevice *)dev; ! 133: struct usb_ctl_request req; ! 134: struct usb_alt_interface aiface; ! 135: int ret, timeout = 50; ! 136: ! 137: if ((request >> 8) == UT_WRITE_DEVICE && ! 138: (request & 0xff) == UR_SET_ADDRESS) { ! 139: ! 140: /* specific SET_ADDRESS support */ ! 141: dev->addr = value; ! 142: return 0; ! 143: } else if ((request >> 8) == UT_WRITE_DEVICE && ! 144: (request & 0xff) == UR_SET_CONFIG) { ! 145: ! 146: ensure_eps_closed(s); /* can't do this without all eps closed */ ! 147: ! 148: ret = ioctl(s->devfd, USB_SET_CONFIG, &value); ! 149: if (ret < 0) { ! 150: #ifdef DEBUG ! 151: printf("handle_control: failed to set configuration - %s\n", ! 152: strerror(errno)); ! 153: #endif ! 154: return USB_RET_STALL; ! 155: } ! 156: ! 157: return 0; ! 158: } else if ((request >> 8) == UT_WRITE_INTERFACE && ! 159: (request & 0xff) == UR_SET_INTERFACE) { ! 160: ! 161: aiface.uai_interface_index = index; ! 162: aiface.uai_alt_no = value; ! 163: ! 164: ensure_eps_closed(s); /* can't do this without all eps closed */ ! 165: ret = ioctl(s->devfd, USB_SET_ALTINTERFACE, &aiface); ! 166: if (ret < 0) { ! 167: #ifdef DEBUG ! 168: printf("handle_control: failed to set alternate interface - %s\n", ! 169: strerror(errno)); ! 170: #endif ! 171: return USB_RET_STALL; ! 172: } ! 173: ! 174: return 0; ! 175: } else { ! 176: req.ucr_request.bmRequestType = request >> 8; ! 177: req.ucr_request.bRequest = request & 0xff; ! 178: USETW(req.ucr_request.wValue, value); ! 179: USETW(req.ucr_request.wIndex, index); ! 180: USETW(req.ucr_request.wLength, length); ! 181: req.ucr_data = data; ! 182: req.ucr_flags = USBD_SHORT_XFER_OK; ! 183: ! 184: ret = ioctl(s->devfd, USB_SET_TIMEOUT, &timeout); ! 185: #if defined(__NetBSD__) || defined(__OpenBSD__) ! 186: if (ret < 0 && errno != EINVAL) { ! 187: #else ! 188: if (ret < 0) { ! 189: #endif ! 190: #ifdef DEBUG ! 191: printf("handle_control: setting timeout failed - %s\n", ! 192: strerror(errno)); ! 193: #endif ! 194: } ! 195: ! 196: ret = ioctl(s->devfd, USB_DO_REQUEST, &req); ! 197: /* ugen returns EIO for usbd_do_request_ no matter what ! 198: * happens with the transfer */ ! 199: if (ret < 0) { ! 200: #ifdef DEBUG ! 201: printf("handle_control: error after request - %s\n", ! 202: strerror(errno)); ! 203: #endif ! 204: return USB_RET_NAK; // STALL ! 205: } else { ! 206: return req.ucr_actlen; ! 207: } ! 208: } ! 209: } ! 210: ! 211: static int usb_host_handle_data(USBDevice *dev, USBPacket *p) ! 212: { ! 213: USBHostDevice *s = (USBHostDevice *)dev; ! 214: int ret, fd, mode; ! 215: int one = 1, shortpacket = 0, timeout = 50; ! 216: sigset_t new_mask, old_mask; ! 217: uint8_t devep = p->ep->nr; ! 218: ! 219: /* protect data transfers from SIGALRM signal */ ! 220: sigemptyset(&new_mask); ! 221: sigaddset(&new_mask, SIGALRM); ! 222: sigprocmask(SIG_BLOCK, &new_mask, &old_mask); ! 223: ! 224: if (p->pid == USB_TOKEN_IN) { ! 225: devep |= 0x80; ! 226: mode = O_RDONLY; ! 227: shortpacket = 1; ! 228: } else { ! 229: mode = O_WRONLY; ! 230: } ! 231: ! 232: fd = ensure_ep_open(s, devep, mode); ! 233: if (fd < 0) { ! 234: sigprocmask(SIG_SETMASK, &old_mask, NULL); ! 235: return USB_RET_NODEV; ! 236: } ! 237: ! 238: if (ioctl(fd, USB_SET_TIMEOUT, &timeout) < 0) { ! 239: #ifdef DEBUG ! 240: printf("handle_data: failed to set timeout - %s\n", ! 241: strerror(errno)); ! 242: #endif ! 243: } ! 244: ! 245: if (shortpacket) { ! 246: if (ioctl(fd, USB_SET_SHORT_XFER, &one) < 0) { ! 247: #ifdef DEBUG ! 248: printf("handle_data: failed to set short xfer mode - %s\n", ! 249: strerror(errno)); ! 250: #endif ! 251: sigprocmask(SIG_SETMASK, &old_mask, NULL); ! 252: } ! 253: } ! 254: ! 255: if (p->pid == USB_TOKEN_IN) ! 256: ret = readv(fd, p->iov.iov, p->iov.niov); ! 257: else ! 258: ret = writev(fd, p->iov.iov, p->iov.niov); ! 259: ! 260: sigprocmask(SIG_SETMASK, &old_mask, NULL); ! 261: ! 262: if (ret < 0) { ! 263: #ifdef DEBUG ! 264: printf("handle_data: error after %s data - %s\n", ! 265: pid == USB_TOKEN_IN ? "reading" : "writing", strerror(errno)); ! 266: #endif ! 267: switch(errno) { ! 268: case ETIMEDOUT: ! 269: case EINTR: ! 270: return USB_RET_NAK; ! 271: default: ! 272: return USB_RET_STALL; ! 273: } ! 274: } else { ! 275: return ret; ! 276: } ! 277: } ! 278: ! 279: static void usb_host_handle_destroy(USBDevice *opaque) ! 280: { ! 281: USBHostDevice *s = (USBHostDevice *)opaque; ! 282: int i; ! 283: ! 284: for (i = 0; i < USB_MAX_ENDPOINTS; i++) ! 285: if (s->ep_fd[i] >= 0) ! 286: close(s->ep_fd[i]); ! 287: ! 288: if (s->devfd < 0) ! 289: return; ! 290: ! 291: close(s->devfd); ! 292: ! 293: g_free(s); ! 294: } ! 295: ! 296: static int usb_host_initfn(USBDevice *dev) ! 297: { ! 298: return 0; ! 299: } ! 300: ! 301: USBDevice *usb_host_device_open(USBBus *guest_bus, const char *devname) ! 302: { ! 303: struct usb_device_info bus_info, dev_info; ! 304: USBDevice *d = NULL, *ret = NULL; ! 305: USBHostDevice *dev; ! 306: char ctlpath[PATH_MAX + 1]; ! 307: char buspath[PATH_MAX + 1]; ! 308: int bfd, dfd, bus, address, i; ! 309: int ugendebug = UGEN_DEBUG_LEVEL; ! 310: ! 311: if (usb_host_find_device(&bus, &address, devname) < 0) { ! 312: goto fail; ! 313: } ! 314: ! 315: snprintf(buspath, PATH_MAX, "/dev/usb%d", bus); ! 316: ! 317: bfd = open(buspath, O_RDWR); ! 318: if (bfd < 0) { ! 319: #ifdef DEBUG ! 320: printf("usb_host_device_open: failed to open usb bus - %s\n", ! 321: strerror(errno)); ! 322: #endif ! 323: goto fail; ! 324: } ! 325: ! 326: bus_info.udi_addr = address; ! 327: if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0) { ! 328: #ifdef DEBUG ! 329: printf("usb_host_device_open: failed to grab bus information - %s\n", ! 330: strerror(errno)); ! 331: #endif ! 332: goto fail_bfd; ! 333: } ! 334: ! 335: #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) ! 336: snprintf(ctlpath, PATH_MAX, "/dev/%s", bus_info.udi_devnames[0]); ! 337: #else ! 338: snprintf(ctlpath, PATH_MAX, "/dev/%s.00", bus_info.udi_devnames[0]); ! 339: #endif ! 340: ! 341: dfd = open(ctlpath, O_RDWR); ! 342: if (dfd < 0) { ! 343: dfd = open(ctlpath, O_RDONLY); ! 344: if (dfd < 0) { ! 345: #ifdef DEBUG ! 346: printf("usb_host_device_open: failed to open usb device %s - %s\n", ! 347: ctlpath, strerror(errno)); ! 348: #endif ! 349: } ! 350: goto fail_dfd; ! 351: } ! 352: ! 353: if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0) { ! 354: #ifdef DEBUG ! 355: printf("usb_host_device_open: failed to grab device info - %s\n", ! 356: strerror(errno)); ! 357: #endif ! 358: goto fail_dfd; ! 359: } ! 360: ! 361: d = usb_create(guest_bus, "usb-host"); ! 362: dev = DO_UPCAST(USBHostDevice, dev, d); ! 363: ! 364: if (dev_info.udi_speed == 1) { ! 365: dev->dev.speed = USB_SPEED_LOW - 1; ! 366: dev->dev.speedmask = USB_SPEED_MASK_LOW; ! 367: } else { ! 368: dev->dev.speed = USB_SPEED_FULL - 1; ! 369: dev->dev.speedmask = USB_SPEED_MASK_FULL; ! 370: } ! 371: ! 372: if (strncmp(dev_info.udi_product, "product", 7) != 0) { ! 373: pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc), ! 374: dev_info.udi_product); ! 375: } else { ! 376: snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc), ! 377: "host:%s", devname); ! 378: } ! 379: ! 380: pstrcpy(dev->devpath, sizeof(dev->devpath), "/dev/"); ! 381: pstrcat(dev->devpath, sizeof(dev->devpath), dev_info.udi_devnames[0]); ! 382: ! 383: /* Mark the endpoints as not yet open */ ! 384: for (i = 0; i < USB_MAX_ENDPOINTS; i++) { ! 385: dev->ep_fd[i] = -1; ! 386: } ! 387: ! 388: ioctl(dfd, USB_SETDEBUG, &ugendebug); ! 389: ! 390: ret = (USBDevice *)dev; ! 391: ! 392: fail_dfd: ! 393: close(dfd); ! 394: fail_bfd: ! 395: close(bfd); ! 396: fail: ! 397: return ret; ! 398: } ! 399: ! 400: static void usb_host_class_initfn(ObjectClass *klass, void *data) ! 401: { ! 402: USBDeviceClass *uc = USB_DEVICE_CLASS(klass); ! 403: ! 404: uc->product_desc = "USB Host Device"; ! 405: uc->init = usb_host_initfn; ! 406: uc->handle_reset = usb_host_handle_reset; ! 407: uc->handle_control = usb_host_handle_control; ! 408: uc->handle_data = usb_host_handle_data; ! 409: uc->handle_destroy = usb_host_handle_destroy; ! 410: } ! 411: ! 412: static TypeInfo usb_host_dev_info = { ! 413: .name = "usb-host", ! 414: .parent = TYPE_USB_DEVICE, ! 415: .instance_size = sizeof(USBHostDevice), ! 416: .class_init = usb_host_class_initfn, ! 417: }; ! 418: ! 419: static void usb_host_register_types(void) ! 420: { ! 421: type_register_static(&usb_host_dev_info); ! 422: } ! 423: ! 424: type_init(usb_host_register_types) ! 425: ! 426: static int usb_host_scan(void *opaque, USBScanFunc *func) ! 427: { ! 428: struct usb_device_info bus_info; ! 429: struct usb_device_info dev_info; ! 430: uint16_t vendor_id, product_id, class_id, speed; ! 431: int bfd, dfd, bus, address; ! 432: char busbuf[20], devbuf[20], product_name[256]; ! 433: int ret = 0; ! 434: ! 435: for (bus = 0; bus < 10; bus++) { ! 436: ! 437: snprintf(busbuf, sizeof(busbuf) - 1, "/dev/usb%d", bus); ! 438: bfd = open(busbuf, O_RDWR); ! 439: if (bfd < 0) ! 440: continue; ! 441: ! 442: for (address = 1; address < 127; address++) { ! 443: ! 444: bus_info.udi_addr = address; ! 445: if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0) ! 446: continue; ! 447: ! 448: /* only list devices that can be used by generic layer */ ! 449: if (strncmp(bus_info.udi_devnames[0], "ugen", 4) != 0) ! 450: continue; ! 451: ! 452: #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) ! 453: snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s", bus_info.udi_devnames[0]); ! 454: #else ! 455: snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s.00", bus_info.udi_devnames[0]); ! 456: #endif ! 457: ! 458: dfd = open(devbuf, O_RDONLY); ! 459: if (dfd < 0) { ! 460: #ifdef DEBUG ! 461: printf("usb_host_scan: couldn't open device %s - %s\n", devbuf, ! 462: strerror(errno)); ! 463: #endif ! 464: continue; ! 465: } ! 466: ! 467: if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0) ! 468: printf("usb_host_scan: couldn't get device information for %s - %s\n", ! 469: devbuf, strerror(errno)); ! 470: ! 471: /* XXX: might need to fixup endianness of word values before copying over */ ! 472: ! 473: vendor_id = dev_info.udi_vendorNo; ! 474: product_id = dev_info.udi_productNo; ! 475: class_id = dev_info.udi_class; ! 476: speed = dev_info.udi_speed; ! 477: ! 478: if (strncmp(dev_info.udi_product, "product", 7) != 0) ! 479: pstrcpy(product_name, sizeof(product_name), ! 480: dev_info.udi_product); ! 481: else ! 482: product_name[0] = '\0'; ! 483: ! 484: ret = func(opaque, bus, address, class_id, vendor_id, ! 485: product_id, product_name, speed); ! 486: ! 487: close(dfd); ! 488: ! 489: if (ret) ! 490: goto the_end; ! 491: } ! 492: ! 493: close(bfd); ! 494: } ! 495: ! 496: the_end: ! 497: return ret; ! 498: } ! 499: ! 500: typedef struct FindDeviceState { ! 501: int vendor_id; ! 502: int product_id; ! 503: int bus_num; ! 504: int addr; ! 505: } FindDeviceState; ! 506: ! 507: static int usb_host_find_device_scan(void *opaque, int bus_num, int addr, ! 508: int class_id, ! 509: int vendor_id, int product_id, ! 510: const char *product_name, int speed) ! 511: { ! 512: FindDeviceState *s = opaque; ! 513: if (vendor_id == s->vendor_id && ! 514: product_id == s->product_id) { ! 515: s->bus_num = bus_num; ! 516: s->addr = addr; ! 517: return 1; ! 518: } else { ! 519: return 0; ! 520: } ! 521: } ! 522: ! 523: ! 524: /* the syntax is : ! 525: 'bus.addr' (decimal numbers) or ! 526: 'vendor_id:product_id' (hexa numbers) */ ! 527: static int usb_host_find_device(int *pbus_num, int *paddr, ! 528: const char *devname) ! 529: { ! 530: const char *p; ! 531: int ret; ! 532: FindDeviceState fs; ! 533: ! 534: p = strchr(devname, '.'); ! 535: if (p) { ! 536: *pbus_num = strtoul(devname, NULL, 0); ! 537: *paddr = strtoul(p + 1, NULL, 0); ! 538: return 0; ! 539: } ! 540: p = strchr(devname, ':'); ! 541: if (p) { ! 542: fs.vendor_id = strtoul(devname, NULL, 16); ! 543: fs.product_id = strtoul(p + 1, NULL, 16); ! 544: ret = usb_host_scan(&fs, usb_host_find_device_scan); ! 545: if (ret) { ! 546: *pbus_num = fs.bus_num; ! 547: *paddr = fs.addr; ! 548: return 0; ! 549: } ! 550: } ! 551: return -1; ! 552: } ! 553: ! 554: /**********************/ ! 555: /* USB host device info */ ! 556: ! 557: struct usb_class_info { ! 558: int class; ! 559: const char *class_name; ! 560: }; ! 561: ! 562: static const struct usb_class_info usb_class_info[] = { ! 563: { USB_CLASS_AUDIO, "Audio"}, ! 564: { USB_CLASS_COMM, "Communication"}, ! 565: { USB_CLASS_HID, "HID"}, ! 566: { USB_CLASS_HUB, "Hub" }, ! 567: { USB_CLASS_PHYSICAL, "Physical" }, ! 568: { USB_CLASS_PRINTER, "Printer" }, ! 569: { USB_CLASS_MASS_STORAGE, "Storage" }, ! 570: { USB_CLASS_CDC_DATA, "Data" }, ! 571: { USB_CLASS_APP_SPEC, "Application Specific" }, ! 572: { USB_CLASS_VENDOR_SPEC, "Vendor Specific" }, ! 573: { USB_CLASS_STILL_IMAGE, "Still Image" }, ! 574: { USB_CLASS_CSCID, "Smart Card" }, ! 575: { USB_CLASS_CONTENT_SEC, "Content Security" }, ! 576: { -1, NULL } ! 577: }; ! 578: ! 579: static const char *usb_class_str(uint8_t class) ! 580: { ! 581: const struct usb_class_info *p; ! 582: for (p = usb_class_info; p->class != -1; p++) { ! 583: if (p->class == class) ! 584: break; ! 585: } ! 586: return p->class_name; ! 587: } ! 588: ! 589: static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id, ! 590: int vendor_id, int product_id, ! 591: const char *product_name, ! 592: int speed) ! 593: { ! 594: const char *class_str, *speed_str; ! 595: ! 596: switch(speed) { ! 597: case USB_SPEED_LOW: ! 598: speed_str = "1.5"; ! 599: break; ! 600: case USB_SPEED_FULL: ! 601: speed_str = "12"; ! 602: break; ! 603: case USB_SPEED_HIGH: ! 604: speed_str = "480"; ! 605: break; ! 606: default: ! 607: speed_str = "?"; ! 608: break; ! 609: } ! 610: ! 611: monitor_printf(mon, " Device %d.%d, speed %s Mb/s\n", ! 612: bus_num, addr, speed_str); ! 613: class_str = usb_class_str(class_id); ! 614: if (class_str) ! 615: monitor_printf(mon, " %s:", class_str); ! 616: else ! 617: monitor_printf(mon, " Class %02x:", class_id); ! 618: monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id); ! 619: if (product_name[0] != '\0') ! 620: monitor_printf(mon, ", %s", product_name); ! 621: monitor_printf(mon, "\n"); ! 622: } ! 623: ! 624: static int usb_host_info_device(void *opaque, ! 625: int bus_num, int addr, ! 626: int class_id, ! 627: int vendor_id, int product_id, ! 628: const char *product_name, ! 629: int speed) ! 630: { ! 631: Monitor *mon = opaque; ! 632: ! 633: usb_info_device(mon, bus_num, addr, class_id, vendor_id, product_id, ! 634: product_name, speed); ! 635: return 0; ! 636: } ! 637: ! 638: void usb_host_info(Monitor *mon) ! 639: { ! 640: usb_host_scan(mon, usb_host_info_device); ! 641: } ! 642: ! 643: /* XXX add this */ ! 644: int usb_host_device_close(const char *devname) ! 645: { ! 646: return 0; ! 647: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.