diff options
Diffstat (limited to 'drivers/usb/core/message.c')
-rw-r--r-- | drivers/usb/core/message.c | 69 |
1 files changed, 62 insertions, 7 deletions
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index adb9c8ee0c1f..ed83f2b1d551 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1298,6 +1298,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) { struct usb_interface *iface; struct usb_host_interface *alt; + struct usb_hcd *hcd = bus_to_hcd(dev->bus); int ret; int manual = 0; unsigned int epaddr; @@ -1320,6 +1321,18 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) return -EINVAL; } + /* Make sure we have enough bandwidth for this alternate interface. + * Remove the current alt setting and add the new alt setting. + */ + mutex_lock(&hcd->bandwidth_mutex); + ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt); + if (ret < 0) { + dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n", + alternate); + mutex_unlock(&hcd->bandwidth_mutex); + return ret; + } + if (dev->quirks & USB_QUIRK_NO_SET_INTF) ret = -EPIPE; else @@ -1335,8 +1348,13 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) "manual set_interface for iface %d, alt %d\n", interface, alternate); manual = 1; - } else if (ret < 0) + } else if (ret < 0) { + /* Re-instate the old alt setting */ + usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting); + mutex_unlock(&hcd->bandwidth_mutex); return ret; + } + mutex_unlock(&hcd->bandwidth_mutex); /* FIXME drivers shouldn't need to replicate/bugfix the logic here * when they implement async or easily-killable versions of this or @@ -1418,6 +1436,7 @@ int usb_reset_configuration(struct usb_device *dev) { int i, retval; struct usb_host_config *config; + struct usb_hcd *hcd = bus_to_hcd(dev->bus); if (dev->state == USB_STATE_SUSPENDED) return -EHOSTUNREACH; @@ -1433,12 +1452,46 @@ int usb_reset_configuration(struct usb_device *dev) } config = dev->actconfig; + retval = 0; + mutex_lock(&hcd->bandwidth_mutex); + /* Make sure we have enough bandwidth for each alternate setting 0 */ + for (i = 0; i < config->desc.bNumInterfaces; i++) { + struct usb_interface *intf = config->interface[i]; + struct usb_host_interface *alt; + + alt = usb_altnum_to_altsetting(intf, 0); + if (!alt) + alt = &intf->altsetting[0]; + if (alt != intf->cur_altsetting) + retval = usb_hcd_alloc_bandwidth(dev, NULL, + intf->cur_altsetting, alt); + if (retval < 0) + break; + } + /* If not, reinstate the old alternate settings */ + if (retval < 0) { +reset_old_alts: + for (; i >= 0; i--) { + struct usb_interface *intf = config->interface[i]; + struct usb_host_interface *alt; + + alt = usb_altnum_to_altsetting(intf, 0); + if (!alt) + alt = &intf->altsetting[0]; + if (alt != intf->cur_altsetting) + usb_hcd_alloc_bandwidth(dev, NULL, + alt, intf->cur_altsetting); + } + mutex_unlock(&hcd->bandwidth_mutex); + return retval; + } retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, 0, config->desc.bConfigurationValue, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); if (retval < 0) - return retval; + goto reset_old_alts; + mutex_unlock(&hcd->bandwidth_mutex); /* re-init hc/hcd interface/endpoint state */ for (i = 0; i < config->desc.bNumInterfaces; i++) { @@ -1647,6 +1700,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) int i, ret; struct usb_host_config *cp = NULL; struct usb_interface **new_interfaces = NULL; + struct usb_hcd *hcd = bus_to_hcd(dev->bus); int n, nintf; if (dev->authorized == 0 || configuration == -1) @@ -1716,12 +1770,11 @@ free_interfaces: * host controller will not allow submissions to dropped endpoints. If * this call fails, the device state is unchanged. */ - if (cp) - ret = usb_hcd_check_bandwidth(dev, cp, NULL); - else - ret = usb_hcd_check_bandwidth(dev, NULL, NULL); + mutex_lock(&hcd->bandwidth_mutex); + ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL); if (ret < 0) { usb_autosuspend_device(dev); + mutex_unlock(&hcd->bandwidth_mutex); goto free_interfaces; } @@ -1747,10 +1800,12 @@ free_interfaces: dev->actconfig = cp; if (!cp) { usb_set_device_state(dev, USB_STATE_ADDRESS); - usb_hcd_check_bandwidth(dev, NULL, NULL); + usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL); usb_autosuspend_device(dev); + mutex_unlock(&hcd->bandwidth_mutex); goto free_interfaces; } + mutex_unlock(&hcd->bandwidth_mutex); usb_set_device_state(dev, USB_STATE_CONFIGURED); /* Initialize the new interface structures and the |