diff options
Diffstat (limited to 'drivers/usb/host/ehci-q.c')
-rw-r--r-- | drivers/usb/host/ehci-q.c | 43 |
1 files changed, 36 insertions, 7 deletions
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 377ed530b920..57a84795c43f 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c | |||
@@ -310,13 +310,13 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); | |||
310 | static unsigned | 310 | static unsigned |
311 | qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) | 311 | qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) |
312 | { | 312 | { |
313 | struct ehci_qtd *last = NULL, *end = qh->dummy; | 313 | struct ehci_qtd *last, *end = qh->dummy; |
314 | struct list_head *entry, *tmp; | 314 | struct list_head *entry, *tmp; |
315 | int last_status = -EINPROGRESS; | 315 | int last_status; |
316 | int stopped; | 316 | int stopped; |
317 | unsigned count = 0; | 317 | unsigned count = 0; |
318 | u8 state; | 318 | u8 state; |
319 | __le32 halt = HALT_BIT(ehci); | 319 | const __le32 halt = HALT_BIT(ehci); |
320 | struct ehci_qh_hw *hw = qh->hw; | 320 | struct ehci_qh_hw *hw = qh->hw; |
321 | 321 | ||
322 | if (unlikely (list_empty (&qh->qtd_list))) | 322 | if (unlikely (list_empty (&qh->qtd_list))) |
@@ -327,11 +327,20 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
327 | * they add urbs to this qh's queue or mark them for unlinking. | 327 | * they add urbs to this qh's queue or mark them for unlinking. |
328 | * | 328 | * |
329 | * NOTE: unlinking expects to be done in queue order. | 329 | * NOTE: unlinking expects to be done in queue order. |
330 | * | ||
331 | * It's a bug for qh->qh_state to be anything other than | ||
332 | * QH_STATE_IDLE, unless our caller is scan_async() or | ||
333 | * scan_periodic(). | ||
330 | */ | 334 | */ |
331 | state = qh->qh_state; | 335 | state = qh->qh_state; |
332 | qh->qh_state = QH_STATE_COMPLETING; | 336 | qh->qh_state = QH_STATE_COMPLETING; |
333 | stopped = (state == QH_STATE_IDLE); | 337 | stopped = (state == QH_STATE_IDLE); |
334 | 338 | ||
339 | rescan: | ||
340 | last = NULL; | ||
341 | last_status = -EINPROGRESS; | ||
342 | qh->needs_rescan = 0; | ||
343 | |||
335 | /* remove de-activated QTDs from front of queue. | 344 | /* remove de-activated QTDs from front of queue. |
336 | * after faults (including short reads), cleanup this urb | 345 | * after faults (including short reads), cleanup this urb |
337 | * then let the queue advance. | 346 | * then let the queue advance. |
@@ -507,6 +516,21 @@ halt: | |||
507 | ehci_qtd_free (ehci, last); | 516 | ehci_qtd_free (ehci, last); |
508 | } | 517 | } |
509 | 518 | ||
519 | /* Do we need to rescan for URBs dequeued during a giveback? */ | ||
520 | if (unlikely(qh->needs_rescan)) { | ||
521 | /* If the QH is already unlinked, do the rescan now. */ | ||
522 | if (state == QH_STATE_IDLE) | ||
523 | goto rescan; | ||
524 | |||
525 | /* Otherwise we have to wait until the QH is fully unlinked. | ||
526 | * Our caller will start an unlink if qh->needs_rescan is | ||
527 | * set. But if an unlink has already started, nothing needs | ||
528 | * to be done. | ||
529 | */ | ||
530 | if (state != QH_STATE_LINKED) | ||
531 | qh->needs_rescan = 0; | ||
532 | } | ||
533 | |||
510 | /* restore original state; caller must unlink or relink */ | 534 | /* restore original state; caller must unlink or relink */ |
511 | qh->qh_state = state; | 535 | qh->qh_state = state; |
512 | 536 | ||
@@ -535,8 +559,10 @@ halt: | |||
535 | & hw->hw_info2) != 0) { | 559 | & hw->hw_info2) != 0) { |
536 | intr_deschedule (ehci, qh); | 560 | intr_deschedule (ehci, qh); |
537 | (void) qh_schedule (ehci, qh); | 561 | (void) qh_schedule (ehci, qh); |
538 | } else | 562 | } else { |
539 | unlink_async (ehci, qh); | 563 | /* Tell the caller to start an unlink */ |
564 | qh->needs_rescan = 1; | ||
565 | } | ||
540 | break; | 566 | break; |
541 | /* otherwise, unlink already started */ | 567 | /* otherwise, unlink already started */ |
542 | } | 568 | } |
@@ -916,6 +942,8 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
916 | if (unlikely(qh->clearing_tt)) | 942 | if (unlikely(qh->clearing_tt)) |
917 | return; | 943 | return; |
918 | 944 | ||
945 | WARN_ON(qh->qh_state != QH_STATE_IDLE); | ||
946 | |||
919 | /* (re)start the async schedule? */ | 947 | /* (re)start the async schedule? */ |
920 | head = ehci->async; | 948 | head = ehci->async; |
921 | timer_action_done (ehci, TIMER_ASYNC_OFF); | 949 | timer_action_done (ehci, TIMER_ASYNC_OFF); |
@@ -934,8 +962,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
934 | } | 962 | } |
935 | 963 | ||
936 | /* clear halt and/or toggle; and maybe recover from silicon quirk */ | 964 | /* clear halt and/or toggle; and maybe recover from silicon quirk */ |
937 | if (qh->qh_state == QH_STATE_IDLE) | 965 | qh_refresh(ehci, qh); |
938 | qh_refresh (ehci, qh); | ||
939 | 966 | ||
940 | /* splice right after start */ | 967 | /* splice right after start */ |
941 | qh->qh_next = head->qh_next; | 968 | qh->qh_next = head->qh_next; |
@@ -1220,6 +1247,8 @@ rescan: | |||
1220 | qh = qh_get (qh); | 1247 | qh = qh_get (qh); |
1221 | qh->stamp = ehci->stamp; | 1248 | qh->stamp = ehci->stamp; |
1222 | temp = qh_completions (ehci, qh); | 1249 | temp = qh_completions (ehci, qh); |
1250 | if (qh->needs_rescan) | ||
1251 | unlink_async(ehci, qh); | ||
1223 | qh_put (qh); | 1252 | qh_put (qh); |
1224 | if (temp != 0) { | 1253 | if (temp != 0) { |
1225 | goto rescan; | 1254 | goto rescan; |