diff options
author | JC Kuo <jckuo@nvidia.com> | 2012-12-06 20:16:19 +0800 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2013-09-14 12:50:54 -0700 |
commit | 8367068a15ded77f50adf581241bc872f72b53d7 (patch) | |
tree | a4f0bdd92bb1f2273c58c3c37e87556116ea344e | |
parent | 88d6e8214235ff143fc95b917e33fbd635cf7f05 (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.c | 15 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 1 |
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]; }; |