Commit ff19840a authored by Mathias Nyman's avatar Mathias Nyman Committed by Greg Kroah-Hartman

xhci: handle no ping response error properly

commit 3b4739b8 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: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 0dcb59bb
...@@ -2348,6 +2348,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, ...@@ -2348,6 +2348,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
u32 trb_comp_code; u32 trb_comp_code;
int ret = 0; int ret = 0;
int td_num = 0; int td_num = 0;
bool handling_skipped_tds = false;
slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
xdev = xhci->devs[slot_id]; xdev = xhci->devs[slot_id];
...@@ -2481,6 +2482,10 @@ static int handle_tx_event(struct xhci_hcd *xhci, ...@@ -2481,6 +2482,10 @@ static int handle_tx_event(struct xhci_hcd *xhci,
ep->skip = true; ep->skip = true;
xhci_dbg(xhci, "Miss service interval error, set skip flag\n"); xhci_dbg(xhci, "Miss service interval error, set skip flag\n");
goto cleanup; goto cleanup;
case COMP_PING_ERR:
ep->skip = true;
xhci_dbg(xhci, "No Ping response error, Skip one Isoc TD\n");
goto cleanup;
default: default:
if (xhci_is_vendor_info_code(xhci, trb_comp_code)) { if (xhci_is_vendor_info_code(xhci, trb_comp_code)) {
status = 0; status = 0;
...@@ -2612,13 +2617,18 @@ static int handle_tx_event(struct xhci_hcd *xhci, ...@@ -2612,13 +2617,18 @@ static int handle_tx_event(struct xhci_hcd *xhci,
ep, &status); ep, &status);
cleanup: 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. * Do not update event ring dequeue pointer if we're in a loop
* Will roll back to continue process missed tds. * processing missed tds.
*/ */
if (trb_comp_code == COMP_MISSED_INT || !ep->skip) { if (!handling_skipped_tds)
inc_deq(xhci, xhci->event_ring); inc_deq(xhci, xhci->event_ring);
}
if (ret) { if (ret) {
urb = td->urb; urb = td->urb;
...@@ -2662,7 +2672,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, ...@@ -2662,7 +2672,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
* Process them as short transfer until reach the td pointed by * Process them as short transfer until reach the td pointed by
* the event. * the event.
*/ */
} while (ep->skip && trb_comp_code != COMP_MISSED_INT); } while (handling_skipped_tds);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment