Commit 926008c9 authored by Dmitry Torokhov's avatar Dmitry Torokhov Committed by Sarah Sharp

USB: xhci: simplify logic of skipping missed isoc TDs

The logic of the handling Missed Service Error Events was pretty
confusing as we were checking the same condition several times.
In addition, it caused compiler warning since the compiler could
not figure out that event_trb is actually unused in case we are
skipping current TD.

Fix that by rearranging "skip" condition checks, and factor out
skip_isoc_td() so that it is called explicitly.
Signed-off-by: default avatarDmitry Torokhov <dtor@vmware.com>
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
parent 575688e1
...@@ -1675,71 +1675,52 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, ...@@ -1675,71 +1675,52 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
struct urb_priv *urb_priv; struct urb_priv *urb_priv;
int idx; int idx;
int len = 0; int len = 0;
int skip_td = 0;
union xhci_trb *cur_trb; union xhci_trb *cur_trb;
struct xhci_segment *cur_seg; struct xhci_segment *cur_seg;
struct usb_iso_packet_descriptor *frame;
u32 trb_comp_code; u32 trb_comp_code;
bool skip_td = false;
ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
trb_comp_code = GET_COMP_CODE(event->transfer_len); trb_comp_code = GET_COMP_CODE(event->transfer_len);
urb_priv = td->urb->hcpriv; urb_priv = td->urb->hcpriv;
idx = urb_priv->td_cnt; idx = urb_priv->td_cnt;
frame = &td->urb->iso_frame_desc[idx];
if (ep->skip) { /* handle completion code */
/* The transfer is partly done */ switch (trb_comp_code) {
*status = -EXDEV; case COMP_SUCCESS:
td->urb->iso_frame_desc[idx].status = -EXDEV; frame->status = 0;
} else { xhci_dbg(xhci, "Successful isoc transfer!\n");
/* handle completion code */ break;
switch (trb_comp_code) { case COMP_SHORT_TX:
case COMP_SUCCESS: frame->status = td->urb->transfer_flags & URB_SHORT_NOT_OK ?
td->urb->iso_frame_desc[idx].status = 0; -EREMOTEIO : 0;
xhci_dbg(xhci, "Successful isoc transfer!\n"); break;
break; case COMP_BW_OVER:
case COMP_SHORT_TX: frame->status = -ECOMM;
if (td->urb->transfer_flags & URB_SHORT_NOT_OK) skip_td = true;
td->urb->iso_frame_desc[idx].status = break;
-EREMOTEIO; case COMP_BUFF_OVER:
else case COMP_BABBLE:
td->urb->iso_frame_desc[idx].status = 0; frame->status = -EOVERFLOW;
break; skip_td = true;
case COMP_BW_OVER: break;
td->urb->iso_frame_desc[idx].status = -ECOMM; case COMP_STALL:
skip_td = 1; frame->status = -EPROTO;
break; skip_td = true;
case COMP_BUFF_OVER: break;
case COMP_BABBLE: case COMP_STOP:
td->urb->iso_frame_desc[idx].status = -EOVERFLOW; case COMP_STOP_INVAL:
skip_td = 1; break;
break; default:
case COMP_STALL: frame->status = -1;
td->urb->iso_frame_desc[idx].status = -EPROTO; break;
skip_td = 1;
break;
case COMP_STOP:
case COMP_STOP_INVAL:
break;
default:
td->urb->iso_frame_desc[idx].status = -1;
break;
}
}
/* calc actual length */
if (ep->skip) {
td->urb->iso_frame_desc[idx].actual_length = 0;
/* Update ring dequeue pointer */
while (ep_ring->dequeue != td->last_trb)
inc_deq(xhci, ep_ring, false);
inc_deq(xhci, ep_ring, false);
return finish_td(xhci, td, event_trb, event, ep, status, true);
} }
if (trb_comp_code == COMP_SUCCESS || skip_td == 1) { if (trb_comp_code == COMP_SUCCESS || skip_td) {
td->urb->iso_frame_desc[idx].actual_length = frame->actual_length = frame->length;
td->urb->iso_frame_desc[idx].length; td->urb->actual_length += frame->length;
td->urb->actual_length +=
td->urb->iso_frame_desc[idx].length;
} else { } else {
for (cur_trb = ep_ring->dequeue, for (cur_trb = ep_ring->dequeue,
cur_seg = ep_ring->deq_seg; cur_trb != event_trb; cur_seg = ep_ring->deq_seg; cur_trb != event_trb;
...@@ -1755,7 +1736,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, ...@@ -1755,7 +1736,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
TRB_LEN(event->transfer_len); TRB_LEN(event->transfer_len);
if (trb_comp_code != COMP_STOP_INVAL) { if (trb_comp_code != COMP_STOP_INVAL) {
td->urb->iso_frame_desc[idx].actual_length = len; frame->actual_length = len;
td->urb->actual_length += len; td->urb->actual_length += len;
} }
} }
...@@ -1766,6 +1747,35 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, ...@@ -1766,6 +1747,35 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
return finish_td(xhci, td, event_trb, event, ep, status, false); return finish_td(xhci, td, event_trb, event, ep, status, false);
} }
static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
struct xhci_transfer_event *event,
struct xhci_virt_ep *ep, int *status)
{
struct xhci_ring *ep_ring;
struct urb_priv *urb_priv;
struct usb_iso_packet_descriptor *frame;
int idx;
ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
urb_priv = td->urb->hcpriv;
idx = urb_priv->td_cnt;
frame = &td->urb->iso_frame_desc[idx];
/* The transfer is partly done */
*status = -EXDEV;
frame->status = -EXDEV;
/* calc actual length */
frame->actual_length = 0;
/* Update ring dequeue pointer */
while (ep_ring->dequeue != td->last_trb)
inc_deq(xhci, ep_ring, false);
inc_deq(xhci, ep_ring, false);
return finish_td(xhci, td, NULL, event, ep, status, true);
}
/* /*
* Process bulk and interrupt tds, update urb status and actual_length. * Process bulk and interrupt tds, update urb status and actual_length.
*/ */
...@@ -2024,36 +2034,42 @@ static int handle_tx_event(struct xhci_hcd *xhci, ...@@ -2024,36 +2034,42 @@ static int handle_tx_event(struct xhci_hcd *xhci,
} }
td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
/* Is this a TRB in the currently executing TD? */ /* Is this a TRB in the currently executing TD? */
event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
td->last_trb, event_dma); td->last_trb, event_dma);
if (event_seg && ep->skip) { if (!event_seg) {
if (!ep->skip ||
!usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
/* HC is busted, give up! */
xhci_err(xhci,
"ERROR Transfer event TRB DMA ptr not "
"part of current TD\n");
return -ESHUTDOWN;
}
ret = skip_isoc_td(xhci, td, event, ep, &status);
goto cleanup;
}
if (ep->skip) {
xhci_dbg(xhci, "Found td. Clear skip flag.\n"); xhci_dbg(xhci, "Found td. Clear skip flag.\n");
ep->skip = false; ep->skip = false;
} }
if (!event_seg &&
(!ep->skip || !usb_endpoint_xfer_isoc(&td->urb->ep->desc))) {
/* HC is busted, give up! */
xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not "
"part of current TD\n");
return -ESHUTDOWN;
}
if (event_seg) { event_trb = &event_seg->trbs[(event_dma - event_seg->dma) /
event_trb = &event_seg->trbs[(event_dma - sizeof(*event_trb)];
event_seg->dma) / sizeof(*event_trb)]; /*
/* * No-op TRB should not trigger interrupts.
* No-op TRB should not trigger interrupts. * If event_trb is a no-op TRB, it means the
* If event_trb is a no-op TRB, it means the * corresponding TD has been cancelled. Just ignore
* corresponding TD has been cancelled. Just ignore * the TD.
* the TD. */
*/ if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK)
if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_TR_NOOP)) {
== TRB_TYPE(TRB_TR_NOOP)) { xhci_dbg(xhci,
xhci_dbg(xhci, "event_trb is a no-op TRB. " "event_trb is a no-op TRB. Skip it\n");
"Skip it\n"); goto cleanup;
goto cleanup;
}
} }
/* Now update the urb's actual_length and give back to /* Now update the urb's actual_length and give back to
......
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