Toralf:
Here's a patch you can try out. It does more or less what I've been
discussing, but with no new Kconfig option.
Alan Stern
Index: usb-3.10/drivers/usb/core/hub.c
===================================================================
--- usb-3.10.orig/drivers/usb/core/hub.c
+++ usb-3.10/drivers/usb/core/hub.c
@@ -2829,7 +2829,6 @@ void usb_enable_ltm(struct usb_device *u
}
EXPORT_SYMBOL_GPL(usb_enable_ltm);
-#ifdef CONFIG_PM
/*
* usb_disable_function_remotewakeup - disable usb3.0
* device's function remote wakeup
@@ -2848,6 +2847,15 @@ static int usb_disable_function_remotewa
USB_CTRL_SET_TIMEOUT);
}
+/* Count of wakeup-enabled devices at or below udev */
+static unsigned wakeup_enabled_descendants(struct usb_device *udev)
+{
+ struct usb_hub *hub = usb_hub_to_struct_hub(udev);
+
+ return udev->do_remote_wakeup +
+ (hub ? hub->wakeup_enabled_descendants : 0);
+}
+
/*
* usb_port_suspend - suspend a usb device's upstream port
* @udev: device that's no longer in active use, not a root hub
@@ -2888,8 +2896,8 @@ static int usb_disable_function_remotewa
* Linux (2.6) currently has NO mechanisms to initiate that: no khubd
* timer, no SRP, no requests through sysfs.
*
- * If Runtime PM isn't enabled or used, non-SuperSpeed devices really get
- * suspended only when their bus goes into global suspend (i.e., the root
+ * If Runtime PM isn't enabled or used, non-SuperSpeed devices may get
+ * suspended when their bus goes into global suspend (i.e., the root
* hub is suspended). Nevertheless, we change @udev->state to
* USB_STATE_SUSPENDED as this is the device's "logical" state. The actual
* upstream port setting is stored in @udev->port_is_suspended.
@@ -2960,15 +2968,21 @@ int usb_port_suspend(struct usb_device *
/* see 7.1.7.6 */
if (hub_is_superspeed(hub->hdev))
status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3);
- else if (PMSG_IS_AUTO(msg))
- status = set_port_feature(hub->hdev, port1,
- USB_PORT_FEAT_SUSPEND);
+
/*
* For system suspend, we do not need to enable the suspend feature
* on individual USB-2 ports. The devices will automatically go
* into suspend a few ms after the root hub stops sending packets.
* The USB 2.0 spec calls this "global suspend".
+ *
+ * However, many USB hubs have a bug: They don't relay wakeup requests
+ * from a downstream port if the port's suspend feature isn't on.
+ * Therefore we will turn on the suspend feature if udev or any of its
+ * descendants is enabled for remote wakeup.
*/
+ else if (PMSG_IS_AUTO(msg) || wakeup_enabled_descendants(udev) > 0)
+ status = set_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_SUSPEND);
else {
really_suspend = false;
status = 0;
@@ -3003,15 +3017,16 @@ int usb_port_suspend(struct usb_device *
if (!PMSG_IS_AUTO(msg))
status = 0;
} else {
- /* device has up to 10 msec to fully suspend */
dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n",
(PMSG_IS_AUTO(msg) ? "auto-" : ""),
udev->do_remote_wakeup);
- usb_set_device_state(udev, USB_STATE_SUSPENDED);
- if (really_suspend) {
+ if (really_suspend)
udev->port_is_suspended = 1;
+
+ /* device has up to 10 msec to fully suspend */
+ if (PMSG_IS_AUTO(msg))
msleep(10);
- }
+ usb_set_device_state(udev, USB_STATE_SUSPENDED);
}
/*
@@ -3249,8 +3264,6 @@ int usb_port_resume(struct usb_device *u
return status;
}
-#endif /* CONFIG_PM */
-
#ifdef CONFIG_PM_RUNTIME
/* caller has locked udev */
@@ -3293,6 +3306,8 @@ static int hub_suspend(struct usb_interf
unsigned port1;
int status;
+ hub->wakeup_enabled_descendants = 0;
+
/* Warn if children aren't already suspended */
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
struct usb_device *udev;
@@ -3303,6 +3318,11 @@ static int hub_suspend(struct usb_interf
if (PMSG_IS_AUTO(msg))
return -EBUSY;
}
+
+ /* Add up the number of wakeup-enabled descendants */
+ if (udev)
+ hub->wakeup_enabled_descendants +=
+ wakeup_enabled_descendants(udev);
}
if (hdev->do_remote_wakeup && hub->quirk_check_port_auto_suspend) {
Index: usb-3.10/drivers/usb/core/hub.h
===================================================================
--- usb-3.10.orig/drivers/usb/core/hub.h
+++ usb-3.10/drivers/usb/core/hub.h
@@ -59,6 +59,9 @@ struct usb_hub {
struct usb_tt tt; /* Transaction Translator */
unsigned mA_per_port; /* current for each child */
+#ifdef CONFIG_PM
+ unsigned wakeup_enabled_descendants;
+#endif
unsigned limited_power:1;
unsigned quiescing:1;
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html