This patch is to add "auto" option to attribute portX/control.
When echo "auto", the port's feature PORT_POWER would be clear
if the port's connect type was mark not-used(connectability and
visibility are both cleared) and with no device attached.

Signed-off-by: Lan Tianyu <tianyu....@intel.com>
---
This patchset is based on the following patchset
http://marc.info/?l=linux-usb&m=134517695007182&w=2
      usb: make usb port a real device
      usb: move children to struct usb_port
      usb/acpi: Bind ACPI node to USB port, not usb_device.
      usb/acpi: Store info on device removability.
      xhci: Handle clear PORT_POWER feature.
      usb/acpi: Use ACPI methods to power off ports.
      usb: Fail a get config when the port is powered off.
      usb : Add sysfs files to control port power.

v2: add "auto" option process in the hub_power_on()
---
 drivers/usb/core/hub.c |   35 +++++++++++++++++++++++++++++------
 1 file changed, 29 insertions(+), 6 deletions(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 3def91e..07c62dd 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -42,6 +42,7 @@
 enum port_power_policy {
        USB_PORT_POWER_ON = 0,
        USB_PORT_POWER_OFF,
+       USB_PORT_POWER_AUTO,
 };
 
 struct usb_port {
@@ -101,6 +102,7 @@ struct usb_hub {
 
 static const char on_string[] = "on";
 static const char off_string[] = "off";
+static const char auto_string[] = "auto";
 static const struct attribute_group *port_dev_group[];
 
 static inline int hub_is_superspeed(struct usb_device *hdev)
@@ -842,6 +844,7 @@ static unsigned hub_power_on(struct usb_hub *hub, bool 
do_delay)
        unsigned delay;
        u16 wHubCharacteristics =
                        le16_to_cpu(hub->descriptor->wHubCharacteristics);
+       struct usb_port *pport;
 
        /* Enable power on each port.  Some hubs have reserved values
         * of LPSM (> 2) in their descriptors, even though they are
@@ -854,13 +857,18 @@ static unsigned hub_power_on(struct usb_hub *hub, bool 
do_delay)
        else
                dev_dbg(hub->intfdev, "trying to enable port power on "
                                "non-switchable hub\n");
-       for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++)
-               if (hub->ports[port1 - 1]->port_power_policy
-                               == USB_PORT_POWER_ON)
-                       set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
-               else
+       for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++) {
+               pport = hub->ports[port1 - 1];
+
+               if (pport->port_power_policy == USB_PORT_POWER_OFF ||
+                               (pport->port_power_policy == USB_PORT_POWER_AUTO
+                                && pport->connect_type == USB_PORT_NOT_USED
+                                && !pport->child))
                        clear_port_feature(hub->hdev, port1,
-                                       USB_PORT_FEAT_POWER);
+                               USB_PORT_FEAT_POWER);
+               else
+                       set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
+       }
 
        /* Wait at least 100 msec for power to become stable */
        delay = max(pgood_delay, (unsigned) 100);
@@ -4703,6 +4711,9 @@ static ssize_t show_port_power_control(struct device *dev,
        case USB_PORT_POWER_OFF:
                result = off_string;
                break;
+       case USB_PORT_POWER_AUTO:
+               result = auto_string;
+               break;
        default:
                return -EINVAL;
        }
@@ -4741,6 +4752,18 @@ static ssize_t store_port_power_control(struct device 
*dev,
                usb_autopm_put_interface(intf);
                if (ret < 0)
                        return -EIO;
+       } else if (len == sizeof(auto_string) - 1
+                       && strncmp(buf, auto_string, len) == 0) {
+               hub_port->port_power_policy = USB_PORT_POWER_AUTO;
+               if (hub_port->connect_type
+                               == USB_PORT_NOT_USED && !hub_port->child) {
+                       usb_autopm_get_interface(intf);
+                       ret = clear_port_feature(hdev, port1,
+                               USB_PORT_FEAT_POWER);
+                       usb_autopm_put_interface(intf);
+                       if (ret < 0)
+                               return -EIO;
+               }
        } else
                return -EINVAL;
 
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to