V4L/DVB (4236): Rearrange things in pvrusb2 driver in preparation for using cx2341x...
[powerpc.git] / drivers / usb / host / uhci-q.c
index 7acc234..c9d72ac 100644 (file)
@@ -64,16 +64,30 @@ static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
                urbp->fsbr = 1;
 }
 
-static void uhci_qh_wants_fsbr(struct uhci_hcd *uhci, struct uhci_qh *qh)
+static void uhci_urbp_wants_fsbr(struct uhci_hcd *uhci, struct urb_priv *urbp)
 {
-       struct urb_priv *urbp =
-                       list_entry(qh->queue.next, struct urb_priv, node);
-
        if (urbp->fsbr) {
-               uhci->fsbr_jiffies = jiffies;
+               uhci->fsbr_is_wanted = 1;
                if (!uhci->fsbr_is_on)
                        uhci_fsbr_on(uhci);
+               else if (uhci->fsbr_expiring) {
+                       uhci->fsbr_expiring = 0;
+                       del_timer(&uhci->fsbr_timer);
+               }
+       }
+}
+
+static void uhci_fsbr_timeout(unsigned long _uhci)
+{
+       struct uhci_hcd *uhci = (struct uhci_hcd *) _uhci;
+       unsigned long flags;
+
+       spin_lock_irqsave(&uhci->lock, flags);
+       if (uhci->fsbr_expiring) {
+               uhci->fsbr_expiring = 0;
+               uhci_fsbr_off(uhci);
        }
+       spin_unlock_irqrestore(&uhci->lock, flags);
 }
 
 
@@ -184,6 +198,24 @@ static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci,
        td->frame = -1;
 }
 
+static inline void uhci_remove_tds_from_frame(struct uhci_hcd *uhci,
+               unsigned int framenum)
+{
+       struct uhci_td *ftd, *ltd;
+
+       framenum &= (UHCI_NUMFRAMES - 1);
+
+       ftd = uhci->frame_cpu[framenum];
+       if (ftd) {
+               ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list);
+               uhci->frame[framenum] = ltd->link;
+               uhci->frame_cpu[framenum] = NULL;
+
+               while (!list_empty(&ftd->fl_list))
+                       list_del_init(ftd->fl_list.prev);
+       }
+}
+
 /*
  * Remove all the TDs for an Isochronous URB from the frame list
  */
@@ -269,7 +301,7 @@ static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh,
        if (qh->type == USB_ENDPOINT_XFER_ISOC) {
                ret = (uhci->frame_number + uhci->is_stopped !=
                                qh->unlink_frame);
-               return ret;
+               goto done;
        }
 
        /* If the URB isn't first on its queue, adjust the link pointer
@@ -286,24 +318,26 @@ static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh,
                td = list_entry(urbp->td_list.prev, struct uhci_td,
                                list);
                ptd->link = td->link;
-               return ret;
+               goto done;
        }
 
        /* If the QH element pointer is UHCI_PTR_TERM then then currently
         * executing URB has already been unlinked, so this one isn't it. */
        if (qh_element(qh) == UHCI_PTR_TERM)
-               return ret;
+               goto done;
        qh->element = UHCI_PTR_TERM;
 
        /* Control pipes have to worry about toggles */
        if (qh->type == USB_ENDPOINT_XFER_CONTROL)
-               return ret;
+               goto done;
 
        /* Save the next toggle value */
        WARN_ON(list_empty(&urbp->td_list));
        td = list_entry(urbp->td_list.next, struct uhci_td, list);
        qh->needs_fixup = 1;
        qh->initial_toggle = uhci_toggle(td_token(td));
+
+done:
        return ret;
 }
 
@@ -523,7 +557,6 @@ static int uhci_map_status(int status, int dir_out)
                return -ENOSR;
        if (status & TD_CTRL_STALLED)                   /* Stalled */
                return -EPIPE;
-       WARN_ON(status & TD_CTRL_ACTIVE);               /* Active */
        return 0;
 }
 
@@ -893,7 +926,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
                                        uhci_packetout(td_token(td)));
                        if ((debug == 1 && ret != -EPIPE) || debug > 1) {
                                /* Some debugging code */
-                               dev_dbg(uhci_dev(uhci),
+                               dev_dbg(&urb->dev->dev,
                                                "%s: failed with status %x\n",
                                                __FUNCTION__, status);
 
@@ -960,12 +993,12 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
                return -EFBIG;
 
        /* 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) {
+                       uhci_get_current_frame_number(uhci);
                        urb->start_frame = uhci->frame_number + 10;
                } else {
-                       i = urb->start_frame - uhci->frame_number;
+                       i = urb->start_frame - uhci->last_iso_frame;
                        if (i <= 0 || i >= UHCI_NUMFRAMES)
                                return -EINVAL;
                }
@@ -974,7 +1007,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
 
        } else {        /* Pick up where the last URB leaves off */
                if (list_empty(&qh->queue)) {
-                       frame = uhci->frame_number + 10;
+                       frame = qh->iso_frame;
                } else {
                        struct urb *lurb;
 
@@ -986,11 +1019,12 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
                }
                if (urb->transfer_flags & URB_ISO_ASAP)
                        urb->start_frame = frame;
-               /* FIXME: Sanity check */
+               else if (urb->start_frame != frame)
+                       return -EINVAL;
        }
 
        /* Make sure we won't have to go too far into the future */
-       if (uhci_frame_before_eq(uhci->frame_number + UHCI_NUMFRAMES,
+       if (uhci_frame_before_eq(uhci->last_iso_frame + UHCI_NUMFRAMES,
                        urb->start_frame + urb->number_of_packets *
                                urb->interval))
                return -EFBIG;
@@ -1020,7 +1054,13 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
        frame = urb->start_frame;
        list_for_each_entry(td, &urbp->td_list, list) {
                uhci_insert_td_in_frame_list(uhci, td, frame);
-               frame += urb->interval;
+               frame += qh->period;
+       }
+
+       if (list_empty(&qh->queue)) {
+               qh->iso_packet_desc = &urb->iso_frame_desc[0];
+               qh->iso_frame = urb->start_frame;
+               qh->iso_status = 0;
        }
 
        return 0;
@@ -1028,37 +1068,44 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb,
 
 static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb)
 {
-       struct uhci_td *td;
-       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-       int status;
-       int i, ret = 0;
-
-       urb->actual_length = urb->error_count = 0;
+       struct uhci_td *td, *tmp;
+       struct urb_priv *urbp = urb->hcpriv;
+       struct uhci_qh *qh = urbp->qh;
 
-       i = 0;
-       list_for_each_entry(td, &urbp->td_list, list) {
+       list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
+               unsigned int ctrlstat;
+               int status;
                int actlength;
-               unsigned int ctrlstat = td_status(td);
 
-               if (ctrlstat & TD_CTRL_ACTIVE)
+               if (uhci_frame_before_eq(uhci->cur_iso_frame, qh->iso_frame))
                        return -EINPROGRESS;
 
-               actlength = uhci_actual_length(ctrlstat);
-               urb->iso_frame_desc[i].actual_length = actlength;
-               urb->actual_length += actlength;
+               uhci_remove_tds_from_frame(uhci, qh->iso_frame);
+
+               ctrlstat = td_status(td);
+               if (ctrlstat & TD_CTRL_ACTIVE) {
+                       status = -EXDEV;        /* TD was added too late? */
+               } else {
+                       status = uhci_map_status(uhci_status_bits(ctrlstat),
+                                       usb_pipeout(urb->pipe));
+                       actlength = uhci_actual_length(ctrlstat);
+
+                       urb->actual_length += actlength;
+                       qh->iso_packet_desc->actual_length = actlength;
+                       qh->iso_packet_desc->status = status;
+               }
 
-               status = uhci_map_status(uhci_status_bits(ctrlstat),
-                               usb_pipeout(urb->pipe));
-               urb->iso_frame_desc[i].status = status;
                if (status) {
                        urb->error_count++;
-                       ret = status;
+                       qh->iso_status = status;
                }
 
-               i++;
+               uhci_remove_td_from_urbp(td);
+               uhci_free_td(uhci, td);
+               qh->iso_frame += qh->period;
+               ++qh->iso_packet_desc;
        }
-
-       return ret;
+       return qh->iso_status;
 }
 
 static int uhci_urb_enqueue(struct usb_hcd *hcd,
@@ -1119,6 +1166,7 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd,
                }
                break;
        case USB_ENDPOINT_XFER_ISOC:
+               urb->error_count = 0;
                bustime = usb_check_bandwidth(urb->dev, urb);
                if (bustime < 0) {
                        ret = bustime;
@@ -1143,7 +1191,7 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd,
         * queue isn't stopped. */
        if (qh->queue.next == &urbp->node && !qh->is_stopped) {
                uhci_activate_qh(uhci, qh);
-               uhci_qh_wants_fsbr(uhci, qh);
+               uhci_urbp_wants_fsbr(uhci, urbp);
        }
        goto done;
 
@@ -1200,9 +1248,18 @@ __acquires(uhci->lock)
 {
        struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
 
-       /* Isochronous TDs get unlinked directly from the frame list */
-       if (qh->type == USB_ENDPOINT_XFER_ISOC)
-               uhci_unlink_isochronous_tds(uhci, urb);
+       /* When giving back the first URB in an Isochronous queue,
+        * reinitialize the QH's iso-related members for the next URB. */
+       if (qh->type == USB_ENDPOINT_XFER_ISOC &&
+                       urbp->node.prev == &qh->queue &&
+                       urbp->node.next != &qh->queue) {
+               struct urb *nurb = list_entry(urbp->node.next,
+                               struct urb_priv, node)->urb;
+
+               qh->iso_packet_desc = &nurb->iso_frame_desc[0];
+               qh->iso_frame = nurb->start_frame;
+               qh->iso_status = 0;
+       }
 
        /* Take the URB off the QH's queue.  If the queue is now empty,
         * this is a perfect time for a toggle fixup. */
@@ -1363,7 +1420,7 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
        unsigned status;
 
        if (qh->type == USB_ENDPOINT_XFER_ISOC)
-               return ret;
+               goto done;
 
        /* Treat an UNLINKING queue as though it hasn't advanced.
         * This is okay because reactivation will treat it as though
@@ -1386,21 +1443,24 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
                        /* We're okay, the queue has advanced */
                        qh->wait_expired = 0;
                        qh->advance_jiffies = jiffies;
-                       return ret;
+                       goto done;
                }
                ret = 0;
        }
 
        /* The queue hasn't advanced; check for timeout */
-       if (!qh->wait_expired && time_after(jiffies,
-                       qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
+       if (qh->wait_expired)
+               goto done;
+
+       if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
 
                /* Detect the Intel bug and work around it */
                if (qh->post_td && qh_element(qh) ==
                                cpu_to_le32(qh->post_td->dma_handle)) {
                        qh->element = qh->post_td->link;
                        qh->advance_jiffies = jiffies;
-                       return 1;
+                       ret = 1;
+                       goto done;
                }
 
                qh->wait_expired = 1;
@@ -1411,7 +1471,14 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh)
                 * starts moving again. */
                if (urbp && urbp->fsbr && !(status & TD_CTRL_IOC))
                        uhci_unlink_qh(uhci, qh);
+
+       } else {
+               /* Unmoving but not-yet-expired queues keep FSBR alive */
+               if (urbp)
+                       uhci_urbp_wants_fsbr(uhci, urbp);
        }
+
+done:
        return ret;
 }
 
@@ -1431,9 +1498,11 @@ static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs)
        uhci->scan_in_progress = 1;
 rescan:
        uhci->need_rescan = 0;
+       uhci->fsbr_is_wanted = 0;
 
        uhci_clear_next_interrupt(uhci);
        uhci_get_current_frame_number(uhci);
+       uhci->cur_iso_frame = uhci->frame_number;
 
        /* Go through all the QH queues and process the URBs in each one */
        for (i = 0; i < UHCI_NUM_SKELQH - 1; ++i) {
@@ -1445,19 +1514,24 @@ rescan:
 
                        if (uhci_advance_check(uhci, qh)) {
                                uhci_scan_qh(uhci, qh, regs);
-                               if (qh->state == QH_STATE_ACTIVE)
-                                       uhci_qh_wants_fsbr(uhci, qh);
+                               if (qh->state == QH_STATE_ACTIVE) {
+                                       uhci_urbp_wants_fsbr(uhci,
+       list_entry(qh->queue.next, struct urb_priv, node));
+                               }
                        }
                }
        }
 
+       uhci->last_iso_frame = uhci->cur_iso_frame;
        if (uhci->need_rescan)
                goto rescan;
        uhci->scan_in_progress = 0;
 
-       if (uhci->fsbr_is_on && time_after(jiffies,
-                       uhci->fsbr_jiffies + FSBR_OFF_DELAY))
-               uhci_fsbr_off(uhci);
+       if (uhci->fsbr_is_on && !uhci->fsbr_is_wanted &&
+                       !uhci->fsbr_expiring) {
+               uhci->fsbr_expiring = 1;
+               mod_timer(&uhci->fsbr_timer, jiffies + FSBR_OFF_DELAY);
+       }
 
        if (list_empty(&uhci->skel_unlink_qh->node))
                uhci_clear_next_interrupt(uhci);