Enables ie a soundcard driver to use capture uframe state,
when a playback urb has completed and a capture urb has not yet.

itd completion is further split up so the itd is only unlinked,
if the usb-frame has completed. Without this, uframe stati were not updated
on a via K8T800 ehci-hcd, where an itd was unlinked while the hcd was still
working on the usb-frame.

Signed-off-by: Karsten Wiese <[EMAIL PROTECTED]>
---
 drivers/usb/host/ehci-sched.c |  218 +++++++++++++++++++++++++++--------------
 drivers/usb/host/ehci.h       |    1 +
 2 files changed, 144 insertions(+), 75 deletions(-)

diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 7b5ae71..71362c4 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1549,22 +1549,19 @@ itd_link_urb (
 }
 
 #define        ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | 
EHCI_ISOC_XACTERR)
-
-static unsigned
-itd_complete (
-       struct ehci_hcd *ehci,
-       struct ehci_itd *itd
-) {
+bool itd_scan(struct ehci_itd *itd,
+             unsigned uframe_from, unsigned uframe_after)
+{
        struct urb                              *urb = itd->urb;
        struct usb_iso_packet_descriptor        *desc;
        u32                                     t;
        unsigned                                uframe;
        int                                     urb_index = -1;
        struct ehci_iso_stream                  *stream = itd->stream;
-       struct usb_device                       *dev;
+       bool                                    completed_urb = false;
 
        /* for each uframe with a packet */
-       for (uframe = 0; uframe < 8; uframe++) {
+       for (uframe = uframe_from; uframe < uframe_after; uframe++) {
                if (likely (itd->index[uframe] == -1))
                        continue;
                urb_index = itd->index[uframe];
@@ -1593,17 +1590,42 @@ itd_complete (
                        desc->status = 0;
                        desc->actual_length = EHCI_ITD_LENGTH (t);
                }
+               if (urb_index + 1 == urb->number_of_packets)
+                       completed_urb = true;
        }
 
+       return completed_urb;
+}
+
+static void
+itd_complete (
+       struct ehci_hcd *ehci,
+       struct ehci_itd *itd
+) {
+       struct urb                              *urb = itd->urb;
+       struct ehci_iso_stream                  *stream = itd->stream;
+
        usb_put_urb (urb);
        itd->urb = NULL;
        itd->stream = NULL;
        list_move (&itd->itd_list, &stream->free_list);
        iso_stream_put (ehci, stream);
 
-       /* handle completion now? */
-       if (likely ((urb_index + 1) != urb->number_of_packets))
-               return 0;
+       if (unlikely (!ehci->periodic_sched))
+               disable_periodic(ehci);
+}
+
+static void
+itd_complete_urb (
+       struct ehci_hcd *ehci,
+       struct ehci_itd *itd
+) {
+       struct urb                              *urb = itd->urb;
+       struct ehci_iso_stream                  *stream = itd->stream;
+       struct usb_device                       *dev;
+
+       usb_put_urb (urb);
+       itd->urb = NULL;
 
        /* ASSERT: it's really the last itd for this urb
        list_for_each_entry (itd, &stream->td_list, itd_list)
@@ -1617,8 +1639,6 @@ itd_complete (
 
        /* defer stopping schedule; completion can submit */
        ehci->periodic_sched--;
-       if (unlikely (!ehci->periodic_sched))
-               (void) disable_periodic (ehci);
        ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
 
        if (unlikely (list_empty (&stream->td_list))) {
@@ -1630,8 +1650,6 @@ itd_complete (
                        (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
        }
        iso_stream_put (ehci, stream);
-
-       return 1;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1926,21 +1944,22 @@ sitd_link_urb (
 #define        SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \
                                | SITD_STS_XACT | SITD_STS_MMF)
 
-static unsigned
-sitd_complete (
-       struct ehci_hcd         *ehci,
-       struct ehci_sitd        *sitd
+static int
+sitd_scan (
+       struct ehci_sitd *sitd,
+       bool live
 ) {
        struct urb                              *urb = sitd->urb;
        struct usb_iso_packet_descriptor        *desc;
        u32                                     t;
        int                                     urb_index = -1;
-       struct ehci_iso_stream                  *stream = sitd->stream;
-       struct usb_device                       *dev;
+
+       t = le32_to_cpup(&sitd->hw_results);
+       if ((t & SITD_STS_ACTIVE) && live)
+               return -1;
 
        urb_index = sitd->index;
        desc = &urb->iso_frame_desc [urb_index];
-       t = le32_to_cpup (&sitd->hw_results);
 
        /* report transfer status */
        if (t & SITD_ERRS) {
@@ -1958,6 +1977,19 @@ sitd_complete (
                desc->actual_length = desc->length - SITD_LENGTH (t);
        }
 
+       return  urb_index + 1 == urb->number_of_packets ? 1 : 0;
+}
+
+static void
+sitd_complete (
+       struct ehci_hcd         *ehci,
+       struct ehci_sitd        *sitd,
+       bool urb_done
+) {
+       struct urb                              *urb = sitd->urb;
+       struct ehci_iso_stream                  *stream = sitd->stream;
+       struct usb_device                       *dev;
+
        usb_put_urb (urb);
        sitd->urb = NULL;
        sitd->stream = NULL;
@@ -1966,9 +1998,8 @@ sitd_complete (
        iso_stream_put (ehci, stream);
 
        /* handle completion now? */
-       if ((urb_index + 1) != urb->number_of_packets)
-               return 0;
-
+       if (!urb_done)
+               return;
        /* ASSERT: it's really the last sitd for this urb
        list_for_each_entry (sitd, &stream->td_list, sitd_list)
                BUG_ON (sitd->urb == urb);
@@ -1994,8 +2025,6 @@ sitd_complete (
                        (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out");
        }
        iso_stream_put (ehci, stream);
-
-       return 1;
 }
 
 
@@ -2095,17 +2124,22 @@ scan_periodic (struct ehci_hcd *ehci)
 
        for (;;) {
                union ehci_shadow       q, *q_p;
+               struct ehci_itd         *itd_done;
+               struct ehci_sitd        *sitd_done;
                __le32                  type, *hw_p;
-               unsigned                uframes;
+               unsigned                uframe_from, uframe_after;
+               int                     live;
+
+               uframe_from = now_uframe & 0x07;
 
                /* don't scan past the live uframe */
                frame = now_uframe >> 3;
                if (frame == (clock >> 3))
-                       uframes = now_uframe & 0x07;
+                       uframe_after = min(clock & 0x07, uframe_from + 1);
                else {
                        /* safe to scan the whole frame at once */
                        now_uframe |= 0x07;
-                       uframes = 8;
+                       uframe_after = 8;
                }
 
 restart:
@@ -2115,11 +2149,85 @@ restart:
                q.ptr = q_p->ptr;
                type = Q_NEXT_TYPE (*hw_p);
                modified = 0;
+               itd_done = NULL;
+               sitd_done = NULL;
+
+               live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state);
+               if (!live && uframe_after)
+                       uframe_after = 8;
+
+               while (q.ptr && (type == Q_TYPE_ITD || type == Q_TYPE_SITD)) {
+                       int done;
+                       switch (type) {
+                       case Q_TYPE_ITD:
+                               rmb ();
+                               done = itd_scan(q.itd,
+                                               uframe_from, uframe_after);
+                               if (uframe_after < 8) {
+                                       q_p = &q.itd->itd_next;
+                                       hw_p = &q.itd->hw_next;
+                                       type = Q_NEXT_TYPE (q.itd->hw_next);
+                               } else {
+                                       *q_p = q.itd->itd_next;
+                                       *hw_p = q.itd->hw_next;
+                                       type = Q_NEXT_TYPE (q.itd->hw_next);
+                                       wmb();
+                                       if (!done)
+                                               itd_complete(ehci, q.itd);
+                               }
+                               if (done) {
+                                       q.itd->itd_next_done = itd_done;
+                                       itd_done = q.itd;
+                               }
+                               q = *q_p;
+                               break;
+                       case Q_TYPE_SITD:
+                               done = sitd_scan(q.sitd, live);
+                               if (done < 0) {
+                                       q_p = &q.sitd->sitd_next;
+                                       hw_p = &q.sitd->hw_next;
+                                       type = Q_NEXT_TYPE (q.sitd->hw_next);
+                                       q = *q_p;
+                                       break;
+                               }
+                               *q_p = q.sitd->sitd_next;
+                               *hw_p = q.sitd->hw_next;
+                               type = Q_NEXT_TYPE (q.sitd->hw_next);
+                               wmb();
+                               if (done > 0) {
+                                       q.sitd->sitd_next.sitd = sitd_done;
+                                       sitd_done = q.sitd;
+                               } else
+                                       sitd_complete(ehci, q.sitd, false);
+                               q = *q_p;
+                               break;
+                       }
+               }
+
+               while (itd_done) {
+                       struct ehci_itd *itd = itd_done;
+                       itd_done = itd->itd_next_done;
+                       itd_complete_urb(ehci, itd);
+                       if (uframe_after >= 8)
+                               itd_complete(ehci, itd);
+                       modified = true;
+               }
+
+               while (sitd_done) {
+                       struct ehci_sitd *sitd = sitd_done;
+                       sitd_done = sitd->sitd_next.sitd;
+                       sitd_complete(ehci, sitd, true);
+                       modified = true;
+               }
+
+               /* assume completion callbacks modify the queue */
+               if (unlikely (modified)) {
+                       uframe_after = 0;
+                       goto restart;
+               }
 
                while (q.ptr != NULL) {
-                       unsigned                uf;
                        union ehci_shadow       temp;
-                       int                     live;
 
                        live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state);
                        switch (type) {
@@ -2143,48 +2251,6 @@ restart:
                                type = Q_NEXT_TYPE (q.fstn->hw_next);
                                q = q.fstn->fstn_next;
                                break;
-                       case Q_TYPE_ITD:
-                               /* skip itds for later in the frame */
-                               rmb ();
-                               for (uf = live ? uframes : 8; uf < 8; uf++) {
-                                       if (0 == (q.itd->hw_transaction [uf]
-                                                       & ITD_ACTIVE))
-                                               continue;
-                                       q_p = &q.itd->itd_next;
-                                       hw_p = &q.itd->hw_next;
-                                       type = Q_NEXT_TYPE (q.itd->hw_next);
-                                       q = *q_p;
-                                       break;
-                               }
-                               if (uf != 8)
-                                       break;
-
-                               /* this one's ready ... HC won't cache the
-                                * pointer for much longer, if at all.
-                                */
-                               *q_p = q.itd->itd_next;
-                               *hw_p = q.itd->hw_next;
-                               type = Q_NEXT_TYPE (q.itd->hw_next);
-                               wmb();
-                               modified = itd_complete (ehci, q.itd);
-                               q = *q_p;
-                               break;
-                       case Q_TYPE_SITD:
-                               if ((q.sitd->hw_results & SITD_ACTIVE)
-                                               && live) {
-                                       q_p = &q.sitd->sitd_next;
-                                       hw_p = &q.sitd->hw_next;
-                                       type = Q_NEXT_TYPE (q.sitd->hw_next);
-                                       q = *q_p;
-                                       break;
-                               }
-                               *q_p = q.sitd->sitd_next;
-                               *hw_p = q.sitd->hw_next;
-                               type = Q_NEXT_TYPE (q.sitd->hw_next);
-                               wmb();
-                               modified = sitd_complete (ehci, q.sitd);
-                               q = *q_p;
-                               break;
                        default:
                                dbg ("corrupt type %d frame %d shadow %p",
                                        type, frame, q.ptr);
@@ -2193,8 +2259,10 @@ restart:
                        }
 
                        /* assume completion callbacks modify the queue */
-                       if (unlikely (modified))
+                       if (unlikely (modified)) {
+                               uframe_after = 0;
                                goto restart;
+                       }
                }
 
                /* stop when we catch up to the HC */
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 46fa57a..1fd8304 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -532,6 +532,7 @@ struct ehci_itd {
        /* the rest is HCD-private */
        dma_addr_t              itd_dma;        /* for this itd */
        union ehci_shadow       itd_next;       /* ptr to periodic q entry */
+       struct ehci_itd         *itd_next_done; /* lists completed itds */
 
        struct urb              *urb;
        struct ehci_iso_stream  *stream;        /* endpoint's queue */
-- 
1.5.0.3

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys-and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to