Commit c8f4fe43 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

[PATCH] USB UHCI: Add root hub states

This patch starts making some serious changes to the UHCI driver.
There's a set of private states for the root hub, and the internal
routines for suspending and resuming work completely differently, with
transitions based on the new states.  Now the driver distinguishes
between a privately auto-stopped state and a publicly suspended state,
and it will properly suspend controllers with broken resume-detect
interrupts instead of resetting them.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent f5946f82
...@@ -237,6 +237,37 @@ static int uhci_show_sc(int port, unsigned short status, char *buf, int len) ...@@ -237,6 +237,37 @@ static int uhci_show_sc(int port, unsigned short status, char *buf, int len)
return out - buf; return out - buf;
} }
static int uhci_show_root_hub_state(struct uhci_hcd *uhci, char *buf, int len)
{
char *out = buf;
char *rh_state;
/* Try to make sure there's enough memory */
if (len < 60)
return 0;
switch (uhci->rh_state) {
case UHCI_RH_RESET:
rh_state = "reset"; break;
case UHCI_RH_SUSPENDED:
rh_state = "suspended"; break;
case UHCI_RH_AUTO_STOPPED:
rh_state = "auto-stopped"; break;
case UHCI_RH_RESUMING:
rh_state = "resuming"; break;
case UHCI_RH_SUSPENDING:
rh_state = "suspending"; break;
case UHCI_RH_RUNNING:
rh_state = "running"; break;
case UHCI_RH_RUNNING_NODEVS:
rh_state = "running, no devs"; break;
default:
rh_state = "?"; break;
}
out += sprintf(out, "Root-hub state: %s\n", rh_state);
return out - buf;
}
static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len) static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len)
{ {
char *out = buf; char *out = buf;
...@@ -408,6 +439,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) ...@@ -408,6 +439,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
spin_lock_irqsave(&uhci->lock, flags); spin_lock_irqsave(&uhci->lock, flags);
out += uhci_show_root_hub_state(uhci, out, len - (out - buf));
out += sprintf(out, "HC status\n"); out += sprintf(out, "HC status\n");
out += uhci_show_status(uhci, out, len - (out - buf)); out += uhci_show_status(uhci, out, len - (out - buf));
......
This diff is collapsed.
...@@ -314,26 +314,29 @@ static inline int __interval_to_skel(int interval) ...@@ -314,26 +314,29 @@ static inline int __interval_to_skel(int interval)
} }
/* /*
* Device states for the host controller. * States for the root hub.
* *
* To prevent "bouncing" in the presence of electrical noise, * To prevent "bouncing" in the presence of electrical noise,
* we insist on a 1-second "grace" period, before switching to * when there are no devices attached we delay for 1 second in the
* the RUNNING or SUSPENDED states, during which the state is * RUNNING_NODEVS state before switching to the AUTO_STOPPED state.
* not allowed to change. *
* * (Note that the AUTO_STOPPED state won't be necessary once the hub
* The resume process is divided into substates in order to avoid * driver learns to autosuspend.)
* potentially length delays during the timer handler.
*
* States in which the host controller is halted must have values <= 0.
*/ */
enum uhci_state { enum uhci_rh_state {
UHCI_RESET, /* In the next 4 states the HC must be halted */
UHCI_RUNNING_GRACE, /* Before RUNNING */ UHCI_RH_RESET,
UHCI_RUNNING, /* The normal state */ UHCI_RH_SUSPENDED,
UHCI_SUSPENDING_GRACE, /* Before SUSPENDED */ UHCI_RH_AUTO_STOPPED,
UHCI_SUSPENDED = -10, /* When no devices are attached */ UHCI_RH_RESUMING,
UHCI_RESUMING_1,
UHCI_RESUMING_2 /* In the next state the HC changes from running to halted, so it
* can legally appear either way */
UHCI_RH_SUSPENDING,
/* In the next two states it's an error if the HC is halted */
UHCI_RH_RUNNING, /* The normal state */
UHCI_RH_RUNNING_NODEVS, /* Running with no devices attached */
}; };
/* /*
...@@ -363,8 +366,9 @@ struct uhci_hcd { ...@@ -363,8 +366,9 @@ struct uhci_hcd {
int fsbr; /* Full-speed bandwidth reclamation */ int fsbr; /* Full-speed bandwidth reclamation */
unsigned long fsbrtimeout; /* FSBR delay */ unsigned long fsbrtimeout; /* FSBR delay */
enum uhci_state state; /* FIXME: needs a spinlock */ enum uhci_rh_state rh_state;
unsigned long state_end; /* Time of next transition */ unsigned long auto_stop_time; /* When to AUTO_STOP */
unsigned int frame_number; /* As of last check */ unsigned int frame_number; /* As of last check */
unsigned int is_stopped; unsigned int is_stopped;
#define UHCI_IS_STOPPED 9999 /* Larger than a frame # */ #define UHCI_IS_STOPPED 9999 /* Larger than a frame # */
...@@ -451,4 +455,11 @@ struct urb_priv { ...@@ -451,4 +455,11 @@ struct urb_priv {
* #2 urb->lock * #2 urb->lock
*/ */
/* Some special IDs */
#define PCI_VENDOR_ID_GENESYS 0x17a0
#define PCI_DEVICE_ID_GL880S_UHCI 0x8083
#define PCI_DEVICE_ID_GL880S_EHCI 0x8084
#endif #endif
...@@ -60,7 +60,7 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) ...@@ -60,7 +60,7 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf)
test_bit(port, &uhci->port_c_suspend)) test_bit(port, &uhci->port_c_suspend))
*buf |= (1 << (port + 1)); *buf |= (1 << (port + 1));
} }
if (*buf && uhci->state == UHCI_SUSPENDED) if (*buf && uhci->is_stopped)
uhci->resume_detect = 1; uhci->resume_detect = 1;
return !!*buf; return !!*buf;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment