aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/xhci-ring.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-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,
1675 struct urb_priv *urb_priv; 1675 struct urb_priv *urb_priv;
1676 int idx; 1676 int idx;
1677 int len = 0; 1677 int len = 0;
1678 int skip_td = 0;
1679 union xhci_trb *cur_trb; 1678 union xhci_trb *cur_trb;
1680 struct xhci_segment *cur_seg; 1679 struct xhci_segment *cur_seg;
1680 struct usb_iso_packet_descriptor *frame;
1681 u32 trb_comp_code; 1681 u32 trb_comp_code;
1682 bool skip_td = false;
1682 1683
1683 ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); 1684 ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
1684 trb_comp_code = GET_COMP_CODE(event->transfer_len); 1685 trb_comp_code = GET_COMP_CODE(event->transfer_len);
1685 urb_priv = td->urb->hcpriv; 1686 urb_priv = td->urb->hcpriv;
1686 idx = urb_priv->td_cnt; 1687 idx = urb_priv->td_cnt;
1688 frame = &td->urb->iso_frame_desc[idx];
1687 1689
1688 if (ep->skip) { 1690 /* handle completion code */
1689 /* The transfer is partly done */ 1691 switch (trb_comp_code) {
1690 *status = -EXDEV; 1692 case COMP_SUCCESS:
1691 td->urb->iso_frame_desc[idx].status = -EXDEV; 1693 frame->status = 0;
1692 } else { 1694 xhci_dbg(xhci, "Successful isoc transfer!\n");
1693 /* handle completion code */ 1695 break;
1694 switch (trb_comp_code) { 1696 case COMP_SHORT_TX:
1695 case COMP_SUCCESS: 1697 frame->status = td->urb->transfer_flags & URB_SHORT_NOT_OK ?
1696 td->urb->iso_frame_desc[idx].status = 0; 1698 -EREMOTEIO : 0;
1697 xhci_dbg(xhci, "Successful isoc transfer!\n"); 1699 break;
1698 break; 1700 case COMP_BW_OVER:
1699 case COMP_SHORT_TX: 1701 frame->status = -ECOMM;
1700 if (td->urb->transfer_flags & URB_SHORT_NOT_OK) 1702 skip_td = true;
1701 td->urb->iso_frame_desc[idx].status = 1703 break;
1702 -EREMOTEIO; 1704 case COMP_BUFF_OVER:
1703 else 1705 case COMP_BABBLE:
1704 td->urb->iso_frame_desc[idx].status = 0; 1706 frame->status = -EOVERFLOW;
1705 break; 1707 skip_td = true;
1706 case COMP_BW_OVER: 1708 break;
1707 td->urb->iso_frame_desc[idx].status = -ECOMM; 1709 case COMP_STALL:
1708 skip_td = 1; 1710 frame->status = -EPROTO;
1709 break; 1711 skip_td = true;
1710 case COMP_BUFF_OVER: 1712 break;
1711 case COMP_BABBLE: 1713 case COMP_STOP:
1712 td->urb->iso_frame_desc[idx].status = -EOVERFLOW; 1714 case COMP_STOP_INVAL:
1713 skip_td = 1; 1715 break;
1714 break; 1716 default:
1715 case COMP_STALL: 1717 frame->status = -1;
1716 td->urb->iso_frame_desc[idx].status = -EPROTO; 1718 break;
1717 skip_td = 1;
1718 break;
1719 case COMP_STOP:
1720 case COMP_STOP_INVAL:
1721 break;
1722 default:
1723 td->urb->iso_frame_desc[idx].status = -1;
1724 break;
1725 }
1726 }
1727
1728 /* calc actual length */
1729 if (ep->skip) {
1730 td->urb->iso_frame_desc[idx].actual_length = 0;
1731 /* Update ring dequeue pointer */
1732 while (ep_ring->dequeue != td->last_trb)
1733 inc_deq(xhci, ep_ring, false);
1734 inc_deq(xhci, ep_ring, false);
1735 return finish_td(xhci, td, event_trb, event, ep, status, true);
1736 } 1719 }
1737 1720
1738 if (trb_comp_code == COMP_SUCCESS || skip_td == 1) { 1721 if (trb_comp_code == COMP_SUCCESS || skip_td) {
1739 td->urb->iso_frame_desc[idx].actual_length = 1722 frame->actual_length = frame->length;
1740 td->urb->iso_frame_desc[idx].length; 1723 td->urb->actual_length += frame->length;
1741 td->urb->actual_length +=
1742 td->urb->iso_frame_desc[idx].length;
1743 } else { 1724 } else {
1744 for (cur_trb = ep_ring->dequeue, 1725 for (cur_trb = ep_ring->dequeue,
1745 cur_seg = ep_ring->deq_seg; cur_trb != event_trb; 1726 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 TRB_LEN(event->transfer_len); 1736 TRB_LEN(event->transfer_len);
1756 1737
1757 if (trb_comp_code != COMP_STOP_INVAL) { 1738 if (trb_comp_code != COMP_STOP_INVAL) {
1758 td->urb->iso_frame_desc[idx].actual_length = len; 1739 frame->actual_length = len;
1759 td->urb->actual_length += len; 1740 td->urb->actual_length += len;
1760 } 1741 }
1761 } 1742 }
@@ -1766,6 +1747,35 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
1766 return finish_td(xhci, td, event_trb, event, ep, status, false); 1747 return finish_td(xhci, td, event_trb, event, ep, status, false);
1767} 1748}
1768 1749
1750static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
1751 struct xhci_transfer_event *event,
1752 struct xhci_virt_ep *ep, int *status)
1753{
1754 struct xhci_ring *ep_ring;
1755 struct urb_priv *urb_priv;
1756 struct usb_iso_packet_descriptor *frame;
1757 int idx;
1758
1759 ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer);
1760 urb_priv = td->urb->hcpriv;
1761 idx = urb_priv->td_cnt;
1762 frame = &td->urb->iso_frame_desc[idx];
1763
1764 /* The transfer is partly done */
1765 *status = -EXDEV;
1766 frame->status = -EXDEV;
1767
1768 /* calc actual length */
1769 frame->actual_length = 0;
1770
1771 /* Update ring dequeue pointer */
1772 while (ep_ring->dequeue != td->last_trb)
1773 inc_deq(xhci, ep_ring, false);
1774 inc_deq(xhci, ep_ring, false);
1775
1776 return finish_td(xhci, td, NULL, event, ep, status, true);
1777}
1778
1769/* 1779/*
1770 * Process bulk and interrupt tds, update urb status and actual_length. 1780 * Process bulk and interrupt tds, update urb status and actual_length.
1771 */ 1781 */
@@ -2024,36 +2034,42 @@ static int handle_tx_event(struct xhci_hcd *xhci,
2024 } 2034 }
2025 2035
2026 td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list); 2036 td = list_entry(ep_ring->td_list.next, struct xhci_td, td_list);
2037
2027 /* Is this a TRB in the currently executing TD? */ 2038 /* Is this a TRB in the currently executing TD? */
2028 event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue, 2039 event_seg = trb_in_td(ep_ring->deq_seg, ep_ring->dequeue,
2029 td->last_trb, event_dma); 2040 td->last_trb, event_dma);
2030 if (event_seg && ep->skip) { 2041 if (!event_seg) {
2042 if (!ep->skip ||
2043 !usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
2044 /* HC is busted, give up! */
2045 xhci_err(xhci,
2046 "ERROR Transfer event TRB DMA ptr not "
2047 "part of current TD\n");
2048 return -ESHUTDOWN;
2049 }
2050
2051 ret = skip_isoc_td(xhci, td, event, ep, &status);
2052 goto cleanup;
2053 }
2054
2055 if (ep->skip) {
2031 xhci_dbg(xhci, "Found td. Clear skip flag.\n"); 2056 xhci_dbg(xhci, "Found td. Clear skip flag.\n");
2032 ep->skip = false; 2057 ep->skip = false;
2033 } 2058 }
2034 if (!event_seg &&
2035 (!ep->skip || !usb_endpoint_xfer_isoc(&td->urb->ep->desc))) {
2036 /* HC is busted, give up! */
2037 xhci_err(xhci, "ERROR Transfer event TRB DMA ptr not "
2038 "part of current TD\n");
2039 return -ESHUTDOWN;
2040 }
2041 2059
2042 if (event_seg) { 2060 event_trb = &event_seg->trbs[(event_dma - event_seg->dma) /
2043 event_trb = &event_seg->trbs[(event_dma - 2061 sizeof(*event_trb)];
2044 event_seg->dma) / sizeof(*event_trb)]; 2062 /*
2045 /* 2063 * No-op TRB should not trigger interrupts.
2046 * No-op TRB should not trigger interrupts. 2064 * If event_trb is a no-op TRB, it means the
2047 * If event_trb is a no-op TRB, it means the 2065 * corresponding TD has been cancelled. Just ignore
2048 * corresponding TD has been cancelled. Just ignore 2066 * the TD.
2049 * the TD. 2067 */
2050 */ 2068 if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK)
2051 if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK) 2069 == TRB_TYPE(TRB_TR_NOOP)) {
2052 == TRB_TYPE(TRB_TR_NOOP)) { 2070 xhci_dbg(xhci,
2053 xhci_dbg(xhci, "event_trb is a no-op TRB. " 2071 "event_trb is a no-op TRB. Skip it\n");
2054 "Skip it\n"); 2072 goto cleanup;
2055 goto cleanup;
2056 }
2057 } 2073 }
2058 2074
2059 /* Now update the urb's actual_length and give back to 2075 /* Now update the urb's actual_length and give back to