Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 165021
b: refs/heads/master
c: 3a44494
h: refs/heads/master
i:
  165019: 9eef223
v: v3
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Sep 23, 2009
1 parent 3432201 commit 7ba9804
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 13 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 04c4ab17c7c39603c5017bee20d3b8ccb2f19816
refs/heads/master: 3a44494e233c0fdd818d485cfea8998500543589
18 changes: 13 additions & 5 deletions trunk/drivers/usb/host/ehci-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -863,12 +863,18 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state) && ehci->reclaim)
end_unlink_async(ehci);

/* if it's not linked then there's nothing to do */
if (qh->qh_state != QH_STATE_LINKED)
;
/* If the QH isn't linked then there's nothing we can do
* unless we were called during a giveback, in which case
* qh_completions() has to deal with it.
*/
if (qh->qh_state != QH_STATE_LINKED) {
if (qh->qh_state == QH_STATE_COMPLETING)
qh->needs_rescan = 1;
return;
}

/* defer till later if busy */
else if (ehci->reclaim) {
if (ehci->reclaim) {
struct ehci_qh *last;

for (last = ehci->reclaim;
Expand Down Expand Up @@ -1001,6 +1007,7 @@ ehci_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep)
qh->qh_state = QH_STATE_IDLE;
switch (qh->qh_state) {
case QH_STATE_LINKED:
case QH_STATE_COMPLETING:
for (tmp = ehci->async->qh_next.qh;
tmp && tmp != qh;
tmp = tmp->qh_next.qh)
Expand Down Expand Up @@ -1065,7 +1072,8 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
usb_settoggle(qh->dev, epnum, is_out, 0);
if (!list_empty(&qh->qtd_list)) {
WARN_ONCE(1, "clear_halt for a busy endpoint\n");
} else if (qh->qh_state == QH_STATE_LINKED) {
} else if (qh->qh_state == QH_STATE_LINKED ||
qh->qh_state == QH_STATE_COMPLETING) {

/* The toggle value in the QH can't be updated
* while the QH is active. Unlink it now;
Expand Down
43 changes: 36 additions & 7 deletions trunk/drivers/usb/host/ehci-q.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,13 +310,13 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
static unsigned
qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
struct ehci_qtd *last = NULL, *end = qh->dummy;
struct ehci_qtd *last, *end = qh->dummy;
struct list_head *entry, *tmp;
int last_status = -EINPROGRESS;
int last_status;
int stopped;
unsigned count = 0;
u8 state;
__le32 halt = HALT_BIT(ehci);
const __le32 halt = HALT_BIT(ehci);
struct ehci_qh_hw *hw = qh->hw;

if (unlikely (list_empty (&qh->qtd_list)))
Expand All @@ -327,11 +327,20 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
* they add urbs to this qh's queue or mark them for unlinking.
*
* NOTE: unlinking expects to be done in queue order.
*
* It's a bug for qh->qh_state to be anything other than
* QH_STATE_IDLE, unless our caller is scan_async() or
* scan_periodic().
*/
state = qh->qh_state;
qh->qh_state = QH_STATE_COMPLETING;
stopped = (state == QH_STATE_IDLE);

rescan:
last = NULL;
last_status = -EINPROGRESS;
qh->needs_rescan = 0;

/* remove de-activated QTDs from front of queue.
* after faults (including short reads), cleanup this urb
* then let the queue advance.
Expand Down Expand Up @@ -507,6 +516,21 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
ehci_qtd_free (ehci, last);
}

/* Do we need to rescan for URBs dequeued during a giveback? */
if (unlikely(qh->needs_rescan)) {
/* If the QH is already unlinked, do the rescan now. */
if (state == QH_STATE_IDLE)
goto rescan;

/* Otherwise we have to wait until the QH is fully unlinked.
* Our caller will start an unlink if qh->needs_rescan is
* set. But if an unlink has already started, nothing needs
* to be done.
*/
if (state != QH_STATE_LINKED)
qh->needs_rescan = 0;
}

/* restore original state; caller must unlink or relink */
qh->qh_state = state;

Expand Down Expand Up @@ -535,8 +559,10 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
& hw->hw_info2) != 0) {
intr_deschedule (ehci, qh);
(void) qh_schedule (ehci, qh);
} else
unlink_async (ehci, qh);
} else {
/* Tell the caller to start an unlink */
qh->needs_rescan = 1;
}
break;
/* otherwise, unlink already started */
}
Expand Down Expand Up @@ -916,6 +942,8 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
if (unlikely(qh->clearing_tt))
return;

WARN_ON(qh->qh_state != QH_STATE_IDLE);

/* (re)start the async schedule? */
head = ehci->async;
timer_action_done (ehci, TIMER_ASYNC_OFF);
Expand All @@ -934,8 +962,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
}

/* clear halt and/or toggle; and maybe recover from silicon quirk */
if (qh->qh_state == QH_STATE_IDLE)
qh_refresh (ehci, qh);
qh_refresh(ehci, qh);

/* splice right after start */
qh->qh_next = head->qh_next;
Expand Down Expand Up @@ -1220,6 +1247,8 @@ static void scan_async (struct ehci_hcd *ehci)
qh = qh_get (qh);
qh->stamp = ehci->stamp;
temp = qh_completions (ehci, qh);
if (qh->needs_rescan)
unlink_async(ehci, qh);
qh_put (qh);
if (temp != 0) {
goto rescan;
Expand Down
1 change: 1 addition & 0 deletions trunk/drivers/usb/host/ehci.h
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ struct ehci_qh {
u32 refcount;
unsigned stamp;

u8 needs_rescan; /* Dequeue during giveback */
u8 qh_state;
#define QH_STATE_LINKED 1 /* HC sees this */
#define QH_STATE_UNLINK 2 /* HC may still see this */
Expand Down

0 comments on commit 7ba9804

Please sign in to comment.