OHCI: implement new semantics for URB_ISO_ASAP
authorAlan Stern <stern@rowland.harvard.edu>
Mon, 1 Oct 2012 14:32:15 +0000 (10:32 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 22 Oct 2012 18:10:24 +0000 (11:10 -0700)
This patch (as1614) updates the isochronous scheduling in ohci-hcd to
match the new semantics for URB_ISO_ASAP.  Testing revealed a hardware
bug in the way my OHCI controller handles expired isochronous TDs;
consequently the patch tries hard to avoid creating them (unlike the
ehci-hcd and uhci-hcd drivers).

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/ohci-hcd.c
drivers/usb/host/ohci-q.c

index 4a1d64d92338e7653035b054decf6e714909a41f..cfc1da30667c83ece8b8b6499ce7a178176bee9c 100644 (file)
@@ -231,13 +231,41 @@ static int ohci_urb_enqueue (
                        frame &= ~(ed->interval - 1);
                        frame |= ed->branch;
                        urb->start_frame = frame;
+               }
+       } else if (ed->type == PIPE_ISOCHRONOUS) {
+               u16     next = ohci_frame_no(ohci) + 2;
+               u16     frame = ed->last_iso + ed->interval;
+
+               /* Behind the scheduling threshold? */
+               if (unlikely(tick_before(frame, next))) {
 
-                       /* yes, only URB_ISO_ASAP is supported, and
-                        * urb->start_frame is never used as input.
+                       /* USB_ISO_ASAP: Round up to the first available slot */
+                       if (urb->transfer_flags & URB_ISO_ASAP)
+                               frame += (next - frame + ed->interval - 1) &
+                                               -ed->interval;
+
+                       /*
+                        * Not ASAP: Use the next slot in the stream.  If
+                        * the entire URB falls before the threshold, fail.
                         */
+                       else if (tick_before(frame + ed->interval *
+                                       (urb->number_of_packets - 1), next)) {
+                               retval = -EXDEV;
+                               usb_hcd_unlink_urb_from_ep(hcd, urb);
+                               goto fail;
+                       }
+
+                       /*
+                        * Some OHCI hardware doesn't handle late TDs
+                        * correctly.  After retiring them it proceeds to
+                        * the next ED instead of the next TD.  Therefore
+                        * we have to omit the late TDs entirely.
+                        */
+                       urb_priv->td_cnt = DIV_ROUND_UP(next - frame,
+                                       ed->interval);
                }
-       } else if (ed->type == PIPE_ISOCHRONOUS)
-               urb->start_frame = ed->last_iso + ed->interval;
+               urb->start_frame = frame;
+       }
 
        /* fill the TDs and link them to the ed; and
         * enable that part of the schedule, if needed
index c5a1ea9145faec6e65809ddf1fdbb7046f6897cc..177a213790d47f6d9abe059c15e8043230f690c8 100644 (file)
@@ -596,7 +596,6 @@ static void td_submit_urb (
                urb_priv->ed->hwHeadP &= ~cpu_to_hc32 (ohci, ED_C);
        }
 
-       urb_priv->td_cnt = 0;
        list_add (&urb_priv->pending, &ohci->pending);
 
        if (data_len)
@@ -672,7 +671,8 @@ static void td_submit_urb (
         * we could often reduce the number of TDs here.
         */
        case PIPE_ISOCHRONOUS:
-               for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
+               for (cnt = urb_priv->td_cnt; cnt < urb->number_of_packets;
+                               cnt++) {
                        int     frame = urb->start_frame;
 
                        // FIXME scheduling should handle frame counter