summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/host/xhci-dbg.c2
-rw-r--r--drivers/usb/host/xhci-ring.c44
-rw-r--r--drivers/usb/host/xhci.h6
3 files changed, 47 insertions, 5 deletions
diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c
index c867ecbeaa60..2d16faefb429 100644
--- a/drivers/usb/host/xhci-dbg.c
+++ b/drivers/usb/host/xhci-dbg.c
@@ -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");
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index af8c0f24cf46..5fbf33987a85 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -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;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 73686928cae3..dbda41e91c84 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -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 */