aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@redhat.com>2011-09-13 11:55:15 +0200
committerGerd Hoffmann <kraxel@redhat.com>2011-10-13 12:58:51 +0200
commit0fcc3bfc0ff76bc3697f12bd3b795f6b139e6ad0 (patch)
tree097f6f0b15b63a7b2281ca1338dd127769f27524
parente627472731c2fe6d42a58ca78855fcd62f58d7c8 (diff)
usb-host: handle USBDEVFS_SETCONFIGURATION returning EBUSY
In case the host uses the usb device usbfs will refuse to set the configuration due to the device being busy. Handle this case by disconnection the interfaces, then trying again. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
-rw-r--r--usb-linux.c36
1 files changed, 35 insertions, 1 deletions
diff --git a/usb-linux.c b/usb-linux.c
index ff1a29c663..7d4d1d7bcf 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -485,6 +485,26 @@ static int usb_host_disconnect_ifaces(USBHostDevice *dev, int nb_interfaces)
return 0;
}
+static int usb_linux_get_num_interfaces(USBHostDevice *s)
+{
+ char device_name[64], line[1024];
+ int num_interfaces = 0;
+
+ if (usb_fs_type != USB_FS_SYS) {
+ return -1;
+ }
+
+ sprintf(device_name, "%d-%s", s->bus_num, s->port);
+ if (!usb_host_read_file(line, sizeof(line), "bNumInterfaces",
+ device_name)) {
+ return -1;
+ }
+ if (sscanf(line, "%d", &num_interfaces) != 1) {
+ return -1;
+ }
+ return num_interfaces;
+}
+
static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
{
const char *op = NULL;
@@ -901,14 +921,28 @@ static int usb_host_set_address(USBHostDevice *s, int addr)
static int usb_host_set_config(USBHostDevice *s, int config)
{
+ int ret, first = 1;
+
trace_usb_host_set_config(s->bus_num, s->addr, config);
usb_host_release_interfaces(s);
- int ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
+again:
+ ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
DPRINTF("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno);
+ if (ret < 0 && errno == EBUSY && first) {
+ /* happens if usb device is in use by host drivers */
+ int count = usb_linux_get_num_interfaces(s);
+ if (count > 0) {
+ DPRINTF("husb: busy -> disconnecting %d interfaces\n", count);
+ usb_host_disconnect_ifaces(s, count);
+ first = 0;
+ goto again;
+ }
+ }
+
if (ret < 0) {
return ctrl_error();
}