hotplug ports maintain power awaiting hotplug events.  Allow userspace to
override this disctinction on a per port basis.

Signed-off-by: Dan Williams <dan.j.willi...@intel.com>
---
 drivers/usb/core/port.c         |   32 +++++++++++++++++++++++++++++-
 drivers/usb/core/usb-platform.c |   42 +++++++++++++++++++++++++++++++++++++++
 drivers/usb/core/usb-platform.h |    2 ++
 3 files changed, 75 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index d609b25746d2..71fbedaf8a93 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -47,7 +47,37 @@ static ssize_t connect_type_show(struct device *dev,
 
        return sprintf(buf, "%s\n", result);
 }
-static DEVICE_ATTR_RO(connect_type);
+
+
+static ssize_t connect_type_store(struct device *dev, struct device_attribute 
*attr,
+                                 const char *buf, size_t len)
+{
+       struct usb_port *port_dev = to_usb_port(dev);
+       ssize_t sz = len;
+       int i;
+       struct action { const char *str; enum usb_port_connect_type type; };
+       static const struct action action[] = {
+               { .str = "hotplug", .type = USB_PORT_CONNECT_TYPE_HOT_PLUG, },
+               { .str = "hardwired", .type = USB_PORT_CONNECT_TYPE_HARD_WIRED 
},
+       };
+
+       if (buf[len-1] == '\n' || buf[len-1] == '\0')
+               sz--;
+
+       for (i = 0; i < ARRAY_SIZE(action); i++) {
+               const struct action *act = &action[i];
+
+               if (sz == strlen(act->str)
+                   && strncmp(buf, act->str, sz) == 0) {
+                       usb_connector_set_connect_type(port_dev, act->type);
+                       return len;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static DEVICE_ATTR_RW(connect_type);
 
 static struct attribute *port_dev_attrs[] = {
        &dev_attr_connect_type.attr,
diff --git a/drivers/usb/core/usb-platform.c b/drivers/usb/core/usb-platform.c
index 0e24110e8ba2..ce559994760e 100644
--- a/drivers/usb/core/usb-platform.c
+++ b/drivers/usb/core/usb-platform.c
@@ -91,6 +91,48 @@ bool usb_connector_notify_flags(struct usb_port *port_dev, 
s32 mask, bool set)
        return true;
 }
 
+void usb_connector_set_connect_type(struct usb_port *port_dev,
+                                   enum usb_port_connect_type type)
+{
+       LIST_HEAD(tmp);
+       struct usb_port *p;
+       struct usb_domain *udom;
+       int flag = PM_QOS_FLAG_NO_NOTIFY;
+       struct usb_connector *uconn = port_dev->connector;
+
+       /* if for some reason userspace does not want hotplug settings
+        * synced
+        */
+       if (dev_pm_qos_flags(&port_dev->dev, flag) == PM_QOS_FLAGS_ALL
+           || !uconn) {
+               pm_runtime_get_sync(&port_dev->dev);
+               port_dev->connect_type = type;
+               pm_runtime_put(&port_dev->dev);
+       }
+
+       udom = uconn->domain;
+       mutex_lock(&udom->lock);
+       uconn->connect_type = type;
+       list_for_each_entry(port_dev, &uconn->ports, node) {
+               pm_runtime_get_sync(&port_dev->dev);
+               port_dev->connect_type = type;
+       }
+
+       /* order the resulting suspensions disconnected ports first */
+       list_for_each_entry_safe(port_dev, p, &uconn->ports, node) {
+               if (port_dev->child)
+                       list_move(&port_dev->node, &tmp);
+               else
+                       pm_runtime_put(&port_dev->dev);
+       }
+
+       list_for_each_entry_safe(port_dev, p, &tmp, node) {
+               list_move(&port_dev->node, &uconn->ports);
+               pm_runtime_put(&port_dev->dev);
+       }
+       mutex_unlock(&udom->lock);
+}
+
 static struct usb_connector *create_connector(struct usb_domain *udom,
                                              struct usb_port *port_dev,
                                              size_t pair_data)
diff --git a/drivers/usb/core/usb-platform.h b/drivers/usb/core/usb-platform.h
index 648dbe6d9e54..f73bc309eef3 100644
--- a/drivers/usb/core/usb-platform.h
+++ b/drivers/usb/core/usb-platform.h
@@ -52,3 +52,5 @@ bool usb_connector_notify_flags(struct usb_port *port_dev, 
s32 mask, bool set);
 enum usb_connector_state usb_connector_state(struct usb_port *port_dev);
 void usb_connector_connect(struct usb_port *port_dev);
 void usb_connector_disconnect(struct usb_port *port_dev);
+void usb_connector_set_connect_type(struct usb_port *port_dev,
+                                   enum usb_port_connect_type type);

--
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