From d209906a45b07a1ada7b41f6af31fc2b3fcf2838 Mon Sep 17 00:00:00 2001 Message-Id: In-Reply-To: <28774cb15d2443e325049ef410956f5635a1484b.1334581530.git.minovotn@redhat.com> References: <28774cb15d2443e325049ef410956f5635a1484b.1334581530.git.minovotn@redhat.com> From: Gerd Hoffmann Date: Fri, 30 Mar 2012 12:35:36 +0200 Subject: [PATCH 6/6] usb-host: rewrite usb_linux_update_endp_table RH-Author: Gerd Hoffmann Message-id: <1333110936-22700-7-git-send-email-kraxel@redhat.com> Patchwork-id: 39043 O-Subject: [RHEL-6.3 qemu-kvm PATCH 6/6] usb-host: rewrite usb_linux_update_endp_table Bugzilla: 807878 RH-Acked-by: Hans de Goede RH-Acked-by: Paolo Bonzini RH-Acked-by: Laszlo Ersek This patch carries a complete rewrite of the usb descriptor parser. Changes / improvements: * We are using the USBDescriptor struct instead of hard-coded offsets now to access descriptor data. * (debug) printfs are all gone, tracepoints have been added instead. * We don't try (and fail) to skip over unneeded descriptors. We parse them all one by one. We keep track of which configuration, interface and altsetting we are looking at and use this information to figure which desciptors are in use and which we can ignore. * On parse errors we clear all endpoint information, which will disallow any communication with the device, except control endpoint messages. This makes sure we don't end up with a silly device state where half of the endpoints got enabled and the other half was left disabled. * Some sanity checks have been added. The new parser is more robust and also leaves complete device information in the trace log if you enable the ush_host_parse_* tracepoints. upstream: http://patchwork.ozlabs.org/patch/149418/ Signed-off-by: Gerd Hoffmann Conflicts: trace-events [ add 'disable', context mismatch ] usb-linux.c [ bunch of upstream refactoring missing ] --- trace-events | 7 ++ usb-linux.c | 237 ++++++++++++++++++++++++++++----------------------------- 2 files changed, 123 insertions(+), 121 deletions(-) Signed-off-by: Michal Novotny --- trace-events | 7 ++ usb-linux.c | 237 ++++++++++++++++++++++++++++----------------------------- 2 files changed, 123 insertions(+), 121 deletions(-) diff --git a/trace-events b/trace-events index 43ae0bd..42a3e15 100644 --- a/trace-events +++ b/trace-events @@ -137,6 +137,13 @@ disable usb_set_addr(int addr) "dev %d" disable usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d" disable usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d" disable usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d" +# usb-linux.c +disable usb_host_parse_device(int bus, int addr, int vendor, int product) "dev %d:%d, id %04x:%04x" +disable usb_host_parse_config(int bus, int addr, int value, int active) "dev %d:%d, value %d, active %d" +disable usb_host_parse_interface(int bus, int addr, int num, int alt, int active) "dev %d:%d, num %d, alt %d, active %d" +disable 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" +disable usb_host_parse_unknown(int bus, int addr, int len, int type) "dev %d:%d, len %d, type %d" +disable usb_host_parse_error(int bus, int addr, const char *errmsg) "dev %d:%d, msg %s" # hw/scsi-bus.c disable scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d" diff --git a/usb-linux.c b/usb-linux.c index 8494df9..961e2cf 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -34,6 +34,7 @@ #include "qemu-timer.h" #include "monitor.h" #include "sysemu.h" +#include "trace.h" #include #include @@ -42,6 +43,7 @@ #include #include #include "hw/usb.h" +#include "hw/usb-desc.h" /* We redefine it to avoid version problems */ struct usb_ctrltransfer { @@ -121,6 +123,7 @@ typedef struct USBHostDevice { uint8_t descr[8192]; int descr_len; int configuration; + int altsetting[16]; int ninterfaces; int closing; uint32_t iso_urb_count; @@ -262,9 +265,8 @@ static int get_iso_buffer_used(USBHostDevice *s, int pid, int ep) } static void set_max_packet_size(USBHostDevice *s, int pid, int ep, - uint8_t *descriptor) + int raw) { - int raw = descriptor[4] + (descriptor[5] << 8); int size, microframes; size = raw & 0x7ff; @@ -986,6 +988,7 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt) if (ret < 0) return ctrl_error(); + s->altsetting[iface] = alt; usb_linux_update_endp_table(s); return 0; } @@ -1065,147 +1068,139 @@ 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) -{ - uint8_t alt_setting; - struct usb_ctrltransfer ct; - int ret; - - if (usb_fs_type == USB_FS_SYS) { - 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)) { - goto usbdevfs; - } - if (sscanf(line, "%d", &alt_setting) != 1) { - goto usbdevfs; - } - return alt_setting; - } - -usbdevfs: - ct.bRequestType = USB_DIR_IN | USB_RECIP_INTERFACE; - ct.bRequest = USB_REQ_GET_INTERFACE; - ct.wValue = 0; - ct.wIndex = interface; - ct.wLength = 1; - ct.data = &alt_setting; - ct.timeout = 50; - ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct); - if (ret < 0) { - /* 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; - 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; for (i = 0; i < MAX_ENDPOINTS; i++) { s->ep_in[i].type = INVALID_EP_TYPE; s->ep_out[i].type = INVALID_EP_TYPE; } - if (s->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->configuration) { - DPRINTF("not requested configuration %d\n", s->configuration); - i += (descriptors[i + 3] << 8) + descriptors[i + 2]; - continue; + for (i = 0;; i += d->bLength) { + if (i+2 >= s->descr_len) { + break; } - - i += descriptors[i]; - - if (descriptors[i + 1] != USB_DT_INTERFACE || - (descriptors[i + 1] == USB_DT_INTERFACE && - descriptors[i + 4] == 0)) { - 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; } - - interface = descriptors[i + 2]; - alt_interface = usb_linux_get_alt_setting(s, s->configuration, - interface); - - /* the current interface descriptor is the active interface - * and has endpoints */ - if (descriptors[i + 3] != alt_interface) { - i += descriptors[i]; - continue; + if (i + d->bLength > s->descr_len) { + trace_usb_host_parse_error(s->bus_num, s->addr, + "descriptor too long"); + goto error; } - - /* advance to the endpoints */ - while (i < length && descriptors[i +1] != USB_DT_ENDPOINT) - i += descriptors[i]; - - 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; - - devep = descriptors[i + 2]; + case USB_DT_CONFIG: + if (d->bLength < 0x09) { + trace_usb_host_parse_error(s->bus_num, s->addr, + "config descriptor too short"); + goto error; + } + configuration = d->u.config.bConfigurationValue; + active = (configuration == s->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->configuration) && + (altsetting == s->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; } - - switch (descriptors[i + 3] & 0x3) { - case 0x00: - type = USBDEVFS_URB_TYPE_CONTROL; - break; - case 0x01: - type = USBDEVFS_URB_TYPE_ISO; - set_max_packet_size(s, pid, ep, descriptors + i); - break; - case 0x02: - type = USBDEVFS_URB_TYPE_BULK; - break; - case 0x03: - type = USBDEVFS_URB_TYPE_INTERRUPT; - break; - default: - DPRINTF("usb_host: malformed endpoint type\n"); - type = USBDEVFS_URB_TYPE_BULK; + trace_usb_host_parse_endpoint(s->bus_num, s->addr, ep, + (devep & USB_DIR_IN) ? "in" : "out", + tname[d->u.endpoint.bmAttributes & 0x3], + active); + + if (active) { + switch (d->u.endpoint.bmAttributes & 0x3) { + case 0x00: + type = USBDEVFS_URB_TYPE_CONTROL; + break; + case 0x01: + type = USBDEVFS_URB_TYPE_ISO; + mps = d->u.endpoint.wMaxPacketSize_lo | + (d->u.endpoint.wMaxPacketSize_hi << 8); + set_max_packet_size(s, pid, ep, mps); + break; + case 0x02: + type = USBDEVFS_URB_TYPE_BULK; + break; + case 0x03: + type = USBDEVFS_URB_TYPE_INTERRUPT; + break; + } + epd = get_endp(s, pid, ep); + assert(epd->type == INVALID_EP_TYPE); + epd->type = type; + epd->halted = 0; } - epd = get_endp(s, pid, ep); - assert(epd->type == INVALID_EP_TYPE); - epd->type = type; - epd->halted = 0; - - i += descriptors[i]; + break; + default: + trace_usb_host_parse_unknown(s->bus_num, s->addr, + d->bLength, d->bDescriptorType); + break; } } return 0; + +error: + for (i = 0; i < MAX_ENDPOINTS; i++) { + s->ep_in[i].type = INVALID_EP_TYPE; + s->ep_out[i].type = INVALID_EP_TYPE; + } + return 1; } /* -- 1.7.7.6