Chinaunix
标题:
请教USB子系统EHCI控制器驱动关于同步端点调度的问题
[打印本页]
作者:
myspace21
时间:
2014-11-14 16:27
标题:
请教USB子系统EHCI控制器驱动关于同步端点调度的问题
最近在看EHCI控制器驱动,看到iso_stream_schedule这个函数的时候就看不懂了,在网上也找不到相关的资料,不知道有哪位大神研究过这个函数的实现原理?哎,好多天了,还没想明白,请各位赐教,多谢了!!
作者:
myspace21
时间:
2014-11-14 16:43
贴上源代码:
/*
* 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;
}
复制代码
作者:
myspace21
时间:
2014-11-20 16:40
顶一下,哪位大神研习过?
欢迎光临 Chinaunix (http://bbs.chinaunix.net/)
Powered by Discuz! X3.2