From 41c01ee7157c04abea1b97ab409cd5065ecd30bd Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 24 May 2011 16:12:31 +0200 Subject: usb-linux: catch ENODEV in more places. Factor out disconnect code (called when a device disappears) to a separate function. Add a check for ENODEV errno to a few more places to make sure we notice disconnects. Signed-off-by: Gerd Hoffmann --- usb-linux.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index fcfa09e..e089485 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -267,6 +267,14 @@ static void async_free(AsyncURB *aurb) qemu_free(aurb); } +static void do_disconnect(USBHostDevice *s) +{ + printf("husb: device %d.%d disconnected\n", + s->bus_num, s->addr); + usb_host_close(s); + usb_host_auto_check(NULL); +} + static void async_complete(void *opaque) { USBHostDevice *s = opaque; @@ -281,10 +289,7 @@ static void async_complete(void *opaque) return; } if (errno == ENODEV && !s->closing) { - printf("husb: device %d.%d disconnected\n", - s->bus_num, s->addr); - usb_host_close(s); - usb_host_auto_check(NULL); + do_disconnect(s); return; } @@ -358,6 +363,7 @@ static void usb_host_async_cancel(USBDevice *dev, USBPacket *p) static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration) { + const char *op = NULL; int dev_descr_len, config_descr_len; int interface, nb_interfaces; int ret, i; @@ -410,9 +416,9 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration) ctrl.ioctl_code = USBDEVFS_DISCONNECT; ctrl.ifno = interface; ctrl.data = 0; + op = "USBDEVFS_DISCONNECT"; ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl); if (ret < 0 && errno != ENODATA) { - perror("USBDEVFS_DISCONNECT"); goto fail; } } @@ -421,6 +427,7 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration) /* XXX: only grab if all interfaces are free */ for (interface = 0; interface < nb_interfaces; interface++) { + op = "USBDEVFS_CLAIMINTERFACE"; ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface); if (ret < 0) { if (errno == EBUSY) { @@ -428,8 +435,7 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration) } else { perror("husb: failed to claim interface"); } - fail: - return 0; + goto fail; } } @@ -439,6 +445,13 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration) dev->ninterfaces = nb_interfaces; dev->configuration = configuration; return 1; + +fail: + if (errno == ENODEV) { + do_disconnect(dev); + } + perror(op); + return 0; } static int usb_host_release_interfaces(USBHostDevice *s) -- cgit v1.1 From 3991c35e8586cc42501de1236d0c155e82223540 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 31 May 2011 11:35:18 +0200 Subject: usb-linux: Get speed from sysfs rather then from the connectinfo ioctl The connectinfo ioctl only differentiates between lo speed devices, and all other speeds, where as we would like to know the real speed. The real speed is available in sysfs so use that when available. Signed-off-by: Gerd Hoffmann --- usb-linux.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index e089485..e990d3a 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -1056,10 +1056,9 @@ static int usb_linux_update_endp_table(USBHostDevice *s) } static int usb_host_open(USBHostDevice *dev, int bus_num, - int addr, char *port, const char *prod_name) + int addr, char *port, const char *prod_name, int speed) { int fd = -1, ret; - struct usbdevfs_connectinfo ci; char buf[1024]; if (dev->fd != -1) { @@ -1114,24 +1113,29 @@ static int usb_host_open(USBHostDevice *dev, int bus_num, goto fail; } - ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci); - if (ret < 0) { - perror("usb_host_device_open: USBDEVFS_CONNECTINFO"); - goto fail; - } - - printf("husb: grabbed usb device %d.%d\n", bus_num, addr); - ret = usb_linux_update_endp_table(dev); if (ret) { goto fail; } - if (ci.slow) { - dev->dev.speed = USB_SPEED_LOW; - } else { - dev->dev.speed = USB_SPEED_HIGH; + if (speed == -1) { + struct usbdevfs_connectinfo ci; + + ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci); + if (ret < 0) { + perror("usb_host_device_open: USBDEVFS_CONNECTINFO"); + goto fail; + } + + if (ci.slow) { + speed = USB_SPEED_LOW; + } else { + speed = USB_SPEED_HIGH; + } } + dev->dev.speed = speed; + + printf("husb: grabbed usb device %d.%d\n", bus_num, addr); if (!prod_name || prod_name[0] == '\0') { snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc), @@ -1345,7 +1349,8 @@ static int usb_host_scan_dev(void *opaque, USBScanFunc *func) } device_count = 0; - bus_num = addr = speed = class_id = product_id = vendor_id = 0; + bus_num = addr = class_id = product_id = vendor_id = 0; + speed = -1; /* Can't get the speed from /[proc|dev]/bus/usb/devices */ for(;;) { if (fgets(line, sizeof(line), f) == NULL) { break; @@ -1655,7 +1660,7 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, char *port, } DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr); - usb_host_open(s, bus_num, addr, port, product_name); + usb_host_open(s, bus_num, addr, port, product_name, speed); } return 0; -- cgit v1.1 From f264cfbf4a010e1be879bf2e62d323443989d72f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 31 May 2011 11:35:19 +0200 Subject: usb-linux: Teach about super speed Signed-off-by: Gerd Hoffmann --- usb-linux.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index e990d3a..ed1d56a 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -1378,7 +1378,9 @@ static int usb_host_scan_dev(void *opaque, USBScanFunc *func) if (get_tag_value(buf, sizeof(buf), line, "Spd=", " ") < 0) { goto fail; } - if (!strcmp(buf, "480")) { + if (!strcmp(buf, "5000")) { + speed = USB_SPEED_SUPER; + } else if (!strcmp(buf, "480")) { speed = USB_SPEED_HIGH; } else if (!strcmp(buf, "1.5")) { speed = USB_SPEED_LOW; @@ -1522,7 +1524,9 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func) if (!usb_host_read_file(line, sizeof(line), "speed", de->d_name)) { goto the_end; } - if (!strcmp(line, "480\n")) { + if (!strcmp(line, "5000\n")) { + speed = USB_SPEED_SUPER; + } else if (!strcmp(line, "480\n")) { speed = USB_SPEED_HIGH; } else if (!strcmp(line, "1.5\n")) { speed = USB_SPEED_LOW; @@ -1799,6 +1803,9 @@ static void usb_info_device(Monitor *mon, int bus_num, int addr, char *port, case USB_SPEED_HIGH: speed_str = "480"; break; + case USB_SPEED_SUPER: + speed_str = "5000"; + break; default: speed_str = "?"; break; -- cgit v1.1 From 61c1117f08f4ed6f7e5cfa59cc9e02cf19c8f92d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 31 May 2011 11:35:20 +0200 Subject: usb-linux: Don't do perror when errno is not set Note that "op" also is not set, so before this change these error paths would feed NULL to perror. Signed-off-by: Gerd Hoffmann --- usb-linux.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index ed1d56a..feb4d36 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -376,7 +376,8 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration) i = 0; dev_descr_len = dev->descr[0]; if (dev_descr_len > dev->descr_len) { - goto fail; + fprintf(stderr, "husb: update iface failed. descr too short\n"); + return 0; } i += dev_descr_len; @@ -404,7 +405,7 @@ static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration) if (i >= dev->descr_len) { fprintf(stderr, "husb: update iface failed. no matching configuration\n"); - goto fail; + return 0; } nb_interfaces = dev->descr[i + 4]; -- cgit v1.1 From 130314f83dc43fc3cc2f431d8cfa1595209673fe Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 31 May 2011 11:35:22 +0200 Subject: usb-linux: Ensure devep != 0 So that we don't index endp_table with a negative index. Signed-off-by: Gerd Hoffmann --- usb-linux.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index feb4d36..2e0495e 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -1029,6 +1029,11 @@ static int usb_linux_update_endp_table(USBHostDevice *s) } devep = descriptors[i + 2]; + if ((devep & 0x0f) == 0) { + fprintf(stderr, "usb-linux: invalid ep descriptor, ep == 0\n"); + return 1; + } + switch (descriptors[i + 3] & 0x3) { case 0x00: type = USBDEVFS_URB_TYPE_CONTROL; -- cgit v1.1 From 97f8616648b426f35621d7f9165c304a3416cd62 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 31 May 2011 11:35:24 +0200 Subject: usb-linux: Don't try to open the same device twice If a user wants to redirect 2 identical usb sticks, in theory this is possible by doing: usb_add host:1234:5678 usb_add host:1234:5678 But this will lead to us trying to open the first stick twice, since we don't break the loop after having found a match in our filter list, so the next' filter list entry will result in us trying to open the same device again. Fix this by adding the missing break. Signed-off-by: Gerd Hoffmann --- usb-linux.c | 1 + 1 file changed, 1 insertion(+) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index 2e0495e..a1b1444 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -1671,6 +1671,7 @@ static int usb_host_auto_scan(void *opaque, int bus_num, int addr, char *port, DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr); usb_host_open(s, bus_num, addr, port, product_name, speed); + break; } return 0; -- cgit v1.1 From 1f45a81bef8bc4aee98e29bd28bfb21edb623879 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 6 Jun 2011 09:45:20 +0200 Subject: usb-linux: only cleanup in host_close when host_open was successful. --- usb-linux.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index a1b1444..a4dfe5c 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -1158,9 +1158,9 @@ static int usb_host_open(USBHostDevice *dev, int bus_num, return 0; fail: - dev->fd = -1; - if (fd != -1) { - close(fd); + if (dev->fd != -1) { + close(dev->fd); + dev->fd = -1; } return -1; } @@ -1169,7 +1169,7 @@ static int usb_host_close(USBHostDevice *dev) { int i; - if (dev->fd == -1) { + if (dev->fd == -1 || !dev->dev.attached) { return -1; } -- cgit v1.1 From f8ddbfbcda3ed83b57df537f2457db25de2cc572 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 31 May 2011 11:35:26 +0200 Subject: usb-linux: Enlarge buffer for descriptors to 8192 bytes 1024 bytes is way to small, one hd UVC webcam I have over here has so many resolutions its descriptors take op close to 4k. Hopefully 8k will be enough for all devices. Signed-off-by: Gerd Hoffmann --- usb-linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'usb-linux.c') diff --git a/usb-linux.c b/usb-linux.c index a4dfe5c..5d2ec5c 100644 --- a/usb-linux.c +++ b/usb-linux.c @@ -115,7 +115,7 @@ typedef struct USBHostDevice { USBDevice dev; int fd; - uint8_t descr[1024]; + uint8_t descr[8192]; int descr_len; int configuration; int ninterfaces; -- cgit v1.1