diff options
Diffstat (limited to 'drivers/usb/core/hub.c')
| -rw-r--r-- | drivers/usb/core/hub.c | 130 | 
1 files changed, 95 insertions, 35 deletions
| diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 350dcd9af5d8..38cc4bae0a82 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -49,7 +49,7 @@ static void hub_event(struct work_struct *work);  DEFINE_MUTEX(usb_port_peer_mutex);  /* cycle leds on hubs that aren't blinking for attention */ -static bool blinkenlights = 0; +static bool blinkenlights;  module_param(blinkenlights, bool, S_IRUGO);  MODULE_PARM_DESC(blinkenlights, "true to cycle leds on hubs"); @@ -78,7 +78,7 @@ MODULE_PARM_DESC(initial_descriptor_timeout,   * otherwise the new scheme is used.  If that fails and "use_both_schemes"   * is set, then the driver will make another attempt, using the other scheme.   */ -static bool old_scheme_first = 0; +static bool old_scheme_first;  module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR);  MODULE_PARM_DESC(old_scheme_first,  		 "start with the old device initialization scheme"); @@ -298,7 +298,7 @@ static void usb_set_lpm_parameters(struct usb_device *udev)  	unsigned int hub_u1_del;  	unsigned int hub_u2_del; -	if (!udev->lpm_capable || udev->speed != USB_SPEED_SUPER) +	if (!udev->lpm_capable || udev->speed < USB_SPEED_SUPER)  		return;  	hub = usb_hub_to_struct_hub(udev->parent); @@ -537,29 +537,34 @@ static int get_hub_status(struct usb_device *hdev,  /*   * USB 2.0 spec Section 11.24.2.7 + * USB 3.1 takes into use the wValue and wLength fields, spec Section 10.16.2.6   */  static int get_port_status(struct usb_device *hdev, int port1, -		struct usb_port_status *data) +			   void *data, u16 value, u16 length)  {  	int i, status = -ETIMEDOUT;  	for (i = 0; i < USB_STS_RETRIES &&  			(status == -ETIMEDOUT || status == -EPIPE); i++) {  		status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), -			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1, -			data, sizeof(*data), USB_STS_TIMEOUT); +			USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, value, +			port1, data, length, USB_STS_TIMEOUT);  	}  	return status;  } -static int hub_port_status(struct usb_hub *hub, int port1, -		u16 *status, u16 *change) +static int hub_ext_port_status(struct usb_hub *hub, int port1, int type, +			       u16 *status, u16 *change, u32 *ext_status)  {  	int ret; +	int len = 4; + +	if (type != HUB_PORT_STATUS) +		len = 8;  	mutex_lock(&hub->status_mutex); -	ret = get_port_status(hub->hdev, port1, &hub->status->port); -	if (ret < 4) { +	ret = get_port_status(hub->hdev, port1, &hub->status->port, type, len); +	if (ret < len) {  		if (ret != -ENODEV)  			dev_err(hub->intfdev,  				"%s failed (err = %d)\n", __func__, ret); @@ -568,13 +573,22 @@ static int hub_port_status(struct usb_hub *hub, int port1,  	} else {  		*status = le16_to_cpu(hub->status->port.wPortStatus);  		*change = le16_to_cpu(hub->status->port.wPortChange); - +		if (type != HUB_PORT_STATUS && ext_status) +			*ext_status = le32_to_cpu( +				hub->status->port.dwExtPortStatus);  		ret = 0;  	}  	mutex_unlock(&hub->status_mutex);  	return ret;  } +static int hub_port_status(struct usb_hub *hub, int port1, +		u16 *status, u16 *change) +{ +	return hub_ext_port_status(hub, port1, HUB_PORT_STATUS, +				   status, change, NULL); +} +  static void kick_hub_wq(struct usb_hub *hub)  {  	struct usb_interface *intf; @@ -2131,7 +2145,7 @@ static void hub_disconnect_children(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 the caller must hold the usb_bus_list_lock, + * If *pdev is a root hub then the caller must hold the usb_bus_idr_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 @@ -2429,7 +2443,7 @@ static void set_usb_port_removable(struct usb_device *udev)   * enumerated.  The device descriptor is available, but not descriptors   * for any device configuration.  The caller must have locked either   * the parent hub (if udev is a normal device) or else the - * usb_bus_list_lock (if udev is a root hub).  The parent's pointer to + * usb_bus_idr_lock (if udev is a root hub).  The parent's pointer to   * udev has already been installed, but udev is not yet visible through   * sysfs or other filesystem code.   * @@ -2612,6 +2626,32 @@ out_authorized:  	return result;  } +/* + * Return 1 if port speed is SuperSpeedPlus, 0 otherwise + * check it from the link protocol field of the current speed ID attribute. + * current speed ID is got from ext port status request. Sublink speed attribute + * table is returned with the hub BOS SSP device capability descriptor + */ +static int port_speed_is_ssp(struct usb_device *hdev, int speed_id) +{ +	int ssa_count; +	u32 ss_attr; +	int i; +	struct usb_ssp_cap_descriptor *ssp_cap = hdev->bos->ssp_cap; + +	if (!ssp_cap) +		return 0; + +	ssa_count = le32_to_cpu(ssp_cap->bmAttributes) & +		USB_SSP_SUBLINK_SPEED_ATTRIBS; + +	for (i = 0; i <= ssa_count; i++) { +		ss_attr = le32_to_cpu(ssp_cap->bmSublinkSpeedAttr[i]); +		if (speed_id == (ss_attr & USB_SSP_SUBLINK_SPEED_SSID)) +			return !!(ss_attr & USB_SSP_SUBLINK_SPEED_LP); +	} +	return 0; +}  /* Returns 1 if @hub is a WUSB root hub, 0 otherwise */  static unsigned hub_is_wusb(struct usb_hub *hub) @@ -2619,7 +2659,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub)  	struct usb_hcd *hcd;  	if (hub->hdev->parent != NULL)  /* not a root hub? */  		return 0; -	hcd = container_of(hub->hdev->bus, struct usb_hcd, self); +	hcd = bus_to_hcd(hub->hdev->bus);  	return hcd->wireless;  } @@ -2645,7 +2685,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub)   */  static bool use_new_scheme(struct usb_device *udev, int retry)  { -	if (udev->speed == USB_SPEED_SUPER) +	if (udev->speed >= USB_SPEED_SUPER)  		return false;  	return USE_NEW_SCHEME(retry); @@ -2676,6 +2716,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,  	int delay_time, ret;  	u16 portstatus;  	u16 portchange; +	u32 ext_portstatus = 0;  	for (delay_time = 0;  			delay_time < HUB_RESET_TIMEOUT; @@ -2684,7 +2725,14 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,  		msleep(delay);  		/* read and decode port status */ -		ret = hub_port_status(hub, port1, &portstatus, &portchange); +		if (hub_is_superspeedplus(hub->hdev)) +			ret = hub_ext_port_status(hub, port1, +						  HUB_EXT_PORT_STATUS, +						  &portstatus, &portchange, +						  &ext_portstatus); +		else +			ret = hub_port_status(hub, port1, &portstatus, +					      &portchange);  		if (ret < 0)  			return ret; @@ -2727,6 +2775,10 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,  	if (hub_is_wusb(hub))  		udev->speed = USB_SPEED_WIRELESS; +	else if (hub_is_superspeedplus(hub->hdev) && +		 port_speed_is_ssp(hub->hdev, ext_portstatus & +				   USB_EXT_PORT_STAT_RX_SPEED_ID)) +		udev->speed = USB_SPEED_SUPER_PLUS;  	else if (hub_is_superspeed(hub->hdev))  		udev->speed = USB_SPEED_SUPER;  	else if (portstatus & USB_PORT_STAT_HIGH_SPEED) @@ -3989,7 +4041,7 @@ int usb_disable_lpm(struct usb_device *udev)  	struct usb_hcd *hcd;  	if (!udev || !udev->parent || -			udev->speed != USB_SPEED_SUPER || +			udev->speed < USB_SPEED_SUPER ||  			!udev->lpm_capable ||  			udev->state < USB_STATE_DEFAULT)  		return 0; @@ -4048,7 +4100,7 @@ void usb_enable_lpm(struct usb_device *udev)  	struct usb_port *port_dev;  	if (!udev || !udev->parent || -			udev->speed != USB_SPEED_SUPER || +			udev->speed < USB_SPEED_SUPER ||  			!udev->lpm_capable ||  			udev->state < USB_STATE_DEFAULT)  		return; @@ -4292,7 +4344,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,  {  	struct usb_device	*hdev = hub->hdev;  	struct usb_hcd		*hcd = bus_to_hcd(hdev->bus); -	int			i, j, retval; +	int			retries, operations, retval, i;  	unsigned		delay = HUB_SHORT_RESET_TIME;  	enum usb_device_speed	oldspeed = udev->speed;  	const char		*speed; @@ -4323,7 +4375,9 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,  	retval = -ENODEV; -	if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) { +	/* Don't allow speed changes at reset, except usb 3.0 to faster */ +	if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed && +	    !(oldspeed == USB_SPEED_SUPER && udev->speed > oldspeed)) {  		dev_dbg(&udev->dev, "device reset changed speed!\n");  		goto fail;  	} @@ -4335,6 +4389,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,  	 * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].  	 */  	switch (udev->speed) { +	case USB_SPEED_SUPER_PLUS:  	case USB_SPEED_SUPER:  	case USB_SPEED_WIRELESS:	/* fixed at 512 */  		udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512); @@ -4361,7 +4416,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,  	else  		speed = usb_speed_string(udev->speed); -	if (udev->speed != USB_SPEED_SUPER) +	if (udev->speed < USB_SPEED_SUPER)  		dev_info(&udev->dev,  				"%s %s USB device number %d using %s\n",  				(udev->config) ? "reset" : "new", speed, @@ -4394,7 +4449,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,  	 * first 8 bytes of the device descriptor to get the ep0 maxpacket  	 * value.  	 */ -	for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) { +	for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {  		bool did_new_scheme = false;  		if (use_new_scheme(udev, retry_counter)) { @@ -4421,7 +4476,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,  			 * 255 is for WUSB devices, we actually need to use  			 * 512 (WUSB1.0[4.8.1]).  			 */ -			for (j = 0; j < 3; ++j) { +			for (operations = 0; operations < 3; ++operations) {  				buf->bMaxPacketSize0 = 0;  				r = usb_control_msg(udev, usb_rcvaddr0pipe(),  					USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, @@ -4441,7 +4496,13 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,  						r = -EPROTO;  					break;  				} -				if (r == 0) +				/* +				 * Some devices time out if they are powered on +				 * when already connected. They need a second +				 * reset. But only on the first attempt, +				 * lest we get into a time out/reset loop +				 */ +				if (r == 0  || (r == -ETIMEDOUT && retries == 0))  					break;  			}  			udev->descriptor.bMaxPacketSize0 = @@ -4473,7 +4534,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,  		 * authorization will assign the final address.  		 */  		if (udev->wusb == 0) { -			for (j = 0; j < SET_ADDRESS_TRIES; ++j) { +			for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {  				retval = hub_set_address(udev, devnum);  				if (retval >= 0)  					break; @@ -4485,11 +4546,12 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,  							devnum, retval);  				goto fail;  			} -			if (udev->speed == USB_SPEED_SUPER) { +			if (udev->speed >= USB_SPEED_SUPER) {  				devnum = udev->devnum;  				dev_info(&udev->dev, -						"%s SuperSpeed USB device number %d using %s\n", +						"%s SuperSpeed%s USB device number %d using %s\n",  						(udev->config) ? "reset" : "new", +					 (udev->speed == USB_SPEED_SUPER_PLUS) ? "Plus" : "",  						devnum, udev->bus->controller->driver->name);  			} @@ -4528,7 +4590,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,  	 * got from those devices show they aren't superspeed devices. Warm  	 * reset the port attached by the devices can fix them.  	 */ -	if ((udev->speed == USB_SPEED_SUPER) && +	if ((udev->speed >= USB_SPEED_SUPER) &&  			(le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {  		dev_err(&udev->dev, "got a wrong device descriptor, "  				"warm reset device\n"); @@ -4539,7 +4601,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,  	}  	if (udev->descriptor.bMaxPacketSize0 == 0xff || -			udev->speed == USB_SPEED_SUPER) +			udev->speed >= USB_SPEED_SUPER)  		i = 512;  	else  		i = udev->descriptor.bMaxPacketSize0; @@ -4749,7 +4811,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,  		udev->level = hdev->level + 1;  		udev->wusb = hub_is_wusb(hub); -		/* Only USB 3.0 devices are connected to SuperSpeed hubs. */ +		/* Devices connected to SuperSpeed hubs are USB 3.0 or later */  		if (hub_is_superspeed(hub->hdev))  			udev->speed = USB_SPEED_SUPER;  		else @@ -5401,6 +5463,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)  	}  	bos = udev->bos; +	udev->bos = NULL;  	for (i = 0; i < SET_CONFIG_TRIES; ++i) { @@ -5493,11 +5556,8 @@ done:  	usb_set_usb2_hardware_lpm(udev, 1);  	usb_unlocked_enable_lpm(udev);  	usb_enable_ltm(udev); -	/* release the new BOS descriptor allocated  by hub_port_init() */ -	if (udev->bos != bos) { -		usb_release_bos_descriptor(udev); -		udev->bos = bos; -	} +	usb_release_bos_descriptor(udev); +	udev->bos = bos;  	return 0;  re_enumerate: | 
