This is a note to let you know that I've just added the patch titled

    EHCI: fix direction handling for interrupt data toggles

to my usb git tree which can be found at
    git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6.git
in the usb-next branch.

The patch will show up in the next release of the linux-next tree
(usually sometime within the next 24 hours during the week.)

The patch will also will be merged in the next major kernel release
during the merge window.

If you have any questions about this process, please let me know.


>From e04f5f7e423018bcec84c11af2058cdce87816f3 Mon Sep 17 00:00:00 2001
From: Alan Stern <st...@rowland.harvard.edu>
Date: Tue, 19 Jul 2011 14:01:23 -0400
Subject: EHCI: fix direction handling for interrupt data toggles

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 <st...@rowland.harvard.edu>
Reported-by: Graeme Gill <gra...@argyllcms.com>
CC: <sta...@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gre...@suse.de>
---
 drivers/usb/host/ehci-q.c |    3 ++-
 drivers/usb/host/ehci.h   |    1 +
 2 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 9bf3c0d..0917e3a 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -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);
@@ -946,6 +946,7 @@ done:
        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;
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index e4feec3..cc7d337 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -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 */
 };
 
-- 
1.7.6


_______________________________________________
stable mailing list
stable@linux.kernel.org
http://linux.kernel.org/mailman/listinfo/stable

Reply via email to