summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2011-06-05 23:10:04 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2011-07-13 05:31:23 +0200
commit20fea53565eb54fba2acd1820864002b6b26b669 (patch)
tree339e0548d62c6b799966a871174b7cd13b21dc36
parent75fe2ba05b86b971290cbc5f731681251ebb88c6 (diff)
xhci: Reject double add of active endpoints.
commit fa75ac379e63c2864e9049b5e8615e40f65c1e70 upstream. While trying to switch a UAS device from the BOT configuration to the UAS configuration via the bConfigurationValue file, Tanya ran into an issue in the USB core. usb_disable_device() sets entries in udev->ep_out and udev->ep_out to NULL, but doesn't call into the xHCI bandwidth management functions to remove the BOT configuration endpoints from the xHCI host's internal structures. The USB core would then attempt to add endpoints for the UAS configuration, and some of the endpoints had the same address as endpoints in the BOT configuration. The xHCI driver blindly added the endpoints again, but the xHCI host controller rejected the Configure Endpoint command because active endpoints were added without being dropped. Make the xHCI driver reject calls to xhci_add_endpoint() that attempt to add active endpoints without first calling xhci_drop_endpoint(). This should be backported to kernels as old as 2.6.31. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Reported-by: Tanya Brokhman <tlinder@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/host/xhci-hcd.c22
1 files changed, 18 insertions, 4 deletions
diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c
index 53d90cacb828..37a72c9edf1b 100644
--- a/drivers/usb/host/xhci-hcd.c
+++ b/drivers/usb/host/xhci-hcd.c
@@ -995,6 +995,7 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
u32 added_ctxs;
unsigned int last_ctx;
u32 new_add_flags, new_drop_flags, new_slot_info;
+ struct xhci_virt_device *virt_dev;
int ret = 0;
ret = xhci_check_args(hcd, udev, ep, 1, __func__);
@@ -1023,11 +1024,25 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
return -EINVAL;
}
- in_ctx = xhci->devs[udev->slot_id]->in_ctx;
- out_ctx = xhci->devs[udev->slot_id]->out_ctx;
+ virt_dev = xhci->devs[udev->slot_id];
+ in_ctx = virt_dev->in_ctx;
+ out_ctx = virt_dev->out_ctx;
ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx);
ep_index = xhci_get_endpoint_index(&ep->desc);
ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index);
+
+ /* If this endpoint is already in use, and the upper layers are trying
+ * to add it again without dropping it, reject the addition.
+ */
+ if (virt_dev->eps[ep_index].ring &&
+ !(le32_to_cpu(ctrl_ctx->drop_flags) &
+ xhci_get_endpoint_flag(&ep->desc))) {
+ xhci_warn(xhci, "Trying to add endpoint 0x%x "
+ "without dropping it.\n",
+ (unsigned int) ep->desc.bEndpointAddress);
+ return -EINVAL;
+ }
+
/* If the HCD has already noted the endpoint is enabled,
* ignore this request.
*/
@@ -1042,8 +1057,7 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
* process context, not interrupt context (or so documenation
* for usb_set_interface() and usb_set_configuration() claim).
*/
- if (xhci_endpoint_init(xhci, xhci->devs[udev->slot_id],
- udev, ep, GFP_KERNEL) < 0) {
+ if (xhci_endpoint_init(xhci, virt_dev, udev, ep, GFP_NOIO) < 0) {
dev_dbg(&udev->dev, "%s - could not initialize ep %#x\n",
__func__, ep->desc.bEndpointAddress);
return -ENOMEM;