Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 79efa097 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman
Browse files

[PATCH] usbcore: port reset for composite devices



This patch (as699) adds usb_reset_composite_device(), a routine for
sending a USB port reset to a device with multiple interfaces owned by
different drivers.  Drivers are notified about impending and completed
resets through two new methods in the usb_driver structure.

The patch modifieds the usbfs ioctl code to make it use the new routine
instead of usb_reset_device().  Follow-up patches will modify the hub,
usb-storage, and usbhid drivers so they can utilize this new API.

Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent efcaa205
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -823,8 +823,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg)

static int proc_resetdevice(struct dev_state *ps)
{
	return usb_reset_device(ps->dev);

	return usb_reset_composite_device(ps->dev, NULL);
}

static int proc_setintf(struct dev_state *ps, void __user *arg)
+81 −3
Original line number Diff line number Diff line
@@ -3007,9 +3007,9 @@ static int config_descriptors_changed(struct usb_device *udev)
 * usb_reset_device - perform a USB port reset to reinitialize a device
 * @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
 *
 * WARNING - don't reset any device unless drivers for all of its
 * interfaces are expecting that reset!  Maybe some driver->reset()
 * method should eventually help ensure sufficient cooperation.
 * WARNING - don't use this routine to reset a composite device
 * (one with multiple interfaces owned by separate drivers)!
 * Use usb_reset_composite_device() instead.
 *
 * Do a port reset, reassign the device's address, and establish its
 * former operating configuration.  If the reset fails, or the device's
@@ -3125,3 +3125,81 @@ re_enumerate:
	hub_port_logical_disconnect(parent_hub, port1);
	return -ENODEV;
}

/**
 * usb_reset_composite_device - warn interface drivers and perform a USB port reset
 * @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
 * @iface: interface bound to the driver making the request (optional)
 *
 * Warns all drivers bound to registered interfaces (using their pre_reset
 * method), performs the port reset, and then lets the drivers know that
 * the reset is over (using their post_reset method).
 *
 * Return value is the same as for usb_reset_device().
 *
 * The caller must own the device lock.  For example, it's safe to use
 * this from a driver probe() routine after downloading new firmware.
 * For calls that might not occur during probe(), drivers should lock
 * the device using usb_lock_device_for_reset().
 *
 * The interface locks are acquired during the pre_reset stage and released
 * during the post_reset stage.  However if iface is not NULL and is
 * currently being probed, we assume that the caller already owns its
 * lock.
 */
int usb_reset_composite_device(struct usb_device *udev,
		struct usb_interface *iface)
{
	int ret;
	struct usb_host_config *config = udev->actconfig;

	if (udev->state == USB_STATE_NOTATTACHED ||
			udev->state == USB_STATE_SUSPENDED) {
		dev_dbg(&udev->dev, "device reset not allowed in state %d\n",
				udev->state);
		return -EINVAL;
	}

	if (iface && iface->condition != USB_INTERFACE_BINDING)
		iface = NULL;

	if (config) {
		int i;
		struct usb_interface *cintf;
		struct usb_driver *drv;

		for (i = 0; i < config->desc.bNumInterfaces; ++i) {
			cintf = config->interface[i];
			if (cintf != iface)
				down(&cintf->dev.sem);
			if (device_is_registered(&cintf->dev) &&
					cintf->dev.driver) {
				drv = to_usb_driver(cintf->dev.driver);
				if (drv->pre_reset)
					(drv->pre_reset)(cintf);
			}
		}
	}

	ret = usb_reset_device(udev);

	if (config) {
		int i;
		struct usb_interface *cintf;
		struct usb_driver *drv;

		for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) {
			cintf = config->interface[i];
			if (device_is_registered(&cintf->dev) &&
					cintf->dev.driver) {
				drv = to_usb_driver(cintf->dev.driver);
				if (drv->post_reset)
					(drv->post_reset)(cintf);
			}
			if (cintf != iface)
				up(&cintf->dev.sem);
		}
	}

	return ret;
}
+1 −0
Original line number Diff line number Diff line
@@ -1207,6 +1207,7 @@ EXPORT_SYMBOL(usb_ifnum_to_if);
EXPORT_SYMBOL(usb_altnum_to_altsetting);

EXPORT_SYMBOL(usb_reset_device);
EXPORT_SYMBOL(usb_reset_composite_device);

EXPORT_SYMBOL(__usb_get_extra_descriptor);

+9 −0
Original line number Diff line number Diff line
@@ -386,6 +386,8 @@ extern int usb_lock_device_for_reset(struct usb_device *udev,

/* USB port reset for device reinitialization */
extern int usb_reset_device(struct usb_device *dev);
extern int usb_reset_composite_device(struct usb_device *dev,
		struct usb_interface *iface);

extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id);

@@ -554,6 +556,10 @@ struct usb_dynids {
 *	do (or don't) show up otherwise in the filesystem.
 * @suspend: Called when the device is going to be suspended by the system.
 * @resume: Called when the device is being resumed by the system.
 * @pre_reset: Called by usb_reset_composite_device() when the device
 *	is about to be reset.
 * @post_reset: Called by usb_reset_composite_device() after the device
 *	has been reset.
 * @id_table: USB drivers use ID table to support hotplugging.
 *	Export this with MODULE_DEVICE_TABLE(usb,...).  This must be set
 *	or your driver's probe function will never get called.
@@ -592,6 +598,9 @@ struct usb_driver {
	int (*suspend) (struct usb_interface *intf, pm_message_t message);
	int (*resume) (struct usb_interface *intf);

	void (*pre_reset) (struct usb_interface *intf);
	void (*post_reset) (struct usb_interface *intf);

	const struct usb_device_id *id_table;

	struct usb_dynids dynids;