ChangeSet 1.1123.18.3, 2003/08/11 15:12:01-07:00, oliver@neukum.org

[PATCH] USB: correct error handling in usb_driver_claim_interface()

this function races with itself, doesn't return errors and races with
releasing interfaces. This patch fixes it by changing the function
prototype, introducing locking and having a correct order in
releasing interfaces.

  - API change to check errors in usb_driver_claim_interface and
    fix a race condition between releasing and reclaiming an interface


 drivers/usb/core/usb.c |   27 +++++++++++++++------------
 include/linux/usb.h    |    2 +-
 2 files changed, 16 insertions(+), 13 deletions(-)


diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
--- a/drivers/usb/core/usb.c	Fri Aug 15 10:48:26 2003
+++ b/drivers/usb/core/usb.c	Fri Aug 15 10:48:26 2003
@@ -261,24 +261,27 @@
  *
  * Few drivers should need to use this routine, since the most natural
  * way to bind to an interface is to return the private data from
- * the driver's probe() method.  Any driver that does use this must
- * first be sure that no other driver has claimed the interface, by
- * checking with usb_interface_claimed().
+ * the driver's probe() method.
  */
-void usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv)
+int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv)
 {
 	if (!iface || !driver)
-		return;
+		return -EINVAL;
 
-	// FIXME change API to report an error in this case
-	if (iface->driver)
-	    err ("%s driver booted %s off interface %p",
-	    	driver->name, iface->driver->name, iface);
-	else
+	lock_kernel();
+	if (iface->driver) {
+		unlock_kernel();
+		err ("%s driver booted %s off interface %p",
+			driver->name, iface->driver->name, iface);
+		return -EBUSY;
+	} else {
 	    dbg("%s driver claimed interface %p", driver->name, iface);
+	}
 
 	iface->driver = driver;
 	usb_set_intfdata(iface, priv);
+	unlock_kernel();
+	return 0;
 }
 
 /**
@@ -324,11 +327,11 @@
 	if (iface->driver && iface->driver != driver)
 		return;
 
-	iface->driver = NULL;
-	usb_set_intfdata(iface, NULL);
 	usb_set_interface(interface_to_usbdev(iface),
 			iface->altsetting[0].desc.bInterfaceNumber,
 			0);
+	usb_set_intfdata(iface, NULL);
+	iface->driver = NULL;
 }
 
 /**
diff -Nru a/include/linux/usb.h b/include/linux/usb.h
--- a/include/linux/usb.h	Fri Aug 15 10:48:26 2003
+++ b/include/linux/usb.h	Fri Aug 15 10:48:26 2003
@@ -290,7 +290,7 @@
 extern int usb_get_current_frame_number (struct usb_device *usb_dev);
 
 /* used these for multi-interface device registration */
-extern void usb_driver_claim_interface(struct usb_driver *driver,
+extern int usb_driver_claim_interface(struct usb_driver *driver,
 			struct usb_interface *iface, void* priv);
 extern int usb_interface_claimed(struct usb_interface *iface);
 extern void usb_driver_release_interface(struct usb_driver *driver,
