This patch adds an sysfs switch to enable/disable a port on an power switchable hub. It also ensures that the associated device gets disconnected from the logical usb tree. Signed-off-by: Michael Grzeschik <m.grzeschik@xxxxxxxxxxxxxx> --- drivers/usb/core/port.c | 47 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index d5bc36ca5b1f77..abc618d87888f3 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -17,6 +17,52 @@ static int usb_port_block_power_off; static const struct attribute_group *port_dev_group[]; +static ssize_t port_power_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_port *port_dev = to_usb_port(dev); + struct usb_device *udev = port_dev->child; + struct usb_device *hdev = to_usb_device(dev->parent->parent); + struct usb_hub *hub = usb_hub_to_struct_hub(hdev); + int port1 = port_dev->portnum; + bool value; + int rc = 0; + + if (!hub) + return -EINVAL; + + if (hub->in_reset) + return -EBUSY; + + rc = strtobool(buf, &value); + if (rc) + return rc; + + if (value) + usb_remote_wakeup(hdev); + + rc = usb_hub_set_port_power(hdev, hub, port1, value); + if (rc) + return rc; + + if (!value) { + usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION); + if (!port_dev->is_superspeed) + usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE); + + if (udev) { + port_dev->child = NULL; + usb_disconnect(&udev); + } + } + + if (!rc) + rc = count; + + return rc; +} +static DEVICE_ATTR_WO(port_power); + static ssize_t location_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -153,6 +199,7 @@ static struct attribute *port_dev_attrs[] = { &dev_attr_location.attr, &dev_attr_quirks.attr, &dev_attr_over_current_count.attr, + &dev_attr_port_power.attr, NULL, }; -- 2.30.2