Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 93358
b: refs/heads/master
c: a082b5c
h: refs/heads/master
v: v3
  • Loading branch information
David Brownell authored and Greg Kroah-Hartman committed Apr 25, 2008
1 parent a17b659 commit 0e41884
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 11 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: 6427f7995338387ddded92f98adec19ddbf0ae5e
refs/heads/master: a082b5c7882bdbd8a86ace8470ca2ecda796d5a7
50 changes: 40 additions & 10 deletions trunk/drivers/usb/host/ehci-q.c
Original file line number Diff line number Diff line change
Expand Up @@ -336,11 +336,20 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* always clean up qtds the hc de-activated */
if ((token & QTD_STS_ACTIVE) == 0) {

/* on STALL, error, and short reads this urb must
* complete and all its qtds must be recycled.
*/
if ((token & QTD_STS_HALT) != 0) {
stopped = 1;

/* magic dummy for some short reads; qh won't advance.
* that silicon quirk can kick in with this dummy too.
*
* other short reads won't stop the queue, including
* control transfers (status stage handles that) or
* most other single-qtd reads ... the queue stops if
* URB_SHORT_NOT_OK was set so the driver submitting
* the urbs could clean it up.
*/
} else if (IS_SHORT_READ (token)
&& !(qtd->hw_alt_next
Expand All @@ -354,18 +363,18 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
&& HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) {
break;

/* scan the whole queue for unlinks whenever it stops */
} else {
stopped = 1;

if (unlikely (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)))
/* cancel everything if we halt, suspend, etc */
if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state))
last_status = -ESHUTDOWN;

/* ignore active urbs unless some previous qtd
* for the urb faulted (including short read) or
* its urb was canceled. we may patch qh or qtds.
/* this qtd is active; skip it unless a previous qtd
* for its urb faulted, or its urb was canceled.
*/
if (likely(last_status == -EINPROGRESS &&
!urb->unlinked))
else if (last_status == -EINPROGRESS && !urb->unlinked)
continue;

/* issue status after short control reads */
Expand All @@ -375,7 +384,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
continue;
}

/* token in overlay may be most current */
/* qh unlinked; token in overlay may be most current */
if (state == QH_STATE_IDLE
&& cpu_to_hc32(ehci, qtd->qtd_dma)
== qh->hw_current)
Expand All @@ -402,11 +411,16 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
if (likely(last_status == -EINPROGRESS))
last_status = qtd_status;

/* if we're removing something not at the queue head,
* patch the hardware queue pointer.
*/
if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
last = list_entry (qtd->qtd_list.prev,
struct ehci_qtd, qtd_list);
last->hw_next = qtd->hw_next;
}

/* remove qtd; it's recycled after possible urb completion */
list_del (&qtd->qtd_list);
last = qtd;
}
Expand All @@ -431,7 +445,15 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
qh_refresh(ehci, qh);
break;
case QH_STATE_LINKED:
/* should be rare for periodic transfers,
/* We won't refresh a QH that's linked (after the HC
* stopped the queue). That avoids a race:
* - HC reads first part of QH;
* - CPU updates that first part and the token;
* - HC reads rest of that QH, including token
* Result: HC gets an inconsistent image, and then
* DMAs to/from the wrong memory (corrupting it).
*
* That should be rare for interrupt transfers,
* except maybe high bandwidth ...
*/
if ((cpu_to_hc32(ehci, QH_SMASK)
Expand Down Expand Up @@ -549,6 +571,12 @@ qh_urb_transaction (
this_qtd_len = qtd_fill(ehci, qtd, buf, len, token, maxpacket);
len -= this_qtd_len;
buf += this_qtd_len;

/*
* short reads advance to a "magic" dummy instead of the next
* qtd ... that forces the queue to stop, for manual cleanup.
* (this will usually be overridden later.)
*/
if (is_input)
qtd->hw_alt_next = ehci->async->hw_alt_next;

Expand All @@ -568,8 +596,10 @@ qh_urb_transaction (
list_add_tail (&qtd->qtd_list, head);
}

/* unless the bulk/interrupt caller wants a chance to clean
* up after short reads, hc should advance qh past this urb
/*
* unless the caller requires manual cleanup after short reads,
* have the alt_next mechanism keep the queue running after the
* last data qtd (the only one, for control and most other cases).
*/
if (likely ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0
|| usb_pipecontrol (urb->pipe)))
Expand Down

0 comments on commit 0e41884

Please sign in to comment.