aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2013-07-11 14:58:04 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-07-16 18:33:02 -0400
commite583d9db9960cf40e0bc8afee4946baa9d71596e (patch)
treeaf009cc8155cc5d751589909bcbaa723857cf96c /drivers/usb/core
parent36ff66db3fb5642906e46e73ca9cf92f1c5974ff (diff)
USB: global suspend and remote wakeup don't mix
The hub driver was recently changed to use "global" suspend for system suspend transitions on non-SuperSpeed buses. This means that we don't suspend devices individually by setting the suspend feature on the upstream hub port; instead devices all go into suspend automatically when the root hub stops transmitting packets. The idea was to save time and to avoid certain kinds of wakeup races. Now it turns out that many hubs are buggy; they don't relay wakeup requests from a downstream port to their upstream port if the downstream port's suspend feature is not set (depending on the speed of the downstream port, whether or not the hub is enabled for remote wakeup, and possibly other factors). We can't have hubs dropping wakeup requests. Therefore this patch goes partway back to the old policy: It sets the suspend feature for a port if the device attached to that port or any of its descendants is enabled for wakeup. People will still be able to benefit from the time savings if they don't care about wakeup and leave it disabled on all their devices. In order to accomplish this, the patch adds a new field to the usb_hub structure: wakeup_enabled_descendants is a count of how many devices below a suspended hub are enabled for remote wakeup. A corresponding new subroutine determines the number of wakeup-enabled devices at or below an arbitrary suspended USB device. This should be applied to the 3.10 stable kernel. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Reported-and-tested-by: Toralf Förster <toralf.foerster@gmx.de> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/hub.c39
-rw-r--r--drivers/usb/core/hub.h3
2 files changed, 34 insertions, 8 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 4191db32f12c..c857471519e3 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2848,6 +2848,15 @@ static int usb_disable_function_remotewakeup(struct usb_device *udev)
2848 USB_CTRL_SET_TIMEOUT); 2848 USB_CTRL_SET_TIMEOUT);
2849} 2849}
2850 2850
2851/* Count of wakeup-enabled devices at or below udev */
2852static unsigned wakeup_enabled_descendants(struct usb_device *udev)
2853{
2854 struct usb_hub *hub = usb_hub_to_struct_hub(udev);
2855
2856 return udev->do_remote_wakeup +
2857 (hub ? hub->wakeup_enabled_descendants : 0);
2858}
2859
2851/* 2860/*
2852 * usb_port_suspend - suspend a usb device's upstream port 2861 * usb_port_suspend - suspend a usb device's upstream port
2853 * @udev: device that's no longer in active use, not a root hub 2862 * @udev: device that's no longer in active use, not a root hub
@@ -2888,8 +2897,8 @@ static int usb_disable_function_remotewakeup(struct usb_device *udev)
2888 * Linux (2.6) currently has NO mechanisms to initiate that: no khubd 2897 * Linux (2.6) currently has NO mechanisms to initiate that: no khubd
2889 * timer, no SRP, no requests through sysfs. 2898 * timer, no SRP, no requests through sysfs.
2890 * 2899 *
2891 * If Runtime PM isn't enabled or used, non-SuperSpeed devices really get 2900 * If Runtime PM isn't enabled or used, non-SuperSpeed devices may not get
2892 * suspended only when their bus goes into global suspend (i.e., the root 2901 * suspended until their bus goes into global suspend (i.e., the root
2893 * hub is suspended). Nevertheless, we change @udev->state to 2902 * hub is suspended). Nevertheless, we change @udev->state to
2894 * USB_STATE_SUSPENDED as this is the device's "logical" state. The actual 2903 * USB_STATE_SUSPENDED as this is the device's "logical" state. The actual
2895 * upstream port setting is stored in @udev->port_is_suspended. 2904 * upstream port setting is stored in @udev->port_is_suspended.
@@ -2960,15 +2969,21 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
2960 /* see 7.1.7.6 */ 2969 /* see 7.1.7.6 */
2961 if (hub_is_superspeed(hub->hdev)) 2970 if (hub_is_superspeed(hub->hdev))
2962 status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3); 2971 status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3);
2963 else if (PMSG_IS_AUTO(msg)) 2972
2964 status = set_port_feature(hub->hdev, port1,
2965 USB_PORT_FEAT_SUSPEND);
2966 /* 2973 /*
2967 * For system suspend, we do not need to enable the suspend feature 2974 * For system suspend, we do not need to enable the suspend feature
2968 * on individual USB-2 ports. The devices will automatically go 2975 * on individual USB-2 ports. The devices will automatically go
2969 * into suspend a few ms after the root hub stops sending packets. 2976 * into suspend a few ms after the root hub stops sending packets.
2970 * The USB 2.0 spec calls this "global suspend". 2977 * The USB 2.0 spec calls this "global suspend".
2978 *
2979 * However, many USB hubs have a bug: They don't relay wakeup requests
2980 * from a downstream port if the port's suspend feature isn't on.
2981 * Therefore we will turn on the suspend feature if udev or any of its
2982 * descendants is enabled for remote wakeup.
2971 */ 2983 */
2984 else if (PMSG_IS_AUTO(msg) || wakeup_enabled_descendants(udev) > 0)
2985 status = set_port_feature(hub->hdev, port1,
2986 USB_PORT_FEAT_SUSPEND);
2972 else { 2987 else {
2973 really_suspend = false; 2988 really_suspend = false;
2974 status = 0; 2989 status = 0;
@@ -3003,15 +3018,16 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
3003 if (!PMSG_IS_AUTO(msg)) 3018 if (!PMSG_IS_AUTO(msg))
3004 status = 0; 3019 status = 0;
3005 } else { 3020 } else {
3006 /* device has up to 10 msec to fully suspend */
3007 dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n", 3021 dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n",
3008 (PMSG_IS_AUTO(msg) ? "auto-" : ""), 3022 (PMSG_IS_AUTO(msg) ? "auto-" : ""),
3009 udev->do_remote_wakeup); 3023 udev->do_remote_wakeup);
3010 usb_set_device_state(udev, USB_STATE_SUSPENDED);
3011 if (really_suspend) { 3024 if (really_suspend) {
3012 udev->port_is_suspended = 1; 3025 udev->port_is_suspended = 1;
3026
3027 /* device has up to 10 msec to fully suspend */
3013 msleep(10); 3028 msleep(10);
3014 } 3029 }
3030 usb_set_device_state(udev, USB_STATE_SUSPENDED);
3015 } 3031 }
3016 3032
3017 /* 3033 /*
@@ -3293,7 +3309,11 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
3293 unsigned port1; 3309 unsigned port1;
3294 int status; 3310 int status;
3295 3311
3296 /* Warn if children aren't already suspended */ 3312 /*
3313 * Warn if children aren't already suspended.
3314 * Also, add up the number of wakeup-enabled descendants.
3315 */
3316 hub->wakeup_enabled_descendants = 0;
3297 for (port1 = 1; port1 <= hdev->maxchild; port1++) { 3317 for (port1 = 1; port1 <= hdev->maxchild; port1++) {
3298 struct usb_device *udev; 3318 struct usb_device *udev;
3299 3319
@@ -3303,6 +3323,9 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
3303 if (PMSG_IS_AUTO(msg)) 3323 if (PMSG_IS_AUTO(msg))
3304 return -EBUSY; 3324 return -EBUSY;
3305 } 3325 }
3326 if (udev)
3327 hub->wakeup_enabled_descendants +=
3328 wakeup_enabled_descendants(udev);
3306 } 3329 }
3307 3330
3308 if (hdev->do_remote_wakeup && hub->quirk_check_port_auto_suspend) { 3331 if (hdev->do_remote_wakeup && hub->quirk_check_port_auto_suspend) {
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 6508e02b3dac..4e4790dea343 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -59,6 +59,9 @@ struct usb_hub {
59 struct usb_tt tt; /* Transaction Translator */ 59 struct usb_tt tt; /* Transaction Translator */
60 60
61 unsigned mA_per_port; /* current for each child */ 61 unsigned mA_per_port; /* current for each child */
62#ifdef CONFIG_PM
63 unsigned wakeup_enabled_descendants;
64#endif
62 65
63 unsigned limited_power:1; 66 unsigned limited_power:1;
64 unsigned quiescing:1; 67 unsigned quiescing:1;