diff options
author | Hans de Goede <hdegoede@redhat.com> | 2012-03-31 16:06:27 +0200 |
---|---|---|
committer | Hans de Goede <hdegoede@redhat.com> | 2012-03-31 16:06:27 +0200 |
commit | d81f0ff7053915bb0e5f599af35e071c2751f90d (patch) | |
tree | e60b356c221e2a1b31abf1e7017e9739891eefd7 /usbredirhost/usbredirhost.c | |
parent | dfd179001fc456020c96572223ecab753a36f312 (diff) |
usbredirhost: Restore device config when releasing the device
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Diffstat (limited to 'usbredirhost/usbredirhost.c')
-rw-r--r-- | usbredirhost/usbredirhost.c | 45 |
1 files changed, 39 insertions, 6 deletions
diff --git a/usbredirhost/usbredirhost.c b/usbredirhost/usbredirhost.c index 0c4ffaf..d54bac3 100644 --- a/usbredirhost/usbredirhost.c +++ b/usbredirhost/usbredirhost.c @@ -110,6 +110,7 @@ struct usbredirhost { libusb_device_handle *handle; struct libusb_device_descriptor desc; struct libusb_config_descriptor *config; + int restore_config; int claimed; int disconnected; int read_status; @@ -425,7 +426,7 @@ static void usbredirhost_parse_config(struct usbredirhost *host) } /* Called from open/close and parser read callbacks */ -static int usbredirhost_claim(struct usbredirhost *host) +static int usbredirhost_claim(struct usbredirhost *host, int initial_claim) { int i, n, r; @@ -451,6 +452,27 @@ static int usbredirhost_claim(struct usbredirhost *host) return usb_redir_ioerror; } + if (initial_claim) { + if (host->config) + host->restore_config = host->config->bConfigurationValue; + else + host->restore_config = -1; /* unconfigured */ + + /* If the device is unconfigured and has only 1 config, we assume + this is the result of the user doing "safely remove hardware", + and we try to reset the device configuration to this config when + we release the device, so that it becomes usable again. */ + if (host->restore_config == -1 && host->desc.bNumConfigurations == 1) { + struct libusb_config_descriptor *config; + + r = libusb_get_config_descriptor(host->dev, 0, &config); + if (r == 0) { + host->restore_config = config->bConfigurationValue; + libusb_free_config_descriptor(config); + } + } + } + /* All interfaces begin at alt setting 0 when (re)claimed */ memset(host->alt_setting, 0, MAX_INTERFACES); @@ -480,7 +502,7 @@ static int usbredirhost_claim(struct usbredirhost *host) /* Called from open/close and parser read callbacks */ static void usbredirhost_release(struct usbredirhost *host, int attach_drivers) { - int i, n, r; + int i, n, r, current_config = -1; if (!host->claimed) return; @@ -499,6 +521,19 @@ static void usbredirhost_release(struct usbredirhost *host, int attach_drivers) if (!attach_drivers) return; + host->claimed = 0; + + if (host->config) + current_config = host->config->bConfigurationValue; + + if (current_config != host->restore_config) { + r = libusb_set_configuration(host->handle, host->restore_config); + if (r < 0) + ERROR("could not restore configuration to %d: %d", + host->restore_config, r); + return; /* set_config automatically binds drivers for the new config */ + } + for (i = 0; host->config && i < host->config->bNumInterfaces; i++) { n = host->config->interface[i].altsetting[0].bInterfaceNumber; r = libusb_attach_kernel_driver(host->handle, n); @@ -509,8 +544,6 @@ static void usbredirhost_release(struct usbredirhost *host, int attach_drivers) n, host->config->bConfigurationValue, r); } } - - host->claimed = 0; } struct usbredirhost *usbredirhost_open( @@ -658,7 +691,7 @@ int usbredirhost_set_device(struct usbredirhost *host, host->dev = libusb_get_device(usb_dev_handle); host->handle = usb_dev_handle; - status = usbredirhost_claim(host); + status = usbredirhost_claim(host, 1); if (status != usb_redir_success) { usbredirhost_clear_device(host); return status; @@ -1416,7 +1449,7 @@ static void usbredirhost_set_configuration(void *priv, uint32_t id, status.status = usb_redir_ioerror; } - claim_status = usbredirhost_claim(host); + claim_status = usbredirhost_claim(host, 0); if (claim_status != usb_redir_success) { usbredirhost_clear_device(host); host->read_status = usbredirhost_read_device_lost; |