|
@@ -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
|