diff options
Diffstat (limited to 'drivers/usb/host/uhci-hub.c')
-rw-r--r-- | drivers/usb/host/uhci-hub.c | 83 |
1 files changed, 79 insertions, 4 deletions
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 4c45ba8390f8..4eace2b19ddb 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c | |||
@@ -33,9 +33,24 @@ static __u8 root_hub_hub_des[] = | |||
33 | /* status change bits: nonzero writes will clear */ | 33 | /* status change bits: nonzero writes will clear */ |
34 | #define RWC_BITS (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC) | 34 | #define RWC_BITS (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC) |
35 | 35 | ||
36 | static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) | 36 | /* A port that either is connected or has a changed-bit set will prevent |
37 | * us from AUTO_STOPPING. | ||
38 | */ | ||
39 | static int any_ports_active(struct uhci_hcd *uhci) | ||
40 | { | ||
41 | int port; | ||
42 | |||
43 | for (port = 0; port < uhci->rh_numports; ++port) { | ||
44 | if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & | ||
45 | (USBPORTSC_CCS | RWC_BITS)) || | ||
46 | test_bit(port, &uhci->port_c_suspend)) | ||
47 | return 1; | ||
48 | } | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf) | ||
37 | { | 53 | { |
38 | struct uhci_hcd *uhci = hcd_to_uhci(hcd); | ||
39 | int port; | 54 | int port; |
40 | 55 | ||
41 | *buf = 0; | 56 | *buf = 0; |
@@ -44,8 +59,6 @@ static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) | |||
44 | test_bit(port, &uhci->port_c_suspend)) | 59 | test_bit(port, &uhci->port_c_suspend)) |
45 | *buf |= (1 << (port + 1)); | 60 | *buf |= (1 << (port + 1)); |
46 | } | 61 | } |
47 | if (*buf && uhci->state == UHCI_SUSPENDED) | ||
48 | uhci->resume_detect = 1; | ||
49 | return !!*buf; | 62 | return !!*buf; |
50 | } | 63 | } |
51 | 64 | ||
@@ -115,6 +128,11 @@ static void uhci_check_ports(struct uhci_hcd *uhci) | |||
115 | set_bit(port, &uhci->resuming_ports); | 128 | set_bit(port, &uhci->resuming_ports); |
116 | uhci->ports_timeout = jiffies + | 129 | uhci->ports_timeout = jiffies + |
117 | msecs_to_jiffies(20); | 130 | msecs_to_jiffies(20); |
131 | |||
132 | /* Make sure we see the port again | ||
133 | * after the resuming period is over. */ | ||
134 | mod_timer(&uhci_to_hcd(uhci)->rh_timer, | ||
135 | uhci->ports_timeout); | ||
118 | } else if (time_after_eq(jiffies, | 136 | } else if (time_after_eq(jiffies, |
119 | uhci->ports_timeout)) { | 137 | uhci->ports_timeout)) { |
120 | uhci_finish_suspend(uhci, port, port_addr); | 138 | uhci_finish_suspend(uhci, port, port_addr); |
@@ -123,6 +141,60 @@ static void uhci_check_ports(struct uhci_hcd *uhci) | |||
123 | } | 141 | } |
124 | } | 142 | } |
125 | 143 | ||
144 | static int uhci_hub_status_data(struct usb_hcd *hcd, char *buf) | ||
145 | { | ||
146 | struct uhci_hcd *uhci = hcd_to_uhci(hcd); | ||
147 | unsigned long flags; | ||
148 | int status; | ||
149 | |||
150 | spin_lock_irqsave(&uhci->lock, flags); | ||
151 | if (uhci->hc_inaccessible) { | ||
152 | status = 0; | ||
153 | goto done; | ||
154 | } | ||
155 | |||
156 | uhci_check_ports(uhci); | ||
157 | status = get_hub_status_data(uhci, buf); | ||
158 | |||
159 | switch (uhci->rh_state) { | ||
160 | case UHCI_RH_SUSPENDING: | ||
161 | case UHCI_RH_SUSPENDED: | ||
162 | /* if port change, ask to be resumed */ | ||
163 | if (status) | ||
164 | usb_hcd_resume_root_hub(hcd); | ||
165 | break; | ||
166 | |||
167 | case UHCI_RH_AUTO_STOPPED: | ||
168 | /* if port change, auto start */ | ||
169 | if (status) | ||
170 | wakeup_rh(uhci); | ||
171 | break; | ||
172 | |||
173 | case UHCI_RH_RUNNING: | ||
174 | /* are any devices attached? */ | ||
175 | if (!any_ports_active(uhci)) { | ||
176 | uhci->rh_state = UHCI_RH_RUNNING_NODEVS; | ||
177 | uhci->auto_stop_time = jiffies + HZ; | ||
178 | } | ||
179 | break; | ||
180 | |||
181 | case UHCI_RH_RUNNING_NODEVS: | ||
182 | /* auto-stop if nothing connected for 1 second */ | ||
183 | if (any_ports_active(uhci)) | ||
184 | uhci->rh_state = UHCI_RH_RUNNING; | ||
185 | else if (time_after_eq(jiffies, uhci->auto_stop_time)) | ||
186 | suspend_rh(uhci, UHCI_RH_AUTO_STOPPED); | ||
187 | break; | ||
188 | |||
189 | default: | ||
190 | break; | ||
191 | } | ||
192 | |||
193 | done: | ||
194 | spin_unlock_irqrestore(&uhci->lock, flags); | ||
195 | return status; | ||
196 | } | ||
197 | |||
126 | /* size of returned buffer is part of USB spec */ | 198 | /* size of returned buffer is part of USB spec */ |
127 | static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | 199 | static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, |
128 | u16 wIndex, char *buf, u16 wLength) | 200 | u16 wIndex, char *buf, u16 wLength) |
@@ -134,6 +206,9 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
134 | u16 wPortChange, wPortStatus; | 206 | u16 wPortChange, wPortStatus; |
135 | unsigned long flags; | 207 | unsigned long flags; |
136 | 208 | ||
209 | if (uhci->hc_inaccessible) | ||
210 | return -ETIMEDOUT; | ||
211 | |||
137 | spin_lock_irqsave(&uhci->lock, flags); | 212 | spin_lock_irqsave(&uhci->lock, flags); |
138 | switch (typeReq) { | 213 | switch (typeReq) { |
139 | 214 | ||