diff options
Diffstat (limited to 'drivers/usb/core/hub.c')
| -rw-r--r-- | drivers/usb/core/hub.c | 184 | 
1 files changed, 112 insertions, 72 deletions
| diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index e6b682c6c23..a7c04e24ca4 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -120,7 +120,7 @@ static inline char *portspeed(struct usb_hub *hub, int portstatus)  	if (hub_is_superspeed(hub->hdev))  		return "5.0 Gb/s";  	if (portstatus & USB_PORT_STAT_HIGH_SPEED) -    		return "480 Mb/s"; +		return "480 Mb/s";  	else if (portstatus & USB_PORT_STAT_LOW_SPEED)  		return "1.5 Mb/s";  	else @@ -135,7 +135,7 @@ struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)  	return usb_get_intfdata(hdev->actconfig->interface[0]);  } -static int usb_device_supports_lpm(struct usb_device *udev) +int usb_device_supports_lpm(struct usb_device *udev)  {  	/* USB 2.1 (and greater) devices indicate LPM support through  	 * their USB 2.0 Extended Capabilities BOS descriptor. @@ -156,6 +156,11 @@ static int usb_device_supports_lpm(struct usb_device *udev)  				"Power management will be impacted.\n");  		return 0;  	} + +	/* udev is root hub */ +	if (!udev->parent) +		return 1; +  	if (udev->parent->lpm_capable)  		return 1; @@ -310,9 +315,9 @@ static void usb_set_lpm_parameters(struct usb_device *udev)  		return;  	udev_u1_del = udev->bos->ss_cap->bU1devExitLat; -	udev_u2_del = udev->bos->ss_cap->bU2DevExitLat; +	udev_u2_del = le16_to_cpu(udev->bos->ss_cap->bU2DevExitLat);  	hub_u1_del = udev->parent->bos->ss_cap->bU1devExitLat; -	hub_u2_del = udev->parent->bos->ss_cap->bU2DevExitLat; +	hub_u2_del = le16_to_cpu(udev->parent->bos->ss_cap->bU2DevExitLat);  	usb_set_lpm_mel(udev, &udev->u1_params, udev_u1_del,  			hub, &udev->parent->u1_params, hub_u1_del); @@ -433,7 +438,7 @@ static void set_port_led(  			case HUB_LED_OFF: s = "off"; break;  			case HUB_LED_AUTO: s = "auto"; break;  			default: s = "??"; break; -			}; s; }), +			} s; }),  			status);  } @@ -857,7 +862,7 @@ static int hub_hub_status(struct usb_hub *hub,  				"%s failed (err = %d)\n", __func__, ret);  	} else {  		*status = le16_to_cpu(hub->status->hub.wHubStatus); -		*change = le16_to_cpu(hub->status->hub.wHubChange);  +		*change = le16_to_cpu(hub->status->hub.wHubChange);  		ret = 0;  	}  	mutex_unlock(&hub->status_mutex); @@ -956,7 +961,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)  	 */  	set_bit(port1, hub->change_bits); - 	kick_khubd(hub); +	kick_khubd(hub);  }  /** @@ -1107,16 +1112,13 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)  			/*  			 * USB3 protocol ports will automatically transition  			 * to Enabled state when detect an USB3.0 device attach. -			 * Do not disable USB3 protocol ports. +			 * Do not disable USB3 protocol ports, just pretend +			 * power was lost  			 */ -			if (!hub_is_superspeed(hdev)) { +			portstatus &= ~USB_PORT_STAT_ENABLE; +			if (!hub_is_superspeed(hdev))  				usb_clear_port_feature(hdev, port1,  						   USB_PORT_FEAT_ENABLE); -				portstatus &= ~USB_PORT_STAT_ENABLE; -			} else { -				/* Pretend that power was lost for USB3 devs */ -				portstatus &= ~USB_PORT_STAT_ENABLE; -			}  		}  		/* Clear status-change flags; we'll debounce later */ @@ -1130,6 +1132,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)  			usb_clear_port_feature(hub->hdev, port1,  					USB_PORT_FEAT_C_ENABLE);  		} +		if (portchange & USB_PORT_STAT_C_RESET) { +			need_debounce_delay = true; +			usb_clear_port_feature(hub->hdev, port1, +					USB_PORT_FEAT_C_RESET); +		}  		if ((portchange & USB_PORT_STAT_C_BH_RESET) &&  				hub_is_superspeed(hub->hdev)) {  			need_debounce_delay = true; @@ -1361,7 +1368,7 @@ static int hub_configure(struct usb_hub *hub,  	if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&  			!(hub_is_superspeed(hdev))) {  		int	i; -		char	portstr [USB_MAXCHILDREN + 1]; +		char	portstr[USB_MAXCHILDREN + 1];  		for (i = 0; i < hdev->maxchild; i++)  			portstr[i] = hub->descriptor->u.hs.DeviceRemovable @@ -1429,32 +1436,32 @@ static int hub_configure(struct usb_hub *hub,  	/* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */  	switch (wHubCharacteristics & HUB_CHAR_TTTT) { -		case HUB_TTTT_8_BITS: -			if (hdev->descriptor.bDeviceProtocol != 0) { -				hub->tt.think_time = 666; -				dev_dbg(hub_dev, "TT requires at most %d " -						"FS bit times (%d ns)\n", -					8, hub->tt.think_time); -			} -			break; -		case HUB_TTTT_16_BITS: -			hub->tt.think_time = 666 * 2; -			dev_dbg(hub_dev, "TT requires at most %d " -					"FS bit times (%d ns)\n", -				16, hub->tt.think_time); -			break; -		case HUB_TTTT_24_BITS: -			hub->tt.think_time = 666 * 3; +	case HUB_TTTT_8_BITS: +		if (hdev->descriptor.bDeviceProtocol != 0) { +			hub->tt.think_time = 666;  			dev_dbg(hub_dev, "TT requires at most %d "  					"FS bit times (%d ns)\n", -				24, hub->tt.think_time); -			break; -		case HUB_TTTT_32_BITS: -			hub->tt.think_time = 666 * 4; -			dev_dbg(hub_dev, "TT requires at most %d " -					"FS bit times (%d ns)\n", -				32, hub->tt.think_time); -			break; +				8, hub->tt.think_time); +		} +		break; +	case HUB_TTTT_16_BITS: +		hub->tt.think_time = 666 * 2; +		dev_dbg(hub_dev, "TT requires at most %d " +				"FS bit times (%d ns)\n", +			16, hub->tt.think_time); +		break; +	case HUB_TTTT_24_BITS: +		hub->tt.think_time = 666 * 3; +		dev_dbg(hub_dev, "TT requires at most %d " +				"FS bit times (%d ns)\n", +			24, hub->tt.think_time); +		break; +	case HUB_TTTT_32_BITS: +		hub->tt.think_time = 666 * 4; +		dev_dbg(hub_dev, "TT requires at most %d " +				"FS bit times (%d ns)\n", +			32, hub->tt.think_time); +		break;  	}  	/* probe() zeroes hub->indicator[] */ @@ -1560,7 +1567,7 @@ static int hub_configure(struct usb_hub *hub,  	/* maybe cycle the hub leds */  	if (hub->has_indicators && blinkenlights) -		hub->indicator [0] = INDICATOR_CYCLE; +		hub->indicator[0] = INDICATOR_CYCLE;  	for (i = 0; i < hdev->maxchild; i++) {  		ret = usb_hub_create_port_device(hub, i + 1); @@ -1978,7 +1985,7 @@ static void choose_devnum(struct usb_device *udev)  		if (devnum >= 128)  			devnum = find_next_zero_bit(bus->devmap.devicemap,  						    128, 1); -		bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1); +		bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);  	}  	if (devnum < 128) {  		set_bit(devnum, bus->devmap.devicemap); @@ -2018,8 +2025,8 @@ static void hub_free_dev(struct usb_device *udev)   * Something got disconnected. Get rid of it and all of its children.   *   * If *pdev is a normal device then the parent hub must already be locked. - * If *pdev is a root hub then this routine will acquire the - * usb_bus_list_lock on behalf of the caller. + * If *pdev is a root hub then the caller must hold the usb_bus_list_lock, + * which protects the set of root hubs as well as the list of buses.   *   * Only hub drivers (including virtual root hub drivers for host   * controllers) should ever call this. @@ -2232,8 +2239,7 @@ static int usb_enumerate_device(struct usb_device *udev)  		udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);  		udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);  		udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL); -	} -	else { +	} else {  		/* read the standard strings and cache them if present */  		udev->product = usb_cache_string(udev, udev->descriptor.iProduct);  		udev->manufacturer = usb_cache_string(udev, @@ -2489,7 +2495,7 @@ error_device_descriptor:  	usb_autosuspend_device(usb_dev);  error_autoresume:  out_authorized: -	usb_unlock_device(usb_dev);	// complements locktree +	usb_unlock_device(usb_dev);	/* complements locktree */  	return result;  } @@ -3108,8 +3114,8 @@ static int finish_port_resume(struct usb_device *udev)   retry_reset_resume:  		status = usb_reset_and_verify_device(udev); - 	/* 10.5.4.5 says be sure devices in the tree are still there. - 	 * For now let's assume the device didn't go crazy on resume, +	/* 10.5.4.5 says be sure devices in the tree are still there. +	 * For now let's assume the device didn't go crazy on resume,  	 * and device drivers will know about any resume quirks.  	 */  	if (status == 0) { @@ -3211,7 +3217,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)  	if (status == 0 && !port_is_suspended(hub, portstatus))  		goto SuspendCleared; -	// dev_dbg(hub->intfdev, "resume port %d\n", port1); +	/* dev_dbg(hub->intfdev, "resume port %d\n", port1); */  	set_bit(port1, hub->busy_bits); @@ -3855,7 +3861,7 @@ EXPORT_SYMBOL_GPL(usb_enable_ltm);   * Between connect detection and reset signaling there must be a delay   * of 100ms at least for debounce and power-settling.  The corresponding   * timer shall restart whenever the downstream port detects a disconnect. - *  + *   * Apparently there are some bluetooth and irda-dongles and a number of   * low-speed devices for which this debounce period may last over a second.   * Not covered by the spec - but easy to deal with. @@ -3949,6 +3955,32 @@ static int hub_set_address(struct usb_device *udev, int devnum)  	return retval;  } +/* + * There are reports of USB 3.0 devices that say they support USB 2.0 Link PM + * when they're plugged into a USB 2.0 port, but they don't work when LPM is + * enabled. + * + * Only enable USB 2.0 Link PM if the port is internal (hardwired), or the + * device says it supports the new USB 2.0 Link PM errata by setting the BESL + * support bit in the BOS descriptor. + */ +static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev) +{ +	int connect_type; + +	if (!udev->usb2_hw_lpm_capable) +		return; + +	connect_type = usb_get_hub_port_connect_type(udev->parent, +			udev->portnum); + +	if ((udev->bos->ext_cap->bmAttributes & USB_BESL_SUPPORT) || +			connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) { +		udev->usb2_hw_lpm_allowed = 1; +		usb_set_usb2_hardware_lpm(udev, 1); +	} +} +  /* Reset device, (re)assign address, get device descriptor.   * Device connection must be stable, no more debouncing needed.   * Returns device in USB_STATE_ADDRESS, except on error. @@ -4055,7 +4087,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,  		udev->tt = &hub->tt;  		udev->ttport = port1;  	} -  +  	/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?  	 * Because device hardware and firmware is sometimes buggy in  	 * this area, and this is how Linux has done it for ages. @@ -4130,11 +4162,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,  #undef GET_DESCRIPTOR_BUFSIZE  		} - 		/* - 		 * If device is WUSB, we already assigned an - 		 * unauthorized address in the Connect Ack sequence; - 		 * authorization will assign the final address. - 		 */ +		/* +		 * If device is WUSB, we already assigned an +		 * unauthorized address in the Connect Ack sequence; +		 * authorization will assign the final address. +		 */  		if (udev->wusb == 0) {  			for (j = 0; j < SET_ADDRESS_TRIES; ++j) {  				retval = hub_set_address(udev, devnum); @@ -4163,7 +4195,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,  			msleep(10);  			if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))  				break; -  		} +		}  		retval = usb_get_device_descriptor(udev, 8);  		if (retval < 8) { @@ -4219,7 +4251,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,  		udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);  		usb_ep0_reinit(udev);  	} -   +  	retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);  	if (retval < (signed)sizeof(udev->descriptor)) {  		if (retval != -ENODEV) @@ -4242,6 +4274,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,  	/* notify HCD that we have a device connected and addressed */  	if (hcd->driver->update_device)  		hcd->driver->update_device(hcd, udev); +	hub_set_initial_usb2_lpm_policy(udev);  fail:  	if (retval) {  		hub_port_disable(hub, port1, 0); @@ -4316,7 +4349,7 @@ hub_power_remaining (struct usb_hub *hub)  	}  	if (remaining < 0) {  		dev_warn(hub->intfdev, "%dmA over power budget!\n", -			- remaining); +			-remaining);  		remaining = 0;  	}  	return remaining; @@ -4427,7 +4460,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,  			set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);  		if (portstatus & USB_PORT_STAT_ENABLE) -  			goto done; +			goto done;  		return;  	}  	if (hub_is_superspeed(hub->hdev)) @@ -4450,7 +4483,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,  		}  		usb_set_device_state(udev, USB_STATE_POWERED); - 		udev->bus_mA = hub->mA_per_port; +		udev->bus_mA = hub->mA_per_port;  		udev->level = hdev->level + 1;  		udev->wusb = hub_is_wusb(hub); @@ -4504,7 +4537,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,  				goto loop_disable;  			}  		} -  +  		/* check for devices running slower than they could */  		if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200  				&& udev->speed == USB_SPEED_FULL @@ -4564,7 +4597,7 @@ loop:  			dev_err(hub_dev, "unable to enumerate USB device on port %d\n",  					port1);  	} -  +  done:  	hub_port_disable(hub, port1, 1);  	if (hcd->driver->relinquish_port && !hub->hdev->parent) @@ -4729,7 +4762,7 @@ static void hub_events(void)  				 * EM interference sometimes causes badly  				 * shielded USB devices to be shutdown by  				 * the hub, this hack enables them again. -				 * Works at least with mouse driver.  +				 * Works at least with mouse driver.  				 */  				if (!(portstatus & USB_PORT_STAT_ENABLE)  				    && !connect_change @@ -4841,7 +4874,7 @@ static void hub_events(void)  				dev_dbg(hub_dev, "over-current change\n");  				clear_hub_feature(hdev, C_HUB_OVER_CURRENT);  				msleep(500);	/* Cool down */ -                        	hub_power_on(hub, true); +				hub_power_on(hub, true);  				hub_hub_status(hub, &status, &unused);  				if (status & HUB_STATUS_OVERCURRENT)  					dev_err(hub_dev, "over-current " @@ -4861,7 +4894,7 @@ static void hub_events(void)  		usb_unlock_device(hdev);  		kref_put(&hub->kref, hub_release); -        } /* end while (1) */ +	} /* end while (1) */  }  static int hub_thread(void *__unused) @@ -4886,7 +4919,7 @@ static int hub_thread(void *__unused)  static const struct usb_device_id hub_id_table[] = {      { .match_flags = USB_DEVICE_ID_MATCH_VENDOR -	           | USB_DEVICE_ID_MATCH_INT_CLASS, +			| USB_DEVICE_ID_MATCH_INT_CLASS,        .idVendor = USB_VENDOR_GENESYS_LOGIC,        .bInterfaceClass = USB_CLASS_HUB,        .driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND}, @@ -5086,6 +5119,12 @@ static int usb_reset_and_verify_device(struct usb_device *udev)  	}  	parent_hub = usb_hub_to_struct_hub(parent_hdev); +	/* Disable USB2 hardware LPM. +	 * It will be re-enabled by the enumeration process. +	 */ +	if (udev->usb2_hw_lpm_enabled == 1) +		usb_set_usb2_hardware_lpm(udev, 0); +  	bos = udev->bos;  	udev->bos = NULL; @@ -5120,13 +5159,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)  	if (ret < 0)  		goto re_enumerate; -  +  	/* Device might have changed firmware (DFU or similar) */  	if (descriptors_changed(udev, &descriptor, bos)) {  		dev_info(&udev->dev, "device firmware changed\n");  		udev->descriptor = descriptor;	/* for disconnect() calls */  		goto re_enumerate; -  	} +	}  	/* Restore the device's previous configuration */  	if (!udev->actconfig) @@ -5151,7 +5190,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)  			udev->actconfig->desc.bConfigurationValue, ret);  		mutex_unlock(hcd->bandwidth_mutex);  		goto re_enumerate; -  	} +	}  	mutex_unlock(hcd->bandwidth_mutex);  	usb_set_device_state(udev, USB_STATE_CONFIGURED); @@ -5193,12 +5232,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)  done:  	/* Now that the alt settings are re-installed, enable LTM and LPM. */ +	usb_set_usb2_hardware_lpm(udev, 1);  	usb_unlocked_enable_lpm(udev);  	usb_enable_ltm(udev);  	usb_release_bos_descriptor(udev);  	udev->bos = bos;  	return 0; -  +  re_enumerate:  	/* LPM state doesn't matter when we're about to destroy the device. */  	hub_port_logical_disconnect(parent_hub, port1); @@ -5461,6 +5501,6 @@ acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,  	if (!hub)  		return NULL; -	return DEVICE_ACPI_HANDLE(&hub->ports[port1 - 1]->dev); +	return ACPI_HANDLE(&hub->ports[port1 - 1]->dev);  }  #endif | 
