On Fri, 23 Mar 2012, Chen Peter-B29397 wrote: > ************************************************************************************************************* > Index: usb-3.3/drivers/usb/core/hcd.c > =================================================================== > --- usb-3.3.orig/drivers/usb/core/hcd.c > +++ usb-3.3/drivers/usb/core/hcd.c > @@ -1978,6 +1978,13 @@ int hcd_bus_suspend(struct usb_device *r > if (status == 0) { > usb_set_device_state(rhdev, USB_STATE_SUSPENDED); > hcd->state = HC_STATE_SUSPENDED; > + > + /* Did we race with a root-hub wakeup event? */ > + if (unlikely(HCD_POLL_PENDING(hcd) && > + rhdev->do_remote_wakeup)) { > + hcd_bus_resume(rhdev, PMSG_AUTO_RESUME); > + status = -EBUSY; > + } > > This patch may not useful, as if the remote wakeup occurs before the bus(roothub) suspend > (taking ehic_bus_suspend as an example), the bus suspend will quit due to there is a resume > signal at one port. If the remote wakeup occurs after bus suspend, but before your patch, > the HCD_FLAG_POLL_PENDING will not be set due to hcd->driver->hub_status_data returns 0 (taking > ehci as an example), as this flag is set after 25ms later after remote wakeup occurs. I promised an update for this patch, and here it is. The new version requires changes to the HCD drivers. Basically, the HCD is now required to return a non-zero value from the hub_status_data routine if a resume is currently in progress on any port. The changes for ehci-hcd are included as a separate patch below. Alan Stern Index: usb-3.3/drivers/usb/core/hcd.c =================================================================== --- usb-3.3.orig/drivers/usb/core/hcd.c +++ usb-3.3/drivers/usb/core/hcd.c @@ -1978,6 +1978,18 @@ int hcd_bus_suspend(struct usb_device *r if (status == 0) { usb_set_device_state(rhdev, USB_STATE_SUSPENDED); hcd->state = HC_STATE_SUSPENDED; + + /* Did we race with a root-hub wakeup event? */ + if (rhdev->do_remote_wakeup) { + char buffer[6]; + + status = hcd->driver->hub_status_data(hcd, buffer); + if (status != 0) { + dev_dbg(&rhdev->dev, "suspend raced with wakeup event\n"); + hcd_bus_resume(rhdev, PMSG_AUTO_RESUME); + status = -EBUSY; + } + } } else { spin_lock_irq(&hcd_root_hub_lock); if (!HCD_DEAD(hcd)) { Index: usb-3.3/drivers/usb/host/ehci-hub.c =================================================================== --- usb-3.3.orig/drivers/usb/host/ehci-hub.c +++ usb-3.3/drivers/usb/host/ehci-hub.c @@ -223,15 +223,10 @@ static int ehci_bus_suspend (struct usb_ * remote wakeup, we must fail the suspend. */ if (hcd->self.root_hub->do_remote_wakeup) { - port = HCS_N_PORTS(ehci->hcs_params); - while (port--) { - if (ehci->reset_done[port] != 0) { - spin_unlock_irq(&ehci->lock); - ehci_dbg(ehci, "suspend failed because " - "port %d is resuming\n", - port + 1); - return -EBUSY; - } + if (ehci->resuming_ports) { + spin_unlock_irq(&ehci->lock); + ehci_dbg(ehci, "suspend failed because a port is resuming\n"); + return -EBUSY; } } @@ -554,16 +549,12 @@ static int ehci_hub_status_data (struct usb_hcd *hcd, char *buf) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 temp, status = 0; + u32 temp, status; u32 mask; int ports, i, retval = 1; unsigned long flags; u32 ppcd = 0; - /* if !USB_SUSPEND, root hub timers won't get shut down ... */ - if (ehci->rh_state != EHCI_RH_RUNNING) - return 0; - /* init status to no-changes */ buf [0] = 0; ports = HCS_N_PORTS (ehci->hcs_params); @@ -572,6 +563,11 @@ ehci_hub_status_data (struct usb_hcd *hc retval++; } + /* Inform the core about resumes-in-progress by returning + * a non-zero value even if there are no status changes. + */ + status = ehci->resuming_ports; + /* Some boards (mostly VIA?) report bogus overcurrent indications, * causing massive log spam unless we completely ignore them. It * may be relevant that VIA VT8235 controllers, where PORT_POWER is @@ -846,6 +842,7 @@ static int ehci_hub_control ( ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESUME), status_reg); + clear_bit(wIndex, &ehci->resuming_ports); retval = handshake(ehci, status_reg, PORT_RESUME, 0, 2000 /* 2msec */); if (retval != 0) { @@ -864,6 +861,7 @@ static int ehci_hub_control ( ehci->reset_done[wIndex])) { status |= USB_PORT_STAT_C_RESET << 16; ehci->reset_done [wIndex] = 0; + clear_bit(wIndex, &ehci->resuming_ports); /* force reset to complete */ ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET), @@ -884,8 +882,10 @@ static int ehci_hub_control ( ehci_readl(ehci, status_reg)); } - if (!(temp & (PORT_RESUME|PORT_RESET))) + if (!(temp & (PORT_RESUME|PORT_RESET))) { ehci->reset_done[wIndex] = 0; + clear_bit(wIndex, &ehci->resuming_ports); + } /* transfer dedicated ports to the companion hc */ if ((temp & PORT_CONNECT) && @@ -920,6 +920,7 @@ static int ehci_hub_control ( status |= USB_PORT_STAT_SUSPEND; } else if (test_bit(wIndex, &ehci->suspended_ports)) { clear_bit(wIndex, &ehci->suspended_ports); + clear_bit(wIndex, &ehci->resuming_ports); ehci->reset_done[wIndex] = 0; if (temp & PORT_PE) set_bit(wIndex, &ehci->port_c_suspend); Index: usb-3.3/drivers/usb/host/ehci.h =================================================================== --- usb-3.3.orig/drivers/usb/host/ehci.h +++ usb-3.3/drivers/usb/host/ehci.h @@ -117,6 +117,8 @@ struct ehci_hcd { /* one per controlle the change-suspend feature turned on */ unsigned long suspended_ports; /* which ports are suspended */ + unsigned long resuming_ports; /* which ports have + start to resume */ /* per-HC memory pools (could be per-bus, but ...) */ struct dma_pool *qh_pool; /* qh per active urb */ -- 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