Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 151415
b: refs/heads/master
c: 79abb1a
h: refs/heads/master
i:
  151413: bd49298
  151411: a522628
  151407: 619e49d
v: v3
  • Loading branch information
Sarah Sharp authored and Greg Kroah-Hartman committed Jun 16, 2009
1 parent 9a4df7f commit d9e1e76
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 1 deletion.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 663c30d0829d556efabd5fbd98fb8473da7fe694
refs/heads/master: 79abb1ab13cee5ba488210798b6e7bbae0b391ac
86 changes: 86 additions & 0 deletions trunk/drivers/usb/core/hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,92 @@ 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.
*/
int usb_hcd_check_bandwidth(struct usb_device *udev,
struct usb_host_config *new_config,
struct usb_interface *new_intf)
{
int num_intfs, i, j;
struct usb_interface_cache *intf_cache;
struct usb_host_interface *alt = 0;
int ret = 0;
struct usb_hcd *hcd;
struct usb_host_endpoint *ep;

hcd = bus_to_hcd(udev->bus);
if (!hcd->driver->check_bandwidth)
return 0;

/* Configuration is being removed - set configuration 0 */
if (!new_config && !new_intf) {
for (i = 1; i < 16; ++i) {
ep = udev->ep_out[i];
if (ep)
hcd->driver->drop_endpoint(hcd, udev, ep);
ep = udev->ep_in[i];
if (ep)
hcd->driver->drop_endpoint(hcd, udev, ep);
}
hcd->driver->check_bandwidth(hcd, udev);
return 0;
}
/* Check if the HCD says there's enough bandwidth. Enable all endpoints
* each interface's alt setting 0 and ask the HCD to check the bandwidth
* of the bus. There will always be bandwidth for endpoint 0, so it's
* ok to exclude it.
*/
if (new_config) {
num_intfs = new_config->desc.bNumInterfaces;
/* Remove endpoints (except endpoint 0, which is always on the
* schedule) from the old config from the schedule
*/
for (i = 1; i < 16; ++i) {
ep = udev->ep_out[i];
if (ep) {
ret = hcd->driver->drop_endpoint(hcd, udev, ep);
if (ret < 0)
goto reset;
}
ep = udev->ep_in[i];
if (ep) {
ret = hcd->driver->drop_endpoint(hcd, udev, ep);
if (ret < 0)
goto reset;
}
}
for (i = 0; i < num_intfs; ++i) {

/* Dig the endpoints for alt setting 0 out of the
* interface cache for this interface
*/
intf_cache = new_config->intf_cache[i];
for (j = 0; j < intf_cache->num_altsetting; j++) {
if (intf_cache->altsetting[j].desc.bAlternateSetting == 0)
alt = &intf_cache->altsetting[j];
}
if (!alt) {
printk(KERN_DEBUG "Did not find alt setting 0 for intf %d\n", i);
continue;
}
for (j = 0; j < alt->desc.bNumEndpoints; j++) {
ret = hcd->driver->add_endpoint(hcd, udev, &alt->endpoint[j]);
if (ret < 0)
goto reset;
}
}
}
ret = hcd->driver->check_bandwidth(hcd, udev);
reset:
if (ret < 0)
hcd->driver->reset_bandwidth(hcd, udev);
return ret;
}

/* Disables the endpoint: synchronizes with the hcd to make sure all
* endpoint state is gone from hardware. usb_hcd_flush_endpoint() must
* have been called previously. Use for set_configuration, set_interface,
Expand Down
32 changes: 32 additions & 0 deletions trunk/drivers/usb/core/hcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,35 @@ struct hc_driver {
int (*alloc_dev)(struct usb_hcd *, struct usb_device *);
/* Called by usb_release_dev to free HC device structures */
void (*free_dev)(struct usb_hcd *, struct usb_device *);

/* Bandwidth computation functions */
/* Note that add_endpoint() can only be called once per endpoint before
* check_bandwidth() or reset_bandwidth() must be called.
* drop_endpoint() can only be called once per endpoint also.
* A call to xhci_drop_endpoint() followed by a call to xhci_add_endpoint() will
* add the endpoint to the schedule with possibly new parameters denoted by a
* different endpoint descriptor in usb_host_endpoint.
* A call to xhci_add_endpoint() followed by a call to xhci_drop_endpoint() is
* not allowed.
*/
/* Allocate endpoint resources and add them to a new schedule */
int (*add_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *);
/* Drop an endpoint from a new schedule */
int (*drop_endpoint)(struct usb_hcd *, struct usb_device *, struct usb_host_endpoint *);
/* Check that a new hardware configuration, set using
* endpoint_enable and endpoint_disable, does not exceed bus
* bandwidth. This must be called before any set configuration
* or set interface requests are sent to the device.
*/
int (*check_bandwidth)(struct usb_hcd *, struct usb_device *);
/* Reset the device schedule to the last known good schedule,
* which was set from a previous successful call to
* check_bandwidth(). This reverts any add_endpoint() and
* drop_endpoint() calls since that last successful call.
* Used for when a check_bandwidth() call fails due to resource
* or bandwidth constraints.
*/
void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
/* Returns the hardware-chosen device address */
int (*address_device)(struct usb_hcd *, struct usb_device *udev);
};
Expand All @@ -252,6 +281,9 @@ 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,
struct usb_host_config *new_config,
struct usb_interface *new_intf);
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
20 changes: 20 additions & 0 deletions trunk/drivers/usb/core/message.c
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,10 @@ EXPORT_SYMBOL_GPL(usb_sg_init);
* could be transferred. That capability is less useful for low or full
* speed interrupt endpoints, which allow at most one packet per millisecond,
* of at most 8 or 64 bytes (respectively).
*
* It is not necessary to call this function to reserve bandwidth for devices
* under an xHCI host controller, as the bandwidth is reserved when the
* configuration or interface alt setting is selected.
*/
void usb_sg_wait(struct usb_sg_request *io)
{
Expand Down Expand Up @@ -1653,6 +1657,21 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
if (ret)
goto free_interfaces;

/* Make sure we have bandwidth (and available HCD resources) for this
* configuration. Remove endpoints from the schedule if we're dropping
* this configuration to set configuration 0. After this point, the
* 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);
if (ret < 0) {
usb_autosuspend_device(dev);
goto free_interfaces;
}

/* if it's already configured, clear out old state first.
* getting rid of old interfaces means unbinding their drivers.
*/
Expand All @@ -1675,6 +1694,7 @@ 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_autosuspend_device(dev);
goto free_interfaces;
}
Expand Down
6 changes: 6 additions & 0 deletions trunk/drivers/usb/core/urb.c
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb);
* If the USB subsystem can't allocate sufficient bandwidth to perform
* the periodic request, submitting such a periodic request should fail.
*
* For devices under xHCI, the bandwidth is reserved at configuration time, or
* when the alt setting is selected. If there is not enough bus bandwidth, the
* configuration/alt setting request will fail. Therefore, submissions to
* periodic endpoints on devices under xHCI should never fail due to bandwidth
* constraints.
*
* Device drivers must explicitly request that repetition, by ensuring that
* some URB is always on the endpoint's queue (except possibly for short
* periods during completion callacks). When there is no longer an urb
Expand Down

0 comments on commit d9e1e76

Please sign in to comment.