Skip to content

Commit 75d7cf7

Browse files
Andiry Xugregkh
authored andcommitted
usbcore: refine warm reset logic
Current waiting time for warm(BH) reset in hub_port_warm_reset() is too short for xHC host to complete the warm reset and report a BH reset change. This patch increases the waiting time for warm reset and merges the function into hub_port_reset(), so it can handle both cold reset and warm reset, and factor out hub_port_finish_reset() to make the code looks cleaner. This fixes the issue that driver fails to clear BH reset change and port is "dead". Signed-off-by: Andiry Xu <[email protected]> Acked-by: Alan Stern <[email protected]> Signed-off-by: Sarah Sharp <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent d782659 commit 75d7cf7

File tree

1 file changed

+112
-104
lines changed

1 file changed

+112
-104
lines changed

drivers/usb/core/hub.c

Lines changed: 112 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -2025,11 +2025,12 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
20252025

20262026
#define HUB_ROOT_RESET_TIME 50 /* times are in msec */
20272027
#define HUB_SHORT_RESET_TIME 10
2028+
#define HUB_BH_RESET_TIME 50
20282029
#define HUB_LONG_RESET_TIME 200
20292030
#define HUB_RESET_TIMEOUT 500
20302031

20312032
static int hub_port_wait_reset(struct usb_hub *hub, int port1,
2032-
struct usb_device *udev, unsigned int delay)
2033+
struct usb_device *udev, unsigned int delay, bool warm)
20332034
{
20342035
int delay_time, ret;
20352036
u16 portstatus;
@@ -2046,145 +2047,151 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
20462047
if (ret < 0)
20472048
return ret;
20482049

2049-
/* Device went away? */
2050-
if (!(portstatus & USB_PORT_STAT_CONNECTION))
2051-
return -ENOTCONN;
2052-
2053-
/* bomb out completely if the connection bounced */
2054-
if ((portchange & USB_PORT_STAT_C_CONNECTION))
2055-
return -ENOTCONN;
2056-
2057-
/* if we`ve finished resetting, then break out of the loop */
2058-
if (!(portstatus & USB_PORT_STAT_RESET) &&
2059-
(portstatus & USB_PORT_STAT_ENABLE)) {
2060-
if (hub_is_wusb(hub))
2061-
udev->speed = USB_SPEED_WIRELESS;
2062-
else if (hub_is_superspeed(hub->hdev))
2063-
udev->speed = USB_SPEED_SUPER;
2064-
else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
2065-
udev->speed = USB_SPEED_HIGH;
2066-
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
2067-
udev->speed = USB_SPEED_LOW;
2068-
else
2069-
udev->speed = USB_SPEED_FULL;
2070-
return 0;
2050+
/*
2051+
* Some buggy devices require a warm reset to be issued even
2052+
* when the port appears not to be connected.
2053+
*/
2054+
if (!warm) {
2055+
/* Device went away? */
2056+
if (!(portstatus & USB_PORT_STAT_CONNECTION))
2057+
return -ENOTCONN;
2058+
2059+
/* bomb out completely if the connection bounced */
2060+
if ((portchange & USB_PORT_STAT_C_CONNECTION))
2061+
return -ENOTCONN;
2062+
2063+
/* if we`ve finished resetting, then break out of
2064+
* the loop
2065+
*/
2066+
if (!(portstatus & USB_PORT_STAT_RESET) &&
2067+
(portstatus & USB_PORT_STAT_ENABLE)) {
2068+
if (hub_is_wusb(hub))
2069+
udev->speed = USB_SPEED_WIRELESS;
2070+
else if (hub_is_superspeed(hub->hdev))
2071+
udev->speed = USB_SPEED_SUPER;
2072+
else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
2073+
udev->speed = USB_SPEED_HIGH;
2074+
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
2075+
udev->speed = USB_SPEED_LOW;
2076+
else
2077+
udev->speed = USB_SPEED_FULL;
2078+
return 0;
2079+
}
2080+
} else {
2081+
if (portchange & USB_PORT_STAT_C_BH_RESET)
2082+
return 0;
20712083
}
20722084

20732085
/* switch to the long delay after two short delay failures */
20742086
if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
20752087
delay = HUB_LONG_RESET_TIME;
20762088

20772089
dev_dbg (hub->intfdev,
2078-
"port %d not reset yet, waiting %dms\n",
2079-
port1, delay);
2090+
"port %d not %sreset yet, waiting %dms\n",
2091+
port1, warm ? "warm " : "", delay);
20802092
}
20812093

20822094
return -EBUSY;
20832095
}
20842096

2097+
static void hub_port_finish_reset(struct usb_hub *hub, int port1,
2098+
struct usb_device *udev, int *status, bool warm)
2099+
{
2100+
switch (*status) {
2101+
case 0:
2102+
if (!warm) {
2103+
struct usb_hcd *hcd;
2104+
/* TRSTRCY = 10 ms; plus some extra */
2105+
msleep(10 + 40);
2106+
update_devnum(udev, 0);
2107+
hcd = bus_to_hcd(udev->bus);
2108+
if (hcd->driver->reset_device) {
2109+
*status = hcd->driver->reset_device(hcd, udev);
2110+
if (*status < 0) {
2111+
dev_err(&udev->dev, "Cannot reset "
2112+
"HCD device state\n");
2113+
break;
2114+
}
2115+
}
2116+
}
2117+
/* FALL THROUGH */
2118+
case -ENOTCONN:
2119+
case -ENODEV:
2120+
clear_port_feature(hub->hdev,
2121+
port1, USB_PORT_FEAT_C_RESET);
2122+
/* FIXME need disconnect() for NOTATTACHED device */
2123+
if (warm) {
2124+
clear_port_feature(hub->hdev, port1,
2125+
USB_PORT_FEAT_C_BH_PORT_RESET);
2126+
clear_port_feature(hub->hdev, port1,
2127+
USB_PORT_FEAT_C_PORT_LINK_STATE);
2128+
} else {
2129+
usb_set_device_state(udev, *status
2130+
? USB_STATE_NOTATTACHED
2131+
: USB_STATE_DEFAULT);
2132+
}
2133+
break;
2134+
}
2135+
}
2136+
2137+
/* Handle port reset and port warm(BH) reset (for USB3 protocol ports) */
20852138
static int hub_port_reset(struct usb_hub *hub, int port1,
2086-
struct usb_device *udev, unsigned int delay)
2139+
struct usb_device *udev, unsigned int delay, bool warm)
20872140
{
20882141
int i, status;
2089-
struct usb_hcd *hcd;
20902142

2091-
hcd = bus_to_hcd(udev->bus);
2092-
/* Block EHCI CF initialization during the port reset.
2093-
* Some companion controllers don't like it when they mix.
2094-
*/
2095-
down_read(&ehci_cf_port_reset_rwsem);
2143+
if (!warm) {
2144+
/* Block EHCI CF initialization during the port reset.
2145+
* Some companion controllers don't like it when they mix.
2146+
*/
2147+
down_read(&ehci_cf_port_reset_rwsem);
2148+
} else {
2149+
if (!hub_is_superspeed(hub->hdev)) {
2150+
dev_err(hub->intfdev, "only USB3 hub support "
2151+
"warm reset\n");
2152+
return -EINVAL;
2153+
}
2154+
}
20962155

20972156
/* Reset the port */
20982157
for (i = 0; i < PORT_RESET_TRIES; i++) {
2099-
status = set_port_feature(hub->hdev,
2100-
port1, USB_PORT_FEAT_RESET);
2101-
if (status)
2158+
status = set_port_feature(hub->hdev, port1, (warm ?
2159+
USB_PORT_FEAT_BH_PORT_RESET :
2160+
USB_PORT_FEAT_RESET));
2161+
if (status) {
21022162
dev_err(hub->intfdev,
2103-
"cannot reset port %d (err = %d)\n",
2104-
port1, status);
2105-
else {
2106-
status = hub_port_wait_reset(hub, port1, udev, delay);
2163+
"cannot %sreset port %d (err = %d)\n",
2164+
warm ? "warm " : "", port1, status);
2165+
} else {
2166+
status = hub_port_wait_reset(hub, port1, udev, delay,
2167+
warm);
21072168
if (status && status != -ENOTCONN)
21082169
dev_dbg(hub->intfdev,
21092170
"port_wait_reset: err = %d\n",
21102171
status);
21112172
}
21122173

21132174
/* return on disconnect or reset */
2114-
switch (status) {
2115-
case 0:
2116-
/* TRSTRCY = 10 ms; plus some extra */
2117-
msleep(10 + 40);
2118-
update_devnum(udev, 0);
2119-
if (hcd->driver->reset_device) {
2120-
status = hcd->driver->reset_device(hcd, udev);
2121-
if (status < 0) {
2122-
dev_err(&udev->dev, "Cannot reset "
2123-
"HCD device state\n");
2124-
break;
2125-
}
2126-
}
2127-
/* FALL THROUGH */
2128-
case -ENOTCONN:
2129-
case -ENODEV:
2130-
clear_port_feature(hub->hdev,
2131-
port1, USB_PORT_FEAT_C_RESET);
2132-
/* FIXME need disconnect() for NOTATTACHED device */
2133-
usb_set_device_state(udev, status
2134-
? USB_STATE_NOTATTACHED
2135-
: USB_STATE_DEFAULT);
2175+
if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
2176+
hub_port_finish_reset(hub, port1, udev, &status, warm);
21362177
goto done;
21372178
}
21382179

21392180
dev_dbg (hub->intfdev,
2140-
"port %d not enabled, trying reset again...\n",
2141-
port1);
2181+
"port %d not enabled, trying %sreset again...\n",
2182+
port1, warm ? "warm " : "");
21422183
delay = HUB_LONG_RESET_TIME;
21432184
}
21442185

21452186
dev_err (hub->intfdev,
21462187
"Cannot enable port %i. Maybe the USB cable is bad?\n",
21472188
port1);
21482189

2149-
done:
2150-
up_read(&ehci_cf_port_reset_rwsem);
2151-
return status;
2152-
}
2153-
2154-
/* Warm reset a USB3 protocol port */
2155-
static int hub_port_warm_reset(struct usb_hub *hub, int port)
2156-
{
2157-
int ret;
2158-
u16 portstatus, portchange;
2159-
2160-
if (!hub_is_superspeed(hub->hdev)) {
2161-
dev_err(hub->intfdev, "only USB3 hub support warm reset\n");
2162-
return -EINVAL;
2163-
}
2164-
2165-
/* Warm reset the port */
2166-
ret = set_port_feature(hub->hdev,
2167-
port, USB_PORT_FEAT_BH_PORT_RESET);
2168-
if (ret) {
2169-
dev_err(hub->intfdev, "cannot warm reset port %d\n", port);
2170-
return ret;
2171-
}
2172-
2173-
msleep(20);
2174-
ret = hub_port_status(hub, port, &portstatus, &portchange);
2175-
2176-
if (portchange & USB_PORT_STAT_C_RESET)
2177-
clear_port_feature(hub->hdev, port, USB_PORT_FEAT_C_RESET);
2178-
2179-
if (portchange & USB_PORT_STAT_C_BH_RESET)
2180-
clear_port_feature(hub->hdev, port,
2181-
USB_PORT_FEAT_C_BH_PORT_RESET);
2182-
2183-
if (portchange & USB_PORT_STAT_C_LINK_STATE)
2184-
clear_port_feature(hub->hdev, port,
2185-
USB_PORT_FEAT_C_PORT_LINK_STATE);
2190+
done:
2191+
if (!warm)
2192+
up_read(&ehci_cf_port_reset_rwsem);
21862193

2187-
return ret;
2194+
return status;
21882195
}
21892196

21902197
/* Check if a port is power on */
@@ -2814,7 +2821,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
28142821

28152822
/* Reset the device; full speed may morph to high speed */
28162823
/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
2817-
retval = hub_port_reset(hub, port1, udev, delay);
2824+
retval = hub_port_reset(hub, port1, udev, delay, false);
28182825
if (retval < 0) /* error or disconnect */
28192826
goto fail;
28202827
/* success, speed is known */
@@ -2935,7 +2942,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
29352942
buf->bMaxPacketSize0;
29362943
kfree(buf);
29372944

2938-
retval = hub_port_reset(hub, port1, udev, delay);
2945+
retval = hub_port_reset(hub, port1, udev, delay, false);
29392946
if (retval < 0) /* error or disconnect */
29402947
goto fail;
29412948
if (oldspeed != udev->speed) {
@@ -3556,7 +3563,8 @@ static void hub_events(void)
35563563
(portstatus & USB_PORT_STAT_LINK_STATE)
35573564
== USB_SS_PORT_LS_SS_INACTIVE) {
35583565
dev_dbg(hub_dev, "warm reset port %d\n", i);
3559-
hub_port_warm_reset(hub, i);
3566+
hub_port_reset(hub, i, NULL,
3567+
HUB_BH_RESET_TIME, true);
35603568
}
35613569

35623570
if (connect_change)

0 commit comments

Comments
 (0)