usb: disable port power control if not supported in wHubCharacteristics
authorDan Williams <dan.j.williams@intel.com>
Wed, 21 May 2014 01:08:12 +0000 (18:08 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 27 May 2014 23:35:09 +0000 (16:35 -0700)
A hub indicates whether it supports per-port power control via the
wHubCharacteristics field in its descriptor.  If it is not supported
a hub will still emulate ClearPortPower(PORT_POWER) requests by
stopping the link state machine.  However, since this does not save
power do not bother suspending.

This also consolidates support checks into a
hub_is_port_power_switchable() helper.

Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/core/hub.c
drivers/usb/core/hub.h
drivers/usb/core/port.c

index 5f43c22ba787971e07ac9fe7ef09b91ea5e95ed5..77b91888abeff6a40be9db62bb92374669e9915c 100644 (file)
@@ -818,8 +818,6 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
        int port1;
        unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
        unsigned delay;
-       u16 wHubCharacteristics =
-                       le16_to_cpu(hub->descriptor->wHubCharacteristics);
 
        /* Enable power on each port.  Some hubs have reserved values
         * of LPSM (> 2) in their descriptors, even though they are
@@ -827,7 +825,7 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
         * but only emulate it.  In all cases, the ports won't work
         * unless we send these messages to the hub.
         */
-       if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2)
+       if (hub_is_port_power_switchable(hub))
                dev_dbg(hub->intfdev, "enabling power on all ports\n");
        else
                dev_dbg(hub->intfdev, "trying to enable port power on "
@@ -4417,8 +4415,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
        struct usb_device *hdev = hub->hdev;
        struct device *hub_dev = hub->intfdev;
        struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
-       unsigned wHubCharacteristics =
-                       le16_to_cpu(hub->descriptor->wHubCharacteristics);
        struct usb_device *udev;
        int status, i;
        unsigned unit_load;
@@ -4503,7 +4499,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                        test_bit(port1, hub->removed_bits)) {
 
                /* maybe switch power back on (e.g. root hub was reset) */
-               if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
+               if (hub_is_port_power_switchable(hub)
                                && !port_is_power_on(hub, portstatus))
                        set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 
index f9b521e601282afbe6091b49178547757e47c727..4bd72dd303d57b4f3f74195262fc48a2d8cd5ae4 100644 (file)
@@ -112,6 +112,16 @@ extern int hub_port_debounce(struct usb_hub *hub, int port1,
 extern int usb_clear_port_feature(struct usb_device *hdev,
                int port1, int feature);
 
+static inline bool hub_is_port_power_switchable(struct usb_hub *hub)
+{
+       __le16 hcs;
+
+       if (!hub)
+               return false;
+       hcs = hub->descriptor->wHubCharacteristics;
+       return (le16_to_cpu(hcs) & HUB_CHAR_LPSM) < HUB_CHAR_NO_LPSM;
+}
+
 static inline int hub_port_debounce_be_connected(struct usb_hub *hub,
                int port1)
 {
index 37647e080599b951c9c29bb141a9eae33fd56e70..168fa6ee3348b85dbacb7458511e5328cc57b882 100644 (file)
@@ -177,12 +177,15 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
 
        pm_runtime_set_active(&port_dev->dev);
 
-       /* It would be dangerous if user space couldn't
-        * prevent usb device from being powered off. So don't
-        * enable port runtime pm if failed to expose port's pm qos.
+       /*
+        * Do not enable port runtime pm if the hub does not support
+        * power switching.  Also, userspace must have final say of
+        * whether a port is permitted to power-off.  Do not enable
+        * runtime pm if we fail to expose pm_qos_no_power_off.
         */
-       if (!dev_pm_qos_expose_flags(&port_dev->dev,
-                       PM_QOS_FLAG_NO_POWER_OFF))
+       if (hub_is_port_power_switchable(hub)
+                       && dev_pm_qos_expose_flags(&port_dev->dev,
+                       PM_QOS_FLAG_NO_POWER_OFF) == 0)
                pm_runtime_enable(&port_dev->dev);
 
        device_enable_async_suspend(&port_dev->dev);