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,