Commit 232f0f0c authored by Alan Stern's avatar Alan Stern Committed by Kleber Sacilotto de Souza

USB: hub: Fix handling of connect changes during sleep

BugLink: https://bugs.launchpad.net/bugs/1878098

commit 9f952e26 upstream.

Commit 8099f58f ("USB: hub: Don't record a connect-change event
during reset-resume") wasn't very well conceived.  The problem it
tried to fix was that if a connect-change event occurred while the
system was asleep (such as a device disconnecting itself from the bus
when it is suspended and then reconnecting when it resumes)
requiring a reset-resume during the system wakeup transition, the hub
port's change_bit entry would remain set afterward.  This would cause
the hub driver to believe another connect-change event had occurred
after the reset-resume, which was wrong and would lead the driver to
send unnecessary requests to the device (which could interfere with a
firmware update).

The commit tried to fix this by not setting the change_bit during the
wakeup.  But this was the wrong thing to do; it means that when a
device is unplugged while the system is asleep, the hub driver doesn't
realize anything has happened: The change_bit flag which would tell it
to handle the disconnect event is clear.

The commit needs to be reverted and the problem fixed in a different
way.  Fortunately an alternative solution was noted in the commit's
Changelog: We can continue to set the change_bit entry in
hub_activate() but then clear it when a reset-resume occurs.  That way
the the hub driver will see the change_bit when a device is
disconnected but won't see it when the device is still present.

That's what this patch does.
Reported-and-tested-by: default avatarPeter Chen <peter.chen@nxp.com>
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Fixes: 8099f58f ("USB: hub: Don't record a connect-change event during reset-resume")
Tested-by: default avatarPaul Zimmerman <pauldzim@gmail.com>
CC: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/Pine.LNX.4.44L0.2004221602480.11262-100000@iolanthe.rowland.orgSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarIan May <ian.may@canonical.com>
Signed-off-by: default avatarKleber Sacilotto de Souza <kleber.souza@canonical.com>
parent d85f2ffd
...@@ -1189,6 +1189,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) ...@@ -1189,6 +1189,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
#ifdef CONFIG_PM #ifdef CONFIG_PM
udev->reset_resume = 1; udev->reset_resume = 1;
#endif #endif
/* Don't set the change_bits when the device
* was powered off.
*/
if (test_bit(port1, hub->power_bits))
set_bit(port1, hub->change_bits);
} else { } else {
/* The power session is gone; tell hub_wq */ /* The power session is gone; tell hub_wq */
...@@ -2991,6 +2996,15 @@ static int check_port_resume_type(struct usb_device *udev, ...@@ -2991,6 +2996,15 @@ static int check_port_resume_type(struct usb_device *udev,
if (portchange & USB_PORT_STAT_C_ENABLE) if (portchange & USB_PORT_STAT_C_ENABLE)
usb_clear_port_feature(hub->hdev, port1, usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_ENABLE); USB_PORT_FEAT_C_ENABLE);
/*
* Whatever made this reset-resume necessary may have
* turned on the port1 bit in hub->change_bits. But after
* a successful reset-resume we want the bit to be clear;
* if it was on it would indicate that something happened
* following the reset-resume.
*/
clear_bit(port1, hub->change_bits);
} }
return status; return status;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment