diff options
Diffstat (limited to 'drivers/usb/host/uhci-hub.c')
-rw-r--r-- | drivers/usb/host/uhci-hub.c | 67 |
1 files changed, 60 insertions, 7 deletions
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 13652de5220..4eace2b19dd 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -49,22 +49,16 @@ static int any_ports_active(struct uhci_hcd *uhci) return 0; } -static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) +static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf) { - struct uhci_hcd *uhci = hcd_to_uhci(hcd); int port; - if (uhci->hc_inaccessible) - return 0; - *buf = 0; for (port = 0; port < uhci->rh_numports; ++port) { if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & RWC_BITS) || test_bit(port, &uhci->port_c_suspend)) *buf |= (1 << (port + 1)); } - if (*buf && uhci->is_stopped) - uhci->resume_detect = 1; return !!*buf; } @@ -134,6 +128,11 @@ static void uhci_check_ports(struct uhci_hcd *uhci) set_bit(port, &uhci->resuming_ports); uhci->ports_timeout = jiffies + msecs_to_jiffies(20); + + /* Make sure we see the port again + * after the resuming period is over. */ + mod_timer(&uhci_to_hcd(uhci)->rh_timer, + uhci->ports_timeout); } else if (time_after_eq(jiffies, uhci->ports_timeout)) { uhci_finish_suspend(uhci, port, port_addr); @@ -142,6 +141,60 @@ static void uhci_check_ports(struct uhci_hcd *uhci) } } +static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + unsigned long flags; + int status; + + spin_lock_irqsave(&uhci->lock, flags); + if (uhci->hc_inaccessible) { + status = 0; + goto done; + } + + uhci_check_ports(uhci); + status = get_hub_status_data(uhci, buf); + + switch (uhci->rh_state) { + case UHCI_RH_SUSPENDING: + case UHCI_RH_SUSPENDED: + /* if port change, ask to be resumed */ + if (status) + usb_hcd_resume_root_hub(hcd); + break; + + case UHCI_RH_AUTO_STOPPED: + /* if port change, auto start */ + if (status) + wakeup_rh(uhci); + break; + + case UHCI_RH_RUNNING: + /* are any devices attached? */ + if (!any_ports_active(uhci)) { + uhci->rh_state = UHCI_RH_RUNNING_NODEVS; + uhci->auto_stop_time = jiffies + HZ; + } + break; + + case UHCI_RH_RUNNING_NODEVS: + /* auto-stop if nothing connected for 1 second */ + if (any_ports_active(uhci)) + uhci->rh_state = UHCI_RH_RUNNING; + else if (time_after_eq(jiffies, uhci->auto_stop_time)) + suspend_rh(uhci, UHCI_RH_AUTO_STOPPED); + break; + + default: + break; + } + +done: + spin_unlock_irqrestore(&uhci->lock, flags); + return status; +} + /* size of returned buffer is part of USB spec */ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) |