diff options
author | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2009-12-03 09:44:29 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-12-11 11:55:27 -0800 |
commit | 74f9fe21e0440066eb337b9f644238cb3050b91c (patch) | |
tree | b4ab839ba1a02cf2e6834ae3013b24e59e1e76c4 /drivers/usb/host/xhci-hcd.c | |
parent | 3342ecda3ffb059f2ffd765a71d9579f0aa036eb (diff) |
USB: xhci: Make reverting an alt setting "unfailable".
When a driver wants to switch to a different alternate setting for an
interface, the USB core will (soon) check whether there is enough
bandwidth. Once the new alternate setting is installed in the xHCI
hardware, the USB core will send a USB_REQ_SET_INTERFACE control
message. That can fail in various ways, and the USB core needs to be
able to reinstate the old alternate setting.
With the old code, reinstating the old alt setting could fail if the
there's not enough memory to allocate new endpoint rings. Keep
around a cache of (at most 31) endpoint rings for this case. When we
successfully switch the xHCI hardware to the new alt setting, the old
alt setting's rings will be stored in the cache. Therefore we'll
always have enough rings to satisfy a conversion back to a previous
device setting.
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/host/xhci-hcd.c')
-rw-r--r-- | drivers/usb/host/xhci-hcd.c | 32 |
1 files changed, 27 insertions, 5 deletions
diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 0d5a8564ed1..5e92c72df64 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -1262,13 +1262,35 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) LAST_CTX_TO_EP_NUM(slot_ctx->dev_info)); xhci_zero_in_ctx(xhci, virt_dev); - /* Free any old rings */ + /* Install new rings and free or cache any old rings */ for (i = 1; i < 31; ++i) { - if (virt_dev->eps[i].new_ring) { - xhci_ring_free(xhci, virt_dev->eps[i].ring); - virt_dev->eps[i].ring = virt_dev->eps[i].new_ring; - virt_dev->eps[i].new_ring = NULL; + int rings_cached; + + if (!virt_dev->eps[i].new_ring) + continue; + /* Only cache or free the old ring if it exists. + * It may not if this is the first add of an endpoint. + */ + if (virt_dev->eps[i].ring) { + rings_cached = virt_dev->num_rings_cached; + if (rings_cached < XHCI_MAX_RINGS_CACHED) { + virt_dev->num_rings_cached++; + rings_cached = virt_dev->num_rings_cached; + virt_dev->ring_cache[rings_cached] = + virt_dev->eps[i].ring; + xhci_dbg(xhci, "Cached old ring, " + "%d ring%s cached\n", + rings_cached, + (rings_cached > 1) ? "s" : ""); + } else { + xhci_ring_free(xhci, virt_dev->eps[i].ring); + xhci_dbg(xhci, "Ring cache full (%d rings), " + "freeing ring\n", + virt_dev->num_rings_cached); + } } + virt_dev->eps[i].ring = virt_dev->eps[i].new_ring; + virt_dev->eps[i].new_ring = NULL; } return ret; |