aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2009-12-03 12:44:36 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-12-11 14:55:27 -0500
commit3f0479e00a3fca9590ae8d9edc4e9c47b7fa0610 (patch)
tree495cee9f35ed0315367a10af86bbb107f05eeb75 /drivers/usb/core
parent91017f9cf5fcfb601b8d583c896ac7de7d200c57 (diff)
USB: Check bandwidth when switching alt settings.
Make the USB core check the bandwidth when switching from one interface alternate setting to another. Also check the bandwidth when resetting a configuration (so that alt setting 0 is used). If this check fails, the device's state is unchanged. If the device refuses the new alt setting, re-instate the old alt setting in the host controller hardware. If a USB device doesn't have an alternate interface setting 0, install the first alt setting in its descriptors when a new configuration is requested, or the device is reset. Add a mutex per root hub to protect bandwidth operations: adding/reseting/changing configurations, and changing alternate interface settings. We want to ensure that the xHCI host controller and the USB device are set up for the same configurations and alternate settings. There are two (possibly three) steps to do this: 1. The host controller needs to check that bandwidth is available for a different setting, by issuing and waiting for a configure endpoint command. 2. Once that returns successfully, a control message is sent to the device. 3. If that fails, the host controller must be notified through another configure endpoint command. The mutex is used to make these three operations seem atomic, to prevent another driver from using more bandwidth for a different device while we're in the middle of these operations. While we're touching the bandwidth code, rename usb_hcd_check_bandwidth() to usb_hcd_alloc_bandwidth(). This function does more than just check that the bandwidth change won't exceed the bus bandwidth; it actually changes the bandwidth configuration in the xHCI host controller. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/hcd.c56
-rw-r--r--drivers/usb/core/hcd.h19
-rw-r--r--drivers/usb/core/hub.c27
-rw-r--r--drivers/usb/core/message.c69
4 files changed, 149 insertions, 22 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index fc235b02ff27..6dac3b802d41 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -38,6 +38,7 @@
38#include <asm/unaligned.h> 38#include <asm/unaligned.h>
39#include <linux/platform_device.h> 39#include <linux/platform_device.h>
40#include <linux/workqueue.h> 40#include <linux/workqueue.h>
41#include <linux/mutex.h>
41 42
42#include <linux/usb.h> 43#include <linux/usb.h>
43 44
@@ -1595,15 +1596,29 @@ rescan:
1595 } 1596 }
1596} 1597}
1597 1598
1598/* Check whether a new configuration or alt setting for an interface 1599/**
1599 * will exceed the bandwidth for the bus (or the host controller resources). 1600 * Check whether a new bandwidth setting exceeds the bus bandwidth.
1600 * Only pass in a non-NULL config or interface, not both! 1601 * @new_config: new configuration to install
1601 * Passing NULL for both new_config and new_intf means the device will be 1602 * @cur_alt: the current alternate interface setting
1602 * de-configured by issuing a set configuration 0 command. 1603 * @new_alt: alternate interface setting that is being installed
1604 *
1605 * To change configurations, pass in the new configuration in new_config,
1606 * and pass NULL for cur_alt and new_alt.
1607 *
1608 * To reset a device's configuration (put the device in the ADDRESSED state),
1609 * pass in NULL for new_config, cur_alt, and new_alt.
1610 *
1611 * To change alternate interface settings, pass in NULL for new_config,
1612 * pass in the current alternate interface setting in cur_alt,
1613 * and pass in the new alternate interface setting in new_alt.
1614 *
1615 * Returns an error if the requested bandwidth change exceeds the
1616 * bus bandwidth or host controller internal resources.
1603 */ 1617 */
1604int usb_hcd_check_bandwidth(struct usb_device *udev, 1618int usb_hcd_alloc_bandwidth(struct usb_device *udev,
1605 struct usb_host_config *new_config, 1619 struct usb_host_config *new_config,
1606 struct usb_interface *new_intf) 1620 struct usb_host_interface *cur_alt,
1621 struct usb_host_interface *new_alt)
1607{ 1622{
1608 int num_intfs, i, j; 1623 int num_intfs, i, j;
1609 struct usb_host_interface *alt = NULL; 1624 struct usb_host_interface *alt = NULL;
@@ -1616,7 +1631,7 @@ int usb_hcd_check_bandwidth(struct usb_device *udev,
1616 return 0; 1631 return 0;
1617 1632
1618 /* Configuration is being removed - set configuration 0 */ 1633 /* Configuration is being removed - set configuration 0 */
1619 if (!new_config && !new_intf) { 1634 if (!new_config && !cur_alt) {
1620 for (i = 1; i < 16; ++i) { 1635 for (i = 1; i < 16; ++i) {
1621 ep = udev->ep_out[i]; 1636 ep = udev->ep_out[i];
1622 if (ep) 1637 if (ep)
@@ -1655,10 +1670,10 @@ int usb_hcd_check_bandwidth(struct usb_device *udev,
1655 for (i = 0; i < num_intfs; ++i) { 1670 for (i = 0; i < num_intfs; ++i) {
1656 /* Set up endpoints for alternate interface setting 0 */ 1671 /* Set up endpoints for alternate interface setting 0 */
1657 alt = usb_find_alt_setting(new_config, i, 0); 1672 alt = usb_find_alt_setting(new_config, i, 0);
1658 if (!alt) { 1673 if (!alt)
1659 printk(KERN_DEBUG "Did not find alt setting 0 for intf %d\n", i); 1674 /* No alt setting 0? Pick the first setting. */
1660 continue; 1675 alt = &new_config->intf_cache[i]->altsetting[0];
1661 } 1676
1662 for (j = 0; j < alt->desc.bNumEndpoints; j++) { 1677 for (j = 0; j < alt->desc.bNumEndpoints; j++) {
1663 ret = hcd->driver->add_endpoint(hcd, udev, &alt->endpoint[j]); 1678 ret = hcd->driver->add_endpoint(hcd, udev, &alt->endpoint[j]);
1664 if (ret < 0) 1679 if (ret < 0)
@@ -1666,6 +1681,22 @@ int usb_hcd_check_bandwidth(struct usb_device *udev,
1666 } 1681 }
1667 } 1682 }
1668 } 1683 }
1684 if (cur_alt && new_alt) {
1685 /* Drop all the endpoints in the current alt setting */
1686 for (i = 0; i < cur_alt->desc.bNumEndpoints; i++) {
1687 ret = hcd->driver->drop_endpoint(hcd, udev,
1688 &cur_alt->endpoint[i]);
1689 if (ret < 0)
1690 goto reset;
1691 }
1692 /* Add all the endpoints in the new alt setting */
1693 for (i = 0; i < new_alt->desc.bNumEndpoints; i++) {
1694 ret = hcd->driver->add_endpoint(hcd, udev,
1695 &new_alt->endpoint[i]);
1696 if (ret < 0)
1697 goto reset;
1698 }
1699 }
1669 ret = hcd->driver->check_bandwidth(hcd, udev); 1700 ret = hcd->driver->check_bandwidth(hcd, udev);
1670reset: 1701reset:
1671 if (ret < 0) 1702 if (ret < 0)
@@ -1982,6 +2013,7 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
1982#ifdef CONFIG_PM 2013#ifdef CONFIG_PM
1983 INIT_WORK(&hcd->wakeup_work, hcd_resume_work); 2014 INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
1984#endif 2015#endif
2016 mutex_init(&hcd->bandwidth_mutex);
1985 2017
1986 hcd->driver = driver; 2018 hcd->driver = driver;
1987 hcd->product_desc = (driver->product_desc) ? driver->product_desc : 2019 hcd->product_desc = (driver->product_desc) ? driver->product_desc :
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 79782a1c43f6..d8b43aee581e 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -111,6 +111,20 @@ struct usb_hcd {
111 u64 rsrc_len; /* memory/io resource length */ 111 u64 rsrc_len; /* memory/io resource length */
112 unsigned power_budget; /* in mA, 0 = no limit */ 112 unsigned power_budget; /* in mA, 0 = no limit */
113 113
114 /* bandwidth_mutex should be taken before adding or removing
115 * any new bus bandwidth constraints:
116 * 1. Before adding a configuration for a new device.
117 * 2. Before removing the configuration to put the device into
118 * the addressed state.
119 * 3. Before selecting a different configuration.
120 * 4. Before selecting an alternate interface setting.
121 *
122 * bandwidth_mutex should be dropped after a successful control message
123 * to the device, or resetting the bandwidth after a failed attempt.
124 */
125 struct mutex bandwidth_mutex;
126
127
114#define HCD_BUFFER_POOLS 4 128#define HCD_BUFFER_POOLS 4
115 struct dma_pool *pool [HCD_BUFFER_POOLS]; 129 struct dma_pool *pool [HCD_BUFFER_POOLS];
116 130
@@ -290,9 +304,10 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev,
290extern void usb_hcd_reset_endpoint(struct usb_device *udev, 304extern void usb_hcd_reset_endpoint(struct usb_device *udev,
291 struct usb_host_endpoint *ep); 305 struct usb_host_endpoint *ep);
292extern void usb_hcd_synchronize_unlinks(struct usb_device *udev); 306extern void usb_hcd_synchronize_unlinks(struct usb_device *udev);
293extern int usb_hcd_check_bandwidth(struct usb_device *udev, 307extern int usb_hcd_alloc_bandwidth(struct usb_device *udev,
294 struct usb_host_config *new_config, 308 struct usb_host_config *new_config,
295 struct usb_interface *new_intf); 309 struct usb_host_interface *old_alt,
310 struct usb_host_interface *new_alt);
296extern int usb_hcd_get_frame_number(struct usb_device *udev); 311extern int usb_hcd_get_frame_number(struct usb_device *udev);
297 312
298extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, 313extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index b38fd6730e2a..e4b0e28f7453 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -3599,6 +3599,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
3599{ 3599{
3600 struct usb_device *parent_hdev = udev->parent; 3600 struct usb_device *parent_hdev = udev->parent;
3601 struct usb_hub *parent_hub; 3601 struct usb_hub *parent_hub;
3602 struct usb_hcd *hcd = bus_to_hcd(udev->bus);
3602 struct usb_device_descriptor descriptor = udev->descriptor; 3603 struct usb_device_descriptor descriptor = udev->descriptor;
3603 int i, ret = 0; 3604 int i, ret = 0;
3604 int port1 = udev->portnum; 3605 int port1 = udev->portnum;
@@ -3642,6 +3643,16 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
3642 /* Restore the device's previous configuration */ 3643 /* Restore the device's previous configuration */
3643 if (!udev->actconfig) 3644 if (!udev->actconfig)
3644 goto done; 3645 goto done;
3646
3647 mutex_lock(&hcd->bandwidth_mutex);
3648 ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
3649 if (ret < 0) {
3650 dev_warn(&udev->dev,
3651 "Busted HC? Not enough HCD resources for "
3652 "old configuration.\n");
3653 mutex_unlock(&hcd->bandwidth_mutex);
3654 goto re_enumerate;
3655 }
3645 ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 3656 ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
3646 USB_REQ_SET_CONFIGURATION, 0, 3657 USB_REQ_SET_CONFIGURATION, 0,
3647 udev->actconfig->desc.bConfigurationValue, 0, 3658 udev->actconfig->desc.bConfigurationValue, 0,
@@ -3650,8 +3661,10 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
3650 dev_err(&udev->dev, 3661 dev_err(&udev->dev,
3651 "can't restore configuration #%d (error=%d)\n", 3662 "can't restore configuration #%d (error=%d)\n",
3652 udev->actconfig->desc.bConfigurationValue, ret); 3663 udev->actconfig->desc.bConfigurationValue, ret);
3664 mutex_unlock(&hcd->bandwidth_mutex);
3653 goto re_enumerate; 3665 goto re_enumerate;
3654 } 3666 }
3667 mutex_unlock(&hcd->bandwidth_mutex);
3655 usb_set_device_state(udev, USB_STATE_CONFIGURED); 3668 usb_set_device_state(udev, USB_STATE_CONFIGURED);
3656 3669
3657 /* Put interfaces back into the same altsettings as before. 3670 /* Put interfaces back into the same altsettings as before.
@@ -3661,7 +3674,8 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
3661 * endpoint state. 3674 * endpoint state.
3662 */ 3675 */
3663 for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { 3676 for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
3664 struct usb_interface *intf = udev->actconfig->interface[i]; 3677 struct usb_host_config *config = udev->actconfig;
3678 struct usb_interface *intf = config->interface[i];
3665 struct usb_interface_descriptor *desc; 3679 struct usb_interface_descriptor *desc;
3666 3680
3667 desc = &intf->cur_altsetting->desc; 3681 desc = &intf->cur_altsetting->desc;
@@ -3670,6 +3684,17 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
3670 usb_enable_interface(udev, intf, true); 3684 usb_enable_interface(udev, intf, true);
3671 ret = 0; 3685 ret = 0;
3672 } else { 3686 } else {
3687 /* We've just reset the device, so it will think alt
3688 * setting 0 is installed. For usb_set_interface() to
3689 * work properly, we need to set the current alternate
3690 * interface setting to 0 (or the first alt setting, if
3691 * the device doesn't have alt setting 0).
3692 */
3693 intf->cur_altsetting =
3694 usb_find_alt_setting(config, i, 0);
3695 if (!intf->cur_altsetting)
3696 intf->cur_altsetting =
3697 &config->intf_cache[i]->altsetting[0];
3673 ret = usb_set_interface(udev, desc->bInterfaceNumber, 3698 ret = usb_set_interface(udev, desc->bInterfaceNumber,
3674 desc->bAlternateSetting); 3699 desc->bAlternateSetting);
3675 } 3700 }
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)
1298{ 1298{
1299 struct usb_interface *iface; 1299 struct usb_interface *iface;
1300 struct usb_host_interface *alt; 1300 struct usb_host_interface *alt;
1301 struct usb_hcd *hcd = bus_to_hcd(dev->bus);
1301 int ret; 1302 int ret;
1302 int manual = 0; 1303 int manual = 0;
1303 unsigned int epaddr; 1304 unsigned int epaddr;
@@ -1320,6 +1321,18 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
1320 return -EINVAL; 1321 return -EINVAL;
1321 } 1322 }
1322 1323
1324 /* Make sure we have enough bandwidth for this alternate interface.
1325 * Remove the current alt setting and add the new alt setting.
1326 */
1327 mutex_lock(&hcd->bandwidth_mutex);
1328 ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt);
1329 if (ret < 0) {
1330 dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n",
1331 alternate);
1332 mutex_unlock(&hcd->bandwidth_mutex);
1333 return ret;
1334 }
1335
1323 if (dev->quirks & USB_QUIRK_NO_SET_INTF) 1336 if (dev->quirks & USB_QUIRK_NO_SET_INTF)
1324 ret = -EPIPE; 1337 ret = -EPIPE;
1325 else 1338 else
@@ -1335,8 +1348,13 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
1335 "manual set_interface for iface %d, alt %d\n", 1348 "manual set_interface for iface %d, alt %d\n",
1336 interface, alternate); 1349 interface, alternate);
1337 manual = 1; 1350 manual = 1;
1338 } else if (ret < 0) 1351 } else if (ret < 0) {
1352 /* Re-instate the old alt setting */
1353 usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting);
1354 mutex_unlock(&hcd->bandwidth_mutex);
1339 return ret; 1355 return ret;
1356 }
1357 mutex_unlock(&hcd->bandwidth_mutex);
1340 1358
1341 /* FIXME drivers shouldn't need to replicate/bugfix the logic here 1359 /* FIXME drivers shouldn't need to replicate/bugfix the logic here
1342 * when they implement async or easily-killable versions of this or 1360 * when they implement async or easily-killable versions of this or
@@ -1418,6 +1436,7 @@ int usb_reset_configuration(struct usb_device *dev)
1418{ 1436{
1419 int i, retval; 1437 int i, retval;
1420 struct usb_host_config *config; 1438 struct usb_host_config *config;
1439 struct usb_hcd *hcd = bus_to_hcd(dev->bus);
1421 1440
1422 if (dev->state == USB_STATE_SUSPENDED) 1441 if (dev->state == USB_STATE_SUSPENDED)
1423 return -EHOSTUNREACH; 1442 return -EHOSTUNREACH;
@@ -1433,12 +1452,46 @@ int usb_reset_configuration(struct usb_device *dev)
1433 } 1452 }
1434 1453
1435 config = dev->actconfig; 1454 config = dev->actconfig;
1455 retval = 0;
1456 mutex_lock(&hcd->bandwidth_mutex);
1457 /* Make sure we have enough bandwidth for each alternate setting 0 */
1458 for (i = 0; i < config->desc.bNumInterfaces; i++) {
1459 struct usb_interface *intf = config->interface[i];
1460 struct usb_host_interface *alt;
1461
1462 alt = usb_altnum_to_altsetting(intf, 0);
1463 if (!alt)
1464 alt = &intf->altsetting[0];
1465 if (alt != intf->cur_altsetting)
1466 retval = usb_hcd_alloc_bandwidth(dev, NULL,
1467 intf->cur_altsetting, alt);
1468 if (retval < 0)
1469 break;
1470 }
1471 /* If not, reinstate the old alternate settings */
1472 if (retval < 0) {
1473reset_old_alts:
1474 for (; i >= 0; i--) {
1475 struct usb_interface *intf = config->interface[i];
1476 struct usb_host_interface *alt;
1477
1478 alt = usb_altnum_to_altsetting(intf, 0);
1479 if (!alt)
1480 alt = &intf->altsetting[0];
1481 if (alt != intf->cur_altsetting)
1482 usb_hcd_alloc_bandwidth(dev, NULL,
1483 alt, intf->cur_altsetting);
1484 }
1485 mutex_unlock(&hcd->bandwidth_mutex);
1486 return retval;
1487 }
1436 retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 1488 retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
1437 USB_REQ_SET_CONFIGURATION, 0, 1489 USB_REQ_SET_CONFIGURATION, 0,
1438 config->desc.bConfigurationValue, 0, 1490 config->desc.bConfigurationValue, 0,
1439 NULL, 0, USB_CTRL_SET_TIMEOUT); 1491 NULL, 0, USB_CTRL_SET_TIMEOUT);
1440 if (retval < 0) 1492 if (retval < 0)
1441 return retval; 1493 goto reset_old_alts;
1494 mutex_unlock(&hcd->bandwidth_mutex);
1442 1495
1443 /* re-init hc/hcd interface/endpoint state */ 1496 /* re-init hc/hcd interface/endpoint state */
1444 for (i = 0; i < config->desc.bNumInterfaces; i++) { 1497 for (i = 0; i < config->desc.bNumInterfaces; i++) {
@@ -1647,6 +1700,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
1647 int i, ret; 1700 int i, ret;
1648 struct usb_host_config *cp = NULL; 1701 struct usb_host_config *cp = NULL;
1649 struct usb_interface **new_interfaces = NULL; 1702 struct usb_interface **new_interfaces = NULL;
1703 struct usb_hcd *hcd = bus_to_hcd(dev->bus);
1650 int n, nintf; 1704 int n, nintf;
1651 1705
1652 if (dev->authorized == 0 || configuration == -1) 1706 if (dev->authorized == 0 || configuration == -1)
@@ -1716,12 +1770,11 @@ free_interfaces:
1716 * host controller will not allow submissions to dropped endpoints. If 1770 * host controller will not allow submissions to dropped endpoints. If
1717 * this call fails, the device state is unchanged. 1771 * this call fails, the device state is unchanged.
1718 */ 1772 */
1719 if (cp) 1773 mutex_lock(&hcd->bandwidth_mutex);
1720 ret = usb_hcd_check_bandwidth(dev, cp, NULL); 1774 ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
1721 else
1722 ret = usb_hcd_check_bandwidth(dev, NULL, NULL);
1723 if (ret < 0) { 1775 if (ret < 0) {
1724 usb_autosuspend_device(dev); 1776 usb_autosuspend_device(dev);
1777 mutex_unlock(&hcd->bandwidth_mutex);
1725 goto free_interfaces; 1778 goto free_interfaces;
1726 } 1779 }
1727 1780
@@ -1747,10 +1800,12 @@ free_interfaces:
1747 dev->actconfig = cp; 1800 dev->actconfig = cp;
1748 if (!cp) { 1801 if (!cp) {
1749 usb_set_device_state(dev, USB_STATE_ADDRESS); 1802 usb_set_device_state(dev, USB_STATE_ADDRESS);
1750 usb_hcd_check_bandwidth(dev, NULL, NULL); 1803 usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
1751 usb_autosuspend_device(dev); 1804 usb_autosuspend_device(dev);
1805 mutex_unlock(&hcd->bandwidth_mutex);
1752 goto free_interfaces; 1806 goto free_interfaces;
1753 } 1807 }
1808 mutex_unlock(&hcd->bandwidth_mutex);
1754 usb_set_device_state(dev, USB_STATE_CONFIGURED); 1809 usb_set_device_state(dev, USB_STATE_CONFIGURED);
1755 1810
1756 /* Initialize the new interface structures and the 1811 /* Initialize the new interface structures and the