Flynn:

As promised, here is a patch for 2.6.17-rc1.  It should restore the old 
behavior and power-consumption for your UHCI controllers.  Please try it 
out and let me know how well it works for you.

Alan Stern



Index: linux-2.6.17-rc1/drivers/usb/host/uhci-q.c
===================================================================
--- linux-2.6.17-rc1.orig/drivers/usb/host/uhci-q.c
+++ linux-2.6.17-rc1/drivers/usb/host/uhci-q.c
@@ -13,7 +13,7 @@
  * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
  *               support from usb-ohci.c by Adam Richter, [EMAIL PROTECTED]).
  * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
- * (C) Copyright 2004-2005 Alan Stern, [EMAIL PROTECTED]
+ * (C) Copyright 2004-2006 Alan Stern, [EMAIL PROTECTED]
  */
 
 static void uhci_free_pending_tds(struct uhci_hcd *uhci);
@@ -161,6 +161,7 @@ static struct uhci_qh *uhci_alloc_qh(str
        if (!qh)
                return NULL;
 
+       memset(qh, 0, sizeof(*qh));
        qh->dma_handle = dma_handle;
 
        qh->element = UHCI_PTR_TERM;
@@ -182,7 +183,6 @@ static struct uhci_qh *uhci_alloc_qh(str
 
        } else {                /* Skeleton QH */
                qh->state = QH_STATE_ACTIVE;
-               qh->udev = NULL;
        }
        return qh;
 }
@@ -207,7 +207,6 @@ static void uhci_free_qh(struct uhci_hcd
 static void uhci_save_toggle(struct uhci_qh *qh, struct urb *urb)
 {
        struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
-       struct uhci_td *td;
 
        /* If the QH element pointer is UHCI_PTR_TERM then then currently
         * executing URB has already been unlinked, so this one isn't it. */
@@ -221,16 +220,8 @@ static void uhci_save_toggle(struct uhci
                        usb_pipetype(urb->pipe) == PIPE_INTERRUPT))
                return;
 
-       /* Find the first active TD; that's the device's toggle state */
-       list_for_each_entry(td, &urbp->td_list, list) {
-               if (td_status(td) & TD_CTRL_ACTIVE) {
-                       qh->needs_fixup = 1;
-                       qh->initial_toggle = uhci_toggle(td_token(td));
-                       return;
-               }
-       }
-
-       WARN_ON(1);
+       qh->needs_fixup = 1;
+       qh->initial_toggle = uhci_toggle(td_token(qh->next_td));
 }
 
 /*
@@ -302,9 +293,13 @@ static void uhci_activate_qh(struct uhci
                struct uhci_td *td = list_entry(urbp->td_list.next,
                                struct uhci_td, list);
 
+               qh->next_td = td;
                qh->element = cpu_to_le32(td->dma_handle);
        }
 
+       qh->advance_jiffies = jiffies;
+       qh->wait_expired = 0;
+
        if (qh->state == QH_STATE_ACTIVE)
                return;
        qh->state = QH_STATE_ACTIVE;
@@ -439,28 +434,48 @@ static void uhci_free_urb_priv(struct uh
        kmem_cache_free(uhci_up_cachep, urbp);
 }
 
-static void uhci_inc_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+
+/*
+ * Full-Speed Bandwidth Reclamation (FSBR).
+ * We turn on FSBR whenever a queue that wants it is advancing,
+ * and leave it on for a short time thereafter.
+ * Invariant: If uhci->fsbr_wanted is true then FSBR is on.
+ */
+static inline void uhci_fsbr_on(struct uhci_hcd *uhci)
 {
-       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+       uhci->fsbr_is_on = 1;
+       uhci->skel_term_qh->link = cpu_to_le32(
+                       uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
+}
 
-       if ((!(urb->transfer_flags & URB_NO_FSBR)) && !urbp->fsbr) {
+static inline void uhci_fsbr_off(struct uhci_hcd *uhci)
+{
+       uhci->fsbr_is_on = 0;
+       uhci->skel_term_qh->link = UHCI_PTR_TERM;
+}
+
+static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+{
+       struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
+
+       if (!(urb->transfer_flags & URB_NO_FSBR))
                urbp->fsbr = 1;
-               if (!uhci->fsbr++ && !uhci->fsbrtimeout)
-                       uhci->skel_term_qh->link = 
cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
-       }
 }
 
-static void uhci_dec_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+static void uhci_qh_wants_fsbr(struct uhci_hcd *uhci, struct uhci_qh *qh)
 {
-       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+       struct urb_priv *urbp =
+                       list_entry(qh->queue.next, struct urb_priv, node);
 
-       if ((!(urb->transfer_flags & URB_NO_FSBR)) && urbp->fsbr) {
-               urbp->fsbr = 0;
-               if (!--uhci->fsbr)
-                       uhci->fsbrtimeout = jiffies + FSBR_DELAY;
+       if (urbp->fsbr) {
+               uhci->fsbr_wanted = 1;
+               uhci->fsbr_jiffies = jiffies;
+               if (!uhci->fsbr_is_on)
+                       uhci_fsbr_on(uhci);
        }
 }
 
+
 /*
  * Map status to standard result codes
  *
@@ -606,7 +621,7 @@ static int uhci_submit_control(struct uh
                qh->skel = uhci->skel_ls_control_qh;
        else {
                qh->skel = uhci->skel_fs_control_qh;
-               uhci_inc_fsbr(uhci, urb);
+               uhci_add_fsbr(uhci, urb);
        }
        return 0;
 
@@ -633,6 +648,7 @@ static int usb_control_retrigger_status(
        urbp->short_transfer = 1;
 
        td = list_entry(urbp->td_list.prev, struct uhci_td, list);
+       urbp->qh->next_td = td;
        urbp->qh->element = cpu_to_le32(td->dma_handle);
 
        return -EINPROGRESS;
@@ -661,8 +677,10 @@ static int uhci_result_control(struct uh
        /* The first TD is the SETUP stage, check the status, but skip */
        /*  the count */
        status = uhci_status_bits(td_status(td));
-       if (status & TD_CTRL_ACTIVE)
+       if (status & TD_CTRL_ACTIVE) {
+               urbp->qh->next_td = td;
                return -EINPROGRESS;
+       }
 
        if (status)
                goto td_error;
@@ -677,8 +695,10 @@ static int uhci_result_control(struct uh
 
                ctrlstat = td_status(td);
                status = uhci_status_bits(ctrlstat);
-               if (status & TD_CTRL_ACTIVE)
+               if (status & TD_CTRL_ACTIVE) {
+                       urbp->qh->next_td = td;
                        return -EINPROGRESS;
+               }
 
                urb->actual_length += uhci_actual_length(ctrlstat);
 
@@ -714,8 +734,10 @@ status_stage:
 #endif
 
        status = uhci_status_bits(status);
-       if (status & TD_CTRL_ACTIVE)
+       if (status & TD_CTRL_ACTIVE) {
+               urbp->qh->next_td = td;
                return -EINPROGRESS;
+       }
 
        if (status)
                goto td_error;
@@ -878,8 +900,10 @@ static int uhci_result_common(struct uhc
                unsigned int ctrlstat = td_status(td);
 
                status = uhci_status_bits(ctrlstat);
-               if (status & TD_CTRL_ACTIVE)
+               if (status & TD_CTRL_ACTIVE) {
+                       urbp->qh->next_td = td;
                        return -EINPROGRESS;
+               }
 
                urb->actual_length += uhci_actual_length(ctrlstat);
 
@@ -954,7 +978,7 @@ static inline int uhci_submit_bulk(struc
        qh->skel = uhci->skel_bulk_qh;
        ret = uhci_submit_common(uhci, urb, qh);
        if (ret == 0)
-               uhci_inc_fsbr(uhci, urb);
+               uhci_add_fsbr(uhci, urb);
        return ret;
 }
 
@@ -1147,8 +1171,12 @@ static int uhci_urb_enqueue(struct usb_h
        /* If the new URB is the first and only one on this QH then either
         * the QH is new and idle or else it's unlinked and waiting to
         * become idle, so we can activate it right away. */
-       if (qh->queue.next == &urbp->node)
+       if (qh->queue.next == &urbp->node) {
+               qh->next_td = list_entry(urbp->td_list.next,
+                               struct uhci_td, list);
+               uhci_qh_wants_fsbr(uhci, qh);
                uhci_activate_qh(uhci, qh);
+       }
        goto done;
 
 err_submit_failed:
@@ -1221,7 +1249,6 @@ __acquires(uhci->lock)
                qh->needs_fixup = 0;
        }
 
-       uhci_dec_fsbr(uhci, urb);       /* Safe since it checks */
        uhci_free_urb_priv(uhci, urbp);
 
        switch (usb_pipetype(urb->pipe)) {
@@ -1348,6 +1375,43 @@ static void uhci_free_pending_tds(struct
 }
 
 /*
+ * Check for queues that have made some forward progress.
+ */
+static int uhci_qh_has_advanced(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+       unsigned status;
+
+restart:
+       status = td_status(qh->next_td);
+       if (!(status & TD_CTRL_ACTIVE)) {
+
+               /* We're okay, the queue has advanced */
+               qh->wait_expired = 0;
+               qh->advance_jiffies = jiffies;
+               return 1;
+       }
+
+       /* The queue hasn't moved; check for timeout */
+       if (!qh->wait_expired && time_after(jiffies,
+                       qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
+               __le32 old_status, new_status, hw_status;
+
+               /* Let us know when the queue starts moving again.
+                * Do this carefully because the hardware might
+                * be updating td->status right now. */
+               old_status = cpu_to_le32(status);
+               new_status = cpu_to_le32(status | TD_CTRL_IOC);
+               hw_status = cmpxchg(&qh->next_td->status,
+                               old_status, new_status);
+               if (hw_status != old_status)
+                       goto restart;
+
+               qh->wait_expired = 1;
+       }
+       return 0;
+}
+
+/*
  * Process events in the schedule, but only in one thread at a time
  */
 static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs)
@@ -1361,7 +1425,8 @@ static void uhci_scan_schedule(struct uh
                return;
        }
        uhci->scan_in_progress = 1;
- rescan:
+       uhci->fsbr_wanted = 0;
+rescan:
        uhci->need_rescan = 0;
 
        uhci_clear_next_interrupt(uhci);
@@ -1377,7 +1442,14 @@ static void uhci_scan_schedule(struct uh
                while ((qh = uhci->next_qh) != uhci->skelqh[i]) {
                        uhci->next_qh = list_entry(qh->node.next,
                                        struct uhci_qh, node);
-                       uhci_scan_qh(uhci, qh, regs);
+
+                       if (qh->state != QH_STATE_ACTIVE ||
+                                       i == 1 ||       /* skel_iso_qh */
+                                       uhci_qh_has_advanced(uhci, qh)) {
+                               uhci_scan_qh(uhci, qh, regs);
+                               if (qh->state == QH_STATE_ACTIVE)
+                                       uhci_qh_wants_fsbr(uhci, qh);
+                       }
                }
        }
 
@@ -1389,21 +1461,15 @@ static void uhci_scan_schedule(struct uh
        if (uhci->is_stopped)
                uhci_free_pending_tds(uhci);
 
+       if (uhci->fsbr_wanted)
+               uhci->fsbr_jiffies = jiffies;
+       else if (uhci->fsbr_is_on && time_after(jiffies, uhci->fsbr_jiffies +
+                       FSBR_OFF_DELAY))
+               uhci_fsbr_off(uhci);
+
        if (list_empty(&uhci->td_remove_list) &&
                        list_empty(&uhci->skel_unlink_qh->node))
                uhci_clear_next_interrupt(uhci);
        else
                uhci_set_next_interrupt(uhci);
 }
-
-static void check_fsbr(struct uhci_hcd *uhci)
-{
-       /* For now, don't scan URBs for FSBR timeouts.
-        * Add it back in later... */
-
-       /* Really disable FSBR */
-       if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies, 
uhci->fsbrtimeout)) {
-               uhci->fsbrtimeout = 0;
-               uhci->skel_term_qh->link = UHCI_PTR_TERM;
-       }
-}
Index: linux-2.6.17-rc1/drivers/usb/host/uhci-hub.c
===================================================================
--- linux-2.6.17-rc1.orig/drivers/usb/host/uhci-hub.c
+++ linux-2.6.17-rc1/drivers/usb/host/uhci-hub.c
@@ -173,7 +173,6 @@ static int uhci_hub_status_data(struct u
        uhci_scan_schedule(uhci, NULL);
        if (uhci->hc_inaccessible)
                goto done;
-       check_fsbr(uhci);
        uhci_check_ports(uhci);
 
        status = get_hub_status_data(uhci, buf);
Index: linux-2.6.17-rc1/drivers/usb/host/uhci-hcd.c
===================================================================
--- linux-2.6.17-rc1.orig/drivers/usb/host/uhci-hcd.c
+++ linux-2.6.17-rc1/drivers/usb/host/uhci-hcd.c
@@ -87,15 +87,6 @@ static void suspend_rh(struct uhci_hcd *
 static void wakeup_rh(struct uhci_hcd *uhci);
 static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
 
-/* If a transfer is still active after this much time, turn off FSBR */
-#define IDLE_TIMEOUT   msecs_to_jiffies(50)
-#define FSBR_DELAY     msecs_to_jiffies(50)
-
-/* When we timeout an idle transfer for FSBR, we'll switch it over to */
-/* depth first traversal. We'll do it in groups of this number of TDs */
-/* to make sure it doesn't hog all of the bandwidth */
-#define DEPTH_INTERVAL 5
-
 #include "uhci-debug.c"
 #include "uhci-q.c"
 #include "uhci-hub.c"
@@ -258,6 +249,8 @@ __acquires(uhci->lock)
        uhci_to_hcd(uhci)->poll_rh = !int_enable;
 
        uhci_scan_schedule(uhci, NULL);
+       uhci->fsbr_wanted = 0;
+       uhci_fsbr_off(uhci);
 }
 
 static void start_rh(struct uhci_hcd *uhci)
@@ -490,9 +483,6 @@ static int uhci_start(struct usb_hcd *hc
 
        hcd->uses_new_polling = 1;
 
-       uhci->fsbr = 0;
-       uhci->fsbrtimeout = 0;
-
        spin_lock_init(&uhci->lock);
 
        INIT_LIST_HEAD(&uhci->td_remove_list);
Index: linux-2.6.17-rc1/drivers/usb/host/uhci-debug.c
===================================================================
--- linux-2.6.17-rc1.orig/drivers/usb/host/uhci-debug.c
+++ linux-2.6.17-rc1/drivers/usb/host/uhci-debug.c
@@ -152,8 +152,10 @@ static int uhci_show_qh(struct uhci_qh *
        if (len < 80 * 6)
                return 0;
 
-       out += sprintf(out, "%*s[%p] link (%08x) element (%08x)\n", space, "",
-                       qh, le32_to_cpu(qh->link), le32_to_cpu(element));
+       out += sprintf(out, "%*s[%p] link (%08x) element (%08x)"
+                       " next_td [%p]\n", space, "",
+                       qh, le32_to_cpu(qh->link), le32_to_cpu(element),
+                       qh->next_td);
 
        if (element & UHCI_PTR_QH)
                out += sprintf(out, "%*s  Element points to QH (bug?)\n", 
space, "");
@@ -275,7 +277,7 @@ static int uhci_show_status(struct uhci_
        unsigned short portsc1, portsc2;
 
        /* Try to make sure there's enough memory */
-       if (len < 80 * 6)
+       if (len < 80 * 8)
                return 0;
 
        usbcmd    = inw(io_addr + 0);
@@ -314,6 +316,7 @@ static int uhci_show_status(struct uhci_
        out += sprintf(out, "  sof       =       %02x\n", sof);
        out += uhci_show_sc(1, portsc1, out, len - (out - buf));
        out += uhci_show_sc(2, portsc2, out, len - (out - buf));
+       out += sprintf(out, "FSBR: %d\n", uhci->fsbr_is_on);
 
        return out - buf;
 }
Index: linux-2.6.17-rc1/drivers/usb/host/uhci-hcd.h
===================================================================
--- linux-2.6.17-rc1.orig/drivers/usb/host/uhci-hcd.h
+++ linux-2.6.17-rc1/drivers/usb/host/uhci-hcd.h
@@ -84,6 +84,13 @@
 #define CAN_SCHEDULE_FRAMES    1000    /* how far in the future frames
                                         * can be scheduled */
 
+/* When no queues need Full-Speed Bandwidth Reclamation,
+ * delay this long before turning FSBR off */
+#define FSBR_OFF_DELAY         msecs_to_jiffies(400)
+
+/* If a queue hasn't advanced after this much time, assume it is stuck */
+#define QH_WAIT_TIMEOUT                msecs_to_jiffies(300)
+
 
 /*
  *     Queue Headers
@@ -129,13 +136,16 @@ struct uhci_qh {
        struct list_head queue;         /* Queue of urbps for this QH */
        struct uhci_qh *skel;           /* Skeleton for this QH */
        struct uhci_td *dummy_td;       /* Dummy TD to end the queue */
+       struct uhci_td *next_td;        /* Next TD to execute */
 
+       unsigned long advance_jiffies;  /* Time of last queue advance */
        unsigned int unlink_frame;      /* When the QH was unlinked */
        int state;                      /* QH_STATE_xxx; see above */
 
        unsigned int initial_toggle:1;  /* Endpoint's current toggle value */
        unsigned int needs_fixup:1;     /* Must fix the TD toggle values */
        unsigned int is_stopped:1;      /* Queue was stopped by an error */
+       unsigned int wait_expired:1;    /* QH_WAIT_TIMEOUT has expired */
 } __attribute__((aligned(16)));
 
 /*
@@ -396,8 +406,7 @@ struct uhci_hcd {
        __le32 *frame;
        void **frame_cpu;               /* CPU's frame list */
 
-       int fsbr;                       /* Full-speed bandwidth reclamation */
-       unsigned long fsbrtimeout;      /* FSBR delay */
+       unsigned long fsbr_jiffies;     /* Time when FSBR was last wanted */
 
        enum uhci_rh_state rh_state;
        unsigned long auto_stop_time;           /* When to AUTO_STOP */
@@ -412,6 +421,8 @@ struct uhci_hcd {
        unsigned int working_RD:1;              /* Suspended root hub doesn't
                                                   need to be polled */
        unsigned int is_initialized:1;          /* Data structure is usable */
+       unsigned int fsbr_wanted:1;             /* Some URB wants FSBR */
+       unsigned int fsbr_is_on:1;              /* FSBR is turned on */
 
        /* Support for port suspend/resume/reset */
        unsigned long port_c_suspend;           /* Bit-arrays of ports */
@@ -455,8 +466,8 @@ struct urb_priv {
        struct uhci_qh *qh;             /* QH for this URB */
        struct list_head td_list;
 
-       unsigned fsbr : 1;              /* URB turned on FSBR */
-       unsigned short_transfer : 1;    /* URB got a short transfer, no
+       unsigned fsbr:1;                /* URB wants FSBR */
+       unsigned short_transfer:1;      /* URB got a short transfer, no
                                         * need to rescan */
 };
 



-------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to