diff options
Diffstat (limited to 'drivers/usb/host/ehci-sched.c')
-rw-r--r-- | drivers/usb/host/ehci-sched.c | 36 |
1 files changed, 33 insertions, 3 deletions
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 327437af2122..3ea05936851f 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c | |||
@@ -615,8 +615,19 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
615 | 615 | ||
616 | static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) | 616 | static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) |
617 | { | 617 | { |
618 | unsigned wait; | 618 | unsigned wait; |
619 | struct ehci_qh_hw *hw = qh->hw; | 619 | struct ehci_qh_hw *hw = qh->hw; |
620 | int rc; | ||
621 | |||
622 | /* If the QH isn't linked then there's nothing we can do | ||
623 | * unless we were called during a giveback, in which case | ||
624 | * qh_completions() has to deal with it. | ||
625 | */ | ||
626 | if (qh->qh_state != QH_STATE_LINKED) { | ||
627 | if (qh->qh_state == QH_STATE_COMPLETING) | ||
628 | qh->needs_rescan = 1; | ||
629 | return; | ||
630 | } | ||
620 | 631 | ||
621 | qh_unlink_periodic (ehci, qh); | 632 | qh_unlink_periodic (ehci, qh); |
622 | 633 | ||
@@ -636,6 +647,24 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
636 | qh->qh_state = QH_STATE_IDLE; | 647 | qh->qh_state = QH_STATE_IDLE; |
637 | hw->hw_next = EHCI_LIST_END(ehci); | 648 | hw->hw_next = EHCI_LIST_END(ehci); |
638 | wmb (); | 649 | wmb (); |
650 | |||
651 | qh_completions(ehci, qh); | ||
652 | |||
653 | /* reschedule QH iff another request is queued */ | ||
654 | if (!list_empty(&qh->qtd_list) && | ||
655 | HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { | ||
656 | rc = qh_schedule(ehci, qh); | ||
657 | |||
658 | /* An error here likely indicates handshake failure | ||
659 | * or no space left in the schedule. Neither fault | ||
660 | * should happen often ... | ||
661 | * | ||
662 | * FIXME kill the now-dysfunctional queued urbs | ||
663 | */ | ||
664 | if (rc != 0) | ||
665 | ehci_err(ehci, "can't reschedule qh %p, err %d\n", | ||
666 | qh, rc); | ||
667 | } | ||
639 | } | 668 | } |
640 | 669 | ||
641 | /*-------------------------------------------------------------------------*/ | 670 | /*-------------------------------------------------------------------------*/ |
@@ -2213,7 +2242,8 @@ restart: | |||
2213 | type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next); | 2242 | type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next); |
2214 | q = q.qh->qh_next; | 2243 | q = q.qh->qh_next; |
2215 | modified = qh_completions (ehci, temp.qh); | 2244 | modified = qh_completions (ehci, temp.qh); |
2216 | if (unlikely (list_empty (&temp.qh->qtd_list))) | 2245 | if (unlikely(list_empty(&temp.qh->qtd_list) || |
2246 | temp.qh->needs_rescan)) | ||
2217 | intr_deschedule (ehci, temp.qh); | 2247 | intr_deschedule (ehci, temp.qh); |
2218 | qh_put (temp.qh); | 2248 | qh_put (temp.qh); |
2219 | break; | 2249 | break; |