|
@@ -2191,6 +2191,10 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
|
|
}
|
|
|
/* Fast path - was this the last TRB in the TD for this URB? */
|
|
|
} else if (event_trb == td->last_trb) {
|
|
|
+ if (td->urb_length_set && trb_comp_code == COMP_SHORT_TX)
|
|
|
+ return finish_td(xhci, td, event_trb, event, ep,
|
|
|
+ status, false);
|
|
|
+
|
|
|
if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
|
|
|
td->urb->actual_length =
|
|
|
td->urb->transfer_buffer_length -
|
|
@@ -2242,6 +2246,12 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
|
|
td->urb->actual_length +=
|
|
|
TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) -
|
|
|
EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
|
|
|
+
|
|
|
+ if (trb_comp_code == COMP_SHORT_TX) {
|
|
|
+ xhci_dbg(xhci, "mid bulk/intr SP, wait for last TRB event\n");
|
|
|
+ td->urb_length_set = true;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return finish_td(xhci, td, event_trb, event, ep, status, false);
|
|
@@ -2274,6 +2284,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
|
|
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];
|
|
@@ -2410,6 +2421,10 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
|
|
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;
|
|
@@ -2546,13 +2561,18 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
|
|
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;
|
|
@@ -2587,7 +2607,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;
|
|
|
}
|