- 论坛徽章:
- 0
|
贴上源代码:
- /*
- * This scheduler plans almost as far into the future as it has actual
- * periodic schedule slots. (Affected by TUNE_FLS, which defaults to
- * "as small as possible" to be cache-friendlier.) That limits the size
- * transfers you can stream reliably; avoid more than 64 msec per urb.
- * Also avoid queue depths of less than ehci's worst irq latency (affected
- * by the per-urb URB_NO_INTERRUPT hint, the log2_irq_thresh module parameter,
- * and other factors); or more than about 230 msec total (for portability,
- * given EHCI_TUNE_FLS and the slop). Or, write a smarter scheduler!
- */
- static int
- iso_stream_schedule (
- struct ehci_hcd *ehci,
- struct urb *urb,
- struct ehci_iso_stream *stream
- )
- {
- u32 now, base, next, start, period, span, now2;
- u32 wrap = 0, skip = 0;
- int status = 0;
- unsigned mod = ehci->periodic_size << 3; /* ehci->periodic_size can be 1024/512/256 */
- struct ehci_iso_sched *sched = urb->hcpriv;
- bool empty = list_empty(&stream->td_list);
- bool new_stream = false;
- period = stream->uperiod; /* urb->interval */
- span = sched->span; /* urb->number_of_packets * (urb->interval >> 3) */
- if (!stream->highspeed)
- span <<= 3;
- /* Start a new isochronous stream? */
- if (unlikely(empty && !hcd_periodic_completion_in_progress(
- ehci_to_hcd(ehci), urb->ep))) {
- /* Schedule the endpoint */
- if (stream->ps.phase == NO_FRAME) {
- int done = 0;
- struct ehci_tt *tt = find_tt(stream->ps.udev);/* check where we attach */
- if (IS_ERR(tt)) {
- status = PTR_ERR(tt);
- goto fail;
- }
- compute_tt_budget(ehci->tt_budget, tt);
- start = ((-(++ehci->random_frame)) << 3) & (period - 1);
- /* find a uframe slot with enough bandwidth.
- * Early uframes are more precious because full-speed
- * iso IN transfers can't use late uframes,
- * and therefore they should be allocated last.
- */
- next = start;
- start += period;
- do {
- start--;
- /* check schedule: enough space? */
- if (stream->highspeed) {
- if (itd_slot_ok(ehci, stream, start))
- done = 1;
- } else {
- if ((start % 8) >= 6)
- continue;
- if (sitd_slot_ok(ehci, stream, start,
- sched, tt))
- done = 1;
- }
- } while (start > next && !done);
- /* no room in the schedule */
- if (!done) {
- ehci_dbg(ehci, "iso sched full %p", urb);
- status = -ENOSPC;
- goto fail;
- }
- stream->ps.phase = (start >> 3) &
- (stream->ps.period - 1);
- stream->ps.bw_phase = stream->ps.phase &
- (stream->ps.bw_period - 1);
- stream->ps.phase_uf = start & 7;
- reserve_release_iso_bandwidth(ehci, stream, 1);
- }
- /* New stream is already scheduled; use the upcoming slot */
- else {
- start = (stream->ps.phase << 3) + stream->ps.phase_uf;
- }
- stream->next_uframe = start;
- new_stream = true;
- }
- now = ehci_read_frame_index(ehci) & (mod - 1);
- /* 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);
- if (unlikely(new_stream))
- goto do_ASAP;
- /*
- * Typical case: reuse current schedule, stream may still be 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.
- */
- 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 + span - period >= mod + wrap)) {
- ehci_dbg(ehci, "request %p would overflow (%u+%u >= %u)\n",
- urb, start, span - period, mod + wrap);
- status = -EFBIG;
- goto fail;
- }
- start += base;
- stream->next_uframe = (start + skip) & (mod - 1);
- /* report high speed start in uframes; full speed, in frames */
- 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 status;
- fail:
- iso_sched_free(stream, sched);
- urb->hcpriv = NULL;
- return status;
- }
复制代码 |
|