On Mon, 28 May 2018, Anshuman Gupta wrote: > > It sounds like you want the code in usb_port_resume() to check and see > > whether the port has already received a wakeup signal and hasn't > > finished processing it yet. > yes. > > > > There's no way to do this for ports on external hubs, but in principle > > it can be done for ports on root hubs. If we see that a wakeup signal > > was received, we can call pm_wakeup_event(). Will that be good enough? > Yeah, i think this will be enough for us. Okay, here's a patch which should do want you want. I have not tested it; let me know if it's okay. (I'm not entirely certain about the xhci-hcd portions. If necessary we can get help from the driver's maintainer.) Alan Stern Index: usb-4.x/drivers/usb/core/hub.c =================================================================== --- usb-4.x.orig/drivers/usb/core/hub.c +++ usb-4.x/drivers/usb/core/hub.c @@ -3636,11 +3636,52 @@ static int hub_suspend(struct usb_interf return 0; } +/* Report wakeup requests from the ports of a resuming root hub */ +static void report_wakeup_requests(struct usb_hub *hub) +{ + struct usb_device *hdev = hub->hdev; + struct usb_hcd *hcd; + unsigned long resuming_ports; + int i; + + if (hdev->parent) + return; /* Not a root hub */ + + hcd = bus_to_hcd(hdev->bus); + if (hcd->driver->get_resuming_ports) { + + /* + * The get_resuming_ports() method returns a bitmap (origin 0) + * of ports which have started wakeup signaling but have not + * yet finished resuming. During system resume we will + * resume all the enabled ports, regardless of any wakeup + * signals, which means the wakeup requests would be lost. + * To prevent this, report them to the PM core here. + */ + resuming_ports = hcd->driver->get_resuming_ports(hcd); + for (i = 0; i < hdev->maxchild; ++i) { + if (test_bit(i, &resuming_ports)) { + struct usb_port *port_dev = hub->ports[i]; + + pm_wakeup_event(&port_dev->dev, 0); + } + } + } +} + static int hub_resume(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata(intf); dev_dbg(&intf->dev, "%s\n", __func__); + + /* + * This should be called only for system resume, not runtime resume. + * We can't tell the difference here, so some wakeup requests will be + * reported more than once. This shouldn't matter much. + */ + report_wakeup_requests(hub); + hub_activate(hub, HUB_RESUME); return 0; } Index: usb-4.x/include/linux/usb/hcd.h =================================================================== --- usb-4.x.orig/include/linux/usb/hcd.h +++ usb-4.x/include/linux/usb/hcd.h @@ -322,6 +322,7 @@ struct hc_driver { int (*bus_suspend)(struct usb_hcd *); int (*bus_resume)(struct usb_hcd *); int (*start_port_reset)(struct usb_hcd *, unsigned port_num); + unsigned long (*get_resuming_ports)(struct usb_hcd *); /* force handover of high-speed port to full-speed companion */ void (*relinquish_port)(struct usb_hcd *, int); Index: usb-4.x/drivers/usb/host/ehci-hcd.c =================================================================== --- usb-4.x.orig/drivers/usb/host/ehci-hcd.c +++ usb-4.x/drivers/usb/host/ehci-hcd.c @@ -1226,6 +1226,7 @@ static const struct hc_driver ehci_hc_dr .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + .get_resuming_ports = ehci_get_resuming_ports, /* * device support Index: usb-4.x/drivers/usb/host/ehci-hub.c =================================================================== --- usb-4.x.orig/drivers/usb/host/ehci-hub.c +++ usb-4.x/drivers/usb/host/ehci-hub.c @@ -512,10 +512,18 @@ static int ehci_bus_resume (struct usb_h return -ESHUTDOWN; } +static unsigned long ehci_get_resuming_ports(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + return ehci->resuming_ports; +} + #else #define ehci_bus_suspend NULL #define ehci_bus_resume NULL +#define ehci_get_resuming_ports NULL #endif /* CONFIG_PM */ Index: usb-4.x/drivers/usb/host/xhci-hub.c =================================================================== --- usb-4.x.orig/drivers/usb/host/xhci-hub.c +++ usb-4.x/drivers/usb/host/xhci-hub.c @@ -1684,4 +1684,15 @@ int xhci_bus_resume(struct usb_hcd *hcd) return 0; } +unsigned long xhci_get_resuming_ports(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_bus_state *bus_state; + + bus_state = &xhci->bus_state[hcd_index(hcd)]; + + /* USB3 port wakeups are reported via usb_wakeup_notification() */ + return bus_state->resuming_ports; /* USB2 ports only */ +} + #endif /* CONFIG_PM */ Index: usb-4.x/drivers/usb/host/xhci.c =================================================================== --- usb-4.x.orig/drivers/usb/host/xhci.c +++ usb-4.x/drivers/usb/host/xhci.c @@ -5019,6 +5019,7 @@ static const struct hc_driver xhci_hc_dr .hub_status_data = xhci_hub_status_data, .bus_suspend = xhci_bus_suspend, .bus_resume = xhci_bus_resume, + .get_resuming_ports = xhci_get_resuming_ports, /* * call back when device connected and addressed Index: usb-4.x/drivers/usb/host/xhci.h =================================================================== --- usb-4.x.orig/drivers/usb/host/xhci.h +++ usb-4.x/drivers/usb/host/xhci.h @@ -2104,9 +2104,11 @@ void xhci_hc_died(struct xhci_hcd *xhci) #ifdef CONFIG_PM int xhci_bus_suspend(struct usb_hcd *hcd); int xhci_bus_resume(struct usb_hcd *hcd); +unsigned long xhci_get_resuming_ports(struct usb_hcd *hcd); #else #define xhci_bus_suspend NULL #define xhci_bus_resume NULL +#define xhci_get_resuming_ports NULL #endif /* CONFIG_PM */ u32 xhci_port_state_to_neutral(u32 state); -- 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