[PATCH] UHCI: store the period in the queue header
[safe/jmp/linux-2.6] / drivers / usb / host / uhci-q.c
index 96ce4c8..7acc234 100644 (file)
@@ -763,6 +763,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
        wmb();
        qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE);
        qh->dummy_td = td;
+       qh->period = urb->interval;
 
        usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
                        usb_pipeout(urb->pipe), toggle);
@@ -790,14 +791,30 @@ static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
        return ret;
 }
 
-static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
+static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
                struct uhci_qh *qh)
 {
+       int exponent;
+
        /* USB 1.1 interrupt transfers only involve one packet per interval.
         * Drivers can submit URBs of any length, but longer ones will need
         * multiple intervals to complete.
         */
-       qh->skel = uhci->skelqh[__interval_to_skel(urb->interval)];
+
+       /* Figure out which power-of-two queue to use */
+       for (exponent = 7; exponent >= 0; --exponent) {
+               if ((1 << exponent) <= urb->interval)
+                       break;
+       }
+       if (exponent < 0)
+               return -EINVAL;
+       urb->interval = 1 << exponent;
+
+       if (qh->period == 0)
+               qh->skel = uhci->skelqh[UHCI_SKEL_INDEX(exponent)];
+       else if (qh->period != urb->interval)
+               return -EINVAL;         /* Can't change the period */
+
        return uhci_submit_common(uhci, urb, qh);
 }
 
@@ -937,31 +954,50 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
        unsigned long destination, status;
        struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
 
-       if (urb->number_of_packets > 900)       /* 900? Why? */
+       /* Values must not be too big (could overflow below) */
+       if (urb->interval >= UHCI_NUMFRAMES ||
+                       urb->number_of_packets >= UHCI_NUMFRAMES)
                return -EFBIG;
 
-       status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
-       destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+       /* Check the period and figure out the starting frame number */
+       uhci_get_current_frame_number(uhci);
+       if (qh->period == 0) {
+               if (urb->transfer_flags & URB_ISO_ASAP) {
+                       urb->start_frame = uhci->frame_number + 10;
+               } else {
+                       i = urb->start_frame - uhci->frame_number;
+                       if (i <= 0 || i >= UHCI_NUMFRAMES)
+                               return -EINVAL;
+               }
+       } else if (qh->period != urb->interval) {
+               return -EINVAL;         /* Can't change the period */
 
-       /* Figure out the starting frame number */
-       if (urb->transfer_flags & URB_ISO_ASAP) {
+       } else {        /* Pick up where the last URB leaves off */
                if (list_empty(&qh->queue)) {
-                       uhci_get_current_frame_number(uhci);
-                       urb->start_frame = (uhci->frame_number + 10);
-
-               } else {                /* Go right after the last one */
-                       struct urb *last_urb;
+                       frame = uhci->frame_number + 10;
+               } else {
+                       struct urb *lurb;
 
-                       last_urb = list_entry(qh->queue.prev,
+                       lurb = list_entry(qh->queue.prev,
                                        struct urb_priv, node)->urb;
-                       urb->start_frame = (last_urb->start_frame +
-                                       last_urb->number_of_packets *
-                                       last_urb->interval);
+                       frame = lurb->start_frame +
+                                       lurb->number_of_packets *
+                                       lurb->interval;
                }
-       } else {
+               if (urb->transfer_flags & URB_ISO_ASAP)
+                       urb->start_frame = frame;
                /* FIXME: Sanity check */
        }
 
+       /* Make sure we won't have to go too far into the future */
+       if (uhci_frame_before_eq(uhci->frame_number + UHCI_NUMFRAMES,
+                       urb->start_frame + urb->number_of_packets *
+                               urb->interval))
+               return -EFBIG;
+
+       status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
+       destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe);
+
        for (i = 0; i < urb->number_of_packets; i++) {
                td = uhci_alloc_td(uhci);
                if (!td)
@@ -978,6 +1014,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
        td->status |= __constant_cpu_to_le32(TD_CTRL_IOC);
 
        qh->skel = uhci->skel_iso_qh;
+       qh->period = urb->interval;
 
        /* Add the TDs to the frame list */
        frame = urb->start_frame;
@@ -1206,6 +1243,7 @@ __acquires(uhci->lock)
                uhci_unlink_qh(uhci, qh);
 
                /* Bandwidth stuff not yet implemented */
+               qh->period = 0;
        }
 }