xhci: xHCI 1.1: Stopped - Short Packet Capability (SPC)
[deliverable/linux.git] / drivers / usb / host / xhci-ring.c
index af8c0f24cf46c059bc5371cb1c503a9b66cc6b8e..5fbf33987a8513aab07fac0238cb3e52140872a7 100644 (file)
@@ -1812,7 +1812,9 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
        if (skip)
                goto td_cleanup;
 
-       if (trb_comp_code == COMP_STOP_INVAL || trb_comp_code == COMP_STOP) {
+       if (trb_comp_code == COMP_STOP_INVAL ||
+                       trb_comp_code == COMP_STOP ||
+                       trb_comp_code == COMP_STOP_SHORT) {
                /* The Endpoint Stop Command completion will take care of any
                 * stopped TDs.  A stopped TD may be restarted, so don't update
                 * the ring dequeue pointer or take this TD off any lists yet.
@@ -1919,8 +1921,22 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
                else
                        *status = 0;
                break;
-       case COMP_STOP_INVAL:
+       case COMP_STOP_SHORT:
+               if (event_trb == ep_ring->dequeue || event_trb == td->last_trb)
+                       xhci_warn(xhci, "WARN: Stopped Short Packet on ctrl setup or status TRB\n");
+               else
+                       td->urb->actual_length =
+                               EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+
+               return finish_td(xhci, td, event_trb, event, ep, status, false);
        case COMP_STOP:
+               /* Did we stop at data stage? */
+               if (event_trb != ep_ring->dequeue && event_trb != td->last_trb)
+                       td->urb->actual_length =
+                               td->urb->transfer_buffer_length -
+                               EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+               /* fall through */
+       case COMP_STOP_INVAL:
                return finish_td(xhci, td, event_trb, event, ep, status, false);
        default:
                if (!xhci_requires_manual_halt_cleanup(xhci,
@@ -2014,6 +2030,8 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
                }
                if ((xhci->quirks & XHCI_TRUST_TX_LENGTH))
                        trb_comp_code = COMP_SHORT_TX;
+       /* fallthrough */
+       case COMP_STOP_SHORT:
        case COMP_SHORT_TX:
                frame->status = td->urb->transfer_flags & URB_SHORT_NOT_OK ?
                                -EREMOTEIO : 0;
@@ -2049,6 +2067,10 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
        if (trb_comp_code == COMP_SUCCESS || skip_td) {
                frame->actual_length = frame->length;
                td->urb->actual_length += frame->length;
+       } else if (trb_comp_code == COMP_STOP_SHORT) {
+               frame->actual_length =
+                       EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+               td->urb->actual_length += frame->actual_length;
        } else {
                for (cur_trb = ep_ring->dequeue,
                     cur_seg = ep_ring->deq_seg; cur_trb != event_trb;
@@ -2129,6 +2151,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
                        *status = 0;
                }
                break;
+       case COMP_STOP_SHORT:
        case COMP_SHORT_TX:
                if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
                        *status = -EREMOTEIO;
@@ -2145,8 +2168,20 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
                                td->urb->ep->desc.bEndpointAddress,
                                td->urb->transfer_buffer_length,
                                EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)));
+       /* Stopped - short packet completion */
+       if (trb_comp_code == COMP_STOP_SHORT) {
+               td->urb->actual_length =
+                       EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+
+               if (td->urb->transfer_buffer_length <
+                               td->urb->actual_length) {
+                       xhci_warn(xhci, "HC gave bad length of %d bytes txed\n",
+                               EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)));
+                       td->urb->actual_length = 0;
+                        /* status will be set by usb core for canceled urbs */
+               }
        /* Fast path - was this the last TRB in the TD for this URB? */
-       if (event_trb == td->last_trb) {
+       } else if (event_trb == td->last_trb) {
                if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
                        td->urb->actual_length =
                                td->urb->transfer_buffer_length -
@@ -2300,6 +2335,9 @@ static int handle_tx_event(struct xhci_hcd *xhci,
        case COMP_STOP_INVAL:
                xhci_dbg(xhci, "Stopped on No-op or Link TRB\n");
                break;
+       case COMP_STOP_SHORT:
+               xhci_dbg(xhci, "Stopped with short packet transfer detected\n");
+               break;
        case COMP_STALL:
                xhci_dbg(xhci, "Stalled endpoint\n");
                ep->ep_state |= EP_HALTED;
This page took 0.026505 seconds and 5 git commands to generate.