Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 174995
b: refs/heads/master
c: 3f0479e
h: refs/heads/master
i:
  174993: 7fbe8eb
  174991: c5b679a
v: v3
  • Loading branch information
Sarah Sharp authored and Greg Kroah-Hartman committed Dec 11, 2009
1 parent 865247e commit 1d8c1b0
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 23 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 91017f9cf5fcfb601b8d583c896ac7de7d200c57
refs/heads/master: 3f0479e00a3fca9590ae8d9edc4e9c47b7fa0610
56 changes: 44 additions & 12 deletions trunk/drivers/usb/core/hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <asm/unaligned.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>

#include <linux/usb.h>

Expand Down Expand Up @@ -1595,15 +1596,29 @@ void usb_hcd_flush_endpoint(struct usb_device *udev,
}
}

/* Check whether a new configuration or alt setting for an interface
* will exceed the bandwidth for the bus (or the host controller resources).
* Only pass in a non-NULL config or interface, not both!
* Passing NULL for both new_config and new_intf means the device will be
* de-configured by issuing a set configuration 0 command.
/**
* Check whether a new bandwidth setting exceeds the bus bandwidth.
* @new_config: new configuration to install
* @cur_alt: the current alternate interface setting
* @new_alt: alternate interface setting that is being installed
*
* To change configurations, pass in the new configuration in new_config,
* and pass NULL for cur_alt and new_alt.
*
* To reset a device's configuration (put the device in the ADDRESSED state),
* pass in NULL for new_config, cur_alt, and new_alt.
*
* To change alternate interface settings, pass in NULL for new_config,
* pass in the current alternate interface setting in cur_alt,
* and pass in the new alternate interface setting in new_alt.
*
* Returns an error if the requested bandwidth change exceeds the
* bus bandwidth or host controller internal resources.
*/
int usb_hcd_check_bandwidth(struct usb_device *udev,
int usb_hcd_alloc_bandwidth(struct usb_device *udev,
struct usb_host_config *new_config,
struct usb_interface *new_intf)
struct usb_host_interface *cur_alt,
struct usb_host_interface *new_alt)
{
int num_intfs, i, j;
struct usb_host_interface *alt = NULL;
Expand All @@ -1616,7 +1631,7 @@ int usb_hcd_check_bandwidth(struct usb_device *udev,
return 0;

/* Configuration is being removed - set configuration 0 */
if (!new_config && !new_intf) {
if (!new_config && !cur_alt) {
for (i = 1; i < 16; ++i) {
ep = udev->ep_out[i];
if (ep)
Expand Down Expand Up @@ -1655,17 +1670,33 @@ int usb_hcd_check_bandwidth(struct usb_device *udev,
for (i = 0; i < num_intfs; ++i) {
/* Set up endpoints for alternate interface setting 0 */
alt = usb_find_alt_setting(new_config, i, 0);
if (!alt) {
printk(KERN_DEBUG "Did not find alt setting 0 for intf %d\n", i);
continue;
}
if (!alt)
/* No alt setting 0? Pick the first setting. */
alt = &new_config->intf_cache[i]->altsetting[0];

for (j = 0; j < alt->desc.bNumEndpoints; j++) {
ret = hcd->driver->add_endpoint(hcd, udev, &alt->endpoint[j]);
if (ret < 0)
goto reset;
}
}
}
if (cur_alt && new_alt) {
/* Drop all the endpoints in the current alt setting */
for (i = 0; i < cur_alt->desc.bNumEndpoints; i++) {
ret = hcd->driver->drop_endpoint(hcd, udev,
&cur_alt->endpoint[i]);
if (ret < 0)
goto reset;
}
/* Add all the endpoints in the new alt setting */
for (i = 0; i < new_alt->desc.bNumEndpoints; i++) {
ret = hcd->driver->add_endpoint(hcd, udev,
&new_alt->endpoint[i]);
if (ret < 0)
goto reset;
}
}
ret = hcd->driver->check_bandwidth(hcd, udev);
reset:
if (ret < 0)
Expand Down Expand Up @@ -1982,6 +2013,7 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
#ifdef CONFIG_PM
INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
#endif
mutex_init(&hcd->bandwidth_mutex);

hcd->driver = driver;
hcd->product_desc = (driver->product_desc) ? driver->product_desc :
Expand Down
19 changes: 17 additions & 2 deletions trunk/drivers/usb/core/hcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,20 @@ struct usb_hcd {
u64 rsrc_len; /* memory/io resource length */
unsigned power_budget; /* in mA, 0 = no limit */

/* bandwidth_mutex should be taken before adding or removing
* any new bus bandwidth constraints:
* 1. Before adding a configuration for a new device.
* 2. Before removing the configuration to put the device into
* the addressed state.
* 3. Before selecting a different configuration.
* 4. Before selecting an alternate interface setting.
*
* bandwidth_mutex should be dropped after a successful control message
* to the device, or resetting the bandwidth after a failed attempt.
*/
struct mutex bandwidth_mutex;


#define HCD_BUFFER_POOLS 4
struct dma_pool *pool [HCD_BUFFER_POOLS];

Expand Down Expand Up @@ -290,9 +304,10 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev,
extern void usb_hcd_reset_endpoint(struct usb_device *udev,
struct usb_host_endpoint *ep);
extern void usb_hcd_synchronize_unlinks(struct usb_device *udev);
extern int usb_hcd_check_bandwidth(struct usb_device *udev,
extern int usb_hcd_alloc_bandwidth(struct usb_device *udev,
struct usb_host_config *new_config,
struct usb_interface *new_intf);
struct usb_host_interface *old_alt,
struct usb_host_interface *new_alt);
extern int usb_hcd_get_frame_number(struct usb_device *udev);

extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
Expand Down
27 changes: 26 additions & 1 deletion trunk/drivers/usb/core/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -3599,6 +3599,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
{
struct usb_device *parent_hdev = udev->parent;
struct usb_hub *parent_hub;
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
struct usb_device_descriptor descriptor = udev->descriptor;
int i, ret = 0;
int port1 = udev->portnum;
Expand Down Expand Up @@ -3642,6 +3643,16 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
/* Restore the device's previous configuration */
if (!udev->actconfig)
goto done;

mutex_lock(&hcd->bandwidth_mutex);
ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
if (ret < 0) {
dev_warn(&udev->dev,
"Busted HC? Not enough HCD resources for "
"old configuration.\n");
mutex_unlock(&hcd->bandwidth_mutex);
goto re_enumerate;
}
ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_CONFIGURATION, 0,
udev->actconfig->desc.bConfigurationValue, 0,
Expand All @@ -3650,8 +3661,10 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
dev_err(&udev->dev,
"can't restore configuration #%d (error=%d)\n",
udev->actconfig->desc.bConfigurationValue, ret);
mutex_unlock(&hcd->bandwidth_mutex);
goto re_enumerate;
}
mutex_unlock(&hcd->bandwidth_mutex);
usb_set_device_state(udev, USB_STATE_CONFIGURED);

/* Put interfaces back into the same altsettings as before.
Expand All @@ -3661,7 +3674,8 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
* endpoint state.
*/
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *intf = udev->actconfig->interface[i];
struct usb_host_config *config = udev->actconfig;
struct usb_interface *intf = config->interface[i];
struct usb_interface_descriptor *desc;

desc = &intf->cur_altsetting->desc;
Expand All @@ -3670,6 +3684,17 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
usb_enable_interface(udev, intf, true);
ret = 0;
} else {
/* We've just reset the device, so it will think alt
* setting 0 is installed. For usb_set_interface() to
* work properly, we need to set the current alternate
* interface setting to 0 (or the first alt setting, if
* the device doesn't have alt setting 0).
*/
intf->cur_altsetting =
usb_find_alt_setting(config, i, 0);
if (!intf->cur_altsetting)
intf->cur_altsetting =
&config->intf_cache[i]->altsetting[0];
ret = usb_set_interface(udev, desc->bInterfaceNumber,
desc->bAlternateSetting);
}
Expand Down
69 changes: 62 additions & 7 deletions trunk/drivers/usb/core/message.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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++) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1716,12 +1770,11 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
* 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;
}

Expand All @@ -1747,10 +1800,12 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
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
Expand Down

0 comments on commit 1d8c1b0

Please sign in to comment.