Skip to content

Commit 37be667

Browse files
matnymangregkh
authored andcommitted
usb: hub: Fix auto-remount of safely removed or ejected USB-3 devices
USB-3 does not have any link state that will avoid negotiating a connection with a plugged-in cable but will signal the host when the cable is unplugged. For USB-3 we used to first set the link to Disabled, then to RxDdetect to be able to detect cable connects or disconnects. But in RxDetect the connected device is detected again and eventually enabled. Instead set the link into U3 and disable remote wakeups for the device. This is what Windows does, and what Alan Stern suggested. Cc: [email protected] Cc: Alan Stern <[email protected]> Acked-by: Alan Stern <[email protected]> Signed-off-by: Mathias Nyman <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent c95a9f8 commit 37be667

File tree

1 file changed

+36
-65
lines changed

1 file changed

+36
-65
lines changed

drivers/usb/core/hub.c

Lines changed: 36 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
103103

104104
static void hub_release(struct kref *kref);
105105
static int usb_reset_and_verify_device(struct usb_device *udev);
106+
static void hub_usb3_port_prepare_disable(struct usb_hub *hub,
107+
struct usb_port *port_dev);
106108

107109
static inline char *portspeed(struct usb_hub *hub, int portstatus)
108110
{
@@ -901,82 +903,28 @@ static int hub_set_port_link_state(struct usb_hub *hub, int port1,
901903
}
902904

903905
/*
904-
* If USB 3.0 ports are placed into the Disabled state, they will no longer
905-
* detect any device connects or disconnects. This is generally not what the
906-
* USB core wants, since it expects a disabled port to produce a port status
907-
* change event when a new device connects.
908-
*
909-
* Instead, set the link state to Disabled, wait for the link to settle into
910-
* that state, clear any change bits, and then put the port into the RxDetect
911-
* state.
906+
* USB-3 does not have a similar link state as USB-2 that will avoid negotiating
907+
* a connection with a plugged-in cable but will signal the host when the cable
908+
* is unplugged. Disable remote wake and set link state to U3 for USB-3 devices
912909
*/
913-
static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
914-
{
915-
int ret;
916-
int total_time;
917-
u16 portchange, portstatus;
918-
919-
if (!hub_is_superspeed(hub->hdev))
920-
return -EINVAL;
921-
922-
ret = hub_port_status(hub, port1, &portstatus, &portchange);
923-
if (ret < 0)
924-
return ret;
925-
926-
/*
927-
* USB controller Advanced Micro Devices, Inc. [AMD] FCH USB XHCI
928-
* Controller [1022:7814] will have spurious result making the following
929-
* usb 3.0 device hotplugging route to the 2.0 root hub and recognized
930-
* as high-speed device if we set the usb 3.0 port link state to
931-
* Disabled. Since it's already in USB_SS_PORT_LS_RX_DETECT state, we
932-
* check the state here to avoid the bug.
933-
*/
934-
if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
935-
USB_SS_PORT_LS_RX_DETECT) {
936-
dev_dbg(&hub->ports[port1 - 1]->dev,
937-
"Not disabling port; link state is RxDetect\n");
938-
return ret;
939-
}
940-
941-
ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED);
942-
if (ret)
943-
return ret;
944-
945-
/* Wait for the link to enter the disabled state. */
946-
for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
947-
ret = hub_port_status(hub, port1, &portstatus, &portchange);
948-
if (ret < 0)
949-
return ret;
950-
951-
if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
952-
USB_SS_PORT_LS_SS_DISABLED)
953-
break;
954-
if (total_time >= HUB_DEBOUNCE_TIMEOUT)
955-
break;
956-
msleep(HUB_DEBOUNCE_STEP);
957-
}
958-
if (total_time >= HUB_DEBOUNCE_TIMEOUT)
959-
dev_warn(&hub->ports[port1 - 1]->dev,
960-
"Could not disable after %d ms\n", total_time);
961-
962-
return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT);
963-
}
964-
965910
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
966911
{
967912
struct usb_port *port_dev = hub->ports[port1 - 1];
968913
struct usb_device *hdev = hub->hdev;
969914
int ret = 0;
970915

971-
if (port_dev->child && set_state)
972-
usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);
973916
if (!hub->error) {
974-
if (hub_is_superspeed(hub->hdev))
975-
ret = hub_usb3_port_disable(hub, port1);
976-
else
917+
if (hub_is_superspeed(hub->hdev)) {
918+
hub_usb3_port_prepare_disable(hub, port_dev);
919+
ret = hub_set_port_link_state(hub, port_dev->portnum,
920+
USB_SS_PORT_LS_U3);
921+
} else {
977922
ret = usb_clear_port_feature(hdev, port1,
978923
USB_PORT_FEAT_ENABLE);
924+
}
979925
}
926+
if (port_dev->child && set_state)
927+
usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);
980928
if (ret && ret != -ENODEV)
981929
dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret);
982930
return ret;
@@ -4142,13 +4090,36 @@ void usb_unlocked_enable_lpm(struct usb_device *udev)
41424090
}
41434091
EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
41444092

4093+
/* usb3 devices use U3 for disabled, make sure remote wakeup is disabled */
4094+
static void hub_usb3_port_prepare_disable(struct usb_hub *hub,
4095+
struct usb_port *port_dev)
4096+
{
4097+
struct usb_device *udev = port_dev->child;
4098+
int ret;
4099+
4100+
if (udev && udev->port_is_suspended && udev->do_remote_wakeup) {
4101+
ret = hub_set_port_link_state(hub, port_dev->portnum,
4102+
USB_SS_PORT_LS_U0);
4103+
if (!ret) {
4104+
msleep(USB_RESUME_TIMEOUT);
4105+
ret = usb_disable_remote_wakeup(udev);
4106+
}
4107+
if (ret)
4108+
dev_warn(&udev->dev,
4109+
"Port disable: can't disable remote wake\n");
4110+
udev->do_remote_wakeup = 0;
4111+
}
4112+
}
41454113

41464114
#else /* CONFIG_PM */
41474115

41484116
#define hub_suspend NULL
41494117
#define hub_resume NULL
41504118
#define hub_reset_resume NULL
41514119

4120+
static inline void hub_usb3_port_prepare_disable(struct usb_hub *hub,
4121+
struct usb_port *port_dev) { }
4122+
41524123
int usb_disable_lpm(struct usb_device *udev)
41534124
{
41544125
return 0;

0 commit comments

Comments
 (0)