diff options
-rw-r--r-- | drivers/usb/core/hcd.c | 86 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 32 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 20 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 6 |
4 files changed, 144 insertions, 0 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 823744d80cb9..b2da4753b12e 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 | */ | ||
1569 | int 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); | ||
1643 | reset: | ||
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 ae6d9db41ca9..d397ecfd5b17 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, | |||
252 | extern void usb_hcd_reset_endpoint(struct usb_device *udev, | 281 | extern void usb_hcd_reset_endpoint(struct usb_device *udev, |
253 | struct usb_host_endpoint *ep); | 282 | struct usb_host_endpoint *ep); |
254 | extern void usb_hcd_synchronize_unlinks(struct usb_device *udev); | 283 | extern void usb_hcd_synchronize_unlinks(struct usb_device *udev); |
284 | extern int usb_hcd_check_bandwidth(struct usb_device *udev, | ||
285 | struct usb_host_config *new_config, | ||
286 | struct usb_interface *new_intf); | ||
255 | extern int usb_hcd_get_frame_number(struct usb_device *udev); | 287 | extern int usb_hcd_get_frame_number(struct usb_device *udev); |
256 | 288 | ||
257 | extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, | 289 | extern 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 9bd26dec7599..3a2e69ec2f29 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 | */ |
514 | void usb_sg_wait(struct usb_sg_request *io) | 518 | void 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 02eb0ef7a4c3..0885d4abdc62 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 |