aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2009-04-27 22:58:26 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-06-16 00:44:49 -0400
commit79abb1ab13cee5ba488210798b6e7bbae0b391ac (patch)
tree9bb008003d30146a092609a59882925640a5c93c /drivers/usb
parent663c30d0829d556efabd5fbd98fb8473da7fe694 (diff)
USB: Support for bandwidth allocation.
Originally, the USB core had no support for allocating bandwidth when a particular configuration or alternate setting for an interface was selected. Instead, the device driver's URB submission would fail if there was not enough bandwidth for a periodic endpoint. Drivers could work around this, by using the scatter-gather list API to guarantee bandwidth. This patch adds host controller API to allow the USB core to allocate or deallocate bandwidth for an endpoint. Endpoints are added to or dropped from a copy of the current schedule by calling add_endpoint() or drop_endpoint(), and then the schedule is atomically evaluated with a call to check_bandwidth(). This allows all the endpoints for a new configuration or alternate setting to be added at the same time that the endpoints from the old configuration or alt setting are dropped. Endpoints must be added to the schedule before any URBs are submitted to them. The HCD must be allowed to reject a new configuration or alt setting before the control transfer is sent to the device requesting the change. It may reject the change because there is not enough bandwidth, not enough internal resources (such as memory on an embedded host controller), or perhaps even for security reasons in a virtualized environment. If the call to check_bandwidth() fails, the USB core must call reset_bandwidth(). This causes the schedule to be reverted back to the state it was in just after the last successful check_bandwidth() call. If the call succeeds, the host controller driver (and hardware) will have changed its internal state to match the new configuration or alternate setting. The USB core can then issue a control transfer to the device to change the configuration or alt setting. This allows the core to test new configurations or alternate settings before unbinding drivers bound to interfaces in the old configuration. WIP: The USB core must add endpoints from all interfaces in a configuration to the schedule, because a driver may claim that interface at any time. A slight optimization might be to add the endpoints to the schedule once a driver claims that interface. FIXME This patch does not cover changing alternate settings, but it does handle a configuration change or de-configuration. FIXME The code for managing the schedule is currently HCD specific. A generic scheduling algorithm could be added for host controllers without built-in scheduling support. For now, if a host controller does not define the check_bandwidth() function, the call to usb_hcd_check_bandwidth() will always succeed. 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')
-rw-r--r--drivers/usb/core/hcd.c86
-rw-r--r--drivers/usb/core/hcd.h32
-rw-r--r--drivers/usb/core/message.c20
-rw-r--r--drivers/usb/core/urb.c6
4 files changed, 144 insertions, 0 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 823744d80cb..b2da4753b12 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1560,6 +1560,92 @@ rescan:
1560 } 1560 }
1561} 1561}
1562 1562
1563/* Check whether a new configuration or alt setting for an interface
1564 * will exceed the bandwidth for the bus (or the host controller resources).
1565 * Only pass in a non-NULL config or interface, not both!
1566 * Passing NULL for both new_config and new_intf means the device will be
1567 * de-configured by issuing a set configuration 0 command.
1568 */
1569int usb_hcd_check_bandwidth(struct usb_device *udev,
1570 struct usb_host_config *new_config,
1571 struct usb_interface *new_intf)
1572{
1573 int num_intfs, i, j;
1574 struct usb_interface_cache *intf_cache;
1575 struct usb_host_interface *alt = 0;
1576 int ret = 0;
1577 struct usb_hcd *hcd;
1578 struct usb_host_endpoint *ep;
1579
1580 hcd = bus_to_hcd(udev->bus);
1581 if (!hcd->driver->check_bandwidth)
1582 return 0;
1583
1584 /* Configuration is being removed - set configuration 0 */
1585 if (!new_config && !new_intf) {
1586 for (i = 1; i < 16; ++i) {
1587 ep = udev->ep_out[i];
1588 if (ep)
1589 hcd->driver->drop_endpoint(hcd, udev, ep);
1590 ep = udev->ep_in[i];
1591 if (ep)
1592 hcd->driver->drop_endpoint(hcd, udev, ep);
1593 }
1594 hcd->driver->check_bandwidth(hcd, udev);
1595 return 0;
1596 }
1597 /* Check if the HCD says there's enough bandwidth. Enable all endpoints
1598 * each interface's alt setting 0 and ask the HCD to check the bandwidth
1599 * of the bus. There will always be bandwidth for endpoint 0, so it's
1600 * ok to exclude it.
1601 */
1602 if (new_config) {
1603 num_intfs = new_config->desc.bNumInterfaces;
1604 /* Remove endpoints (except endpoint 0, which is always on the
1605 * schedule) from the old config from the schedule
1606 */
1607 for (i = 1; i < 16; ++i) {
1608 ep = udev->ep_out[i];
1609 if (ep) {
1610 ret = hcd->driver->drop_endpoint(hcd, udev, ep);
1611 if (ret < 0)
1612 goto reset;
1613 }
1614 ep = udev->ep_in[i];
1615 if (ep) {
1616 ret = hcd->driver->drop_endpoint(hcd, udev, ep);
1617 if (ret < 0)
1618 goto reset;
1619 }
1620 }
1621 for (i = 0; i < num_intfs; ++i) {
1622
1623 /* Dig the endpoints for alt setting 0 out of the
1624 * interface cache for this interface
1625 */
1626 intf_cache = new_config->intf_cache[i];
1627 for (j = 0; j < intf_cache->num_altsetting; j++) {
1628 if (intf_cache->altsetting[j].desc.bAlternateSetting == 0)
1629 alt = &intf_cache->altsetting[j];
1630 }
1631 if (!alt) {
1632 printk(KERN_DEBUG "Did not find alt setting 0 for intf %d\n", i);
1633 continue;
1634 }
1635 for (j = 0; j < alt->desc.bNumEndpoints; j++) {
1636 ret = hcd->driver->add_endpoint(hcd, udev, &alt->endpoint[j]);
1637 if (ret < 0)
1638 goto reset;
1639 }
1640 }
1641 }
1642 ret = hcd->driver->check_bandwidth(hcd, udev);
1643reset:
1644 if (ret < 0)
1645 hcd->driver->reset_bandwidth(hcd, udev);
1646 return ret;
1647}
1648
1563/* Disables the endpoint: synchronizes with the hcd to make sure all 1649/* Disables the endpoint: synchronizes with the hcd to make sure all
1564 * endpoint state is gone from hardware. usb_hcd_flush_endpoint() must 1650 * endpoint state is gone from hardware. usb_hcd_flush_endpoint() must
1565 * have been called previously. Use for set_configuration, set_interface, 1651 * have been called previously. Use for set_configuration, set_interface,
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index ae6d9db41ca..d397ecfd5b1 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -232,6 +232,35 @@ struct hc_driver {
232 int (*alloc_dev)(struct usb_hcd *, struct usb_device *); 232 int (*alloc_dev)(struct usb_hcd *, struct usb_device *);
233 /* Called by usb_release_dev to free HC device structures */ 233 /* Called by usb_release_dev to free HC device structures */
234 void (*free_dev)(struct usb_hcd *, struct usb_device *); 234 void (*free_dev)(struct usb_hcd *, struct usb_device *);
235
236 /* Bandwidth computation functions */
237 /* Note that add_endpoint() can only be called once per endpoint before
238 * check_bandwidth() or reset_bandwidth() must be called.
239 * drop_endpoint() can only be called once per endpoint also.
240 * A call to xhci_drop_endpoint() followed by a call to xhci_add_endpoint() will
241 * add the endpoint to the schedule with possibly new parameters denoted by a
242 * different endpoint descriptor in usb_host_endpoint.
243 * A call to xhci_add_endpoint() followed by a call to xhci_drop_endpoint() is
244 * not allowed.
245 */
246 /* Allocate endpoint resources and add them to a new schedule */
247 int (*add_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *);
248 /* Drop an endpoint from a new schedule */
249 int (*drop_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *);
250 /* Check that a new hardware configuration, set using
251 * endpoint_enable and endpoint_disable, does not exceed bus
252 * bandwidth. This must be called before any set configuration
253 * or set interface requests are sent to the device.
254 */
255 int (*check_bandwidth)(struct usb_hcd *, struct usb_device *);
256 /* Reset the device schedule to the last known good schedule,
257 * which was set from a previous successful call to
258 * check_bandwidth(). This reverts any add_endpoint() and
259 * drop_endpoint() calls since that last successful call.
260 * Used for when a check_bandwidth() call fails due to resource
261 * or bandwidth constraints.
262 */
263 void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
235 /* Returns the hardware-chosen device address */ 264 /* Returns the hardware-chosen device address */
236 int (*address_device)(struct usb_hcd *, struct usb_device *udev); 265 int (*address_device)(struct usb_hcd *, struct usb_device *udev);
237}; 266};
@@ -252,6 +281,9 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev,
252extern void usb_hcd_reset_endpoint(struct usb_device *udev, 281extern void usb_hcd_reset_endpoint(struct usb_device *udev,
253 struct usb_host_endpoint *ep); 282 struct usb_host_endpoint *ep);
254extern void usb_hcd_synchronize_unlinks(struct usb_device *udev); 283extern void usb_hcd_synchronize_unlinks(struct usb_device *udev);
284extern int usb_hcd_check_bandwidth(struct usb_device *udev,
285 struct usb_host_config *new_config,
286 struct usb_interface *new_intf);
255extern int usb_hcd_get_frame_number(struct usb_device *udev); 287extern int usb_hcd_get_frame_number(struct usb_device *udev);
256 288
257extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, 289extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 9bd26dec759..3a2e69ec2f2 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -510,6 +510,10 @@ EXPORT_SYMBOL_GPL(usb_sg_init);
510 * could be transferred. That capability is less useful for low or full 510 * could be transferred. That capability is less useful for low or full
511 * speed interrupt endpoints, which allow at most one packet per millisecond, 511 * speed interrupt endpoints, which allow at most one packet per millisecond,
512 * of at most 8 or 64 bytes (respectively). 512 * of at most 8 or 64 bytes (respectively).
513 *
514 * It is not necessary to call this function to reserve bandwidth for devices
515 * under an xHCI host controller, as the bandwidth is reserved when the
516 * configuration or interface alt setting is selected.
513 */ 517 */
514void usb_sg_wait(struct usb_sg_request *io) 518void usb_sg_wait(struct usb_sg_request *io)
515{ 519{
@@ -1653,6 +1657,21 @@ free_interfaces:
1653 if (ret) 1657 if (ret)
1654 goto free_interfaces; 1658 goto free_interfaces;
1655 1659
1660 /* Make sure we have bandwidth (and available HCD resources) for this
1661 * configuration. Remove endpoints from the schedule if we're dropping
1662 * this configuration to set configuration 0. After this point, the
1663 * host controller will not allow submissions to dropped endpoints. If
1664 * this call fails, the device state is unchanged.
1665 */
1666 if (cp)
1667 ret = usb_hcd_check_bandwidth(dev, cp, NULL);
1668 else
1669 ret = usb_hcd_check_bandwidth(dev, NULL, NULL);
1670 if (ret < 0) {
1671 usb_autosuspend_device(dev);
1672 goto free_interfaces;
1673 }
1674
1656 /* if it's already configured, clear out old state first. 1675 /* if it's already configured, clear out old state first.
1657 * getting rid of old interfaces means unbinding their drivers. 1676 * getting rid of old interfaces means unbinding their drivers.
1658 */ 1677 */
@@ -1675,6 +1694,7 @@ free_interfaces:
1675 dev->actconfig = cp; 1694 dev->actconfig = cp;
1676 if (!cp) { 1695 if (!cp) {
1677 usb_set_device_state(dev, USB_STATE_ADDRESS); 1696 usb_set_device_state(dev, USB_STATE_ADDRESS);
1697 usb_hcd_check_bandwidth(dev, NULL, NULL);
1678 usb_autosuspend_device(dev); 1698 usb_autosuspend_device(dev);
1679 goto free_interfaces; 1699 goto free_interfaces;
1680 } 1700 }
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 02eb0ef7a4c..0885d4abdc6 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -241,6 +241,12 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb);
241 * If the USB subsystem can't allocate sufficient bandwidth to perform 241 * If the USB subsystem can't allocate sufficient bandwidth to perform
242 * the periodic request, submitting such a periodic request should fail. 242 * the periodic request, submitting such a periodic request should fail.
243 * 243 *
244 * For devices under xHCI, the bandwidth is reserved at configuration time, or
245 * when the alt setting is selected. If there is not enough bus bandwidth, the
246 * configuration/alt setting request will fail. Therefore, submissions to
247 * periodic endpoints on devices under xHCI should never fail due to bandwidth
248 * constraints.
249 *
244 * Device drivers must explicitly request that repetition, by ensuring that 250 * Device drivers must explicitly request that repetition, by ensuring that
245 * some URB is always on the endpoint's queue (except possibly for short 251 * some URB is always on the endpoint's queue (except possibly for short
246 * periods during completion callacks). When there is no longer an urb 252 * periods during completion callacks). When there is no longer an urb