diff options
author | Keiya Nobuta <nobuta.keiya@fujitsu.com> | 2020-01-09 08:14:48 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2020-01-23 10:20:32 +0300 |
commit | cd20d0e31c63f8c5ac1dbcb57b96a757c0dd56cd (patch) | |
tree | 0d44cbeba2046fa2327e2a7dd333cb3a4b2b57e6 /drivers/usb | |
parent | f8812ddea4ea3b8a6ee5e4b9fb2333da7ee4022c (diff) | |
download | linux-cd20d0e31c63f8c5ac1dbcb57b96a757c0dd56cd.tar.xz |
usb: core: hub: Improved device recognition on remote wakeup
commit 9c06ac4c83df6d6fbdbf7488fbad822b4002ba19 upstream.
If hub_activate() is called before D+ has stabilized after remote
wakeup, the following situation might occur:
__ ___________________
/ \ /
D+ __/ \__/
Hub _______________________________
| ^ ^ ^
| | | |
Host _____v__|___|___________|______
| | | |
| | | \-- Interrupt Transfer (*3)
| | \-- ClearPortFeature (*2)
| \-- GetPortStatus (*1)
\-- Host detects remote wakeup
- D+ goes high, Host starts running by remote wakeup
- D+ is not stable, goes low
- Host requests GetPortStatus at (*1) and gets the following hub status:
- Current Connect Status bit is 0
- Connect Status Change bit is 1
- D+ stabilizes, goes high
- Host requests ClearPortFeature and thus Connect Status Change bit is
cleared at (*2)
- After waiting 100 ms, Host starts the Interrupt Transfer at (*3)
- Since the Connect Status Change bit is 0, Hub returns NAK.
In this case, port_event() is not called in hub_event() and Host cannot
recognize device. To solve this issue, flag change_bits even if only
Connect Status Change bit is 1 when got in the first GetPortStatus.
This issue occurs rarely because it only if D+ changes during a very
short time between GetPortStatus and ClearPortFeature. However, it is
fatal if it occurs in embedded system.
Signed-off-by: Keiya Nobuta <nobuta.keiya@fujitsu.com>
Cc: stable <stable@vger.kernel.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Link: https://lore.kernel.org/r/20200109051448.28150-1-nobuta.keiya@fujitsu.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/hub.c | 1 |
1 files changed, 1 insertions, 0 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 4efccf8bf99f..7d5ecf36a33c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1164,6 +1164,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) * PORT_OVER_CURRENT is not. So check for any of them. */ if (udev || (portstatus & USB_PORT_STAT_CONNECTION) || + (portchange & USB_PORT_STAT_C_CONNECTION) || (portstatus & USB_PORT_STAT_OVERCURRENT) || (portchange & USB_PORT_STAT_C_OVERCURRENT)) set_bit(port1, hub->change_bits); |