]> git.ipfire.org Git - people/arne_f/kernel.git/commitdiff
xhci: cleanup and refactor process_bulk_intr_td()
authorMathias Nyman <mathias.nyman@linux.intel.com>
Fri, 11 Nov 2016 13:13:17 +0000 (15:13 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 14 Nov 2016 09:18:21 +0000 (10:18 +0100)
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>
drivers/usb/host/xhci-ring.c

index 65b11a3ba47621867e8dded2f412d4e7609b6ef0..6ff7d33e3b79c91b94f82dcc1bc5e18ffd484753 100644 (file)
@@ -1919,6 +1919,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.
  */
@@ -2140,88 +2155,57 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
        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);
 }