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

Reply via email to