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