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
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel