diff options
Diffstat (limited to 'drivers/usb/core')
| -rw-r--r-- | drivers/usb/core/devices.c | 18 | ||||
| -rw-r--r-- | drivers/usb/core/driver.c | 25 | ||||
| -rw-r--r-- | drivers/usb/core/generic.c | 7 | ||||
| -rw-r--r-- | drivers/usb/core/hcd.c | 19 | ||||
| -rw-r--r-- | drivers/usb/core/hub.c | 97 | ||||
| -rw-r--r-- | drivers/usb/core/message.c | 66 | ||||
| -rw-r--r-- | drivers/usb/core/urb.c | 29 | ||||
| -rw-r--r-- | drivers/usb/core/usb.c | 13 | 
8 files changed, 173 insertions, 101 deletions
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index f460de31ace..cbacea933b1 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -591,16 +591,14 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,  	/* Now look at all of this device's children. */  	usb_hub_for_each_child(usbdev, chix, childdev) { -		if (childdev) { -			usb_lock_device(childdev); -			ret = usb_device_dump(buffer, nbytes, skip_bytes, -					      file_offset, childdev, bus, -					      level + 1, chix - 1, ++cnt); -			usb_unlock_device(childdev); -			if (ret == -EFAULT) -				return total_written; -			total_written += ret; -		} +		usb_lock_device(childdev); +		ret = usb_device_dump(buffer, nbytes, skip_bytes, +				      file_offset, childdev, bus, +				      level + 1, chix - 1, ++cnt); +		usb_unlock_device(childdev); +		if (ret == -EFAULT) +			return total_written; +		total_written += ret;  	}  	return total_written;  } diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 6056db7af41..88dde95b679 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -32,8 +32,6 @@  #include "usb.h" -#ifdef CONFIG_HOTPLUG -  /*   * Adds a new dynamic USBdevice ID to this driver,   * and cause the driver to probe for all devices again. @@ -194,20 +192,6 @@ static void usb_free_dynids(struct usb_driver *usb_drv)  	}  	spin_unlock(&usb_drv->dynids.lock);  } -#else -static inline int usb_create_newid_files(struct usb_driver *usb_drv) -{ -	return 0; -} - -static void usb_remove_newid_files(struct usb_driver *usb_drv) -{ -} - -static inline void usb_free_dynids(struct usb_driver *usb_drv) -{ -} -#endif  static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *intf,  							struct usb_driver *drv) @@ -790,7 +774,6 @@ static int usb_device_match(struct device *dev, struct device_driver *drv)  	return 0;  } -#ifdef	CONFIG_HOTPLUG  static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)  {  	struct usb_device *usb_dev; @@ -832,14 +815,6 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)  	return 0;  } -#else - -static int usb_uevent(struct device *dev, struct kobj_uevent_env *env) -{ -	return -ENODEV; -} -#endif	/* CONFIG_HOTPLUG */ -  /**   * usb_register_device_driver - register a USB device (not interface) driver   * @new_udriver: USB operations for the device driver diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 69ecd3c9231..eff2010eb63 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -47,6 +47,9 @@ int usb_choose_configuration(struct usb_device *udev)  	int insufficient_power = 0;  	struct usb_host_config *c, *best; +	if (usb_device_is_owned(udev)) +		return 0; +  	best = NULL;  	c = udev->config;  	num_configs = udev->descriptor.bNumConfigurations; @@ -160,9 +163,7 @@ static int generic_probe(struct usb_device *udev)  	/* Choose and set the configuration.  This registers the interfaces  	 * with the driver core and lets interface drivers bind to them.  	 */ -	if (usb_device_is_owned(udev)) -		;		/* Don't configure if the device is owned */ -	else if (udev->authorized == 0) +	if (udev->authorized == 0)  		dev_err(&udev->dev, "Device is not authorized for usage\n");  	else {  		c = usb_choose_configuration(udev); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index f034716190f..4225d5e7213 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2039,8 +2039,9 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)  	status = hcd->driver->bus_resume(hcd);  	clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);  	if (status == 0) { -		/* TRSMRCY = 10 msec */ -		msleep(10); +		struct usb_device *udev; +		int port1; +  		spin_lock_irq(&hcd_root_hub_lock);  		if (!HCD_DEAD(hcd)) {  			usb_set_device_state(rhdev, rhdev->actconfig @@ -2050,6 +2051,20 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)  			hcd->state = HC_STATE_RUNNING;  		}  		spin_unlock_irq(&hcd_root_hub_lock); + +		/* +		 * Check whether any of the enabled ports on the root hub are +		 * unsuspended.  If they are then a TRSMRCY delay is needed +		 * (this is what the USB-2 spec calls a "global resume"). +		 * Otherwise we can skip the delay. +		 */ +		usb_hub_for_each_child(rhdev, port1, udev) { +			if (udev->state != USB_STATE_NOTATTACHED && +					!udev->port_is_suspended) { +				usleep_range(10000, 11000);	/* TRSMRCY */ +				break; +			} +		}  	} else {  		hcd->state = old_state;  		dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 1af04bdeaf0..a815fd2cc5e 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -39,6 +39,9 @@  #endif  #endif +#define USB_VENDOR_GENESYS_LOGIC		0x05e3 +#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND	0x01 +  struct usb_port {  	struct usb_device *child;  	struct device dev; @@ -86,6 +89,8 @@ struct usb_hub {  	unsigned		quiescing:1;  	unsigned		disconnected:1; +	unsigned		quirk_check_port_auto_suspend:1; +  	unsigned		has_indicators:1;  	u8			indicator[USB_MAXCHILDREN];  	struct delayed_work	leds; @@ -736,7 +741,6 @@ static void hub_tt_work(struct work_struct *work)  	struct usb_hub		*hub =  		container_of(work, struct usb_hub, tt.clear_work);  	unsigned long		flags; -	int			limit = 100;  	spin_lock_irqsave (&hub->tt.lock, flags);  	while (!list_empty(&hub->tt.clear_list)) { @@ -746,9 +750,6 @@ static void hub_tt_work(struct work_struct *work)  		const struct hc_driver	*drv;  		int			status; -		if (!hub->quiescing && --limit < 0) -			break; -  		next = hub->tt.clear_list.next;  		clear = list_entry (next, struct usb_tt_clear, clear_list);  		list_del (&clear->clear_list); @@ -1612,6 +1613,41 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)  	desc = intf->cur_altsetting;  	hdev = interface_to_usbdev(intf); +	/* +	 * Set default autosuspend delay as 0 to speedup bus suspend, +	 * based on the below considerations: +	 * +	 * - Unlike other drivers, the hub driver does not rely on the +	 *   autosuspend delay to provide enough time to handle a wakeup +	 *   event, and the submitted status URB is just to check future +	 *   change on hub downstream ports, so it is safe to do it. +	 * +	 * - The patch might cause one or more auto supend/resume for +	 *   below very rare devices when they are plugged into hub +	 *   first time: +	 * +	 *   	devices having trouble initializing, and disconnect +	 *   	themselves from the bus and then reconnect a second +	 *   	or so later +	 * +	 *   	devices just for downloading firmware, and disconnects +	 *   	themselves after completing it +	 * +	 *   For these quite rare devices, their drivers may change the +	 *   autosuspend delay of their parent hub in the probe() to one +	 *   appropriate value to avoid the subtle problem if someone +	 *   does care it. +	 * +	 * - The patch may cause one or more auto suspend/resume on +	 *   hub during running 'lsusb', but it is probably too +	 *   infrequent to worry about. +	 * +	 * - Change autosuspend delay of hub can avoid unnecessary auto +	 *   suspend timer for hub, also may decrease power consumption +	 *   of USB bus. +	 */ +	pm_runtime_set_autosuspend_delay(&hdev->dev, 0); +  	/* Hubs have proper suspend/resume support. */  	usb_enable_autosuspend(hdev); @@ -1670,6 +1706,9 @@ descriptor_error:  	if (hdev->speed == USB_SPEED_HIGH)  		highspeed_hubs++; +	if (id->driver_info & HUB_QUIRK_CHECK_PORT_AUTOSUSPEND) +		hub->quirk_check_port_auto_suspend = 1; +  	if (hub_configure(hub, endpoint) >= 0)  		return 0; @@ -2012,7 +2051,7 @@ static void show_string(struct usb_device *udev, char *id, char *string)  {  	if (!string)  		return; -	dev_printk(KERN_INFO, &udev->dev, "%s: %s\n", id, string); +	dev_info(&udev->dev, "%s: %s\n", id, string);  }  static void announce_device(struct usb_device *udev) @@ -2879,6 +2918,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)  				(PMSG_IS_AUTO(msg) ? "auto-" : ""),  				udev->do_remote_wakeup);  		usb_set_device_state(udev, USB_STATE_SUSPENDED); +		udev->port_is_suspended = 1;  		msleep(10);  	}  	usb_mark_last_busy(hub->hdev); @@ -3043,6 +3083,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)   SuspendCleared:  	if (status == 0) { +		udev->port_is_suspended = 0;  		if (hub_is_superspeed(hub->hdev)) {  			if (portchange & USB_PORT_STAT_C_LINK_STATE)  				clear_port_feature(hub->hdev, port1, @@ -3126,6 +3167,21 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)  #endif +static int check_ports_changed(struct usb_hub *hub) +{ +	int port1; + +	for (port1 = 1; port1 <= hub->hdev->maxchild; ++port1) { +		u16 portstatus, portchange; +		int status; + +		status = hub_port_status(hub, port1, &portstatus, &portchange); +		if (!status && portchange) +			return 1; +	} +	return 0; +} +  static int hub_suspend(struct usb_interface *intf, pm_message_t msg)  {  	struct usb_hub		*hub = usb_get_intfdata (intf); @@ -3144,6 +3200,16 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)  				return -EBUSY;  		}  	} + +	if (hdev->do_remote_wakeup && hub->quirk_check_port_auto_suspend) { +		/* check if there are changes pending on hub ports */ +		if (check_ports_changed(hub)) { +			if (PMSG_IS_AUTO(msg)) +				return -EBUSY; +			pm_wakeup_event(&hdev->dev, 2000); +		} +	} +  	if (hub_is_superspeed(hdev) && hdev->do_remote_wakeup) {  		/* Enable hub to send remote wakeup for all ports. */  		for (port1 = 1; port1 <= hdev->maxchild; port1++) { @@ -3972,6 +4038,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,  	if (retval)  		goto fail; +	if (hcd->phy && !hdev->parent) +		usb_phy_notify_connect(hcd->phy, udev->speed); +  	/*  	 * Some superspeed devices have finished the link training process  	 * and attached to a superspeed hub port, but the device descriptor @@ -4166,8 +4235,12 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,  	}  	/* Disconnect any existing devices under this port */ -	if (udev) +	if (udev) { +		if (hcd->phy && !hdev->parent && +				!(portstatus & USB_PORT_STAT_CONNECTION)) +			usb_phy_notify_disconnect(hcd->phy, udev->speed);  		usb_disconnect(&hub->ports[port1 - 1]->child); +	}  	clear_bit(port1, hub->change_bits);  	/* We can forget about a "removed" device when there's a physical @@ -4190,13 +4263,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,  		}  	} -	if (hcd->phy && !hdev->parent) { -		if (portstatus & USB_PORT_STAT_CONNECTION) -			usb_phy_notify_connect(hcd->phy, port1); -		else -			usb_phy_notify_disconnect(hcd->phy, port1); -	} -  	/* Return now if debouncing failed or nothing is connected or  	 * the device was "removed".  	 */ @@ -4648,6 +4714,11 @@ 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, +      .idVendor = USB_VENDOR_GENESYS_LOGIC, +      .bInterfaceClass = USB_CLASS_HUB, +      .driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},      { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,        .bDeviceClass = USB_CLASS_HUB},      { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 1ed5afd91e6..131f73649b6 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1540,7 +1540,6 @@ static void usb_release_interface(struct device *dev)  	kfree(intf);  } -#ifdef	CONFIG_HOTPLUG  static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env)  {  	struct usb_device *usb_dev; @@ -1575,14 +1574,6 @@ static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env)  	return 0;  } -#else - -static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env) -{ -	return -ENODEV; -} -#endif	/* CONFIG_HOTPLUG */ -  struct device_type usb_if_device_type = {  	.name =		"usb_interface",  	.release =	usb_release_interface, @@ -1795,7 +1786,8 @@ free_interfaces:  	if (dev->actconfig && usb_disable_lpm(dev)) {  		dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);  		mutex_unlock(hcd->bandwidth_mutex); -		return -ENOMEM; +		ret = -ENOMEM; +		goto free_interfaces;  	}  	ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);  	if (ret < 0) { @@ -1806,29 +1798,8 @@ free_interfaces:  		goto free_interfaces;  	} -	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), -			      USB_REQ_SET_CONFIGURATION, 0, configuration, 0, -			      NULL, 0, USB_CTRL_SET_TIMEOUT); -	if (ret < 0) { -		/* All the old state is gone, so what else can we do? -		 * The device is probably useless now anyway. -		 */ -		cp = NULL; -	} - -	dev->actconfig = cp; -	if (!cp) { -		usb_set_device_state(dev, USB_STATE_ADDRESS); -		usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL); -		/* Leave LPM disabled while the device is unconfigured. */ -		mutex_unlock(hcd->bandwidth_mutex); -		usb_autosuspend_device(dev); -		goto free_interfaces; -	} -	mutex_unlock(hcd->bandwidth_mutex); -	usb_set_device_state(dev, USB_STATE_CONFIGURED); - -	/* Initialize the new interface structures and the +	/* +	 * Initialize the new interface structures and the  	 * hc/hcd/usbcore interface/endpoint state.  	 */  	for (i = 0; i < nintf; ++i) { @@ -1872,6 +1843,35 @@ free_interfaces:  	}  	kfree(new_interfaces); +	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), +			      USB_REQ_SET_CONFIGURATION, 0, configuration, 0, +			      NULL, 0, USB_CTRL_SET_TIMEOUT); +	if (ret < 0 && cp) { +		/* +		 * All the old state is gone, so what else can we do? +		 * The device is probably useless now anyway. +		 */ +		usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL); +		for (i = 0; i < nintf; ++i) { +			usb_disable_interface(dev, cp->interface[i], true); +			put_device(&cp->interface[i]->dev); +			cp->interface[i] = NULL; +		} +		cp = NULL; +	} + +	dev->actconfig = cp; +	mutex_unlock(hcd->bandwidth_mutex); + +	if (!cp) { +		usb_set_device_state(dev, USB_STATE_ADDRESS); + +		/* Leave LPM disabled while the device is unconfigured. */ +		usb_autosuspend_device(dev); +		return ret; +	} +	usb_set_device_state(dev, USB_STATE_CONFIGURED); +  	if (cp->string == NULL &&  			!(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS))  		cp->string = usb_cache_string(dev, cp->desc.iConfiguration); diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 9d912bfdcff..e0d9d948218 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -214,9 +214,25 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb);   * urb->interval is modified to reflect the actual transfer period used   * (normally some power of two units).  And for isochronous urbs,   * urb->start_frame is modified to reflect when the URB's transfers were - * scheduled to start.  Not all isochronous transfer scheduling policies - * will work, but most host controller drivers should easily handle ISO - * queues going from now until 10-200 msec into the future. + * scheduled to start. + * + * Not all isochronous transfer scheduling policies will work, but most + * host controller drivers should easily handle ISO queues going from now + * until 10-200 msec into the future.  Drivers should try to keep at + * least one or two msec of data in the queue; many controllers require + * that new transfers start at least 1 msec in the future when they are + * added.  If the driver is unable to keep up and the queue empties out, + * the behavior for new submissions is governed by the URB_ISO_ASAP flag. + * If the flag is set, or if the queue is idle, then the URB is always + * assigned to the first available (and not yet expired) slot in the + * endpoint's schedule.  If the flag is not set and the queue is active + * then the URB is always assigned to the next slot in the schedule + * following the end of the endpoint's previous URB, even if that slot is + * in the past.  When a packet is assigned in this way to a slot that has + * already expired, the packet is not transmitted and the corresponding + * usb_iso_packet_descriptor's status field will return -EXDEV.  If this + * would happen to all the packets in the URB, submission fails with a + * -EXDEV error code.   *   * For control endpoints, the synchronous usb_control_msg() call is   * often used (in non-interrupt context) instead of this call. @@ -305,8 +321,13 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)  	struct usb_host_endpoint	*ep;  	int				is_out; -	if (!urb || urb->hcpriv || !urb->complete) +	if (!urb || !urb->complete)  		return -EINVAL; +	if (urb->hcpriv) { +		WARN_ONCE(1, "URB %p submitted while active\n", urb); +		return -EBUSY; +	} +  	dev = urb->dev;  	if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED))  		return -ENODEV; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index cd8fb44a3e1..f81b9257273 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -233,7 +233,6 @@ static void usb_release_dev(struct device *dev)  	kfree(udev);  } -#ifdef	CONFIG_HOTPLUG  static int usb_dev_uevent(struct device *dev, struct kobj_uevent_env *env)  {  	struct usb_device *usb_dev; @@ -249,14 +248,6 @@ static int usb_dev_uevent(struct device *dev, struct kobj_uevent_env *env)  	return 0;  } -#else - -static int usb_dev_uevent(struct device *dev, struct kobj_uevent_env *env) -{ -	return -ENODEV; -} -#endif	/* CONFIG_HOTPLUG */ -  #ifdef	CONFIG_PM  /* USB device Power-Management thunks. @@ -370,14 +361,14 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,  				 struct usb_bus *bus, unsigned port1)  {  	struct usb_device *dev; -	struct usb_hcd *usb_hcd = container_of(bus, struct usb_hcd, self); +	struct usb_hcd *usb_hcd = bus_to_hcd(bus);  	unsigned root_hub = 0;  	dev = kzalloc(sizeof(*dev), GFP_KERNEL);  	if (!dev)  		return NULL; -	if (!usb_get_hcd(bus_to_hcd(bus))) { +	if (!usb_get_hcd(usb_hcd)) {  		kfree(dev);  		return NULL;  	}  | 
