X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fehci-sched.c;h=1fc2befc4fdc1d6d8a82b045702b8e9678bb656d;hb=8c05dc598e5bc0eb33791de23157cf1e47cb580e;hp=85dd24ed97a6d6cf6700405797da29d313e76958;hpb=1d7b24ff335489dd7e864b71ba5b35eaa25b625a;p=pandora-kernel.git diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 85dd24ed97a6..1fc2befc4fdc 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -761,7 +761,7 @@ static int check_intr_schedule ( unsigned i; /* TODO : this may need FSTN for SSPLIT in uframe 5. */ - for (i=uframe+1; i<8 && iperiod, qh->c_usecs)) goto done; @@ -1055,7 +1055,7 @@ iso_stream_init ( } else stream->raw_mask = smask_out [hs_transfers - 1]; bandwidth = stream->usecs + stream->c_usecs; - bandwidth /= interval << 3; + bandwidth /= interval; /* stream->splits gets created from raw_mask later */ stream->address = cpu_to_hc32(ehci, addr); @@ -1286,6 +1286,10 @@ sitd_slot_ok ( mask = stream->raw_mask << (uframe & 7); + /* for OUT, don't wrap SSPLIT into H-microframe 7 */ + if (((stream->raw_mask & 0xff) << (uframe & 7)) >= (1 << 7)) + return 0; + /* for IN, don't wrap CSPLIT into the next frame */ if (mask & ~0xffff) return 0; @@ -1370,10 +1374,12 @@ iso_stream_schedule ( struct ehci_iso_stream *stream ) { - u32 now, base, next, start, period, span; - int status; + u32 now, base, next, start, period, span, now2; + u32 wrap = 0, skip = 0; + int status = 0; unsigned mod = ehci->periodic_size << 3; struct ehci_iso_sched *sched = urb->hcpriv; + bool empty = list_empty(&stream->td_list); period = urb->interval; span = sched->span; @@ -1384,69 +1390,31 @@ iso_stream_schedule ( now = ehci_read_frame_index(ehci) & (mod - 1); - /* Typical case: reuse current schedule, stream is still active. - * Hopefully there are no gaps from the host falling behind - * (irq delays etc). If there are, the behavior depends on - * whether URB_ISO_ASAP is set. - */ - if (likely (!list_empty (&stream->td_list))) { - - /* Take the isochronous scheduling threshold into account */ - if (ehci->i_thresh) - next = now + ehci->i_thresh; /* uframe cache */ - else - next = (now + 2 + 7) & ~0x07; /* full frame cache */ - - /* - * Use ehci->last_iso_frame as the base. There can't be any - * TDs scheduled for earlier than that. - */ - base = ehci->last_iso_frame << 3; - next = (next - base) & (mod - 1); - start = (stream->next_uframe - base) & (mod - 1); - - /* Is the schedule already full? */ - if (unlikely(start < period)) { - ehci_dbg(ehci, "iso sched full %p (%u-%u < %u mod %u)\n", - urb, stream->next_uframe, base, - period, mod); - status = -ENOSPC; - goto fail; - } - - /* Behind the scheduling threshold? */ - if (unlikely(start < next)) { - unsigned now2 = (now - base) & (mod - 1); - - /* USB_ISO_ASAP: Round up to the first available slot */ - if (urb->transfer_flags & URB_ISO_ASAP) - start += (next - start + period - 1) & -period; - - /* - * Not ASAP: Use the next slot in the stream, - * no matter what. - */ - else if (start + span - period < now2) { - ehci_dbg(ehci, "iso underrun %p (%u+%u < %u)\n", - urb, start + base, - span - period, now2 + base); - } - } + /* Take the isochronous scheduling threshold into account */ + if (ehci->i_thresh) + next = now + ehci->i_thresh; /* uframe cache */ + else + next = (now + 2 + 7) & ~0x07; /* full frame cache */ - start += base; - } + /* + * Use ehci->last_iso_frame as the base. There can't be any + * TDs scheduled for earlier than that. + */ + base = ehci->last_iso_frame << 3; + next = (next - base) & (mod - 1); - /* need to schedule; when's the next (u)frame we could start? - * this is bigger than ehci->i_thresh allows; scheduling itself - * isn't free, the delay should handle reasonably slow cpus. it + /* + * Need to schedule; when's the next (u)frame we could start? + * This is bigger than ehci->i_thresh allows; scheduling itself + * isn't free, the delay should handle reasonably slow cpus. It * can also help high bandwidth if the dma and irq loads don't * jump until after the queue is primed. */ - else { + if (unlikely(empty && !hcd_periodic_completion_in_progress( + ehci_to_hcd(ehci), urb->ep))) { int done = 0; - base = now & ~0x07; - start = base + SCHEDULING_DELAY; + start = (now & ~0x07) + SCHEDULING_DELAY; /* find a uframe slot with enough bandwidth. * Early uframes are more precious because full-speed @@ -1477,27 +1445,96 @@ iso_stream_schedule ( status = -ENOSPC; goto fail; } + + start = (start - base) & (mod - 1); + goto use_start; + } + + /* + * Typical case: reuse current schedule, stream is still active. + * Hopefully there are no gaps from the host falling behind + * (irq delays etc). If there are, the behavior depends on + * whether URB_ISO_ASAP is set. + */ + start = (stream->next_uframe - base) & (mod - 1); + now2 = (now - base) & (mod - 1); + + /* Is the schedule already full? */ + if (unlikely(!empty && start < period)) { + ehci_dbg(ehci, "iso sched full %p (%u-%u < %u mod %u)\n", + urb, stream->next_uframe, base, period, mod); + status = -ENOSPC; + goto fail; + } + + /* Is the next packet scheduled after the base time? */ + if (likely(!empty || start <= now2 + period)) { + + /* URB_ISO_ASAP: make sure that start >= next */ + if (unlikely(start < next && + (urb->transfer_flags & URB_ISO_ASAP))) + goto do_ASAP; + + /* Otherwise use start, if it's not in the past */ + if (likely(start >= now2)) + goto use_start; + + /* Otherwise we got an underrun while the queue was empty */ + } else { + if (urb->transfer_flags & URB_ISO_ASAP) + goto do_ASAP; + wrap = mod; + now2 += mod; + } + + /* How many uframes and packets do we need to skip? */ + skip = (now2 - start + period - 1) & -period; + if (skip >= span) { /* Entirely in the past? */ + ehci_dbg(ehci, "iso underrun %p (%u+%u < %u) [%u]\n", + urb, start + base, span - period, now2 + base, + base); + + /* Try to keep the last TD intact for scanning later */ + skip = span - period; + + /* Will it come before the current scan position? */ + if (empty) { + skip = span; /* Skip the entire URB */ + status = 1; /* and give it back immediately */ + iso_sched_free(stream, sched); + sched = NULL; + } } + urb->error_count = skip / period; + if (sched) + sched->first_packet = urb->error_count; + goto use_start; + + do_ASAP: + /* Use the first slot after "next" */ + start = next + ((start - next) & (period - 1)); + use_start: /* Tried to schedule too far into the future? */ - if (unlikely(start - base + span - period >= mod)) { + if (unlikely(start + span - period >= mod + wrap)) { ehci_dbg(ehci, "request %p would overflow (%u+%u >= %u)\n", - urb, start - base, span - period, mod); + urb, start, span - period, mod + wrap); status = -EFBIG; goto fail; } - stream->next_uframe = start & (mod - 1); + start += base; + stream->next_uframe = (start + skip) & (mod - 1); /* report high speed start in uframes; full speed, in frames */ - urb->start_frame = stream->next_uframe; + urb->start_frame = start & (mod - 1); if (!stream->highspeed) urb->start_frame >>= 3; /* Make sure scan_isoc() sees these */ if (ehci->isoc_count == 0) ehci->last_iso_frame = now >> 3; - return 0; + return status; fail: iso_sched_free(stream, sched); @@ -1610,7 +1647,8 @@ static void itd_link_urb( ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; /* fill iTDs uframe by uframe */ - for (packet = 0, itd = NULL; packet < urb->number_of_packets; ) { + for (packet = iso_sched->first_packet, itd = NULL; + packet < urb->number_of_packets;) { if (itd == NULL) { /* ASSERT: we have all necessary itds */ // BUG_ON (list_empty (&iso_sched->td_list)); @@ -1804,10 +1842,14 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, if (unlikely(status)) goto done_not_linked; status = iso_stream_schedule(ehci, urb, stream); - if (likely (status == 0)) + if (likely(status == 0)) { itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); - else + } else if (status > 0) { + status = 0; + ehci_urb_done(ehci, urb, 0); + } else { usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); + } done_not_linked: spin_unlock_irqrestore (&ehci->lock, flags); done: @@ -2008,7 +2050,7 @@ static void sitd_link_urb( ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; /* fill sITDs frame by frame */ - for (packet = 0, sitd = NULL; + for (packet = sched->first_packet, sitd = NULL; packet < urb->number_of_packets; packet++) { @@ -2178,10 +2220,14 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, if (unlikely(status)) goto done_not_linked; status = iso_stream_schedule(ehci, urb, stream); - if (status == 0) + if (likely(status == 0)) { sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream); - else + } else if (status > 0) { + status = 0; + ehci_urb_done(ehci, urb, 0); + } else { usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); + } done_not_linked: spin_unlock_irqrestore (&ehci->lock, flags); done: