This patch primarily:

- Gets rid of usb_interface->{driver,private_data} since those
  roles are handled now in the driver model core.

- Updates the driver model core to make the rest of that possible.
  That means adding new methods so driver binding can be done in
  the open, not just as a side effect of registration.

Along the way:

- Resolves two minor FIXMEs, returning a status code from
  a routine that can fail (changes signature and kerneldoc)
  and using the right timeout symbol.

- Fixes a couple usb.c potential oopses where container_of()
  result was tested for null, not the input, and (related)
  a superfluous test.

- Catches what looked like a self-deadlock case in usbfs.

- Removes some now-dubious code from mdc800, and updates
  a diagnostic to be more correct.

I'm not quite sure how the get_bus/put_bus stuff is supposed
to work.  This stuff doesn't return the refcounts it acquires,
like other code in "bus.c".  Can you sanity check that?

This is lightly tested.  Driver bind/unbind seemed to behave
as it should, even "audio.c" which is one of several drivers
which needs to bind to devices in pairs (new notion for the
driver model core).  I don't happen to test a user mode driver,
that'd cover code paths that changed (so it deserves testing).

- Dave


--- ./include/linux-dist/usb.h  Sun Dec  8 10:57:47 2002
+++ ./include/linux/usb.h       Sun Dec  8 16:20:47 2002
@@ -116,9 +116,7 @@
        unsigned num_altsetting;        /* number of alternate settings */
        unsigned max_altsetting;        /* total memory allocated */
 
-       struct usb_driver *driver;      /* driver */
        struct device dev;              /* interface specific device info */
-       void *private_data;
 };
 #define        to_usb_interface(d) container_of(d, struct usb_interface, dev)
 #define        interface_to_usbdev(intf) \
@@ -262,12 +260,16 @@
 /* for drivers using iso endpoints */
 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,
+/* use these for multi-interface drivers */
+struct usb_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,
                        struct usb_interface *iface);
+
+/* */
 const struct usb_device_id *usb_match_id(struct usb_interface *interface,
                                         const struct usb_device_id *id);
 
--- ./drivers/usb-dist/core/usb.c       Sun Dec  8 10:57:46 2002
+++ ./drivers/usb/core/usb.c    Sun Dec  8 16:09:47 2002
@@ -7,8 +7,7 @@
  * (C) Copyright Gregory P. Smith 1999
  * (C) Copyright Deti Fliegl 1999 (new USB architecture)
  * (C) Copyright Randy Dunlap 2000
- * (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id,
-       more docs, etc)
+ * (C) Copyright David Brownell 2000-2002 
  * (C) Copyright Yggdrasil Computing, Inc. 2000
  *     (usb_device_id matching changes by Adam J. Richter)
  * (C) Copyright Greg Kroah-Hartman 2002
@@ -95,8 +94,6 @@
                error = driver->probe (intf, id);
                up (&driver->serialize);
        }
-       if (!error)
-               intf->driver = driver;
 
        module_put(driver->owner);
 
@@ -111,7 +108,7 @@
        intf = list_entry(dev,struct usb_interface,dev);
        driver = to_usb_driver(dev->driver);
 
-       if (!driver) {
+       if (!dev->driver) {
                err("%s does not have a valid driver to work with!",
                    __FUNCTION__);
                return -ENODEV;
@@ -129,12 +126,8 @@
         * module unload */
        down(&driver->serialize);
 
-       if (intf->driver && intf->driver->disconnect)
-               intf->driver->disconnect(intf);
-
-       /* if driver->disconnect didn't release the interface */
-       if (intf->driver)
-               usb_driver_release_interface(driver, intf);
+       if (driver->disconnect)
+               driver->disconnect(intf);
 
        up(&driver->serialize);
        module_put(driver->owner);
@@ -269,10 +262,13 @@
  * @iface: the interface to which it will be bound
  * @priv: driver data associated with that interface
  *
+ * Returns zero on success, or negative errno if the interface has
+ * already been claimed.
+ *
  * This is used by usb device drivers that need to claim more than one
  * interface on a device when probing (audio and acm are current examples).
  * No device driver should directly modify internal usb_interface or
- * usb_device structure members.
+ * usb_device structure members, or call this outside of its probe().
  *
  * 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
@@ -280,20 +276,20 @@
  * first be sure that no other driver has claimed the interface, by
  * checking with usb_interface_claimed().
  */
-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;
+       int     retval;
 
-       // 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
-           dbg("%s driver claimed interface %p", driver->name, iface);
+       if (!iface || !driver)
+               return -ENODEV;
 
-       iface->driver = driver;
-       iface->private_data = priv;
+       retval = __device_bind (&iface->dev, &driver->driver);
+       if (retval == 0)
+               dev_set_drvdata (&iface->dev, priv);
+       dbg ("%s driver %s interface %p", driver->name,
+                       retval ? "didn't claim" : "claimed", iface);
+       return retval;
 }
 
 /**
@@ -312,7 +308,7 @@
        if (!iface)
                return 0;
 
-       return (iface->driver != NULL);
+       return (iface->dev.driver != NULL);
 } /* usb_interface_claimed() */
 
 /**
@@ -332,11 +328,11 @@
 void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface 
*iface)
 {
        /* this should never happen, don't release something that's not ours */
-       if (!iface || iface->driver != driver)
+       if (!iface || iface->dev.driver != &driver->driver)
                return;
 
-       iface->driver = NULL;
-       iface->private_data = NULL;
+       __device_unbind (&iface->dev);
+       dev_set_drvdata (&iface->dev, NULL);
 }
 
 /**
@@ -408,7 +404,7 @@
        struct usb_device *dev;
 
        /* proc_connectinfo in devio.c may call us with id == NULL. */
-       if (id == NULL)
+       if (interface == NULL || id == NULL)
                return NULL;
 
        intf = &interface->altsetting [interface->act_altsetting];
@@ -521,14 +517,11 @@
 
        if (!dev)
                return -ENODEV;
+       intf = to_usb_interface(dev);
 
        if (dev->driver == &usb_generic_driver)
                return 0;
 
-       intf = to_usb_interface(dev);
-       if (!intf)
-               return -ENODEV;
-
        usb_dev = interface_to_usbdev (intf);
        if (!usb_dev)
                return -ENODEV;
@@ -844,8 +837,7 @@
 int usb_set_address(struct usb_device *dev)
 {
        return usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS,
-               // FIXME USB_CTRL_SET_TIMEOUT
-               0, dev->devnum, 0, NULL, 0, HZ * USB_CTRL_GET_TIMEOUT);
+               0, dev->devnum, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
 }
 
 
--- ./drivers/usb-dist/core/devices.c   Sun Dec  8 10:49:58 2002
+++ ./drivers/usb/core/devices.c        Sun Dec  8 12:56:12 2002
@@ -235,10 +235,13 @@
 static char *usb_dump_interface_descriptor(char *start, char *end, const struct 
usb_interface *iface, int setno)
 {
        struct usb_interface_descriptor *desc = &iface->altsetting[setno].desc;
+       struct usb_driver *driver = 0;
 
        if (start > end)
                return start;
        lock_kernel(); /* driver might be unloaded */
+       if (iface->dev.driver)
+               driver = to_usb_driver (iface->dev.driver);
        start += sprintf(start, format_iface,
                         desc->bInterfaceNumber,
                         desc->bAlternateSetting,
@@ -247,7 +250,7 @@
                         class_decode(desc->bInterfaceClass),
                         desc->bInterfaceSubClass,
                         desc->bInterfaceProtocol,
-                        iface->driver ? iface->driver->name : "(none)");
+                        driver ? driver->name : "(none)");
        unlock_kernel();
        return start;
 }
--- ./drivers/usb-dist/core/devio.c     Sun Dec  8 10:57:46 2002
+++ ./drivers/usb/core/devio.c  Sun Dec  8 17:04:46 2002
@@ -347,6 +347,11 @@
        .disconnect =   driver_disconnect,
 };
 
+/* NOTE:  usbfs necessarily doesn't use probe() to bind to interfaces.
+ * it's a kind of "generic driver", that user mode tools bind to
+ * interfaces on usb devices (or unbind) as needed.
+ */
+
 static int claimintf(struct dev_state *ps, unsigned int intf)
 {
        struct usb_device *dev = ps->dev;
@@ -360,14 +365,13 @@
        if (test_bit(intf, &ps->ifclaimed))
                return 0;
        iface = &dev->actconfig->interface[intf];
-       err = -EBUSY;
-       lock_kernel();
-       if (!usb_interface_claimed(iface)) {
-               usb_driver_claim_interface(&usbdevfs_driver, iface, ps);
+
+       /* device model handles locking */ 
+       err = device_bind (&iface->dev, &usbdevfs_driver.driver);
+       if (err == 0) {
+               dev_set_drvdata (&iface->dev, ps);
                set_bit(intf, &ps->ifclaimed);
-               err = 0;
        }
-       unlock_kernel();
        return err;
 }
 
@@ -381,10 +385,10 @@
                return -EINVAL;
        err = -EINVAL;
        dev = ps->dev;
+       iface = &dev->actconfig->interface[intf];
        down(&dev->serialize);
        if (dev && test_and_clear_bit(intf, &ps->ifclaimed)) {
-               iface = &dev->actconfig->interface[intf];
-               usb_driver_release_interface(&usbdevfs_driver, iface);
+               device_unbind (&iface->dev);
                err = 0;
        }
        up(&dev->serialize);
@@ -674,6 +678,7 @@
 {
        struct usbdevfs_getdriver gd;
        struct usb_interface *interface;
+       struct usb_driver *driver;
        int ret;
 
        if (copy_from_user(&gd, arg, sizeof(gd)))
@@ -683,9 +688,10 @@
        interface = usb_ifnum_to_if(ps->dev, gd.interface);
        if (!interface)
                return -EINVAL;
-       if (!interface->driver)
+       if (!interface->dev.driver)
                return -ENODATA;
-       strcpy(gd.driver, interface->driver->name);
+       driver = to_usb_driver (interface->dev.driver);
+       strcpy(gd.driver, driver->name);
        if (copy_to_user(arg, &gd, sizeof(gd)))
                return -EFAULT;
        return 0;
@@ -718,7 +724,7 @@
                        continue;
 
                err ("%s - this function is broken", __FUNCTION__);
-               if (intf->driver && ps->dev) {
+               if (intf->dev.driver && ps->dev) {
                        usb_device_probe (&intf->dev);
                }
        }
@@ -739,7 +745,7 @@
        interface = usb_ifnum_to_if(ps->dev, setintf.interface);
        if (!interface)
                return -EINVAL;
-       if (interface->driver) {
+       if (interface->dev.driver) {
                if ((ret = checkintf(ps, ret)))
                        return ret;
        }
@@ -1089,13 +1095,16 @@
                retval = -EINVAL;
        else switch (ctrl.ioctl_code) {
 
-       /* disconnect kernel driver from interface, leaving it unbound.  */
-       /* maybe unbound - you get no guarantee it stays unbound */
-       case USBDEVFS_DISCONNECT:
-               /* this function is misdesigned - retained for compatibility */
+       /* disconnect kernel driver from interface, so it's unbound for now */
+       case USBDEVFS_DISCONNECT:
+               if (test_bit(ctrl.ifno, &ps->ifclaimed)) {
+                       // use USBDEVFS_RELEASEINTERFACE instead
+                       retval = -EDEADLK;
+                       break;
+               }
                lock_kernel();
-               driver = ifp->driver;
-               if (driver) {
+               if (ifp->dev.driver) {
+                       driver = to_usb_driver (ifp->dev.driver);
                        dbg ("disconnect '%s' from dev %d interface %d",
                             driver->name, ps->dev->devnum, ctrl.ifno);
                        usb_device_remove(&ifp->dev);
@@ -1118,8 +1127,8 @@
                 * driver module.
                 */
                lock_kernel ();
-               driver = ifp->driver;
-               if (driver == 0 || driver->ioctl == 0) {
+               driver = to_usb_driver (ifp->dev.driver);
+               if (ifp->dev.driver == 0 || driver->ioctl == 0) {
                        unlock_kernel();
                        retval = -ENOSYS;
                } else {
--- ./drivers/usb-dist/misc/usbtest.c   Sun Dec  8 10:57:46 2002
+++ ./drivers/usb/misc/usbtest.c        Sun Dec  8 11:00:10 2002
@@ -978,7 +978,7 @@
 
        dev_set_drvdata (&intf->dev, 0);
        info ("unbound %s", dev->id);
-       kfree (intf->private_data);
+       kfree (dev);
 }
 
 /* Basic testing only needs a device that can source or sink bulk traffic.
--- ./drivers/usb-dist/storage/scsiglue.c       Sun Dec  8 10:49:56 2002
+++ ./drivers/usb/storage/scsiglue.c    Sun Dec  8 13:01:50 2002
@@ -254,7 +254,7 @@
                        &pusb_dev_save->actconfig->interface[i];
 
                /* if this is an unclaimed interface, skip it */
-               if (!intf->driver) {
+               if (!intf->dev.driver) {
                        continue;
                }
 
--- ./drivers/usb-dist/image/mdc800.c   Sun Dec  8 10:57:46 2002
+++ ./drivers/usb/image/mdc800.c        Sun Dec  8 13:15:07 2002
@@ -467,7 +467,6 @@
        }
 
 
-       usb_driver_claim_interface (&mdc800_usb_driver, intf, mdc800);
        if (usb_set_interface (dev, intf_desc->desc.bInterfaceNumber, 0) < 0)
        {
                err ("MDC800 Configuration fails.");
@@ -549,12 +548,10 @@
                usb_unlink_urb (mdc800->write_urb);
                usb_unlink_urb (mdc800->download_urb);
 
-               usb_driver_release_interface (&mdc800_usb_driver, intf);
-
                mdc800->dev=0;
                dev_set_drvdata(&intf->dev, NULL);
        }
-       info ("Mustek MDC800 disconnected from USB.");
+       info ("Mustek MDC800 driver disconnected from USB device.");
 }
 
 
--- ./include/linux-dist/device.h       Sun Dec  8 10:57:47 2002
+++ ./include/linux/device.h    Sun Dec  8 11:57:32 2002
@@ -311,6 +311,12 @@
 extern int device_add(struct device * dev);
 extern void device_del(struct device * dev);
 
+/* locked and unlocked driver binding primitives */
+extern int __device_bind(struct device * dev, struct device_driver *driver);
+extern int device_bind(struct device * dev, struct device_driver *driver);
+extern void __device_unbind(struct device * dev);
+extern void device_unbind(struct device * dev);
+
 /* driverfs interface for exporting device attributes */
 
 struct device_attribute {
--- ./drivers/base-dist/bus.c   Sun Dec  8 10:57:44 2002
+++ ./drivers/base/bus.c        Sun Dec  8 15:59:56 2002
@@ -326,18 +326,67 @@
        return error;
 }
 
+/**
+ *     __device_bind - bind specific driver to a device, without locking
+ *     @dev:   device with no current driver binding
+ *     @drv:   driver being bound; known to be a usable match.
+ *
+ *     Some bus models (notably USB) allow logical devices to be packaged in
+ *     groups, so that binding one device must atomically bind another one.
+ *     This routine may be used from one of those devices' probe() routines,
+ *     where the bus rwsem is already locked, to achieve that binding; no
+ *     probe() routine is invoked to validate the new binding, or (re)enter
+ *     initialization code.
+ *
+ *     Returns zero on success, or else negative errno if binding would
+ *     have overwritten an existing binding.
+ */
+int __device_bind (struct device * dev, struct device_driver * drv)
+{
+       if (dev->driver)
+               return -EINVAL;
+       dev->driver = drv;
+       attach(dev);
+       return 0;
+}
 
 /**
- *     detach - do dirty work of detaching device from its driver.
- *     @dev:   device.
- *     @drv:   its driver.
+ *     device_bind - bind specific driver to a device
+ *     @dev:   device with no current driver binding
+ *     @drv:   driver being bound; known to be a usable match.
+ *
+ *     When drivers are bound to device outside of a probe(), use this
+ *     routine to acquire the bus rwsem before calling __device_bind().
+ *     Returns zero on success, or else negative errno.
+ */
+int device_bind(struct device *dev, struct device_driver *drv)
+{
+       struct bus_type *bus = get_bus(dev->bus);
+       int             retval;
+
+       if (!bus)
+               return -EINVAL;
+       down_write(&bus->subsys.rwsem);
+       retval = __device_bind(dev, drv);
+       up_write(&bus->subsys.rwsem);
+       // put_bus(bus);
+       return retval;
+}
+
+/**
+ *     __device_unbind - disconnect driver from a device (unlocked)
+ *     @dev:   device whose driver is being unbound.
  *
  *     Note that calls to this function are serialized by taking the 
- *     bus's rwsem in both bus_remove_device() and bus_remove_driver().
+ *     bus's rwsem in bus_remove_device(), bus_remove_driver(),
+ *     or device_unbind().  If the driver has a remove() method,
+ *     that is invoked.
  */
 
-static void detach(struct device * dev, struct device_driver * drv)
+void __device_unbind(struct device * dev)
 {
+       struct device_driver    *drv = dev->driver;
+
        if (drv) {
                sysfs_remove_link(&drv->kobj,dev->kobj.name);
                list_del_init(&dev->driver_list);
@@ -350,13 +399,32 @@
 
 
 /**
+ *     device_unbind - disconnect driver from a device
+ *     @dev:   device
+ *
+ *     This acquires the bus rwsem and calls __device_unbind.
+ */
+void device_unbind(struct device * dev)
+{
+       struct bus_type *bus = get_bus(dev->bus);
+
+       if (bus) {
+               down_write(&bus->subsys.rwsem);
+               __device_unbind(dev);
+               up_write(&bus->subsys.rwsem);
+               // put_bus(bus);
+       }
+}
+
+
+/**
  *     device_detach - detach device from its driver.
  *     @dev:   device.
  */
 
 static void device_detach(struct device * dev)
 {
-       detach(dev,dev->driver);
+       __device_unbind (dev);
 }
 
 
@@ -370,7 +438,7 @@
        struct list_head * entry, * next;
        list_for_each_safe(entry,next,&drv->devices) {
                struct device * dev = container_of(entry,struct device,driver_list);
-               detach(dev,drv);
+               __device_unbind (dev);
        }
        
 }
@@ -550,5 +618,10 @@
 EXPORT_SYMBOL(get_bus);
 EXPORT_SYMBOL(put_bus);
 
+EXPORT_SYMBOL(__device_bind);
+EXPORT_SYMBOL(device_bind);
+EXPORT_SYMBOL(__device_unbind);
+EXPORT_SYMBOL(device_unbind);
+
 EXPORT_SYMBOL(bus_create_file);
 EXPORT_SYMBOL(bus_remove_file);

Reply via email to