xhci: xHCI 1.1: Stopped - Short Packet Capability (SPC)
authorLu Baolu <baolu.lu@linux.intel.com>
Thu, 6 Aug 2015 16:24:01 +0000 (19:24 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 8 Aug 2015 22:16:00 +0000 (15:16 -0700)
This patch enables xhci driver to support SPC by handling
Stopped - Short Packet event in transfer event path.

If SPC = '1' and the stop endpoint command is executed, after a Short
Packet condition has been detected, but before the end of the TD has been
reached, (i.e. the TD is in progress for pipe), then a Transfer Event TRB
with its Completion Code set to Stopped - Short Packet and its TRB
Transfer Length set to value of the EDTLA shall be forced for the
interrupted TRB, irrespective of whether its IOC or ISP flags are set.
This Transfer Event TRB will precede the Command Completion Event TRB for
the command, and is referred to as a Stopped Transfer Event.

Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/xhci-dbg.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.h

index c867ecbeaa60f3f830b8f6efad9d9631a829ffeb..2d16faefb429b03a529304136c1eaf19deb15572 100644 (file)
@@ -101,6 +101,8 @@ static void xhci_print_cap_regs(struct xhci_hcd *xhci)
                        HCC_64BIT_ADDR(temp) ? "64" : "32");
        xhci_dbg(xhci, "  HC %s Contiguous Frame ID Capability\n",
                        HCC_CFC(temp) ? "has" : "hasn't");
+       xhci_dbg(xhci, "  HC %s generate Stopped - Short Package event\n",
+                       HCC_SPC(temp) ? "can" : "can't");
        /* FIXME */
        xhci_dbg(xhci, "  FIXME: more HCCPARAMS debugging\n");
 
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;
index 73686928cae36095bc7c11ee4fbe8d77bf07873d..dbda41e91c843f2e289c4a4aa0d4129f2641f2a9 100644 (file)
@@ -119,6 +119,8 @@ struct xhci_cap_regs {
 #define HCC_LTC(p)             ((p) & (1 << 6))
 /* true: no secondary Stream ID Support */
 #define HCC_NSS(p)             ((p) & (1 << 7))
+/* true: HC supports Stopped - Short Packet */
+#define HCC_SPC(p)             ((p) & (1 << 9))
 /* true: HC has Contiguous Frame ID Capability */
 #define HCC_CFC(p)             ((p) & (1 << 11))
 /* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */
@@ -1063,8 +1065,8 @@ struct xhci_transfer_event {
 #define COMP_STOP      26
 /* Same as COMP_EP_STOPPED, but the transferred length in the event is invalid */
 #define COMP_STOP_INVAL        27
-/* Control Abort Error - Debug Capability - control pipe aborted */
-#define COMP_DBG_ABORT 28
+/* Same as COMP_EP_STOPPED, but a short packet detected */
+#define COMP_STOP_SHORT        28
 /* Max Exit Latency Too Large Error */
 #define COMP_MEL_ERR   29
 /* TRB type 30 reserved */