USB: EHCI: fix ITD list order
Clemens Ladisch [Mon, 1 Mar 2010 08:12:50 +0000 (09:12 +0100)]
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>

drivers/usb/host/ehci-sched.c

index 39340ae..cd1e8bf 100644 (file)
@@ -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 */