Commit 49597169 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB: ehci update: 2/3, microframe scanning

This patch is needed to make high bandwidth ISO streams behave,
but could resolve some other scanning glitches.  Current users
of periodic transfers (interrupt transfer modes for hubs, mice,
and keyboards) shouldn't even notice this change.

It makes the periodic schedule scan handle cases where a given
frame's schedule slot reports completions in several different
microframes.  So far that's been uncommon, but it's typical
for high bandwidth iso (or even with busier interrupt trees than
this driver has supported yet).

It also starts to remove the assumption that each ITD only uses
one microframe; but most of those changes are in the next patch.
And it fixes a bug where some status bits were mis-interpreted as
significant bits in the ITD transfer length.
parent cf270dfe
......@@ -868,26 +868,31 @@ static unsigned
itd_complete (
struct ehci_hcd *ehci,
struct ehci_itd *itd,
unsigned uframe,
struct pt_regs *regs
) {
struct urb *urb = itd->urb;
struct usb_iso_packet_descriptor *desc;
u32 t;
unsigned uframe;
int urb_index = -1;
/* update status for this uframe's transfers */
desc = &urb->iso_frame_desc [itd->index];
/* for each uframe with a packet */
for (uframe = 0; uframe < 8; uframe++) {
if (itd->hw_transaction [uframe] == 0)
continue;
urb_index = itd->index;
desc = &urb->iso_frame_desc [urb_index];
t = itd->hw_transaction [uframe];
t = le32_to_cpup (&itd->hw_transaction [uframe]);
itd->hw_transaction [uframe] = 0;
if (t & EHCI_ISOC_ACTIVE)
desc->status = -EXDEV;
else if (t & ISO_ERRS) {
/* report transfer status */
if (unlikely (t & ISO_ERRS)) {
urb->error_count++;
if (t & EHCI_ISOC_BUF_ERR)
desc->status = usb_pipein (urb->pipe)
? -ENOSR /* couldn't read */
: -ECOMM; /* couldn't write */
? -ENOSR /* hc couldn't read */
: -ECOMM; /* hc couldn't write */
else if (t & EHCI_ISOC_BABBLE)
desc->status = -EOVERFLOW;
else /* (t & EHCI_ISOC_XACTERR) */
......@@ -895,10 +900,11 @@ itd_complete (
/* HC need not update length with this error */
if (!(t & EHCI_ISOC_BABBLE))
desc->actual_length += EHCI_ITD_LENGTH (t);
} else {
desc->actual_length = EHCI_ITD_LENGTH (t);
} else if (likely ((t & EHCI_ISOC_ACTIVE) == 0)) {
desc->status = 0;
desc->actual_length += EHCI_ITD_LENGTH (t);
desc->actual_length = EHCI_ITD_LENGTH (t);
}
}
vdbg ("itd %p urb %p packet %d/%d trans %x status %d len %d",
......@@ -906,7 +912,7 @@ itd_complete (
t, desc->status, desc->actual_length);
/* handle completion now? */
if ((itd->index + 1) != urb->number_of_packets)
if (likely ((urb_index + 1) != urb->number_of_packets))
return 0;
/*
......@@ -986,24 +992,23 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
* When running, scan from last scan point up to "now"
* else clean up by scanning everything that's left.
* Touches as few pages as possible: cache-friendly.
* Don't scan ISO entries more than once, though.
*/
frame = ehci->next_uframe >> 3;
now_uframe = ehci->next_uframe;
if (HCD_IS_RUNNING (ehci->hcd.state))
now_uframe = readl (&ehci->regs->frame_index);
clock = readl (&ehci->regs->frame_index) % mod;
else
now_uframe = (frame << 3) - 1;
now_uframe %= mod;
clock = now_uframe >> 3;
clock = now_uframe + mod - 1;
for (;;) {
union ehci_shadow q, *q_p;
u32 type, *hw_p;
unsigned uframes;
frame = now_uframe >> 3;
restart:
/* scan schedule to _before_ current frame index */
if (frame == clock)
if ((frame == (clock >> 3))
&& HCD_IS_RUNNING (ehci->hcd.state))
uframes = now_uframe & 0x07;
else
uframes = 8;
......@@ -1043,34 +1048,31 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
case Q_TYPE_ITD:
last = (q.itd->hw_next == EHCI_LIST_END);
/* Unlink each (S)ITD we see, since the ISO
* URB model forces constant rescheduling.
* That complicates sharing uframes in ITDs,
* and means we need to skip uframes the HC
* hasn't yet processed.
*/
for (uf = 0; uf < uframes; uf++) {
if (q.itd->hw_transaction [uf] != 0) {
temp = q;
*q_p = q.itd->itd_next;
*hw_p = q.itd->hw_next;
type = Q_NEXT_TYPE (*hw_p);
/* might free q.itd ... */
count += itd_complete (ehci,
temp.itd, uf, regs);
break;
}
}
/* we might skip this ITD's uframe ... */
if (uf == uframes) {
/* skip itds for later in the frame */
rmb ();
for (uf = uframes; uf < 8; uf++) {
if (0 == (q.itd->hw_transaction [uf]
& ISO_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;
wmb();
/* always rescan here; simpler */
count += itd_complete (ehci, q.itd, regs);
goto restart;
#ifdef have_split_iso
case Q_TYPE_SITD:
last = (q.sitd->hw_next == EHCI_LIST_END);
......@@ -1104,7 +1106,7 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
// FIXME: likewise assumes HC doesn't halt mid-scan
if (frame == clock) {
if (now_uframe == clock) {
unsigned now;
if (!HCD_IS_RUNNING (ehci->hcd.state))
......@@ -1115,9 +1117,13 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
break;
/* rescan the rest of this frame, then ... */
now_uframe = now;
clock = now_uframe >> 3;
} else
frame = (frame + 1) % ehci->periodic_size;
clock = now;
} else {
/* FIXME sometimes we can scan the next frame
* right away, not always inching up on it ...
*/
now_uframe++;
now_uframe %= mod;
}
}
}
......@@ -400,9 +400,11 @@ struct ehci_itd {
#define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */
#define EHCI_ISOC_BABBLE (1<<29) /* babble detected */
#define EHCI_ISOC_XACTERR (1<<28) /* XactErr - transaction error */
#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x7fff)
#define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff)
#define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */
#define ISO_ACTIVE __constant_cpu_to_le32(EHCI_ISOC_ACTIVE)
u32 hw_bufp [7]; /* see EHCI 3.3.3 */
u32 hw_bufp_hi [7]; /* Appendix B */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment