Here is a patch that adds a new routine to usbcore: usb_reset_composite_device(). The routine is a wrapper around usb_reset_device(), but before doing the reset it warns all drivers bound to the affected device, and after the reset it lets them know that the reset is finished.
These notifications are sent by means of two new methods added to usb_driver: pre_reset and post_reset. Drivers that don't implement the methods won't notice anything different. Part of the patch changes the reset ioctl in usbfs to use the new routine. I've got two other patches updating usb-hid and usb-storage similarly. Other drivers that call usb_reset_device include microtek, ub, and various serial drivers. Certainly ub will want to use the new API; I don't know about the others. Also, drivers might want to add pre_reset and post_reset methods, if it seems likely that they will ever be involved with a composite device. Basically these methods need to do pretty much the same things as suspend/resume -- i.e., unlink current URBs and quiesce the driver, then start it up again. The main difference is that post_reset needs to reinitialize the device, since everything besides the altsetting will be lost during the reset. Comments? Alan Stern Index: usb-2.6/drivers/usb/core/hub.c =================================================================== --- usb-2.6.orig/drivers/usb/core/hub.c +++ usb-2.6/drivers/usb/core/hub.c @@ -2987,9 +2987,9 @@ static int config_descriptors_changed(st * 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 @@ -3105,3 +3105,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 = 0; i < config->desc.bNumInterfaces; ++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; +} Index: usb-2.6/include/linux/usb.h =================================================================== --- usb-2.6.orig/include/linux/usb.h +++ usb-2.6/include/linux/usb.h @@ -387,6 +387,8 @@ extern int usb_lock_device_for_reset(str /* 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); @@ -567,6 +569,10 @@ extern ssize_t usb_store_new_id(struct u * 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. @@ -605,6 +611,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; Index: usb-2.6/drivers/usb/core/devio.c =================================================================== --- usb-2.6.orig/drivers/usb/core/devio.c +++ usb-2.6/drivers/usb/core/devio.c @@ -823,8 +823,7 @@ static int proc_connectinfo(struct dev_s 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) Index: usb-2.6/drivers/usb/core/usb.c =================================================================== --- usb-2.6.orig/drivers/usb/core/usb.c +++ usb-2.6/drivers/usb/core/usb.c @@ -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); ------------------------------------------------------- All the advantages of Linux Managed Hosting--Without the Cost and Risk! Fully trained technicians. The highest number of Red Hat certifications in the hosting industry. Fanatical Support. Click to learn more http://sel.as-us.falkag.net/sel?cmd=lnk&kid=107521&bid=248729&dat=121642 _______________________________________________ linux-usb-devel@lists.sourceforge.net To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel