| From 86094b9cfac8da1500e6b495b2b490a6ce483be7 Mon Sep 17 00:00:00 2001 |
| From: Keiya Nobuta <nobuta.keiya@fujitsu.com> |
| Date: Thu, 9 Jan 2020 14:14:48 +0900 |
| Subject: [PATCH] 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> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c |
| index ea801737002a..1bde40aaaefa 100644 |
| --- a/drivers/usb/core/hub.c |
| +++ b/drivers/usb/core/hub.c |
| @@ -1191,6 +1191,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); |
| -- |
| 2.7.4 |
| |