| From: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Date: Mon, 12 Oct 2015 11:30:11 +0300 |
| Subject: xhci: don't finish a TD if we get a short transfer event mid TD |
| |
| commit e210c422b6fdd2dc123bedc588f399aefd8bf9de upstream. |
| |
| If the difference is big enough between the bytes asked and received |
| in a bulk transfer we can get a short transfer event pointing to a TRB in |
| the middle of the TD. We don't want to handle the TD yet as we will anyway |
| receive a new event for the last TRB in the TD. |
| |
| Hold off from finishing the TD and removing it from the list until we |
| receive an event for the last TRB in the TD |
| |
| Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| [bwh: Backported to 3.2: adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| drivers/usb/host/xhci-ring.c | 10 ++++++++++ |
| 1 file changed, 10 insertions(+) |
| |
| --- a/drivers/usb/host/xhci-ring.c |
| +++ b/drivers/usb/host/xhci-ring.c |
| @@ -2187,6 +2187,10 @@ static int process_bulk_intr_td(struct x |
| EVENT_TRB_LEN(le32_to_cpu(event->transfer_len))); |
| /* Fast path - was this the last TRB in the TD for this URB? */ |
| 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 - |
| @@ -2238,6 +2242,12 @@ static int process_bulk_intr_td(struct x |
| 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); |