diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 30ce237569dd..a99e3eb28aa5 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1173,22 +1173,25 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) port_resumed)) set_bit(port1, hub->change_bits); - } else if (udev->persist_enabled) { + } else { struct usb_port *port_dev = hub->ports[port1 - 1]; + if (udev->persist_enabled) { #ifdef CONFIG_PM - udev->reset_resume = 1; + udev->reset_resume = 1; #endif - /* Don't set the change_bits when the device - * was powered off. - */ - if (port_dev->power_is_on) - set_bit(port1, hub->change_bits); + /* Don't set the change_bits when the device + * was powered off. + */ + if (port_dev->power_is_on) + set_bit(port1, hub->change_bits); + } - } else { - /* The power session is gone; tell khubd */ - usb_set_device_state(udev, USB_STATE_NOTATTACHED); - set_bit(port1, hub->change_bits); + /* The power session may be gone; wait for port + * recovery before letting khubd take action on + * this port + */ + set_bit(port1, hub->delayed_change_bits); } } @@ -3285,6 +3288,11 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) usb_unlocked_enable_lpm(udev); } + if (test_and_clear_bit(port1, hub->delayed_change_bits)) { + set_bit(port1, hub->change_bits); + kick_khubd(hub); + } + return status; } diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 4e4790dea343..74e87c0380f8 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -45,8 +45,9 @@ struct usb_hub { unsigned long event_bits[1]; /* status change bitmask */ unsigned long change_bits[1]; /* ports with logical connect status change */ - unsigned long busy_bits[1]; /* ports being reset or - resumed */ + unsigned long busy_bits[1]; /* ports being reset */ + unsigned long delayed_change_bits[1]; /* ports awaiting + recovery */ unsigned long removed_bits[1]; /* ports with a "removed" device present */ unsigned long wakeup_bits[1]; /* ports that have signaled