USB: optimize port debouncing during hub activation
authorAlan Stern <stern@rowland.harvard.edu>
Mon, 28 Apr 2008 15:07:07 +0000 (11:07 -0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 21 Jul 2008 22:15:52 +0000 (15:15 -0700)
This patch (as1082) makes a small optimization to the way the hub
driver carries out port debouncing immediately after a hub is
activated (i.e., initialized, reset, or resumed).  If any port-change
statuses are observed, the code will delay for a minimal debounce
period -- thereby making a good start at debouncing all the ports at
once.

If this wasn't sufficient then khubd will debounce any port that still
requires attention.  But in most cases it should suffice; it's rare
for a device to need more than a minimal debounce delay.  (In the
cases of hub initialization or reset even that is most likely not
needed, since any devices plugged in at such times have probably been
attached for a while.)

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/hub.c

index 8ea095e..bc80168 100644 (file)
@@ -130,6 +130,10 @@ MODULE_PARM_DESC(use_both_schemes,
 DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
 EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
 
+#define HUB_DEBOUNCE_TIMEOUT   1500
+#define HUB_DEBOUNCE_STEP        25
+#define HUB_DEBOUNCE_STABLE     100
+
 
 static inline char *portspeed(int portstatus)
 {
@@ -643,6 +647,7 @@ static void hub_restart(struct usb_hub *hub, enum hub_activation_type type)
 {
        struct usb_device *hdev = hub->hdev;
        int port1;
+       bool need_debounce_delay = false;
 
        /* Check each port and set hub->change_bits to let khubd know
         * which ports need attention.
@@ -673,6 +678,18 @@ static void hub_restart(struct usb_hub *hub, enum hub_activation_type type)
                        portstatus &= ~USB_PORT_STAT_ENABLE;
                }
 
+               /* Clear status-change flags; we'll debounce later */
+               if (portchange & USB_PORT_STAT_C_CONNECTION) {
+                       need_debounce_delay = true;
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_CONNECTION);
+               }
+               if (portchange & USB_PORT_STAT_C_ENABLE) {
+                       need_debounce_delay = true;
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_ENABLE);
+               }
+
                if (!udev || udev->state == USB_STATE_NOTATTACHED) {
                        /* Tell khubd to disconnect the device or
                         * check for a new connection
@@ -702,6 +719,16 @@ static void hub_restart(struct usb_hub *hub, enum hub_activation_type type)
                }
        }
 
+       /* If no port-status-change flags were set, we don't need any
+        * debouncing.  If flags were set we can try to debounce the
+        * ports all at once right now, instead of letting khubd do them
+        * one at a time later on.
+        *
+        * If any port-status changes do occur during this delay, khubd
+        * will see them later and handle them normally.
+        */
+       if (need_debounce_delay)
+               msleep(HUB_DEBOUNCE_STABLE);
        hub_activate(hub);
 }
 
@@ -2211,11 +2238,6 @@ static inline int remote_wakeup(struct usb_device *udev)
  * every 25ms for transient disconnects.  When the port status has been
  * unchanged for 100ms it returns the port status.
  */
-
-#define HUB_DEBOUNCE_TIMEOUT   1500
-#define HUB_DEBOUNCE_STEP        25
-#define HUB_DEBOUNCE_STABLE     100
-
 static int hub_port_debounce(struct usb_hub *hub, int port1)
 {
        int ret;