diff options
Diffstat (limited to 'drivers/usb/core/hub.c')
| -rw-r--r-- | drivers/usb/core/hub.c | 44 | 
1 files changed, 33 insertions, 11 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index bdeadc112d29..ddbf32d599cb 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -124,6 +124,10 @@ struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)  int usb_device_supports_lpm(struct usb_device *udev)  { +	/* Some devices have trouble with LPM */ +	if (udev->quirks & USB_QUIRK_NO_LPM) +		return 0; +  	/* USB 2.1 (and greater) devices indicate LPM support through  	 * their USB 2.0 Extended Capabilities BOS descriptor.  	 */ @@ -1031,10 +1035,20 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)  	unsigned delay;  	/* Continue a partial initialization */ -	if (type == HUB_INIT2) -		goto init2; -	if (type == HUB_INIT3) +	if (type == HUB_INIT2 || type == HUB_INIT3) { +		device_lock(hub->intfdev); + +		/* Was the hub disconnected while we were waiting? */ +		if (hub->disconnected) { +			device_unlock(hub->intfdev); +			kref_put(&hub->kref, hub_release); +			return; +		} +		if (type == HUB_INIT2) +			goto init2;  		goto init3; +	} +	kref_get(&hub->kref);  	/* The superspeed hub except for root hub has to use Hub Depth  	 * value as an offset into the route string to locate the bits @@ -1232,6 +1246,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)  			queue_delayed_work(system_power_efficient_wq,  					&hub->init_work,  					msecs_to_jiffies(delay)); +			device_unlock(hub->intfdev);  			return;		/* Continues at init3: below */  		} else {  			msleep(delay); @@ -1253,6 +1268,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)  	/* Allow autosuspend if it was suppressed */  	if (type <= HUB_INIT3)  		usb_autopm_put_interface_async(to_usb_interface(hub->intfdev)); + +	if (type == HUB_INIT2 || type == HUB_INIT3) +		device_unlock(hub->intfdev); + +	kref_put(&hub->kref, hub_release);  }  /* Implement the continuations for the delays above */ @@ -4512,6 +4532,8 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,  		goto fail;  	} +	usb_detect_quirks(udev); +  	if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {  		retval = usb_get_bos_descriptor(udev);  		if (!retval) { @@ -4710,7 +4732,6 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,  		if (status < 0)  			goto loop; -		usb_detect_quirks(udev);  		if (udev->quirks & USB_QUIRK_DELAY_INIT)  			msleep(1000); @@ -5326,9 +5347,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev)  	if (udev->usb2_hw_lpm_enabled == 1)  		usb_set_usb2_hardware_lpm(udev, 0); -	bos = udev->bos; -	udev->bos = NULL; -  	/* Disable LPM and LTM while we reset the device and reinstall the alt  	 * settings.  Device-initiated LPM settings, and system exit latency  	 * settings are cleared when the device is reset, so we have to set @@ -5337,15 +5355,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev)  	ret = usb_unlocked_disable_lpm(udev);  	if (ret) {  		dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); -		goto re_enumerate; +		goto re_enumerate_no_bos;  	}  	ret = usb_disable_ltm(udev);  	if (ret) {  		dev_err(&udev->dev, "%s Failed to disable LTM\n.",  				__func__); -		goto re_enumerate; +		goto re_enumerate_no_bos;  	} +	bos = udev->bos; +	udev->bos = NULL; +  	for (i = 0; i < SET_CONFIG_TRIES; ++i) {  		/* ep0 maxpacket size may change; let the HCD know about it. @@ -5442,10 +5463,11 @@ done:  	return 0;  re_enumerate: -	/* LPM state doesn't matter when we're about to destroy the device. */ -	hub_port_logical_disconnect(parent_hub, port1);  	usb_release_bos_descriptor(udev);  	udev->bos = bos; +re_enumerate_no_bos: +	/* LPM state doesn't matter when we're about to destroy the device. */ +	hub_port_logical_disconnect(parent_hub, port1);  	return -ENODEV;  }  |