Skip to content

Commit 5372c65

Browse files
matnymangregkh
authored andcommitted
xhci: process isoc TD properly when there was a transaction error mid TD.
The last TRB of a isoc TD might not trigger an event if there was an error event for a TRB mid TD. This is seen on a NEC Corporation uPD720200 USB 3.0 Host After an error mid a multi-TRB TD the xHC should according to xhci 4.9.1 generate events for passed TRBs with IOC flag set if it proceeds to the next TD. This event is either a copy of the original error, or a "success" transfer event. If that event is missing then the driver and xHC host get out of sync as the driver is still expecting a transfer event for that first TD, while xHC host is already sending events for the next TD in the list. This leads to "Transfer event TRB DMA ptr not part of current TD" messages. As a solution we tag the isoc TDs that get error events mid TD. If an event doesn't match the first TD, then check if the tag is set, and event points to the next TD. In that case give back the fist TD and process the next TD normally Make sure TD status and transferred length stay valid in both cases with and without final TD completion event. Reported-by: Michał Pecio <[email protected]> Closes: https://lore.kernel.org/linux-usb/20240112235205.1259f60c@foxbook/ Tested-by: Michał Pecio <[email protected]> Cc: [email protected] Signed-off-by: Mathias Nyman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 09f1972 commit 5372c65

File tree

2 files changed

+61
-14
lines changed

2 files changed

+61
-14
lines changed

drivers/usb/host/xhci-ring.c

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2376,6 +2376,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
23762376
/* handle completion code */
23772377
switch (trb_comp_code) {
23782378
case COMP_SUCCESS:
2379+
/* Don't overwrite status if TD had an error, see xHCI 4.9.1 */
2380+
if (td->error_mid_td)
2381+
break;
23792382
if (remaining) {
23802383
frame->status = short_framestatus;
23812384
if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
@@ -2401,8 +2404,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
24012404
break;
24022405
case COMP_USB_TRANSACTION_ERROR:
24032406
frame->status = -EPROTO;
2407+
sum_trbs_for_length = true;
24042408
if (ep_trb != td->last_trb)
2405-
return 0;
2409+
td->error_mid_td = true;
24062410
break;
24072411
case COMP_STOPPED:
24082412
sum_trbs_for_length = true;
@@ -2422,6 +2426,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
24222426
break;
24232427
}
24242428

2429+
if (td->urb_length_set)
2430+
goto finish_td;
2431+
24252432
if (sum_trbs_for_length)
24262433
frame->actual_length = sum_trb_lengths(xhci, ep->ring, ep_trb) +
24272434
ep_trb_len - remaining;
@@ -2430,6 +2437,14 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
24302437

24312438
td->urb->actual_length += frame->actual_length;
24322439

2440+
finish_td:
2441+
/* Don't give back TD yet if we encountered an error mid TD */
2442+
if (td->error_mid_td && ep_trb != td->last_trb) {
2443+
xhci_dbg(xhci, "Error mid isoc TD, wait for final completion event\n");
2444+
td->urb_length_set = true;
2445+
return 0;
2446+
}
2447+
24332448
return finish_td(xhci, ep, ep_ring, td, trb_comp_code);
24342449
}
24352450

@@ -2808,17 +2823,51 @@ static int handle_tx_event(struct xhci_hcd *xhci,
28082823
}
28092824

28102825
if (!ep_seg) {
2811-
if (!ep->skip ||
2812-
!usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
2813-
/* Some host controllers give a spurious
2814-
* successful event after a short transfer.
2815-
* Ignore it.
2816-
*/
2817-
if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) &&
2818-
ep_ring->last_td_was_short) {
2819-
ep_ring->last_td_was_short = false;
2820-
goto cleanup;
2826+
2827+
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
2828+
skip_isoc_td(xhci, td, ep, status);
2829+
goto cleanup;
2830+
}
2831+
2832+
/*
2833+
* Some hosts give a spurious success event after a short
2834+
* transfer. Ignore it.
2835+
*/
2836+
if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) &&
2837+
ep_ring->last_td_was_short) {
2838+
ep_ring->last_td_was_short = false;
2839+
goto cleanup;
2840+
}
2841+
2842+
/*
2843+
* xhci 4.10.2 states isoc endpoints should continue
2844+
* processing the next TD if there was an error mid TD.
2845+
* So host like NEC don't generate an event for the last
2846+
* isoc TRB even if the IOC flag is set.
2847+
* xhci 4.9.1 states that if there are errors in mult-TRB
2848+
* TDs xHC should generate an error for that TRB, and if xHC
2849+
* proceeds to the next TD it should genete an event for
2850+
* any TRB with IOC flag on the way. Other host follow this.
2851+
* So this event might be for the next TD.
2852+
*/
2853+
if (td->error_mid_td &&
2854+
!list_is_last(&td->td_list, &ep_ring->td_list)) {
2855+
struct xhci_td *td_next = list_next_entry(td, td_list);
2856+
2857+
ep_seg = trb_in_td(xhci, td_next->start_seg, td_next->first_trb,
2858+
td_next->last_trb, ep_trb_dma, false);
2859+
if (ep_seg) {
2860+
/* give back previous TD, start handling new */
2861+
xhci_dbg(xhci, "Missing TD completion event after mid TD error\n");
2862+
ep_ring->dequeue = td->last_trb;
2863+
ep_ring->deq_seg = td->last_trb_seg;
2864+
inc_deq(xhci, ep_ring);
2865+
xhci_td_cleanup(xhci, td, ep_ring, td->status);
2866+
td = td_next;
28212867
}
2868+
}
2869+
2870+
if (!ep_seg) {
28222871
/* HC is busted, give up! */
28232872
xhci_err(xhci,
28242873
"ERROR Transfer event TRB DMA ptr not "
@@ -2830,9 +2879,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
28302879
ep_trb_dma, true);
28312880
return -ESHUTDOWN;
28322881
}
2833-
2834-
skip_isoc_td(xhci, td, ep, status);
2835-
goto cleanup;
28362882
}
28372883
if (trb_comp_code == COMP_SHORT_PACKET)
28382884
ep_ring->last_td_was_short = true;

drivers/usb/host/xhci.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,7 @@ struct xhci_td {
15491549
struct xhci_segment *bounce_seg;
15501550
/* actual_length of the URB has already been set */
15511551
bool urb_length_set;
1552+
bool error_mid_td;
15521553
unsigned int num_trbs;
15531554
};
15541555

0 commit comments

Comments
 (0)