UHCI: implement new semantics for URB_ISO_ASAP
authorAlan Stern <stern@rowland.harvard.edu>
Mon, 1 Oct 2012 14:32:09 +0000 (10:32 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 22 Oct 2012 18:10:24 +0000 (11:10 -0700)
This patch (as1613) updates the isochronous scheduling in uhci-hcd to
match the new semantics for URB_ISO_ASAP.  The amount of code
alteration is smaller than it looks because of a change in the
indentation level.

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

index d2c6f5a..15921fd 100644 (file)
@@ -1256,7 +1256,8 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
                struct uhci_qh *qh)
 {
        struct uhci_td *td = NULL;      /* Since urb->number_of_packets > 0 */
-       int i, frame;
+       int i;
+       unsigned frame, next;
        unsigned long destination, status;
        struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
 
@@ -1265,37 +1266,29 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
                        urb->number_of_packets >= UHCI_NUMFRAMES)
                return -EFBIG;
 
+       uhci_get_current_frame_number(uhci);
+
        /* Check the period and figure out the starting frame number */
        if (!qh->bandwidth_reserved) {
                qh->period = urb->interval;
-               if (urb->transfer_flags & URB_ISO_ASAP) {
-                       qh->phase = -1;         /* Find the best phase */
-                       i = uhci_check_bandwidth(uhci, qh);
-                       if (i)
-                               return i;
-
-                       /* Allow a little time to allocate the TDs */
-                       uhci_get_current_frame_number(uhci);
-                       frame = uhci->frame_number + 10;
-
-                       /* Move forward to the first frame having the
-                        * correct phase */
-                       urb->start_frame = frame + ((qh->phase - frame) &
-                                       (qh->period - 1));
-               } else {
-                       i = urb->start_frame - uhci->last_iso_frame;
-                       if (i <= 0 || i >= UHCI_NUMFRAMES)
-                               return -EINVAL;
-                       qh->phase = urb->start_frame & (qh->period - 1);
-                       i = uhci_check_bandwidth(uhci, qh);
-                       if (i)
-                               return i;
-               }
+               qh->phase = -1;         /* Find the best phase */
+               i = uhci_check_bandwidth(uhci, qh);
+               if (i)
+                       return i;
+
+               /* Allow a little time to allocate the TDs */
+               next = uhci->frame_number + 10;
+               frame = qh->phase;
+
+               /* Round up to the first available slot */
+               frame += (next - frame + qh->period - 1) & -qh->period;
 
        } else if (qh->period != urb->interval) {
                return -EINVAL;         /* Can't change the period */
 
        } else {
+               next = uhci->frame_number + 2;
+
                /* Find the next unused frame */
                if (list_empty(&qh->queue)) {
                        frame = qh->iso_frame;
@@ -1308,25 +1301,31 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
                                        lurb->number_of_packets *
                                        lurb->interval;
                }
-               if (urb->transfer_flags & URB_ISO_ASAP) {
-                       /* Skip some frames if necessary to insure
-                        * the start frame is in the future.
+
+               /* Fell behind? */
+               if (uhci_frame_before_eq(frame, next)) {
+
+                       /* USB_ISO_ASAP: Round up to the first available slot */
+                       if (urb->transfer_flags & URB_ISO_ASAP)
+                               frame += (next - frame + qh->period - 1) &
+                                               -qh->period;
+
+                       /*
+                        * Not ASAP: Use the next slot in the stream.  If
+                        * the entire URB falls before the threshold, fail.
                         */
-                       uhci_get_current_frame_number(uhci);
-                       if (uhci_frame_before_eq(frame, uhci->frame_number)) {
-                               frame = uhci->frame_number + 1;
-                               frame += ((qh->phase - frame) &
-                                       (qh->period - 1));
-                       }
-               }       /* Otherwise pick up where the last URB leaves off */
-               urb->start_frame = frame;
+                       else if (!uhci_frame_before_eq(next,
+                                       frame + (urb->number_of_packets - 1) *
+                                               qh->period))
+                               return -EXDEV;
+               }
        }
 
        /* Make sure we won't have to go too far into the future */
        if (uhci_frame_before_eq(uhci->last_iso_frame + UHCI_NUMFRAMES,
-                       urb->start_frame + urb->number_of_packets *
-                               urb->interval))
+                       frame + urb->number_of_packets * urb->interval))
                return -EFBIG;
+       urb->start_frame = frame;
 
        status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
        destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);