]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
usb: xhci: fix Short Packet handling for isochronous
authorJC Kuo <jckuo@nvidia.com>
Thu, 6 Dec 2012 12:16:19 +0000 (20:16 +0800)
committerDan Willemsen <dwillemsen@nvidia.com>
Sat, 14 Sep 2013 19:50:54 +0000 (12:50 -0700)
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>
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h

index cc3bfc5d590df6b1182b87d2620e03fa1e72fe3f..555434189a8918a8524faee2bf3bef90324333b3 100644 (file)
@@ -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);
 }
 
index dcf7ecd0b2f570c7cb8f718bc0b3bb92978c85dc..ea2fce8b6cedc102a284839416976fd786be380b 100644 (file)
@@ -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)) {
index 77600cefcaf1df6ed3209a41c2a00dcdbb31b4dc..2482810101b7589d1bc6e22e731fd433a2a1dcfa 100644 (file)
@@ -1337,6 +1337,7 @@ struct xhci_scratchpad {
 struct urb_priv {
        int     length;
        int     td_cnt;
+       bool    finishing_short_td;
        struct  xhci_td *td[0];
 };