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.williams@xxxxxxxxx> --- 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html