| From 3b4739b8951d650becbcd855d7d6f18ac98a9a85 Mon Sep 17 00:00:00 2001 |
| From: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Date: Mon, 12 Oct 2015 11:30:12 +0300 |
| Subject: xhci: handle no ping response error properly |
| |
| From: Mathias Nyman <mathias.nyman@linux.intel.com> |
| |
| commit 3b4739b8951d650becbcd855d7d6f18ac98a9a85 upstream. |
| |
| If a host fails to wake up a isochronous SuperSpeed device from U1/U2 |
| in time for a isoch transfer it will generate a "No ping response error" |
| Host will then move to the next transfer descriptor. |
| |
| Handle this case in the same way as missed service errors, tag the |
| current TD as skipped and handle it on the next transfer event. |
| |
| Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/usb/host/xhci-ring.c | 20 +++++++++++++++----- |
| 1 file changed, 15 insertions(+), 5 deletions(-) |
| |
| --- a/drivers/usb/host/xhci-ring.c |
| +++ b/drivers/usb/host/xhci-ring.c |
| @@ -2348,6 +2348,7 @@ static int handle_tx_event(struct xhci_h |
| u32 trb_comp_code; |
| int ret = 0; |
| int td_num = 0; |
| + bool handling_skipped_tds = false; |
| |
| slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); |
| xdev = xhci->devs[slot_id]; |
| @@ -2481,6 +2482,10 @@ static int handle_tx_event(struct xhci_h |
| ep->skip = true; |
| xhci_dbg(xhci, "Miss service interval error, set skip flag\n"); |
| goto cleanup; |
| + case COMP_PING_ERR: |
| + ep->skip = true; |
| + xhci_dbg(xhci, "No Ping response error, Skip one Isoc TD\n"); |
| + goto cleanup; |
| default: |
| if (xhci_is_vendor_info_code(xhci, trb_comp_code)) { |
| status = 0; |
| @@ -2612,13 +2617,18 @@ static int handle_tx_event(struct xhci_h |
| ep, &status); |
| |
| cleanup: |
| + |
| + |
| + handling_skipped_tds = ep->skip && |
| + trb_comp_code != COMP_MISSED_INT && |
| + trb_comp_code != COMP_PING_ERR; |
| + |
| /* |
| - * Do not update event ring dequeue pointer if ep->skip is set. |
| - * Will roll back to continue process missed tds. |
| + * Do not update event ring dequeue pointer if we're in a loop |
| + * processing missed tds. |
| */ |
| - if (trb_comp_code == COMP_MISSED_INT || !ep->skip) { |
| + if (!handling_skipped_tds) |
| inc_deq(xhci, xhci->event_ring); |
| - } |
| |
| if (ret) { |
| urb = td->urb; |
| @@ -2662,7 +2672,7 @@ cleanup: |
| * Process them as short transfer until reach the td pointed by |
| * the event. |
| */ |
| - } while (ep->skip && trb_comp_code != COMP_MISSED_INT); |
| + } while (handling_skipped_tds); |
| |
| return 0; |
| } |