summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJC Kuo <jckuo@nvidia.com>2012-12-06 20:16:19 +0800
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 12:50:54 -0700
commit8367068a15ded77f50adf581241bc872f72b53d7 (patch)
treea4f0bdd92bb1f2273c58c3c37e87556116ea344e
parent88d6e8214235ff143fc95b917e33fbd635cf7f05 (diff)
usb: xhci: fix Short Packet handling for isochronous
When Short Packet happens on a multiple-TRBs TD, xHCD needs to calculate the exact amount of transferred data because upper layer driver wants it. In order to achieve, xHCD has to: 1. set ISP bit for all TRBs belongs to a IN TD, and 2. set IOC bit for the last TRB of the IN TD. Once HC detects a Short Transfer, HC will send Short Packet event for the TRB which encountered Short Packet and also send Short Packet event fot the last TRB which has IOC bit set. With those two events, xHCD can calculate the exact amount of bytes which xHC has completed for the TD. (4.10.1.1) Bug 1158352 Change-Id: I38f04825ddc3e12f124e12a9abf05a36beb43886 Signed-off-by: JC Kuo <jckuo@nvidia.com> Signed-off-by: Ajay Gupta <ajayg@nvidia.com> Reviewed-on: http://git-master/r/192883 Reviewed-by: Mrutyunjay Sawant <msawant@nvidia.com> Tested-by: Mrutyunjay Sawant <msawant@nvidia.com>
-rw-r--r--drivers/usb/host/xhci-ring.c15
-rw-r--r--drivers/usb/host/xhci.c1
-rw-r--r--drivers/usb/host/xhci.h1
3 files changed, 17 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index cc3bfc5d590d..555434189a89 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2135,6 +2135,12 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
frame->actual_length = frame->length;
td->urb->actual_length += frame->length;
} else {
+ if (urb_priv->finishing_short_td &&
+ (event_trb == td->last_trb)) {
+ urb_priv->finishing_short_td = false;
+ /* get event for last trb, can finish this short td */
+ goto finish_td;
+ }
for (cur_trb = ep_ring->dequeue,
cur_seg = ep_ring->deq_seg; cur_trb != event_trb;
next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
@@ -2149,8 +2155,17 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
frame->actual_length = len;
td->urb->actual_length += len;
}
+ if ((trb_comp_code == COMP_SHORT_TX) &&
+ (event_trb != td->last_trb)) {
+ /* last trb has IOC, expect HC to send event for it */
+ while (ep_ring->dequeue != td->last_trb)
+ inc_deq(xhci, ep_ring);
+ urb_priv->finishing_short_td = true;
+ return 0;
+ }
}
+finish_td:
return finish_td(xhci, td, event_trb, event, ep, status, false);
}
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index dcf7ecd0b2f5..ea2fce8b6ced 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1308,6 +1308,7 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
urb_priv->length = size;
urb_priv->td_cnt = 0;
+ urb_priv->finishing_short_td = false;
urb->hcpriv = urb_priv;
if (usb_endpoint_xfer_control(&urb->ep->desc)) {
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 77600cefcaf1..2482810101b7 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1337,6 +1337,7 @@ struct xhci_scratchpad {
struct urb_priv {
int length;
int td_cnt;
+ bool finishing_short_td;
struct xhci_td *td[0];
};