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: Dmitry Torokhov <dtor@vmware.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
This commit is contained in:
Dmitry Torokhov 2011-03-23 20:47:05 -07:00 committed by Sarah Sharp
parent 575688e1e5
commit 926008c938

View File

@ -1675,71 +1675,52 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
struct urb_priv *urb_priv;
int idx;
int len = 0;
int skip_td = 0;
union xhci_trb *cur_trb;
struct xhci_segment *cur_seg;
struct usb_iso_packet_descriptor *frame;
u32 trb_comp_code;
bool skip_td = false;
ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
trb_comp_code = GET_COMP_CODE(event->transfer_len);
urb_priv = td->urb->hcpriv;
idx = urb_priv->td_cnt;
frame = &td->urb->iso_frame_desc[idx];
if (ep->skip) {
/* The transfer is partly done */
*status = -EXDEV;
td->urb->iso_frame_desc[idx].status = -EXDEV;
} else {
/* handle completion code */
switch (trb_comp_code) {
case COMP_SUCCESS:
td->urb->iso_frame_desc[idx].status = 0;
frame->status = 0;
xhci_dbg(xhci, "Successful isoc transfer!\n");
break;
case COMP_SHORT_TX:
if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
td->urb->iso_frame_desc[idx].status =
-EREMOTEIO;
else
td->urb->iso_frame_desc[idx].status = 0;
frame->status = td->urb->transfer_flags & URB_SHORT_NOT_OK ?
-EREMOTEIO : 0;
break;
case COMP_BW_OVER:
td->urb->iso_frame_desc[idx].status = -ECOMM;
skip_td = 1;
frame->status = -ECOMM;
skip_td = true;
break;
case COMP_BUFF_OVER:
case COMP_BABBLE:
td->urb->iso_frame_desc[idx].status = -EOVERFLOW;
skip_td = 1;
frame->status = -EOVERFLOW;
skip_td = true;
break;
case COMP_STALL:
td->urb->iso_frame_desc[idx].status = -EPROTO;
skip_td = 1;
frame->status = -EPROTO;
skip_td = true;
break;
case COMP_STOP:
case COMP_STOP_INVAL:
break;
default:
td->urb->iso_frame_desc[idx].status = -1;
frame->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) {
td->urb->iso_frame_desc[idx].actual_length =
td->urb->iso_frame_desc[idx].length;
td->urb->actual_length +=
td->urb->iso_frame_desc[idx].length;
if (trb_comp_code == COMP_SUCCESS || skip_td) {
frame->actual_length = frame->length;
td->urb->actual_length += frame->length;
} else {
for (cur_trb = ep_ring->dequeue,
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,
TRB_LEN(event->transfer_len);
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;
}
}
@ -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);
}
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.
*/
@ -2024,24 +2034,31 @@ static int handle_tx_event(struct xhci_hcd *xhci,
}
td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
/* Is this a TRB in the currently executing TD? */
event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
td->last_trb, event_dma);
if (event_seg && ep->skip) {
xhci_dbg(xhci, "Found td. Clear skip flag.\n");
ep->skip = false;
}
if (!event_seg &&
(!ep->skip || !usb_endpoint_xfer_isoc(&td->urb->ep->desc))) {
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 "
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) / sizeof(*event_trb)];
ret = skip_isoc_td(xhci, td, event, ep, &status);
goto cleanup;
}
if (ep->skip) {
xhci_dbg(xhci, "Found td. Clear skip flag.\n");
ep->skip = false;
}
event_trb = &event_seg->trbs[(event_dma - event_seg->dma) /
sizeof(*event_trb)];
/*
* No-op TRB should not trigger interrupts.
* If event_trb is a no-op TRB, it means the
@ -2050,11 +2067,10 @@ static int handle_tx_event(struct xhci_hcd *xhci,
*/
if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK)
== TRB_TYPE(TRB_TR_NOOP)) {
xhci_dbg(xhci, "event_trb is a no-op TRB. "
"Skip it\n");
xhci_dbg(xhci,
"event_trb is a no-op TRB. Skip it\n");
goto cleanup;
}
}
/* Now update the urb's actual_length and give back to
* the core