diff options
author | Oliver Neukum <oneukum@suse.de> | 2012-01-05 15:39:57 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2012-01-24 14:22:42 -0800 |
commit | 1493138af1463112e42eebcdab5db61452821e97 (patch) | |
tree | 3c78d317b08745907dec3d1b42c11d43eb15c089 /drivers/usb/core/driver.c | |
parent | dcd6c92267155e70a94b3927bce681ce74b80d1f (diff) |
USB: code cleanup in suspend/resume path (3rd try)
Do the cleanup to avoid behaviorial parameters Linus
requested.
Signed-off-by: Oliver Neukum <oneukum@suse.de>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core/driver.c')
-rw-r--r-- | drivers/usb/core/driver.c | 93 |
1 files changed, 63 insertions, 30 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index d40ff9568813..b7dfdecc7fda 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -958,13 +958,8 @@ void usb_rebind_intf(struct usb_interface *intf) int rc; /* Delayed unbind of an existing driver */ - if (intf->dev.driver) { - struct usb_driver *driver = - to_usb_driver(intf->dev.driver); - - dev_dbg(&intf->dev, "forced unbind\n"); - usb_driver_release_interface(driver, intf); - } + if (intf->dev.driver) + usb_forced_unbind_intf(intf); /* Try to rebind the interface */ if (!intf->dev.power.is_prepared) { @@ -977,15 +972,13 @@ void usb_rebind_intf(struct usb_interface *intf) #ifdef CONFIG_PM -#define DO_UNBIND 0 -#define DO_REBIND 1 - -/* Unbind drivers for @udev's interfaces that don't support suspend/resume, - * or rebind interfaces that have been unbound, according to @action. +/* Unbind drivers for @udev's interfaces that don't support suspend/resume + * There is no check for reset_resume here because it can be determined + * only during resume whether reset_resume is needed. * * The caller must hold @udev's device lock. */ -static void do_unbind_rebind(struct usb_device *udev, int action) +static void unbind_no_pm_drivers_interfaces(struct usb_device *udev) { struct usb_host_config *config; int i; @@ -996,23 +989,53 @@ static void do_unbind_rebind(struct usb_device *udev, int action) if (config) { for (i = 0; i < config->desc.bNumInterfaces; ++i) { intf = config->interface[i]; - switch (action) { - case DO_UNBIND: - if (intf->dev.driver) { - drv = to_usb_driver(intf->dev.driver); - if (!drv->suspend || !drv->resume) - usb_forced_unbind_intf(intf); - } - break; - case DO_REBIND: - if (intf->needs_binding) - usb_rebind_intf(intf); - break; + + if (intf->dev.driver) { + drv = to_usb_driver(intf->dev.driver); + if (!drv->suspend || !drv->resume) + usb_forced_unbind_intf(intf); } } } } +/* Unbind drivers for @udev's interfaces that failed to support reset-resume. + * These interfaces have the needs_binding flag set by usb_resume_interface(). + * + * The caller must hold @udev's device lock. + */ +static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev) +{ + struct usb_host_config *config; + int i; + struct usb_interface *intf; + + config = udev->actconfig; + if (config) { + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + intf = config->interface[i]; + if (intf->dev.driver && intf->needs_binding) + usb_forced_unbind_intf(intf); + } + } +} + +static void do_rebind_interfaces(struct usb_device *udev) +{ + struct usb_host_config *config; + int i; + struct usb_interface *intf; + + config = udev->actconfig; + if (config) { + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + intf = config->interface[i]; + if (intf->needs_binding) + usb_rebind_intf(intf); + } + } +} + static int usb_suspend_device(struct usb_device *udev, pm_message_t msg) { struct usb_device_driver *udriver; @@ -1302,7 +1325,12 @@ int usb_suspend(struct device *dev, pm_message_t msg) { struct usb_device *udev = to_usb_device(dev); - do_unbind_rebind(udev, DO_UNBIND); + unbind_no_pm_drivers_interfaces(udev); + + /* From now on we are sure all drivers support suspend/resume + * but not necessarily reset_resume() + * so we may still need to unbind and rebind upon resume + */ choose_wakeup(udev, msg); return usb_suspend_both(udev, msg); } @@ -1313,15 +1341,20 @@ int usb_resume(struct device *dev, pm_message_t msg) struct usb_device *udev = to_usb_device(dev); int status; - /* For PM complete calls, all we do is rebind interfaces */ + /* For PM complete calls, all we do is rebind interfaces + * whose needs_binding flag is set + */ if (msg.event == PM_EVENT_ON) { if (udev->state != USB_STATE_NOTATTACHED) - do_unbind_rebind(udev, DO_REBIND); + do_rebind_interfaces(udev); status = 0; /* For all other calls, take the device back to full power and * tell the PM core in case it was autosuspended previously. - * Unbind the interfaces that will need rebinding later. + * Unbind the interfaces that will need rebinding later, + * because they fail to support reset_resume. + * (This can't be done in usb_resume_interface() + * above because it doesn't own the right set of locks.) */ } else { status = usb_resume_both(udev, msg); @@ -1329,7 +1362,7 @@ int usb_resume(struct device *dev, pm_message_t msg) pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); - do_unbind_rebind(udev, DO_REBIND); + unbind_no_reset_resume_drivers_interfaces(udev); } } |