[RFC 3/8] xHCI: Clear all USB 2.0 change bits on port disable.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The USB core disables the USB 2.0 ports in several different error
cases.  It expects that all USB hubs will act like external hubs, and
always return an interrupt event if a port change bit is set.  External
USB 2.0 and USB 3.0 hubs are level-triggered, so this works for them.

However, xHCI roothubs are edge-triggered.  If no port status change
bits are set, and a new change bit is set, the xHCI host sends an
interrupt and a Port Status Change Event.  It will not generate another
port event until all change bits are cleared and a new one is set.

This opens up issues with the USB core port disabling code.  Right now,
it assumes that it can simply clear the port enable bit, and wait for
the hub to send an interrupt when a change bit is set.  If previous
change bits weren't cleared, it's assumed to be fine because the hub
will continue to remind the USB core about them through the interrupt
endpoint.

This doesn't work for USB 2.0 roothub ports under xHCI.  If we don't
atomically clear an already-set change bit as we disable the port, the
xHC will not send an interrupt on a new change bit.  This means that
we'll lose device connect notifications.

An example scenario:
 - hub_port_init notices a device connect, and resets the port
 - USB 2.0 reset fails because the device disconnected
 - xHC sets the connect status change (CSC) bit and reset change bit
 - hub_port_reset clears the reset change bit
 - hub_port_init immediately goes to disable the port without clearing
   the CSC bit
 - xHC disables the port
 - The device reconnects.  However, the CSC bit is already set, so the
   xHC never interrupts with a Port Status Change event.

We could attempt to clear any port status change bits in the USB core's
port disable function before the port is disabled.  However, that's racy
because a change bit could be set in between any of the five necessary
Clear Port Feature calls to clear a change bit.  It's also not useful
for external hubs (since they are level-triggered), so the atomic change
bit clearing code belongs in the xHCI host hub code.

This patch should be backported to stable kernels as old as 2.6.34, that
contain the commit 6219c047d3fe18dee4916d6898fc94f5a7ffd156 "USB: xhci:
Allow roothub ports to be disabled."

Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
Cc: stable@xxxxxxxxxxxxxxx
---
 drivers/usb/host/xhci-hub.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index a686cf4..7d9dcd6 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -340,6 +340,10 @@ static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,
 		return;
 	}
 
+	/* Clear all change bits, so that we get a device connect event. */
+	port_status |= PORT_CSC | PORT_PEC | PORT_WRC |
+		PORT_OCC | PORT_RC | PORT_PLC |
+		PORT_CEC;
 	/* Write 1 to disable the port */
 	xhci_writel(xhci, port_status | PORT_PE, addr);
 	port_status = xhci_readl(xhci, addr);
-- 
1.7.9

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux