[PATCH] USB: When hot reset for USB3 fails, try warm reset.

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

 



When a hot reset (standard USB port reset) fails on a USB 3.0 port, the
host controller transitions to the "Error" state.  It reports the port
link state as "Inactive", sets the link state change flag, and (if the
device disconnects) also reports the disconnect and connect change status.
It's also supposed to transition the link state to "RxDetect", but the NEC
µPD720200 xHCI host does not.

Unfortunately, Harald found that the combination of the NEC µPD720200 and
a LogiLink USB 3.0 to SATA adapter triggered this issue.  The USB core
would reset the device, the port would go into this error state, and the
device would never be enumerated.  This combination works under Windows,
but not under Linux.

When a hot reset fails on a USB 3.0 port, and the link state is reported
as Inactive, fall back to a warm port reset instead.  Harald confirms that
with a warm port reset (along with all the change bits being correctly
cleared), the USB 3.0 device will successfully enumerate.

Harald also had to add two other patches ("xhci: Set change bit when warm
reset change is set." and "usbcore: refine warm reset logic") to make this
setup work.  Since the warm reset refinement patch is not destined for the
stable kernels (it's too big), this patch should not be backported either.

This fixes https://bugzilla.kernel.org/show_bug.cgi?id=41752

Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
Tested-by: Harald Brennich <harald.brennich@xxxxxx>
---

Greg,

Here's one more patch for the 3.2 kernel.  It relies on Andiry's
"usbcore: refine warm reset logic" patch, so it shouldn't be queued for
stable.

Sarah Sharp

 drivers/usb/core/hub.c |   43 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 43 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index b0217dc..58b1d6f 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2029,6 +2029,17 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
 #define HUB_LONG_RESET_TIME	200
 #define HUB_RESET_TIMEOUT	500
 
+static int hub_port_reset(struct usb_hub *hub, int port1,
+			struct usb_device *udev, unsigned int delay, bool warm);
+
+/* Is a USB 3.0 port in the Inactive state? */
+static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus)
+{
+	return hub_is_superspeed(hub->hdev) &&
+		(portstatus & USB_PORT_STAT_LINK_STATE) ==
+		USB_SS_PORT_LS_SS_INACTIVE;
+}
+
 static int hub_port_wait_reset(struct usb_hub *hub, int port1,
 			struct usb_device *udev, unsigned int delay, bool warm)
 {
@@ -2052,6 +2063,38 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
 		 * when the port appears not to be connected.
 		 */
 		if (!warm) {
+			/*
+			 * Some buggy devices can cause an NEC host controller
+			 * to transition to the "Error" state after a hot port
+			 * reset.  This will show up as the port state in
+			 * "Inactive", and the port may also report a
+			 * disconnect.  Forcing a warm port reset seems to make
+			 * the device work.
+			 *
+			 * See https://bugzilla.kernel.org/show_bug.cgi?id=41752
+			 */
+			if (hub_port_inactive(hub, portstatus)) {
+				int ret;
+
+				if ((portchange & USB_PORT_STAT_C_CONNECTION))
+					clear_port_feature(hub->hdev, port1,
+							USB_PORT_FEAT_C_CONNECTION);
+				if (portchange & USB_PORT_STAT_C_LINK_STATE)
+					clear_port_feature(hub->hdev, port1,
+							USB_PORT_FEAT_C_PORT_LINK_STATE);
+				if (portchange & USB_PORT_STAT_C_RESET)
+					clear_port_feature(hub->hdev, port1,
+							USB_PORT_FEAT_C_RESET);
+				dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
+						port1);
+				ret = hub_port_reset(hub, port1,
+						udev, HUB_BH_RESET_TIME,
+						true);
+				if ((portchange & USB_PORT_STAT_C_CONNECTION))
+					clear_port_feature(hub->hdev, port1,
+							USB_PORT_FEAT_C_CONNECTION);
+				return ret;
+			}
 			/* Device went away? */
 			if (!(portstatus & USB_PORT_STAT_CONNECTION))
 				return -ENOTCONN;
-- 
1.7.4.1

--
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