[PATCH v2 06/14] USB: centralize usb port power policy

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

 



Move all port power policy evaluation to usb_port_runtime_suspend().
Makes it clearer what blocks port power off and is preparation for
follow on changes that
1/ make a usb_port a proper (device model) parent
   of a usb_device
2/ advertise to userspace what constraints are keeping a
   port powered
3/ changing the meaning of the usb_port runtime suspended
   state.
4/ add new constraints peer-port-power-state and connect_type

Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx>
---
 drivers/usb/core/hub.c  |   16 +++++++++++++++-
 drivers/usb/core/hub.h  |    7 +++++++
 drivers/usb/core/port.c |   22 ++++++++++++++++++++--
 3 files changed, 42 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 683f528f5ea7..0e4c85380760 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2066,6 +2066,20 @@ void usb_disconnect(struct usb_device **pdev)
 	put_device(&udev->dev);
 }
 
+/* outside of khubd context a device may be deleted at any time */
+struct usb_device *usb_port_get_child(struct usb_port *port_dev)
+{
+	struct usb_device *udev = NULL;
+
+	spin_lock_irq(&device_state_lock);
+	udev = port_dev->child;
+	if (udev)
+		get_device(&udev->dev);
+	spin_unlock_irq(&device_state_lock);
+
+	return udev;
+}
+
 #ifdef CONFIG_USB_ANNOUNCE_NEW_DEVICES
 static void show_string(struct usb_device *udev, char *id, char *string)
 {
@@ -3039,7 +3053,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
 		usb_set_device_state(udev, USB_STATE_SUSPENDED);
 	}
 
-	if (status == 0 && !udev->do_remote_wakeup && udev->persist_enabled) {
+	if (status == 0) {
 		pm_runtime_put_sync(&port_dev->dev);
 		port_dev->did_runtime_put = true;
 	}
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 44508d41c095..4e34365e41e3 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -110,3 +110,10 @@ extern int usb_set_port_feature(struct usb_device *hdev,
 extern void usb_assign_port_poweroff(struct usb_hub *hub, int port1, int val);
 #define usb_set_port_poweroff(h, p) usb_assign_port_poweroff(h, p, 1)
 #define usb_clear_port_poweroff(h, p) usb_assign_port_poweroff(h, p, 0)
+extern struct usb_device *usb_port_get_child(struct usb_port *port_dev);
+static inline void usb_port_put_child(struct usb_device *udev)
+{
+	if (!udev)
+		return;
+	put_device(&udev->dev);
+}
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 094a9d0e31c7..6257c1ec5e2d 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -93,6 +93,25 @@ static int usb_port_runtime_resume(struct device *dev)
 	return retval;
 }
 
+static const char *power_on_reason(struct usb_port *port_dev)
+{
+	s32 flag = PM_QOS_FLAG_NO_POWER_OFF;
+	const char *reason = NULL;
+	struct usb_device *udev;
+
+	if (dev_pm_qos_flags(&port_dev->dev, flag) == PM_QOS_FLAGS_ALL)
+		return "pm_qos_no_power_off";
+
+	udev = usb_port_get_child(port_dev);
+	if (udev && udev->do_remote_wakeup)
+		reason = "wakeup enabled";
+	else if (udev && !udev->persist_enabled)
+		reason = "persist disabled";
+	usb_port_put_child(udev);
+
+	return reason;
+}
+
 static int usb_port_runtime_suspend(struct device *dev)
 {
 	struct usb_port *port_dev = to_usb_port(dev);
@@ -105,8 +124,7 @@ static int usb_port_runtime_suspend(struct device *dev)
 	if (!hub)
 		return -EINVAL;
 
-	if (dev_pm_qos_flags(&port_dev->dev, PM_QOS_FLAG_NO_POWER_OFF)
-			== PM_QOS_FLAGS_ALL)
+	if (power_on_reason(port_dev))
 		return -EAGAIN;
 
 	usb_autopm_get_interface(intf);

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