summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Torokhov <dtor@vmware.com>2011-03-23 20:47:05 -0700
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2011-04-13 16:19:48 -0700
commit926008c9386dde09b015753b6681c502177baa30 (patch)
treeac2f5d77acd5cb70ed9fb823123d791a34315823
parent575688e1e5f462c44ddd608ce3ec9f38b64c3c0d (diff)
downloadlinux-926008c9386dde09b015753b6681c502177baa30.tar.bz2
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>
-rw-r--r--drivers/usb/host/xhci-ring.c172
1 files changed, 94 insertions, 78 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 0161eb053225..b69a0a136e66 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -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;
- 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;
- break;
- case COMP_BW_OVER:
- td->urb->iso_frame_desc[idx].status = -ECOMM;
- skip_td = 1;
- break;
- case COMP_BUFF_OVER:
- case COMP_BABBLE:
- td->urb->iso_frame_desc[idx].status = -EOVERFLOW;
- skip_td = 1;
- break;
- case COMP_STALL:
- td->urb->iso_frame_desc[idx].status = -EPROTO;
- 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);
+ /* handle completion code */
+ switch (trb_comp_code) {
+ case COMP_SUCCESS:
+ frame->status = 0;
+ xhci_dbg(xhci, "Successful isoc transfer!\n");
+ break;
+ case COMP_SHORT_TX:
+ frame->status = td->urb->transfer_flags & URB_SHORT_NOT_OK ?
+ -EREMOTEIO : 0;
+ break;
+ case COMP_BW_OVER:
+ frame->status = -ECOMM;
+ skip_td = true;
+ break;
+ case COMP_BUFF_OVER:
+ case COMP_BABBLE:
+ frame->status = -EOVERFLOW;
+ skip_td = true;
+ break;
+ case COMP_STALL:
+ frame->status = -EPROTO;
+ skip_td = true;
+ break;
+ case COMP_STOP:
+ case COMP_STOP_INVAL:
+ break;
+ default:
+ frame->status = -1;
+ break;
}
- 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,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);
+
/* 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) {
+ 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");
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) / sizeof(*event_trb)];
- /*
- * No-op TRB should not trigger interrupts.
- * If event_trb is a no-op TRB, it means the
- * corresponding TD has been cancelled. Just ignore
- * the TD.
- */
- 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");
- goto cleanup;
- }
+ 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
+ * corresponding TD has been cancelled. Just ignore
+ * the TD.
+ */
+ 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");
+ goto cleanup;
}
/* Now update the urb's actual_length and give back to