diff options
author | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2009-09-28 20:21:37 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-10-09 16:52:06 -0400 |
commit | e34b2fbf28741310d1d59a217d34e050ce7867e8 (patch) | |
tree | 849303efcb6905a4070ce8aed5c337d42a255196 | |
parent | e4ab05df573834b8c70d19db426b7d6286782c1d (diff) |
USB: xhci: Handle canceled URBs when HC dies.
When the host controller dies (e.g. it is removed from a PCI card slot),
the xHCI driver cannot expect commands to complete. The buggy code this
patch fixes would mark an URB as canceled and then expect the URB to be
completed when the stop endpoint command completed. That would never
happen if the host controller was dead, so the USB core would just hang in
the disconnect code.
If the host controller died, and the driver asks to cancel an URB, free
any structures associated with that URB and immediately give it back.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/host/xhci-hcd.c | 12 |
1 files changed, 12 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 8719a3f6851d..592e742c5f35 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c | |||
@@ -782,6 +782,7 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) | |||
782 | { | 782 | { |
783 | unsigned long flags; | 783 | unsigned long flags; |
784 | int ret; | 784 | int ret; |
785 | u32 temp; | ||
785 | struct xhci_hcd *xhci; | 786 | struct xhci_hcd *xhci; |
786 | struct xhci_td *td; | 787 | struct xhci_td *td; |
787 | unsigned int ep_index; | 788 | unsigned int ep_index; |
@@ -794,6 +795,17 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) | |||
794 | ret = usb_hcd_check_unlink_urb(hcd, urb, status); | 795 | ret = usb_hcd_check_unlink_urb(hcd, urb, status); |
795 | if (ret || !urb->hcpriv) | 796 | if (ret || !urb->hcpriv) |
796 | goto done; | 797 | goto done; |
798 | temp = xhci_readl(xhci, &xhci->op_regs->status); | ||
799 | if (temp == 0xffffffff) { | ||
800 | xhci_dbg(xhci, "HW died, freeing TD.\n"); | ||
801 | td = (struct xhci_td *) urb->hcpriv; | ||
802 | |||
803 | usb_hcd_unlink_urb_from_ep(hcd, urb); | ||
804 | spin_unlock_irqrestore(&xhci->lock, flags); | ||
805 | usb_hcd_giveback_urb(xhci_to_hcd(xhci), urb, -ESHUTDOWN); | ||
806 | kfree(td); | ||
807 | return ret; | ||
808 | } | ||
797 | 809 | ||
798 | xhci_dbg(xhci, "Cancel URB %p\n", urb); | 810 | xhci_dbg(xhci, "Cancel URB %p\n", urb); |
799 | xhci_dbg(xhci, "Event ring:\n"); | 811 | xhci_dbg(xhci, "Event ring:\n"); |