aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2011-06-06 02:10:04 -0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2011-06-15 17:04:35 -0400
commitfa75ac379e63c2864e9049b5e8615e40f65c1e70 (patch)
tree1aa8b56c20f2547c0ce14a35403edd2f575e3403 /drivers/usb
parent357f45db920393aac983a137bd74095f612d5a01 (diff)
xhci: Reject double add of active endpoints.
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> Cc: stable@kernel.org
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/xhci.c22
1 files changed, 18 insertions, 4 deletions
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 06e7023258d0..e5a01713f937 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1401,6 +1401,7 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
1401 u32 added_ctxs; 1401 u32 added_ctxs;
1402 unsigned int last_ctx; 1402 unsigned int last_ctx;
1403 u32 new_add_flags, new_drop_flags, new_slot_info; 1403 u32 new_add_flags, new_drop_flags, new_slot_info;
1404 struct xhci_virt_device *virt_dev;
1404 int ret = 0; 1405 int ret = 0;
1405 1406
1406 ret = xhci_check_args(hcd, udev, ep, 1, true, __func__); 1407 ret = xhci_check_args(hcd, udev, ep, 1, true, __func__);
@@ -1425,11 +1426,25 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
1425 return 0; 1426 return 0;
1426 } 1427 }
1427 1428
1428 in_ctx = xhci->devs[udev->slot_id]->in_ctx; 1429 virt_dev = xhci->devs[udev->slot_id];
1429 out_ctx = xhci->devs[udev->slot_id]->out_ctx; 1430 in_ctx = virt_dev->in_ctx;
1431 out_ctx = virt_dev->out_ctx;
1430 ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx); 1432 ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx);
1431 ep_index = xhci_get_endpoint_index(&ep->desc); 1433 ep_index = xhci_get_endpoint_index(&ep->desc);
1432 ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index); 1434 ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index);
1435
1436 /* If this endpoint is already in use, and the upper layers are trying
1437 * to add it again without dropping it, reject the addition.
1438 */
1439 if (virt_dev->eps[ep_index].ring &&
1440 !(le32_to_cpu(ctrl_ctx->drop_flags) &
1441 xhci_get_endpoint_flag(&ep->desc))) {
1442 xhci_warn(xhci, "Trying to add endpoint 0x%x "
1443 "without dropping it.\n",
1444 (unsigned int) ep->desc.bEndpointAddress);
1445 return -EINVAL;
1446 }
1447
1433 /* If the HCD has already noted the endpoint is enabled, 1448 /* If the HCD has already noted the endpoint is enabled,
1434 * ignore this request. 1449 * ignore this request.
1435 */ 1450 */
@@ -1445,8 +1460,7 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
1445 * process context, not interrupt context (or so documenation 1460 * process context, not interrupt context (or so documenation
1446 * for usb_set_interface() and usb_set_configuration() claim). 1461 * for usb_set_interface() and usb_set_configuration() claim).
1447 */ 1462 */
1448 if (xhci_endpoint_init(xhci, xhci->devs[udev->slot_id], 1463 if (xhci_endpoint_init(xhci, virt_dev, udev, ep, GFP_NOIO) < 0) {
1449 udev, ep, GFP_NOIO) < 0) {
1450 dev_dbg(&udev->dev, "%s - could not initialize ep %#x\n", 1464 dev_dbg(&udev->dev, "%s - could not initialize ep %#x\n",
1451 __func__, ep->desc.bEndpointAddress); 1465 __func__, ep->desc.bEndpointAddress);
1452 return -ENOMEM; 1466 return -ENOMEM;