| From d6882162971f3b10808477b84bd2207802bf6ec6 Mon Sep 17 00:00:00 2001 |
| From: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Date: Fri, 11 Nov 2016 15:13:17 +0200 |
| Subject: [PATCH 281/299] xhci: cleanup and refactor process_bulk_intr_td() |
| |
| mostly refactoring, but does also change error message if |
| we get a bad length in the event trb. |
| |
| Adds a helper function to sum trb lengths of partial TDs |
| |
| Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| (cherry picked from commit 30a65b45bfb1a978f5576eeb8280d332c0aada74) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| drivers/usb/host/xhci-ring.c | 108 ++++++++++++++++++------------------------- |
| 1 file changed, 46 insertions(+), 62 deletions(-) |
| |
| --- a/drivers/usb/host/xhci-ring.c |
| +++ b/drivers/usb/host/xhci-ring.c |
| @@ -1944,6 +1944,21 @@ td_cleanup: |
| return ret; |
| } |
| |
| +/* sum trb lengths from ring dequeue up to stop_trb, _excluding_ stop_trb */ |
| +static int sum_trb_lengths(struct xhci_hcd *xhci, struct xhci_ring *ring, |
| + union xhci_trb *stop_trb) |
| +{ |
| + u32 sum; |
| + union xhci_trb *trb = ring->dequeue; |
| + struct xhci_segment *seg = ring->deq_seg; |
| + |
| + for (sum = 0; trb != stop_trb; next_trb(xhci, ring, &seg, &trb)) { |
| + if (!trb_is_noop(trb) && !trb_is_link(trb)) |
| + sum += TRB_LEN(le32_to_cpu(trb->generic.field[2])); |
| + } |
| + return sum; |
| +} |
| + |
| /* |
| * Process control tds, update urb status and actual_length. |
| */ |
| @@ -2165,88 +2180,57 @@ static int process_bulk_intr_td(struct x |
| struct xhci_virt_ep *ep, int *status) |
| { |
| struct xhci_ring *ep_ring; |
| - union xhci_trb *cur_trb; |
| - struct xhci_segment *cur_seg; |
| u32 trb_comp_code; |
| + u32 remaining, requested, event_trb_len; |
| |
| ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); |
| trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); |
| + remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); |
| + event_trb_len = TRB_LEN(le32_to_cpu(event_trb->generic.field[2])); |
| + requested = td->urb->transfer_buffer_length; |
| |
| switch (trb_comp_code) { |
| case COMP_SUCCESS: |
| - /* Double check that the HW transferred everything. */ |
| - if (event_trb != td->last_trb || |
| - EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) { |
| + /* handle success with untransferred data as short packet */ |
| + if (event_trb != td->last_trb || remaining) { |
| xhci_warn(xhci, "WARN Successful completion on short TX\n"); |
| - if ((xhci->quirks & XHCI_TRUST_TX_LENGTH)) |
| - trb_comp_code = COMP_SHORT_TX; |
| + xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n", |
| + td->urb->ep->desc.bEndpointAddress, |
| + requested, remaining); |
| } |
| *status = 0; |
| break; |
| - case COMP_STOP_SHORT: |
| case COMP_SHORT_TX: |
| + xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n", |
| + td->urb->ep->desc.bEndpointAddress, |
| + requested, remaining); |
| *status = 0; |
| break; |
| + case COMP_STOP_SHORT: |
| + td->urb->actual_length = remaining; |
| + goto finish_td; |
| + case COMP_STOP_INVAL: |
| + /* stopped on ep trb with invalid length, exclude it */ |
| + event_trb_len = 0; |
| + remaining = 0; |
| + break; |
| default: |
| - /* Others already handled above */ |
| + /* do nothing */ |
| break; |
| } |
| - if (trb_comp_code == COMP_SHORT_TX) |
| - xhci_dbg(xhci, "ep %#x - asked for %d bytes, " |
| - "%d bytes untransferred\n", |
| - td->urb->ep->desc.bEndpointAddress, |
| - td->urb->transfer_buffer_length, |
| - EVENT_TRB_LEN(le32_to_cpu(event->transfer_len))); |
| - /* Stopped - short packet completion */ |
| - if (trb_comp_code == COMP_STOP_SHORT) { |
| - td->urb->actual_length = |
| - EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); |
| |
| - if (td->urb->transfer_buffer_length < |
| - td->urb->actual_length) { |
| - xhci_warn(xhci, "HC gave bad length of %d bytes txed\n", |
| - EVENT_TRB_LEN(le32_to_cpu(event->transfer_len))); |
| - td->urb->actual_length = 0; |
| - /* status will be set by usb core for canceled urbs */ |
| - } |
| - /* Fast path - was this the last TRB in the TD for this URB? */ |
| - } else if (event_trb == td->last_trb) { |
| - if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) { |
| - td->urb->actual_length = |
| - td->urb->transfer_buffer_length - |
| - EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); |
| - if (td->urb->transfer_buffer_length < |
| - td->urb->actual_length) { |
| - xhci_warn(xhci, "HC gave bad length of %d bytes left\n", |
| - EVENT_TRB_LEN(le32_to_cpu(event->transfer_len))); |
| - td->urb->actual_length = 0; |
| - *status = 0; |
| - } |
| - /* Don't overwrite a previously set error code */ |
| - if (*status == -EINPROGRESS) |
| - *status = 0; |
| - } |
| - } else { |
| - /* Slow path - walk the list, starting from the dequeue |
| - * pointer, to get the actual length transferred. |
| - */ |
| + if (event_trb == td->last_trb) |
| + td->urb->actual_length = requested - remaining; |
| + else |
| + td->urb->actual_length = |
| + sum_trb_lengths(xhci, ep_ring, event_trb) + |
| + event_trb_len - remaining; |
| +finish_td: |
| + if (remaining > requested) { |
| + xhci_warn(xhci, "bad transfer trb length %d in event trb\n", |
| + remaining); |
| td->urb->actual_length = 0; |
| - for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg; |
| - cur_trb != event_trb; |
| - next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { |
| - if (!trb_is_noop(cur_trb) && !trb_is_link(cur_trb)) |
| - td->urb->actual_length += |
| - TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])); |
| - } |
| - /* If the ring didn't stop on a Link or No-op TRB, add |
| - * in the actual bytes transferred from the Normal TRB |
| - */ |
| - if (trb_comp_code != COMP_STOP_INVAL) |
| - td->urb->actual_length += |
| - TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) - |
| - EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); |
| } |
| - |
| return finish_td(xhci, td, event_trb, event, ep, status, false); |
| } |
| |