summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2010-03-01 09:12:50 +0100
committerGreg Kroah-Hartman <gregkh@suse.de>2010-03-19 07:24:05 -0700
commit92bc3648e6027384479852b770a542722fadee7c (patch)
tree9803348ef9fd9186ad375abf89a4e7862e9badc4
parentbf162019b7f5bda9eb3241ae22de831df2126132 (diff)
USB: EHCI: fix ITD list order
When isochronous URBs are shorter than one frame and when more than one ITD in a frame has been completed before the interrupt can be handled, scan_periodic() completes the URBs in the order in which they are found in the descriptor list. Therefore, the descriptor list must contain the ITDs in the correct order, i.e., a new ITD must be linked in after any previous ITDs of the same endpoint. This should fix garbled capture data in the USB audio drivers. Signed-off-by: Clemens Ladisch <clemens@ladisch.de> Reported-by: Colin Fletcher <colin.m.fletcher@googlemail.com> Cc: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/host/ehci-sched.c24
1 files changed, 19 insertions, 5 deletions
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 39340ae00ac4..cd1e8bf5327e 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1565,13 +1565,27 @@ itd_patch(
static inline void
itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
{
- /* always prepend ITD/SITD ... only QH tree is order-sensitive */
- itd->itd_next = ehci->pshadow [frame];
- itd->hw_next = ehci->periodic [frame];
- ehci->pshadow [frame].itd = itd;
+ union ehci_shadow *prev = &ehci->pshadow[frame];
+ __hc32 *hw_p = &ehci->periodic[frame];
+ union ehci_shadow here = *prev;
+ __hc32 type = 0;
+
+ /* skip any iso nodes which might belong to previous microframes */
+ while (here.ptr) {
+ type = Q_NEXT_TYPE(ehci, *hw_p);
+ if (type == cpu_to_hc32(ehci, Q_TYPE_QH))
+ break;
+ prev = periodic_next_shadow(ehci, prev, type);
+ hw_p = shadow_next_periodic(ehci, &here, type);
+ here = *prev;
+ }
+
+ itd->itd_next = here;
+ itd->hw_next = *hw_p;
+ prev->itd = itd;
itd->frame = frame;
wmb ();
- ehci->periodic[frame] = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD);
+ *hw_p = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD);
}
/* fit urb's itds into the selected schedule slot; activate as needed */