All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] usb: hub: port: add sysfs entry to switch port power
@ 2022-06-02  1:27 Michael Grzeschik
  2022-06-02 14:39 ` Alan Stern
  0 siblings, 1 reply; 6+ messages in thread
From: Michael Grzeschik @ 2022-06-02  1:27 UTC (permalink / raw)
  To: linux-usb; +Cc: stern, gregkh, kernel

In some cases the port of an hub needs to be disabled or switched off
and on again. E.g. when the connected device needs to be re-enumerated.
Or it needs to be explicitly disabled while the rest of the usb tree
stays working.

For this purpose this patch adds an sysfs switch to enable/disable the
port on any hub. In the case the hub is supporting power switching, the
power line will be disabled to the connected device.

When the port gets disabled, the associated device gets disconnected and
removed from the logical usb tree. No further device will be enumerated
on that port until the port gets enabled again.

Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>

---
v1 -> v2:
         - improved patch description
	 - moved usb_hub_set_port_power to end of function
	 - renamed value to set
         - removed udev variable
         - added usb_set_configuration set to -1 before removing device
         - calling autosuspend of udev before usb_disconnect, ensuring hub_suspend succeeds
         - removed port_dev->child = NULL assignment
         - directly returning count on no failure success
         - removed test for hub->in_reset
	 - using usb_autopm_get_interface/usb_autopm_put_interface around hub handling
	 - locking usb_disconnect call
	 - using &port_dev->child instead of local udev pointer
	 - added Documentation/ABI

 Documentation/ABI/testing/sysfs-bus-usb | 13 +++++++
 drivers/usb/core/port.c                 | 49 +++++++++++++++++++++++++
 2 files changed, 62 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index 7efe31ed3a25c7..9c87ca50bcab79 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -253,6 +253,19 @@ Description:
 		only if the system firmware is capable of describing the
 		connection between a port and its connector.
 
+What:		/sys/bus/usb/devices/.../<hub_interface>/port<X>/port_power
+Date:		June 2022
+Contact:	Michael Grzeschik <m.grzeschik@pengutronix.de>
+Description:
+		To disable or enable a hub port the sysfs file port_power exists
+		for each hub port. When disabling the hub port it is unusable anymore,
+		which means no enumeration will take place on this port until enabled again.
+
+		When disabling the port set (<hubdev-portX>/port_power to 0) the
+		USB_PORT_FEAT_C_CONNECTION, USB_PORT_FEAT_POWER and (for high speed hubs) the
+		USB_PORT_FEAT_C_ENABLE port features are cleared. It all gets reversed when the
+		port will be enabled again (set <hubdev-portX>/port_power to 1).
+
 What:		/sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout
 Date:		May 2013
 Contact:	Mathias Nyman <mathias.nyman@linux.intel.com>
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index d5bc36ca5b1f77..3e707db88291e9 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -17,6 +17,54 @@ 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 *hdev = to_usb_device(dev->parent->parent);
+	struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
+	struct usb_interface *intf = to_usb_interface(hub->intfdev);
+	int port1 = port_dev->portnum;
+	bool set;
+	int rc;
+
+	if (!hub)
+		return -EINVAL;
+
+	rc = strtobool(buf, &set);
+	if (rc)
+		return rc;
+
+	rc = usb_autopm_get_interface(intf);
+	if (rc < 0)
+		return rc;
+
+	if (!set) {
+		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 (port_dev->child) {
+			usb_set_configuration(port_dev->child, -1);
+			usb_autosuspend_device(port_dev->child);
+			usb_lock_device(hdev);
+			usb_disconnect(&port_dev->child);
+			usb_unlock_device(hdev);
+		}
+	}
+
+	rc = usb_hub_set_port_power(hdev, hub, port1, set);
+	if (rc) {
+		usb_autopm_put_interface(intf);
+		return rc;
+	}
+
+	usb_autopm_put_interface(intf);
+
+	return count;
+}
+static DEVICE_ATTR_WO(port_power);
+
 static ssize_t location_show(struct device *dev,
 			     struct device_attribute *attr, char *buf)
 {
@@ -153,6 +201,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


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2022-06-03  0:25 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-02  1:27 [PATCH v2] usb: hub: port: add sysfs entry to switch port power Michael Grzeschik
2022-06-02 14:39 ` Alan Stern
2022-06-02 14:59   ` Michael Grzeschik
2022-06-02 19:16     ` Alan Stern
2022-06-02 21:34       ` Michael Grzeschik
2022-06-03  0:25         ` Alan Stern

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.