usb_rebind_interface()

Alan Stern <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
On Fri, 11 May 2007, Alan Stern wrote:

> Maybe they don't bother to do so; I'm not familiar with those drivers.  So 
> all right, I'll write the routine for you.

And here you go.  Better check with Greg to see if there's any objection 
to exporting driver_probe_device() from the driver core.

I haven't tested this, but it compiles.

Alan Stern


Index: usb-2.6/drivers/base/base.h
===================================================================
--- usb-2.6.orig/drivers/base/base.h
+++ usb-2.6/drivers/base/base.h
@@ -25,7 +25,6 @@ extern int bus_add_driver(struct device_
 extern void bus_remove_driver(struct device_driver *);
 
 extern void driver_detach(struct device_driver * drv);
-extern int driver_probe_device(struct device_driver *, struct device *);
 
 extern void sysdev_shutdown(void);
 extern int sysdev_suspend(pm_message_t state);
Index: usb-2.6/drivers/base/dd.c
===================================================================
--- usb-2.6.orig/drivers/base/dd.c
+++ usb-2.6/drivers/base/dd.c
@@ -200,6 +200,7 @@ int driver_probe_device(struct device_dr
 done:
 	return ret;
 }
+EXPORT_SYMBOL_GPL(driver_probe_device);
 
 static int __device_attach(struct device_driver * drv, void * data)
 {
Index: usb-2.6/drivers/usb/core/message.c
===================================================================
--- usb-2.6.orig/drivers/usb/core/message.c
+++ usb-2.6/drivers/usb/core/message.c
@@ -1634,6 +1634,73 @@ int usb_driver_set_configuration(struct 
 }
 EXPORT_SYMBOL_GPL(usb_driver_set_configuration);
 
+struct rebind_interface_request {
+	struct usb_device	*udev;
+	struct usb_interface	*intf;
+	struct device_driver	*driver;
+	struct work_struct	work;
+};
+
+/* Worker routine for usb_rebind_interface() */
+static void rebind_interface_work(struct work_struct *work)
+{
+	struct rebind_interface_request *req =
+		container_of(work, struct rebind_interface_request, work);
+	struct device *dev = &req->intf->dev;
+
+	if (req->udev->state == USB_STATE_NOTATTACHED)
+		goto done;
+
+	usb_lock_device(req->udev);
+	down(&dev->sem);
+	if (device_is_registered(dev) && dev->driver == req->driver) {
+		device_release_driver(dev);
+		driver_probe_device(req->driver, dev);
+	}
+	up(&dev->sem);
+	usb_unlock_device(req->udev);
+ done:
+	put_driver(req->driver);
+	usb_put_intf(req->intf);
+	usb_put_dev(req->udev);
+	kfree(req);
+}
+
+/**
+ * usb_rebind_interface - Unbind a driver from and then rebind it to an interface
+ * @intf: the interface whose driver should be unbound and rebound
+ * Context: In process context, must be able to sleep
+ *
+ * If a device interface driver finds itself in a deep hole with no way to
+ * get out (for example, if its post_reset() method is unable to restore
+ * the device's state because the driver doesn't know what that state
+ * should be), it can call this routine.  The driver will be unbound and
+ * then rebound, giving it a fresh start at managing the interface.
+ *
+ * The unbind and rebind calls are made from a workqueue, so that the driver's
+ * disconnect() method can run without fear of deadlock.
+ *
+ * Returns 0 if the request was succesfully queued, error code otherwise.
+ * The caller has no way to know whether the queued request will eventually
+ * succeed.
+ */
+int usb_rebind_interface(struct usb_interface *intf)
+{
+	struct rebind_interface_request *req;
+
+	req = kmalloc(sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+	req->udev = usb_get_dev(interface_to_usbdev(intf));
+	req->intf = usb_get_intf(intf);
+	req->driver = get_driver(intf->dev.driver);
+	INIT_WORK(&req->work, rebind_interface_work);
+
+	schedule_work(&req->work);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_rebind_interface);
+
 // synchronous request completion model
 EXPORT_SYMBOL(usb_control_msg);
 EXPORT_SYMBOL(usb_bulk_msg);
Index: usb-2.6/include/linux/device.h
===================================================================
--- usb-2.6.orig/include/linux/device.h
+++ usb-2.6/include/linux/device.h
@@ -530,6 +530,7 @@ extern void device_release_driver(struct
 extern int  __must_check device_attach(struct device * dev);
 extern int __must_check driver_attach(struct device_driver *drv);
 extern int __must_check device_reprobe(struct device *dev);
+extern int driver_probe_device(struct device_driver *, struct device *);
 
 /*
  * Easy functions for dynamically creating devices on the fly
Index: usb-2.6/include/linux/usb.h
===================================================================
--- usb-2.6.orig/include/linux/usb.h
+++ usb-2.6/include/linux/usb.h
@@ -1322,8 +1322,9 @@ extern int usb_clear_halt(struct usb_dev
 extern int usb_reset_configuration(struct usb_device *dev);
 extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);
 
-/* this request isn't really synchronous, but it belongs with the others */
+/* these requests aren't really synchronous, but they belong with the others */
 extern int usb_driver_set_configuration(struct usb_device *udev, int config);
+extern int usb_rebind_interface(struct usb_interface *intf);
 
 /*
  * timeouts, in milliseconds, used for sending/receiving control messages


-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.