aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2012-11-01 14:20:44 -0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2013-01-03 17:10:35 -0500
commitd3b9d7a9051d7024a93c76a84b2f84b3b66ad6d5 (patch)
tree7f99cb052f5e5afa4f1d3d788bba23b5c9f6029d /drivers/usb/core
parenta24a6078754f28528bc91e7e7b3e6ae86bd936d8 (diff)
USB: Fix connected device switch to Inactive state.
A USB 3.0 device can transition to the Inactive state if a U1 or U2 exit transition fails. The current code in hub_events simply issues a warm reset, but does not call any pre-reset or post-reset driver methods (or unbind/rebind drivers without them). Therefore the drivers won't know their device has just been reset. hub_events should instead call usb_reset_device. This means hub_port_reset now needs to figure out whether it should issue a warm reset or a hot reset. Remove the FIXME note about needing disconnect() for a NOTATTACHED device. This patch fixes that. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Acked-by: Alan Stern <stern@rowland.harvard.edu>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/hub.c30
1 files changed, 25 insertions, 5 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index d1fceb219d97..b00f10956b00 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2605,7 +2605,6 @@ static void hub_port_finish_reset(struct usb_hub *hub, int port1,
2605 case -ENODEV: 2605 case -ENODEV:
2606 clear_port_feature(hub->hdev, 2606 clear_port_feature(hub->hdev,
2607 port1, USB_PORT_FEAT_C_RESET); 2607 port1, USB_PORT_FEAT_C_RESET);
2608 /* FIXME need disconnect() for NOTATTACHED device */
2609 if (hub_is_superspeed(hub->hdev)) { 2608 if (hub_is_superspeed(hub->hdev)) {
2610 clear_port_feature(hub->hdev, port1, 2609 clear_port_feature(hub->hdev, port1,
2611 USB_PORT_FEAT_C_BH_PORT_RESET); 2610 USB_PORT_FEAT_C_BH_PORT_RESET);
@@ -2639,6 +2638,18 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
2639 * Some companion controllers don't like it when they mix. 2638 * Some companion controllers don't like it when they mix.
2640 */ 2639 */
2641 down_read(&ehci_cf_port_reset_rwsem); 2640 down_read(&ehci_cf_port_reset_rwsem);
2641 } else if (!warm) {
2642 /*
2643 * If the caller hasn't explicitly requested a warm reset,
2644 * double check and see if one is needed.
2645 */
2646 status = hub_port_status(hub, port1,
2647 &portstatus, &portchange);
2648 if (status < 0)
2649 goto done;
2650
2651 if (hub_port_warm_reset_required(hub, portstatus))
2652 warm = true;
2642 } 2653 }
2643 2654
2644 /* Reset the port */ 2655 /* Reset the port */
@@ -4690,12 +4701,21 @@ static void hub_events(void)
4690 */ 4701 */
4691 if (hub_port_warm_reset_required(hub, portstatus)) { 4702 if (hub_port_warm_reset_required(hub, portstatus)) {
4692 int status; 4703 int status;
4704 struct usb_device *udev =
4705 hub->ports[i - 1]->child;
4693 4706
4694 dev_dbg(hub_dev, "warm reset port %d\n", i); 4707 dev_dbg(hub_dev, "warm reset port %d\n", i);
4695 status = hub_port_reset(hub, i, NULL, 4708 if (!udev) {
4696 HUB_BH_RESET_TIME, true); 4709 status = hub_port_reset(hub, i,
4697 if (status < 0) 4710 NULL, HUB_BH_RESET_TIME,
4698 hub_port_disable(hub, i, 1); 4711 true);
4712 if (status < 0)
4713 hub_port_disable(hub, i, 1);
4714 } else {
4715 usb_lock_device(udev);
4716 status = usb_reset_device(udev);
4717 usb_unlock_device(udev);
4718 }
4699 connect_change = 0; 4719 connect_change = 0;
4700 } 4720 }
4701 4721