diff options
-rw-r--r-- | hw/usb/host-linux.c | 194 | ||||
-rw-r--r-- | trace-events | 6 |
2 files changed, 107 insertions, 93 deletions
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c index a382f0ad98..061a1b7825 100644 --- a/hw/usb/host-linux.c +++ b/hw/usb/host-linux.c @@ -42,6 +42,7 @@ #include <linux/usbdevice_fs.h> #include <linux/version.h> #include "hw/usb.h" +#include "hw/usb/desc.h" /* We redefine it to avoid version problems */ struct usb_ctrltransfer { @@ -1108,121 +1109,128 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p, return USB_RET_ASYNC; } -static uint8_t usb_linux_get_alt_setting(USBHostDevice *s, - uint8_t configuration, uint8_t interface) -{ - char device_name[64], line[1024]; - int alt_setting; - - sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port, - (int)configuration, (int)interface); - - if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting", - device_name)) { - /* Assume alt 0 on error */ - return 0; - } - if (sscanf(line, "%d", &alt_setting) != 1) { - /* Assume alt 0 on error */ - return 0; - } - return alt_setting; -} - /* returns 1 on problem encountered or 0 for success */ static int usb_linux_update_endp_table(USBHostDevice *s) { - uint8_t *descriptors; - uint8_t devep, type, alt_interface; - uint16_t raw; - int interface, length, i, ep, pid; + static const char *tname[] = { + [USB_ENDPOINT_XFER_CONTROL] = "control", + [USB_ENDPOINT_XFER_ISOC] = "isoc", + [USB_ENDPOINT_XFER_BULK] = "bulk", + [USB_ENDPOINT_XFER_INT] = "int", + }; + uint8_t devep, type; + uint16_t mps, v, p; + int ep, pid; + unsigned int i, configuration = -1, interface = -1, altsetting = -1; struct endp_data *epd; + USBDescriptor *d; + bool active = false; usb_ep_init(&s->dev); - if (s->dev.configuration == 0) { - /* not configured yet -- leave all endpoints disabled */ - return 0; - } - - /* get the desired configuration, interface, and endpoint descriptors - * from device description */ - descriptors = &s->descr[18]; - length = s->descr_len - 18; - i = 0; - - while (i < length) { - if (descriptors[i + 1] != USB_DT_CONFIG) { - fprintf(stderr, "invalid descriptor data\n"); - return 1; - } else if (descriptors[i + 5] != s->dev.configuration) { - DPRINTF("not requested configuration %d\n", s->dev.configuration); - i += (descriptors[i + 3] << 8) + descriptors[i + 2]; - continue; - } - i += descriptors[i]; - - if (descriptors[i + 1] != USB_DT_INTERFACE || - (descriptors[i + 1] == USB_DT_INTERFACE && - descriptors[i + 4] == 0)) { - i += descriptors[i]; - continue; + for (i = 0;; i += d->bLength) { + if (i+2 >= s->descr_len) { + break; } - - interface = descriptors[i + 2]; - alt_interface = usb_linux_get_alt_setting(s, s->dev.configuration, - interface); - - /* the current interface descriptor is the active interface - * and has endpoints */ - if (descriptors[i + 3] != alt_interface) { - i += descriptors[i]; - continue; + d = (void *)(s->descr + i); + if (d->bLength < 2) { + trace_usb_host_parse_error(s->bus_num, s->addr, + "descriptor too short"); + goto error; } - - /* advance to the endpoints */ - while (i < length && descriptors[i +1] != USB_DT_ENDPOINT) { - i += descriptors[i]; + if (i + d->bLength > s->descr_len) { + trace_usb_host_parse_error(s->bus_num, s->addr, + "descriptor too long"); + goto error; } - - if (i >= length) + switch (d->bDescriptorType) { + case 0: + trace_usb_host_parse_error(s->bus_num, s->addr, + "invalid descriptor type"); + goto error; + case USB_DT_DEVICE: + if (d->bLength < 0x12) { + trace_usb_host_parse_error(s->bus_num, s->addr, + "device descriptor too short"); + goto error; + } + v = (d->u.device.idVendor_hi << 8) | d->u.device.idVendor_lo; + p = (d->u.device.idProduct_hi << 8) | d->u.device.idProduct_lo; + trace_usb_host_parse_device(s->bus_num, s->addr, v, p); break; - - while (i < length) { - if (descriptors[i + 1] != USB_DT_ENDPOINT) { - break; + case USB_DT_CONFIG: + if (d->bLength < 0x09) { + trace_usb_host_parse_error(s->bus_num, s->addr, + "config descriptor too short"); + goto error; } - - devep = descriptors[i + 2]; + configuration = d->u.config.bConfigurationValue; + active = (configuration == s->dev.configuration); + trace_usb_host_parse_config(s->bus_num, s->addr, + configuration, active); + break; + case USB_DT_INTERFACE: + if (d->bLength < 0x09) { + trace_usb_host_parse_error(s->bus_num, s->addr, + "interface descriptor too short"); + goto error; + } + interface = d->u.interface.bInterfaceNumber; + altsetting = d->u.interface.bAlternateSetting; + active = (configuration == s->dev.configuration) && + (altsetting == s->dev.altsetting[interface]); + trace_usb_host_parse_interface(s->bus_num, s->addr, + interface, altsetting, active); + break; + case USB_DT_ENDPOINT: + if (d->bLength < 0x07) { + trace_usb_host_parse_error(s->bus_num, s->addr, + "endpoint descriptor too short"); + goto error; + } + devep = d->u.endpoint.bEndpointAddress; pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT; ep = devep & 0xf; if (ep == 0) { - fprintf(stderr, "usb-linux: invalid ep descriptor, ep == 0\n"); - return 1; + trace_usb_host_parse_error(s->bus_num, s->addr, + "invalid endpoint address"); + goto error; } - type = descriptors[i + 3] & 0x3; - raw = descriptors[i + 4] + (descriptors[i + 5] << 8); - usb_ep_set_max_packet_size(&s->dev, pid, ep, raw); - assert(usb_ep_get_type(&s->dev, pid, ep) == - USB_ENDPOINT_XFER_INVALID); - usb_ep_set_type(&s->dev, pid, ep, type); - usb_ep_set_ifnum(&s->dev, pid, ep, interface); - if ((s->options & (1 << USB_HOST_OPT_PIPELINE)) && - (type == USB_ENDPOINT_XFER_BULK)) { - usb_ep_set_pipeline(&s->dev, pid, ep, true); - } + type = d->u.endpoint.bmAttributes & 0x3; + mps = d->u.endpoint.wMaxPacketSize_lo | + (d->u.endpoint.wMaxPacketSize_hi << 8); + trace_usb_host_parse_endpoint(s->bus_num, s->addr, ep, + (devep & USB_DIR_IN) ? "in" : "out", + tname[type], active); + + if (active) { + usb_ep_set_max_packet_size(&s->dev, pid, ep, mps); + assert(usb_ep_get_type(&s->dev, pid, ep) == + USB_ENDPOINT_XFER_INVALID); + usb_ep_set_type(&s->dev, pid, ep, type); + usb_ep_set_ifnum(&s->dev, pid, ep, interface); + if ((s->options & (1 << USB_HOST_OPT_PIPELINE)) && + (type == USB_ENDPOINT_XFER_BULK)) { + usb_ep_set_pipeline(&s->dev, pid, ep, true); + } - epd = get_endp(s, pid, ep); - epd->halted = 0; + epd = get_endp(s, pid, ep); + epd->halted = 0; + } - i += descriptors[i]; + break; + default: + trace_usb_host_parse_unknown(s->bus_num, s->addr, + d->bLength, d->bDescriptorType); + break; } } -#ifdef DEBUG - usb_ep_dump(&s->dev); -#endif return 0; + +error: + usb_ep_init(&s->dev); + return 1; } /* diff --git a/trace-events b/trace-events index e7ce5b2cb3..d2e787962f 100644 --- a/trace-events +++ b/trace-events @@ -337,6 +337,12 @@ usb_host_reset(int bus, int addr) "dev %d:%d" usb_host_auto_scan_enabled(void) usb_host_auto_scan_disabled(void) usb_host_claim_port(int bus, int hub, int port) "bus %d, hub addr %d, port %d" +usb_host_parse_device(int bus, int addr, int vendor, int product) "dev %d:%d, id %04x:%04x" +usb_host_parse_config(int bus, int addr, int value, int active) "dev %d:%d, value %d, active %d" +usb_host_parse_interface(int bus, int addr, int num, int alt, int active) "dev %d:%d, num %d, alt %d, active %d" +usb_host_parse_endpoint(int bus, int addr, int ep, const char *dir, const char *type, int active) "dev %d:%d, ep %d, %s, %s, active %d" +usb_host_parse_unknown(int bus, int addr, int len, int type) "dev %d:%d, len %d, type %d" +usb_host_parse_error(int bus, int addr, const char *errmsg) "dev %d:%d, msg %s" # hw/scsi-bus.c scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d" |