Commit 30a65b45 authored by Mathias Nyman's avatar Mathias Nyman Committed by Greg Kroah-Hartman

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: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 0b6c324c
......@@ -1919,6 +1919,21 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
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);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment