On Mon, 26 Aug 2013 14:50:53 -0400 (EDT) Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> wrote: > This patch does the real work. It fixes up ehci-hcd so that an URB > submitted by a completion handler will keep the isochronous stream > alive, even if the handler was delayed by running in a tasklet and the > queue has emptied out. > > As I mentioned earlier, this is not a simple change. > > Alan Stern > > > > drivers/usb/host/ehci-sched.c | 164 +++++++++++++++++++++++++----------------- > drivers/usb/host/ehci.h | 1 > 2 files changed, 101 insertions(+), 64 deletions(-) Below is the approach I proposed(mentioned in another thread), which should be simper than this one, any comments? drivers/usb/host/ehci-sched.c | 53 ++++++++++++++++++++++++++++++++++++++--- drivers/usb/host/ehci.h | 1 + 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 83be03f..80ef95d 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1370,6 +1370,33 @@ sitd_slot_ok ( return 1; } +/* rebase in case of underun without ISO_ASAP together */ +static int iso_stream_rebase(struct ehci_hcd *ehci, struct urb *urb, + struct ehci_iso_stream *stream, u32 span, u32 period) +{ + u32 i, end; + u32 mod = ehci->periodic_size; + + if (ehci->last_base == -1) + return 0; + + end = ((stream->next_uframe + span - period) >> 3) & (mod - 1); + for (i = ehci->last_base; i != ehci->last_iso_frame; + i = (i + 1) & (mod - 1)) { + /* don't schedule URB which is behind base totally */ + if (end == i) { + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].length = 0; + urb->iso_frame_desc[i].status = 0; + } + return 1; + } + if (((stream->next_uframe >> 3) & (mod - 1)) == i) + ehci->last_iso_frame = i; + } + return 0; +} + /* * This scheduler plans almost as far into the future as it has actual * periodic schedule slots. (Affected by TUNE_FLS, which defaults to @@ -1409,7 +1436,9 @@ iso_stream_schedule ( * (irq delays etc). If there are, the behavior depends on * whether URB_ISO_ASAP is set. */ - if (likely (!list_empty (&stream->td_list))) { + if (likely (!list_empty (&stream->td_list) || + hcd_complete_in_progress( + ehci_to_hcd(ehci), urb))) { /* Take the isochronous scheduling threshold into account */ if (ehci->i_thresh) @@ -1417,6 +1446,14 @@ iso_stream_schedule ( else next = (now + 2 + 7) & ~0x07; /* full frame cache */ + if (list_empty(&stream->td_list) && + !(urb->transfer_flags & URB_ISO_ASAP)) + if (iso_stream_rebase(ehci, urb, stream, span, + period)) { + status = 1; + goto fail; + } + /* * Use ehci->last_iso_frame as the base. There can't be any * TDs scheduled for earlier than that. @@ -1517,6 +1554,7 @@ iso_stream_schedule ( /* Make sure scan_isoc() sees these */ if (ehci->isoc_count == 0) ehci->last_iso_frame = now >> 3; + ehci->last_base = -1; return 0; fail: @@ -1838,7 +1876,10 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, status = iso_stream_schedule(ehci, urb, stream); if (likely (status == 0)) itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); - else + else if (status > 0) { + ehci_urb_done(ehci, urb, 0); + status = 0; + } else usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); done_not_linked: spin_unlock_irqrestore (&ehci->lock, flags); @@ -2224,7 +2265,10 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, status = iso_stream_schedule(ehci, urb, stream); if (status == 0) sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); - else + else if (status > 0) { + ehci_urb_done(ehci, urb, 0); + status = 0; + } else usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); done_not_linked: spin_unlock_irqrestore (&ehci->lock, flags); @@ -2255,6 +2299,9 @@ static void scan_isoc(struct ehci_hcd *ehci) } ehci->now_frame = now_frame; + if (ehci->last_base == -1) + ehci->last_base = ehci->last_iso_frame; + frame = ehci->last_iso_frame; for (;;) { union ehci_shadow q, *q_p; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 2822e79..279e182 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -149,6 +149,7 @@ struct ehci_hcd { /* one per controller */ unsigned intr_unlink_wait_cycle; unsigned intr_unlink_cycle; unsigned now_frame; /* frame from HC hardware */ + unsigned last_base; /* previous 'last_iso_frame' */ unsigned last_iso_frame; /* last frame scanned for iso */ unsigned intr_count; /* intr activity count */ unsigned isoc_count; /* isoc activity count */ Thanks, -- Ming Lei -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html