diff options
Diffstat (limited to 'drivers/usb/core')
| -rw-r--r-- | drivers/usb/core/driver.c | 3 | ||||
| -rw-r--r-- | drivers/usb/core/generic.c | 2 | ||||
| -rw-r--r-- | drivers/usb/core/hcd.c | 11 | ||||
| -rw-r--r-- | drivers/usb/core/hub.c | 37 | ||||
| -rw-r--r-- | drivers/usb/core/port.c | 32 | ||||
| -rw-r--r-- | drivers/usb/core/usb.c | 46 | 
6 files changed, 71 insertions, 60 deletions
| diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 072968c40ade..355ed33a2179 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-2.0  /* - * drivers/usb/driver.c - most of the driver model stuff for usb + * drivers/usb/core/driver.c - most of the driver model stuff for usb   *   * (C) Copyright 2005 Greg Kroah-Hartman <[email protected]>   * @@ -834,6 +834,7 @@ const struct usb_device_id *usb_device_match_id(struct usb_device *udev,  	return NULL;  } +EXPORT_SYMBOL_GPL(usb_device_match_id);  bool usb_driver_applicable(struct usb_device *udev,  			   struct usb_device_driver *udrv) diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 26f9fb9f67ca..740342a2812a 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -1,6 +1,6 @@  // SPDX-License-Identifier: GPL-2.0  /* - * drivers/usb/generic.c - generic driver for USB devices (not interfaces) + * drivers/usb/core/generic.c - generic driver for USB devices (not interfaces)   *   * (C) Copyright 2005 Greg Kroah-Hartman <[email protected]>   * diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 4d326ee12c36..3e01dd6e509b 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -753,6 +753,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)  {  	struct urb	*urb;  	int		length; +	int		status;  	unsigned long	flags;  	char		buffer[6];	/* Any root hubs with > 31 ports? */ @@ -770,11 +771,17 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)  		if (urb) {  			clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);  			hcd->status_urb = NULL; +			if (urb->transfer_buffer_length >= length) { +				status = 0; +			} else { +				status = -EOVERFLOW; +				length = urb->transfer_buffer_length; +			}  			urb->actual_length = length;  			memcpy(urb->transfer_buffer, buffer, length);  			usb_hcd_unlink_urb_from_ep(hcd, urb); -			usb_hcd_giveback_urb(hcd, urb, 0); +			usb_hcd_giveback_urb(hcd, urb, status);  		} else {  			length = 0;  			set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags); @@ -1281,7 +1288,7 @@ static int hcd_alloc_coherent(struct usb_bus *bus,  		return -EFAULT;  	} -	vaddr = hcd_buffer_alloc(bus, size + sizeof(vaddr), +	vaddr = hcd_buffer_alloc(bus, size + sizeof(unsigned long),  				 mem_flags, dma_handle);  	if (!vaddr)  		return -ENOMEM; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 00070a8a6507..47a1c8bddf86 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1110,7 +1110,10 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)  		} else {  			hub_power_on(hub, true);  		} -	} +	/* Give some time on remote wakeup to let links to transit to U0 */ +	} else if (hub_is_superspeed(hub->hdev)) +		msleep(20); +   init2:  	/* @@ -1225,7 +1228,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)  			 */  			if (portchange || (hub_is_superspeed(hub->hdev) &&  						port_resumed)) -				set_bit(port1, hub->change_bits); +				set_bit(port1, hub->event_bits);  		} else if (udev->persist_enabled) {  #ifdef CONFIG_PM @@ -2777,6 +2780,8 @@ static unsigned hub_is_wusb(struct usb_hub *hub)  #define PORT_INIT_TRIES		4  #endif	/* CONFIG_USB_FEW_INIT_RETRIES */ +#define DETECT_DISCONNECT_TRIES 5 +  #define HUB_ROOT_RESET_TIME	60	/* times are in msec */  #define HUB_SHORT_RESET_TIME	10  #define HUB_BH_RESET_TIME	50 @@ -3570,7 +3575,7 @@ static int finish_port_resume(struct usb_device *udev)   * This routine should only be called when persist is enabled.   */  static int wait_for_connected(struct usb_device *udev, -		struct usb_hub *hub, int *port1, +		struct usb_hub *hub, int port1,  		u16 *portchange, u16 *portstatus)  {  	int status = 0, delay_ms = 0; @@ -3584,7 +3589,7 @@ static int wait_for_connected(struct usb_device *udev,  		}  		msleep(20);  		delay_ms += 20; -		status = hub_port_status(hub, *port1, portstatus, portchange); +		status = hub_port_status(hub, port1, portstatus, portchange);  	}  	dev_dbg(&udev->dev, "Waited %dms for CONNECT\n", delay_ms);  	return status; @@ -3690,7 +3695,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)  	}  	if (udev->persist_enabled) -		status = wait_for_connected(udev, hub, &port1, &portchange, +		status = wait_for_connected(udev, hub, port1, &portchange,  				&portstatus);  	status = check_port_resume_type(udev, @@ -5543,6 +5548,7 @@ static void port_event(struct usb_hub *hub, int port1)  	struct usb_device *udev = port_dev->child;  	struct usb_device *hdev = hub->hdev;  	u16 portstatus, portchange; +	int i = 0;  	connect_change = test_bit(port1, hub->change_bits);  	clear_bit(port1, hub->event_bits); @@ -5619,17 +5625,27 @@ static void port_event(struct usb_hub *hub, int port1)  		connect_change = 1;  	/* -	 * Warm reset a USB3 protocol port if it's in -	 * SS.Inactive state. +	 * Avoid trying to recover a USB3 SS.Inactive port with a warm reset if +	 * the device was disconnected. A 12ms disconnect detect timer in +	 * SS.Inactive state transitions the port to RxDetect automatically. +	 * SS.Inactive link error state is common during device disconnect.  	 */ -	if (hub_port_warm_reset_required(hub, port1, portstatus)) { -		dev_dbg(&port_dev->dev, "do warm reset\n"); -		if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION) +	while (hub_port_warm_reset_required(hub, port1, portstatus)) { +		if ((i++ < DETECT_DISCONNECT_TRIES) && udev) { +			u16 unused; + +			msleep(20); +			hub_port_status(hub, port1, &portstatus, &unused); +			dev_dbg(&port_dev->dev, "Wait for inactive link disconnect detect\n"); +			continue; +		} else if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)  				|| udev->state == USB_STATE_NOTATTACHED) { +			dev_dbg(&port_dev->dev, "do warm reset, port only\n");  			if (hub_port_reset(hub, port1, NULL,  					HUB_BH_RESET_TIME, true) < 0)  				hub_port_disable(hub, port1, 1);  		} else { +			dev_dbg(&port_dev->dev, "do warm reset, full device\n");  			usb_unlock_port(port_dev);  			usb_lock_device(udev);  			usb_reset_device(udev); @@ -5637,6 +5653,7 @@ static void port_event(struct usb_hub *hub, int port1)  			usb_lock_port(port_dev);  			connect_change = 0;  		} +		break;  	}  	if (connect_change) diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index dfcca9c876c7..c2bbf97a79be 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -9,6 +9,7 @@  #include <linux/slab.h>  #include <linux/pm_qos.h> +#include <linux/component.h>  #include "hub.h" @@ -528,6 +529,32 @@ static void find_and_link_peer(struct usb_hub *hub, int port1)  		link_peers_report(port_dev, peer);  } +static int connector_bind(struct device *dev, struct device *connector, void *data) +{ +	int ret; + +	ret = sysfs_create_link(&dev->kobj, &connector->kobj, "connector"); +	if (ret) +		return ret; + +	ret = sysfs_create_link(&connector->kobj, &dev->kobj, dev_name(dev)); +	if (ret) +		sysfs_remove_link(&dev->kobj, "connector"); + +	return ret; +} + +static void connector_unbind(struct device *dev, struct device *connector, void *data) +{ +	sysfs_remove_link(&connector->kobj, dev_name(dev)); +	sysfs_remove_link(&dev->kobj, "connector"); +} + +static const struct component_ops connector_ops = { +	.bind = connector_bind, +	.unbind = connector_unbind, +}; +  int usb_hub_create_port_device(struct usb_hub *hub, int port1)  {  	struct usb_port *port_dev; @@ -577,6 +604,10 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)  	find_and_link_peer(hub, port1); +	retval = component_add(&port_dev->dev, &connector_ops); +	if (retval) +		dev_warn(&port_dev->dev, "failed to add component\n"); +  	/*  	 * Enable runtime pm and hold a refernce that hub_configure()  	 * will drop once the PM_QOS_NO_POWER_OFF flag state has been set @@ -619,5 +650,6 @@ void usb_hub_remove_port_device(struct usb_hub *hub, int port1)  	peer = port_dev->peer;  	if (peer)  		unlink_peers(port_dev, peer); +	component_del(&port_dev->dev, &connector_ops);  	device_unregister(&port_dev->dev);  } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 62368c4ed37a..2ce3667ec6fa 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -398,52 +398,6 @@ int usb_for_each_dev(void *data, int (*fn)(struct usb_device *, void *))  }  EXPORT_SYMBOL_GPL(usb_for_each_dev); -struct each_hub_arg { -	void *data; -	int (*fn)(struct device *, void *); -}; - -static int __each_hub(struct usb_device *hdev, void *data) -{ -	struct each_hub_arg *arg = (struct each_hub_arg *)data; -	struct usb_hub *hub; -	int ret = 0; -	int i; - -	hub = usb_hub_to_struct_hub(hdev); -	if (!hub) -		return 0; - -	mutex_lock(&usb_port_peer_mutex); - -	for (i = 0; i < hdev->maxchild; i++) { -		ret = arg->fn(&hub->ports[i]->dev, arg->data); -		if (ret) -			break; -	} - -	mutex_unlock(&usb_port_peer_mutex); - -	return ret; -} - -/** - * usb_for_each_port - interate over all USB ports in the system - * @data: data pointer that will be handed to the callback function - * @fn: callback function to be called for each USB port - * - * Iterate over all USB ports and call @fn for each, passing it @data. If it - * returns anything other than 0, we break the iteration prematurely and return - * that value. - */ -int usb_for_each_port(void *data, int (*fn)(struct device *, void *)) -{ -	struct each_hub_arg arg = {data, fn}; - -	return usb_for_each_dev(&arg, __each_hub); -} -EXPORT_SYMBOL_GPL(usb_for_each_port); -  /**   * usb_release_dev - free a usb device structure when all users of it are finished.   * @dev: device that's been disconnected |