Skip to content

Commit

Permalink
[PATCH] USB: ehci: microframe handling fix
Browse files Browse the repository at this point in the history
This patch has a one line oops fix, plus related cleanups.

 - The bugfix uses microframe scheduling data given to the hardware to
   test "is this a periodic QH", rather than testing for nonzero period.
   (Prevents an oops by providing the correct answer.)

 - The cleanup going along with the patch should make it clearer what's
   going on whenever those bitfields are accessed.

The bug came about when, around January, two new kinds of EHCI interrupt
scheduling operation were added, involving both the high speed (24 KBytes
per millisec) and low/full speed (1-64 bytes per millisec) microframe
scheduling.  A driver for the Edirol UA-1000 Audio Capture Unit ran into
the oops; it used one of the newly supported high speed modes.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
  • Loading branch information
David Brownell authored and Linus Torvalds committed Aug 5, 2005
1 parent 003ba51 commit 7dedacf
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 9 deletions.
2 changes: 1 addition & 1 deletion drivers/usb/host/ehci-dbg.c
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ show_periodic (struct class_device *class_dev, char *buf)
p.qh->period,
le32_to_cpup (&p.qh->hw_info2)
/* uframe masks */
& 0xffff,
& (QH_CMASK | QH_SMASK),
p.qh);
size -= temp;
next += temp;
Expand Down
5 changes: 3 additions & 2 deletions drivers/usb/host/ehci-q.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ __acquires(ehci->lock)
struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv;

/* S-mask in a QH means it's an interrupt urb */
if ((qh->hw_info2 & __constant_cpu_to_le32 (0x00ff)) != 0) {
if ((qh->hw_info2 & __constant_cpu_to_le32 (QH_SMASK)) != 0) {

/* ... update hc-wide periodic stats (for usbfs) */
ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
Expand Down Expand Up @@ -428,7 +428,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
/* should be rare for periodic transfers,
* except maybe high bandwidth ...
*/
if (qh->period) {
if ((__constant_cpu_to_le32 (QH_SMASK)
& qh->hw_info2) != 0) {
intr_deschedule (ehci, qh);
(void) qh_schedule (ehci, qh);
} else
Expand Down
13 changes: 7 additions & 6 deletions drivers/usb/host/ehci-sched.c
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)

dev_dbg (&qh->dev->dev,
"link qh%d-%04x/%p start %d [%d/%d us]\n",
period, le32_to_cpup (&qh->hw_info2) & 0xffff,
period, le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK),
qh, qh->start, qh->usecs, qh->c_usecs);

/* high bandwidth, or otherwise every microframe */
Expand Down Expand Up @@ -385,7 +385,8 @@ static void qh_unlink_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)

dev_dbg (&qh->dev->dev,
"unlink qh%d-%04x/%p start %d [%d/%d us]\n",
qh->period, le32_to_cpup (&qh->hw_info2) & 0xffff,
qh->period,
le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK),
qh, qh->start, qh->usecs, qh->c_usecs);

/* qh->qh_next still "live" to HC */
Expand All @@ -411,7 +412,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
* active high speed queues may need bigger delays...
*/
if (list_empty (&qh->qtd_list)
|| (__constant_cpu_to_le32 (0x0ff << 8)
|| (__constant_cpu_to_le32 (QH_CMASK)
& qh->hw_info2) != 0)
wait = 2;
else
Expand Down Expand Up @@ -533,7 +534,7 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)

/* reuse the previous schedule slots, if we can */
if (frame < qh->period) {
uframe = ffs (le32_to_cpup (&qh->hw_info2) & 0x00ff);
uframe = ffs (le32_to_cpup (&qh->hw_info2) & QH_SMASK);
status = check_intr_schedule (ehci, frame, --uframe,
qh, &c_mask);
} else {
Expand Down Expand Up @@ -569,10 +570,10 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
qh->start = frame;

/* reset S-frame and (maybe) C-frame masks */
qh->hw_info2 &= __constant_cpu_to_le32 (~0xffff);
qh->hw_info2 &= __constant_cpu_to_le32(~(QH_CMASK | QH_SMASK));
qh->hw_info2 |= qh->period
? cpu_to_le32 (1 << uframe)
: __constant_cpu_to_le32 (0xff);
: __constant_cpu_to_le32 (QH_SMASK);
qh->hw_info2 |= c_mask;
} else
ehci_dbg (ehci, "reused qh %p schedule\n", qh);
Expand Down
5 changes: 5 additions & 0 deletions drivers/usb/host/ehci.h
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,11 @@ struct ehci_qh {
__le32 hw_info1; /* see EHCI 3.6.2 */
#define QH_HEAD 0x00008000
__le32 hw_info2; /* see EHCI 3.6.2 */
#define QH_SMASK 0x000000ff
#define QH_CMASK 0x0000ff00
#define QH_HUBADDR 0x007f0000
#define QH_HUBPORT 0x3f800000
#define QH_MULT 0xc0000000
__le32 hw_current; /* qtd list - see EHCI 3.6.4 */

/* qtd overlay (hardware parts of a struct ehci_qtd) */
Expand Down

0 comments on commit 7dedacf

Please sign in to comment.