diff options
author | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2009-07-27 15:05:21 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-07-28 17:31:13 -0400 |
commit | c92bcfa7b4038d8ffe1f02e21269f18eb0b64144 (patch) | |
tree | 779257c92d050d3d19eb0351f73ee59bcc5fa84f /drivers/usb/host/xhci-hcd.c | |
parent | d115b04818e57bdbc7ccde4d0660b15e33013dc8 (diff) |
USB: xhci: Stall handling bug fixes.
Correct the xHCI code to handle stalls on USB endpoints. We need to move
the endpoint ring's dequeue pointer past the stalled transfer, or the HW
will try to restart the transfer the next time the doorbell is rung.
Don't attempt to clear a halt on an endpoint if we haven't seen a stalled
transfer for it. The USB core will attempt to clear a halt on all
endpoints when it selects a new configuration.
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 | 24 |
1 files changed, 24 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index 057a07e876be..816c39caca1c 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c | |||
@@ -1089,6 +1089,8 @@ void xhci_endpoint_reset(struct usb_hcd *hcd, | |||
1089 | unsigned int ep_index; | 1089 | unsigned int ep_index; |
1090 | unsigned long flags; | 1090 | unsigned long flags; |
1091 | int ret; | 1091 | int ret; |
1092 | struct xhci_dequeue_state deq_state; | ||
1093 | struct xhci_ring *ep_ring; | ||
1092 | 1094 | ||
1093 | xhci = hcd_to_xhci(hcd); | 1095 | xhci = hcd_to_xhci(hcd); |
1094 | udev = (struct usb_device *) ep->hcpriv; | 1096 | udev = (struct usb_device *) ep->hcpriv; |
@@ -1098,11 +1100,33 @@ void xhci_endpoint_reset(struct usb_hcd *hcd, | |||
1098 | if (!ep->hcpriv) | 1100 | if (!ep->hcpriv) |
1099 | return; | 1101 | return; |
1100 | ep_index = xhci_get_endpoint_index(&ep->desc); | 1102 | ep_index = xhci_get_endpoint_index(&ep->desc); |
1103 | ep_ring = xhci->devs[udev->slot_id]->ep_rings[ep_index]; | ||
1104 | if (!ep_ring->stopped_td) { | ||
1105 | xhci_dbg(xhci, "Endpoint 0x%x not halted, refusing to reset.\n", | ||
1106 | ep->desc.bEndpointAddress); | ||
1107 | return; | ||
1108 | } | ||
1101 | 1109 | ||
1102 | xhci_dbg(xhci, "Queueing reset endpoint command\n"); | 1110 | xhci_dbg(xhci, "Queueing reset endpoint command\n"); |
1103 | spin_lock_irqsave(&xhci->lock, flags); | 1111 | spin_lock_irqsave(&xhci->lock, flags); |
1104 | ret = xhci_queue_reset_ep(xhci, udev->slot_id, ep_index); | 1112 | ret = xhci_queue_reset_ep(xhci, udev->slot_id, ep_index); |
1113 | /* | ||
1114 | * Can't change the ring dequeue pointer until it's transitioned to the | ||
1115 | * stopped state, which is only upon a successful reset endpoint | ||
1116 | * command. Better hope that last command worked! | ||
1117 | */ | ||
1105 | if (!ret) { | 1118 | if (!ret) { |
1119 | xhci_dbg(xhci, "Cleaning up stalled endpoint ring\n"); | ||
1120 | /* We need to move the HW's dequeue pointer past this TD, | ||
1121 | * or it will attempt to resend it on the next doorbell ring. | ||
1122 | */ | ||
1123 | xhci_find_new_dequeue_state(xhci, udev->slot_id, | ||
1124 | ep_index, ep_ring->stopped_td, &deq_state); | ||
1125 | xhci_dbg(xhci, "Queueing new dequeue state\n"); | ||
1126 | xhci_queue_new_dequeue_state(xhci, ep_ring, | ||
1127 | udev->slot_id, | ||
1128 | ep_index, &deq_state); | ||
1129 | kfree(ep_ring->stopped_td); | ||
1106 | xhci_ring_cmd_db(xhci); | 1130 | xhci_ring_cmd_db(xhci); |
1107 | } | 1131 | } |
1108 | spin_unlock_irqrestore(&xhci->lock, flags); | 1132 | spin_unlock_irqrestore(&xhci->lock, flags); |