[PATCH v2 08/14] USB: show port power state

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Let userspace discover what is keeping a port powered.  Here are the
states and the actions userspace can take to enable port power off.

PORT=<port sysfs directory>

'active': attached child device is actively using the port
	echo auto > $PORT/<child>/power/control

'pm_qos_no_power_off': child inactive, pm qos flag keeps power enabled
	echo 0 > $PORT/power/pm_qos_no_power_off

'wakeup enabled': port kept powered to handle remote wakeup events
	echo 0 > $PORT/<child>/power/wakeup

'persist disabled': child device may not cleanly recover from
		    suspend/resume cycle
	echo 1 > $PORT/<child>/power/persist

'off': port power is off

Reading this file makes an attempt to settle in-flight pm_runtime
transitions.

Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx>
---
 Documentation/ABI/testing/sysfs-bus-usb |   19 ++++++++++
 drivers/usb/core/port.c                 |   60 +++++++++++++++++++++++++++++--
 2 files changed, 75 insertions(+), 4 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index 1430f584b266..91bfcfcfbfe3 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -148,6 +148,25 @@ Description:
 		The file will read "hotplug", "wired" and "not used" if the
 		information is available, and "unknown" otherwise.
 
+What:		/sys/bus/usb/devices/.../(hub interface)/portX/power/power_state
+Date:		November 2013
+Contact:	Dan Williams <dan.j.williams@xxxxxxxxx>
+Description:
+		There are several gating factors that prevent a usb_port
+		from being powered down.  Let userspace discover what is
+		keeping a port powered.
+
+		'active': attached child device is actively using the
+			  port
+		'pm_qos_no_power_off': child inactive, pm qos flag keeps
+				       power enabled
+		'wakeup enabled': child inactive, port kept powered to
+				  handle remote wakeup events
+		'persist disabled': child inactive, but child device
+				    will not cleanly recover from a
+				    suspend/resume cycle
+		'off': port power is off
+
 What:		/sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout
 Date:		May 2013
 Contact:	Mathias Nyman <mathias.nyman@xxxxxxxxxxxxxxx>
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 6f20c0c8fe34..f7d1cd8a97d3 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -152,6 +152,55 @@ struct device_type usb_port_device_type = {
 	.pm =		&usb_port_pm_ops,
 };
 
+#ifdef CONFIG_PM
+static ssize_t power_state_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	const char *state;
+	struct usb_port *port_dev = to_usb_port(dev);
+	struct usb_device *child = usb_port_get_child(port_dev);
+
+	if (child) {
+		pm_runtime_barrier(&child->dev);
+		put_device(&child->dev);
+	}
+
+	pm_runtime_barrier(dev);
+
+	if (pm_runtime_active(dev))
+		state = "active";
+	else
+		state = power_on_reason(port_dev);
+
+	if (!state)
+		state = "off";
+
+	return sprintf(buf, "%s\n", state);
+}
+static DEVICE_ATTR_RO(power_state);
+
+static int add_port_power_state(struct usb_port *port_dev)
+{
+	return sysfs_add_file_to_group(&port_dev->dev.kobj,
+				       &dev_attr_power_state.attr,
+				       power_group_name);
+}
+
+static void remove_port_power_state(struct usb_port *port_dev)
+{
+	sysfs_remove_file_from_group(&port_dev->dev.kobj,
+				     &dev_attr_power_state.attr,
+				     power_group_name);
+}
+#else
+static int add_port_power_state(struct usb_port *port_dev)
+{
+	return 0;
+}
+
+static void remove_port_power_state(struct usb_port *port_dev) { }
+#endif
+
 int usb_hub_create_port_device(struct usb_hub *hub, int port1)
 {
 	struct usb_port *port_dev = NULL;
@@ -174,6 +223,8 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
 	if (retval)
 		goto error_register;
 
+	add_port_power_state(port_dev);
+
 	pm_runtime_set_active(&port_dev->dev);
 
 	/* It would be dangerous if user space couldn't
@@ -193,9 +244,10 @@ exit:
 	return retval;
 }
 
-void usb_hub_remove_port_device(struct usb_hub *hub,
-				       int port1)
+void usb_hub_remove_port_device(struct usb_hub *hub, int port1)
 {
-	device_unregister(&hub->ports[port1 - 1]->dev);
-}
+	struct usb_port *port_dev = hub->ports[port1 - 1];
 
+	remove_port_power_state(port_dev);
+	device_unregister(&port_dev->dev);
+}

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




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux