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 [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html