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