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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html