diff options
Diffstat (limited to 'drivers/usb/host/ehci-q.c')
-rw-r--r-- | drivers/usb/host/ehci-q.c | 62 |
1 files changed, 33 insertions, 29 deletions
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index a8f5408c161d..794d27e07807 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c | |||
@@ -139,63 +139,65 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
139 | 139 | ||
140 | /*-------------------------------------------------------------------------*/ | 140 | /*-------------------------------------------------------------------------*/ |
141 | 141 | ||
142 | static void qtd_copy_status ( | 142 | static int qtd_copy_status ( |
143 | struct ehci_hcd *ehci, | 143 | struct ehci_hcd *ehci, |
144 | struct urb *urb, | 144 | struct urb *urb, |
145 | size_t length, | 145 | size_t length, |
146 | u32 token | 146 | u32 token |
147 | ) | 147 | ) |
148 | { | 148 | { |
149 | int status = -EINPROGRESS; | ||
150 | |||
149 | /* count IN/OUT bytes, not SETUP (even short packets) */ | 151 | /* count IN/OUT bytes, not SETUP (even short packets) */ |
150 | if (likely (QTD_PID (token) != 2)) | 152 | if (likely (QTD_PID (token) != 2)) |
151 | urb->actual_length += length - QTD_LENGTH (token); | 153 | urb->actual_length += length - QTD_LENGTH (token); |
152 | 154 | ||
153 | /* don't modify error codes */ | 155 | /* don't modify error codes */ |
154 | if (unlikely(urb->unlinked)) | 156 | if (unlikely(urb->unlinked)) |
155 | return; | 157 | return status; |
156 | 158 | ||
157 | /* force cleanup after short read; not always an error */ | 159 | /* force cleanup after short read; not always an error */ |
158 | if (unlikely (IS_SHORT_READ (token))) | 160 | if (unlikely (IS_SHORT_READ (token))) |
159 | urb->status = -EREMOTEIO; | 161 | status = -EREMOTEIO; |
160 | 162 | ||
161 | /* serious "can't proceed" faults reported by the hardware */ | 163 | /* serious "can't proceed" faults reported by the hardware */ |
162 | if (token & QTD_STS_HALT) { | 164 | if (token & QTD_STS_HALT) { |
163 | if (token & QTD_STS_BABBLE) { | 165 | if (token & QTD_STS_BABBLE) { |
164 | /* FIXME "must" disable babbling device's port too */ | 166 | /* FIXME "must" disable babbling device's port too */ |
165 | urb->status = -EOVERFLOW; | 167 | status = -EOVERFLOW; |
166 | } else if (token & QTD_STS_MMF) { | 168 | } else if (token & QTD_STS_MMF) { |
167 | /* fs/ls interrupt xfer missed the complete-split */ | 169 | /* fs/ls interrupt xfer missed the complete-split */ |
168 | urb->status = -EPROTO; | 170 | status = -EPROTO; |
169 | } else if (token & QTD_STS_DBE) { | 171 | } else if (token & QTD_STS_DBE) { |
170 | urb->status = (QTD_PID (token) == 1) /* IN ? */ | 172 | status = (QTD_PID (token) == 1) /* IN ? */ |
171 | ? -ENOSR /* hc couldn't read data */ | 173 | ? -ENOSR /* hc couldn't read data */ |
172 | : -ECOMM; /* hc couldn't write data */ | 174 | : -ECOMM; /* hc couldn't write data */ |
173 | } else if (token & QTD_STS_XACT) { | 175 | } else if (token & QTD_STS_XACT) { |
174 | /* timeout, bad crc, wrong PID, etc; retried */ | 176 | /* timeout, bad crc, wrong PID, etc; retried */ |
175 | if (QTD_CERR (token)) | 177 | if (QTD_CERR (token)) |
176 | urb->status = -EPIPE; | 178 | status = -EPIPE; |
177 | else { | 179 | else { |
178 | ehci_dbg (ehci, "devpath %s ep%d%s 3strikes\n", | 180 | ehci_dbg (ehci, "devpath %s ep%d%s 3strikes\n", |
179 | urb->dev->devpath, | 181 | urb->dev->devpath, |
180 | usb_pipeendpoint (urb->pipe), | 182 | usb_pipeendpoint (urb->pipe), |
181 | usb_pipein (urb->pipe) ? "in" : "out"); | 183 | usb_pipein (urb->pipe) ? "in" : "out"); |
182 | urb->status = -EPROTO; | 184 | status = -EPROTO; |
183 | } | 185 | } |
184 | /* CERR nonzero + no errors + halt --> stall */ | 186 | /* CERR nonzero + no errors + halt --> stall */ |
185 | } else if (QTD_CERR (token)) | 187 | } else if (QTD_CERR (token)) |
186 | urb->status = -EPIPE; | 188 | status = -EPIPE; |
187 | else /* unknown */ | 189 | else /* unknown */ |
188 | urb->status = -EPROTO; | 190 | status = -EPROTO; |
189 | 191 | ||
190 | ehci_vdbg (ehci, | 192 | ehci_vdbg (ehci, |
191 | "dev%d ep%d%s qtd token %08x --> status %d\n", | 193 | "dev%d ep%d%s qtd token %08x --> status %d\n", |
192 | usb_pipedevice (urb->pipe), | 194 | usb_pipedevice (urb->pipe), |
193 | usb_pipeendpoint (urb->pipe), | 195 | usb_pipeendpoint (urb->pipe), |
194 | usb_pipein (urb->pipe) ? "in" : "out", | 196 | usb_pipein (urb->pipe) ? "in" : "out", |
195 | token, urb->status); | 197 | token, status); |
196 | 198 | ||
197 | /* if async CSPLIT failed, try cleaning out the TT buffer */ | 199 | /* if async CSPLIT failed, try cleaning out the TT buffer */ |
198 | if (urb->status != -EPIPE | 200 | if (status != -EPIPE |
199 | && urb->dev->tt && !usb_pipeint (urb->pipe) | 201 | && urb->dev->tt && !usb_pipeint (urb->pipe) |
200 | && ((token & QTD_STS_MMF) != 0 | 202 | && ((token & QTD_STS_MMF) != 0 |
201 | || QTD_CERR(token) == 0) | 203 | || QTD_CERR(token) == 0) |
@@ -212,10 +214,12 @@ static void qtd_copy_status ( | |||
212 | usb_hub_tt_clear_buffer (urb->dev, urb->pipe); | 214 | usb_hub_tt_clear_buffer (urb->dev, urb->pipe); |
213 | } | 215 | } |
214 | } | 216 | } |
217 | |||
218 | return status; | ||
215 | } | 219 | } |
216 | 220 | ||
217 | static void | 221 | static void |
218 | ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb) | 222 | ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status) |
219 | __releases(ehci->lock) | 223 | __releases(ehci->lock) |
220 | __acquires(ehci->lock) | 224 | __acquires(ehci->lock) |
221 | { | 225 | { |
@@ -231,17 +235,13 @@ __acquires(ehci->lock) | |||
231 | qh_put (qh); | 235 | qh_put (qh); |
232 | } | 236 | } |
233 | 237 | ||
234 | spin_lock (&urb->lock); | ||
235 | if (unlikely(urb->unlinked)) { | 238 | if (unlikely(urb->unlinked)) { |
236 | COUNT(ehci->stats.unlink); | 239 | COUNT(ehci->stats.unlink); |
237 | } else { | 240 | } else { |
238 | if (likely(urb->status == -EINPROGRESS || | 241 | if (likely(status == -EINPROGRESS)) |
239 | (urb->status == -EREMOTEIO && | 242 | status = 0; |
240 | !(urb->transfer_flags & URB_SHORT_NOT_OK)))) | ||
241 | urb->status = 0; | ||
242 | COUNT(ehci->stats.complete); | 243 | COUNT(ehci->stats.complete); |
243 | } | 244 | } |
244 | spin_unlock (&urb->lock); | ||
245 | 245 | ||
246 | #ifdef EHCI_URB_TRACE | 246 | #ifdef EHCI_URB_TRACE |
247 | ehci_dbg (ehci, | 247 | ehci_dbg (ehci, |
@@ -249,13 +249,14 @@ __acquires(ehci->lock) | |||
249 | __FUNCTION__, urb->dev->devpath, urb, | 249 | __FUNCTION__, urb->dev->devpath, urb, |
250 | usb_pipeendpoint (urb->pipe), | 250 | usb_pipeendpoint (urb->pipe), |
251 | usb_pipein (urb->pipe) ? "in" : "out", | 251 | usb_pipein (urb->pipe) ? "in" : "out", |
252 | urb->status, | 252 | status, |
253 | urb->actual_length, urb->transfer_buffer_length); | 253 | urb->actual_length, urb->transfer_buffer_length); |
254 | #endif | 254 | #endif |
255 | 255 | ||
256 | /* complete() can reenter this HCD */ | 256 | /* complete() can reenter this HCD */ |
257 | usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); | 257 | usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb); |
258 | spin_unlock (&ehci->lock); | 258 | spin_unlock (&ehci->lock); |
259 | urb->status = status; | ||
259 | usb_hcd_giveback_urb (ehci_to_hcd(ehci), urb); | 260 | usb_hcd_giveback_urb (ehci_to_hcd(ehci), urb); |
260 | spin_lock (&ehci->lock); | 261 | spin_lock (&ehci->lock); |
261 | } | 262 | } |
@@ -276,6 +277,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
276 | { | 277 | { |
277 | struct ehci_qtd *last = NULL, *end = qh->dummy; | 278 | struct ehci_qtd *last = NULL, *end = qh->dummy; |
278 | struct list_head *entry, *tmp; | 279 | struct list_head *entry, *tmp; |
280 | int last_status = -EINPROGRESS; | ||
279 | int stopped; | 281 | int stopped; |
280 | unsigned count = 0; | 282 | unsigned count = 0; |
281 | int do_status = 0; | 283 | int do_status = 0; |
@@ -304,6 +306,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
304 | struct ehci_qtd *qtd; | 306 | struct ehci_qtd *qtd; |
305 | struct urb *urb; | 307 | struct urb *urb; |
306 | u32 token = 0; | 308 | u32 token = 0; |
309 | int qtd_status; | ||
307 | 310 | ||
308 | qtd = list_entry (entry, struct ehci_qtd, qtd_list); | 311 | qtd = list_entry (entry, struct ehci_qtd, qtd_list); |
309 | urb = qtd->urb; | 312 | urb = qtd->urb; |
@@ -311,11 +314,12 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
311 | /* clean up any state from previous QTD ...*/ | 314 | /* clean up any state from previous QTD ...*/ |
312 | if (last) { | 315 | if (last) { |
313 | if (likely (last->urb != urb)) { | 316 | if (likely (last->urb != urb)) { |
314 | ehci_urb_done (ehci, last->urb); | 317 | ehci_urb_done(ehci, last->urb, last_status); |
315 | count++; | 318 | count++; |
316 | } | 319 | } |
317 | ehci_qtd_free (ehci, last); | 320 | ehci_qtd_free (ehci, last); |
318 | last = NULL; | 321 | last = NULL; |
322 | last_status = -EINPROGRESS; | ||
319 | } | 323 | } |
320 | 324 | ||
321 | /* ignore urbs submitted during completions we reported */ | 325 | /* ignore urbs submitted during completions we reported */ |
@@ -351,13 +355,13 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
351 | stopped = 1; | 355 | stopped = 1; |
352 | 356 | ||
353 | if (unlikely (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) | 357 | if (unlikely (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) |
354 | urb->status = -ESHUTDOWN; | 358 | last_status = -ESHUTDOWN; |
355 | 359 | ||
356 | /* ignore active urbs unless some previous qtd | 360 | /* ignore active urbs unless some previous qtd |
357 | * for the urb faulted (including short read) or | 361 | * for the urb faulted (including short read) or |
358 | * its urb was canceled. we may patch qh or qtds. | 362 | * its urb was canceled. we may patch qh or qtds. |
359 | */ | 363 | */ |
360 | if (likely(urb->status == -EINPROGRESS && | 364 | if (likely(last_status == -EINPROGRESS && |
361 | !urb->unlinked)) | 365 | !urb->unlinked)) |
362 | continue; | 366 | continue; |
363 | 367 | ||
@@ -386,14 +390,14 @@ halt: | |||
386 | } | 390 | } |
387 | 391 | ||
388 | /* remove it from the queue */ | 392 | /* remove it from the queue */ |
389 | spin_lock (&urb->lock); | 393 | qtd_status = qtd_copy_status(ehci, urb, qtd->length, token); |
390 | qtd_copy_status (ehci, urb, qtd->length, token); | 394 | if (unlikely(qtd_status == -EREMOTEIO)) { |
391 | if (unlikely(urb->status == -EREMOTEIO)) { | ||
392 | do_status = (!urb->unlinked && | 395 | do_status = (!urb->unlinked && |
393 | usb_pipecontrol(urb->pipe)); | 396 | usb_pipecontrol(urb->pipe)); |
394 | urb->status = 0; | 397 | qtd_status = 0; |
395 | } | 398 | } |
396 | spin_unlock (&urb->lock); | 399 | if (likely(last_status == -EINPROGRESS)) |
400 | last_status = qtd_status; | ||
397 | 401 | ||
398 | if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { | 402 | if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { |
399 | last = list_entry (qtd->qtd_list.prev, | 403 | last = list_entry (qtd->qtd_list.prev, |
@@ -406,7 +410,7 @@ halt: | |||
406 | 410 | ||
407 | /* last urb's completion might still need calling */ | 411 | /* last urb's completion might still need calling */ |
408 | if (likely (last != NULL)) { | 412 | if (likely (last != NULL)) { |
409 | ehci_urb_done (ehci, last->urb); | 413 | ehci_urb_done(ehci, last->urb, last_status); |
410 | count++; | 414 | count++; |
411 | ehci_qtd_free (ehci, last); | 415 | ehci_qtd_free (ehci, last); |
412 | } | 416 | } |