| From stern@rowland.harvard.edu Thu May 6 13:51:54 2010 |
| From: Alan Stern <stern@rowland.harvard.edu> |
| Date: Fri, 30 Apr 2010 12:09:23 -0400 (EDT) |
| Subject: USB: fix remote wakeup settings during system sleep |
| To: Greg KH <greg@kroah.com> |
| Message-ID: <Pine.LNX.4.44L0.1004301205450.1535-100000@iolanthe.rowland.org> |
| |
| This is a backport of commit 5f677f1d45b2bf08085bbba7394392dfa586fa8e. |
| Some of the functionality had to be removed, but it should still fix |
| the webcam problem. |
| |
| This patch (as1363b) changes the way USB remote wakeup is handled |
| during system sleeps. It won't be enabled unless an interface driver |
| specifically needs it. Also, it won't be enabled during the FREEZE or |
| QUIESCE phases of hibernation, when the system doesn't respond to |
| wakeup events anyway. |
| |
| This will fix problems people have reported with certain USB webcams |
| that generate wakeup requests when they shouldn't, and as a result |
| cause system suspends to fail. See |
| |
| https://bugs.launchpad.net/ubuntu/+source/linux/+bug/515109 |
| |
| |
| Signed-off-by: Alan Stern <stern@rowland.harvard.edu> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| drivers/usb/core/driver.c | 32 ++++++++++++++++++++++++++++++-- |
| 1 file changed, 30 insertions(+), 2 deletions(-) |
| |
| --- a/drivers/usb/core/driver.c |
| +++ b/drivers/usb/core/driver.c |
| @@ -1255,9 +1255,8 @@ static int usb_suspend_both(struct usb_d |
| udev->state == USB_STATE_SUSPENDED) |
| goto done; |
| |
| - udev->do_remote_wakeup = device_may_wakeup(&udev->dev); |
| - |
| if (msg.event & PM_EVENT_AUTO) { |
| + udev->do_remote_wakeup = device_may_wakeup(&udev->dev); |
| status = autosuspend_check(udev, 0); |
| if (status < 0) |
| goto done; |
| @@ -1789,6 +1788,34 @@ int usb_external_resume_device(struct us |
| return status; |
| } |
| |
| +static void choose_wakeup(struct usb_device *udev, pm_message_t msg) |
| +{ |
| + int w, i; |
| + struct usb_interface *intf; |
| + |
| + /* Remote wakeup is needed only when we actually go to sleep. |
| + * For things like FREEZE and QUIESCE, if the device is already |
| + * autosuspended then its current wakeup setting is okay. |
| + */ |
| + if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_QUIESCE) { |
| + udev->do_remote_wakeup = 0; |
| + return; |
| + } |
| + |
| + /* If remote wakeup is permitted, see whether any interface drivers |
| + * actually want it. |
| + */ |
| + w = 0; |
| + if (device_may_wakeup(&udev->dev) && udev->actconfig) { |
| + for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { |
| + intf = udev->actconfig->interface[i]; |
| + w |= intf->needs_remote_wakeup; |
| + } |
| + } |
| + |
| + udev->do_remote_wakeup = w; |
| +} |
| + |
| int usb_suspend(struct device *dev, pm_message_t msg) |
| { |
| struct usb_device *udev; |
| @@ -1808,6 +1835,7 @@ int usb_suspend(struct device *dev, pm_m |
| } |
| |
| udev->skip_sys_resume = 0; |
| + choose_wakeup(udev, msg); |
| return usb_external_suspend_device(udev, msg); |
| } |
| |