Skip to content

Commit

Permalink
EHCI: fix direction handling for interrupt data toggles
Browse files Browse the repository at this point in the history
This patch (as1480) fixes a rather obscure bug in ehci-hcd.  The
qh_update() routine needs to know the number and direction of the
endpoint corresponding to its QH argument.  The number can be taken
directly from the QH data structure, but the direction isn't stored
there.  The direction is taken instead from the first qTD linked to
the QH.

However, it turns out that for interrupt transfers, qh_update() gets
called before the qTDs are linked to the QH.  As a result, qh_update()
computes a bogus direction value, which messes up the endpoint toggle
handling.  Under the right combination of circumstances this causes
usb_reset_endpoint() not to work correctly, which causes packets to be
dropped and communications to fail.

Now, it's silly for the QH structure not to have direct access to all
the descriptor information for the corresponding endpoint.  Ultimately
it may get a pointer to the usb_host_endpoint structure; for now,
adding a copy of the direction flag solves the immediate problem.

This allows the Spyder2 color-calibration system (a low-speed USB
device that sends all its interrupt data packets with the toggle set
to 0 and hance requires constant use of usb_reset_endpoint) to work
when connected through a high-speed hub.  Thanks to Graeme Gill for
supplying the hardware that allowed me to track down this bug.

Signed-off-by: Alan Stern <[email protected]>
Reported-by: Graeme Gill <[email protected]>
CC: <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
AlanStern authored and gregkh committed Jul 19, 2011
1 parent 026dfaf commit e04f5f7
Show file tree
Hide file tree
Showing 2 changed files with 3 additions and 1 deletion.
3 changes: 2 additions & 1 deletion drivers/usb/host/ehci-q.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
if (!(hw->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
unsigned is_out, epnum;

is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8));
is_out = qh->is_out;
epnum = (hc32_to_cpup(ehci, &hw->hw_info1) >> 8) & 0x0f;
if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
hw->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
Expand Down Expand Up @@ -946,6 +946,7 @@ qh_make (
hw = qh->hw;
hw->hw_info1 = cpu_to_hc32(ehci, info1);
hw->hw_info2 = cpu_to_hc32(ehci, info2);
qh->is_out = !is_input;
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
qh_refresh (ehci, qh);
return qh;
Expand Down
1 change: 1 addition & 0 deletions drivers/usb/host/ehci.h
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ struct ehci_qh {
#define NO_FRAME ((unsigned short)~0) /* pick new start */

struct usb_device *dev; /* access to TT */
unsigned is_out:1; /* bulk or intr OUT */
unsigned clearing_tt:1; /* Clear-TT-Buf in progress */
};

Expand Down

0 comments on commit e04f5f7

Please sign in to comment.