| From fa75ac379e63c2864e9049b5e8615e40f65c1e70 Mon Sep 17 00:00:00 2001 |
| From: Sarah Sharp <sarah.a.sharp@linux.intel.com> |
| Date: Sun, 5 Jun 2011 23:10:04 -0700 |
| Subject: xhci: Reject double add of active endpoints. |
| |
| From: Sarah Sharp <sarah.a.sharp@linux.intel.com> |
| |
| 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> |
| |
| --- |
| drivers/usb/host/xhci-hcd.c | 22 ++++++++++++++++++---- |
| 1 file changed, 18 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/usb/host/xhci-hcd.c |
| +++ b/drivers/usb/host/xhci-hcd.c |
| @@ -979,6 +979,7 @@ int xhci_add_endpoint(struct usb_hcd *hc |
| 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__); |
| @@ -1007,11 +1008,25 @@ int xhci_add_endpoint(struct usb_hcd *hc |
| 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. |
| */ |
| @@ -1026,8 +1041,7 @@ int xhci_add_endpoint(struct usb_hcd *hc |
| * 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; |