From 7dedacf4270a810fadcca887ac85d267b5f1882d Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 4 Aug 2005 18:06:41 -0700 Subject: [PATCH] [PATCH] USB: ehci: microframe handling fix 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 Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman Signed-off-by: Linus Torvalds --- drivers/usb/host/ehci-dbg.c | 2 +- drivers/usb/host/ehci-q.c | 5 +++-- drivers/usb/host/ehci-sched.c | 13 +++++++------ drivers/usb/host/ehci.h | 5 +++++ 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 50cb018310..b01efb6b36 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -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; diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 4f97a4ad1e..20df01a79b 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -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--; @@ -428,7 +428,8 @@ halt: /* 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 diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 9af4f64532..b56f25864e 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -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 */ @@ -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 */ @@ -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 @@ -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 { @@ -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); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 4df4982317..a754215753 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -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) */ -- 2.39.5