diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 18 | ||||
-rw-r--r-- | drivers/usb/host/ehci-q.c | 43 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 1 |
3 files changed, 50 insertions, 12 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 6887aac5e73d..d7e85b6231b3 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -863,12 +863,18 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
863 | if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state) && ehci->reclaim) | 863 | if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state) && ehci->reclaim) |
864 | end_unlink_async(ehci); | 864 | end_unlink_async(ehci); |
865 | 865 | ||
866 | /* if it's not linked then there's nothing to do */ | 866 | /* If the QH isn't linked then there's nothing we can do |
867 | if (qh->qh_state != QH_STATE_LINKED) | 867 | * unless we were called during a giveback, in which case |
868 | ; | 868 | * qh_completions() has to deal with it. |
869 | */ | ||
870 | if (qh->qh_state != QH_STATE_LINKED) { | ||
871 | if (qh->qh_state == QH_STATE_COMPLETING) | ||
872 | qh->needs_rescan = 1; | ||
873 | return; | ||
874 | } | ||
869 | 875 | ||
870 | /* defer till later if busy */ | 876 | /* defer till later if busy */ |
871 | else if (ehci->reclaim) { | 877 | if (ehci->reclaim) { |
872 | struct ehci_qh *last; | 878 | struct ehci_qh *last; |
873 | 879 | ||
874 | for (last = ehci->reclaim; | 880 | for (last = ehci->reclaim; |
@@ -1001,6 +1007,7 @@ rescan: | |||
1001 | qh->qh_state = QH_STATE_IDLE; | 1007 | qh->qh_state = QH_STATE_IDLE; |
1002 | switch (qh->qh_state) { | 1008 | switch (qh->qh_state) { |
1003 | case QH_STATE_LINKED: | 1009 | case QH_STATE_LINKED: |
1010 | case QH_STATE_COMPLETING: | ||
1004 | for (tmp = ehci->async->qh_next.qh; | 1011 | for (tmp = ehci->async->qh_next.qh; |
1005 | tmp && tmp != qh; | 1012 | tmp && tmp != qh; |
1006 | tmp = tmp->qh_next.qh) | 1013 | tmp = tmp->qh_next.qh) |
@@ -1065,7 +1072,8 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) | |||
1065 | usb_settoggle(qh->dev, epnum, is_out, 0); | 1072 | usb_settoggle(qh->dev, epnum, is_out, 0); |
1066 | if (!list_empty(&qh->qtd_list)) { | 1073 | if (!list_empty(&qh->qtd_list)) { |
1067 | WARN_ONCE(1, "clear_halt for a busy endpoint\n"); | 1074 | WARN_ONCE(1, "clear_halt for a busy endpoint\n"); |
1068 | } else if (qh->qh_state == QH_STATE_LINKED) { | 1075 | } else if (qh->qh_state == QH_STATE_LINKED || |
1076 | qh->qh_state == QH_STATE_COMPLETING) { | ||
1069 | 1077 | ||
1070 | /* The toggle value in the QH can't be updated | 1078 | /* The toggle value in the QH can't be updated |
1071 | * while the QH is active. Unlink it now; | 1079 | * while the QH is active. Unlink it now; |
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; |
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index ec3dba6b8e48..064e76821ff5 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h | |||
@@ -341,6 +341,7 @@ struct ehci_qh { | |||
341 | u32 refcount; | 341 | u32 refcount; |
342 | unsigned stamp; | 342 | unsigned stamp; |
343 | 343 | ||
344 | u8 needs_rescan; /* Dequeue during giveback */ | ||
344 | u8 qh_state; | 345 | u8 qh_state; |
345 | #define QH_STATE_LINKED 1 /* HC sees this */ | 346 | #define QH_STATE_LINKED 1 /* HC sees this */ |
346 | #define QH_STATE_UNLINK 2 /* HC may still see this */ | 347 | #define QH_STATE_UNLINK 2 /* HC may still see this */ |