[PATCH] usb: hub: port: add sysfs entry to switch port power

Michael Grzeschik
2022-05-10T23:13:25Z

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@pengutronix.de>
---
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