This gets rid of the strange "half bound" state that secondary
interfaces have been in:  the driver model and sysfs haven't
yet reflected those bindings.

    - Remove interface.driver (interface.dev.driver suffices), so
      there's only the driver model notion of binding.

    - Call device_bind_driver() from usb_driver_claim_interface(),
      so the driver model and sysfs show the binding, if that
      interface has already been registered.

    - Call device_release_driver() from usb_driver_release_interface(),
      reversing the effect of that binding.  So usb_unbind_interface()
      doesn't use release_interface any more (to break the recursion).

    - Updates docs ... those driver model calls pass along a
      requirement to hold the bus writelock, which probe() and
      disconnect() calls already hold.

    - Remove usb_driver.serialize (bus writelock suffices) to get
      rid of self-deadlock if disconnect() uses release_interface.

- Deprecates usb_interface_claimed(); it's like check_region().

Notable change:  claimed interfaces now get normal disconnect() methods,
which multi-interface drivers (audio, cdc, etc) may not expect.  Easy
to work around ... say by using a null driver data pointer to indicate
secondary interfaces, for which disconnect() should be a NOP.

- Dave



--- 1.90/include/linux/usb.h    Mon Sep 15 06:58:01 2003
+++ edited/include/linux/usb.h  Tue Oct  7 09:35:28 2003
@@ -118,7 +118,6 @@
        unsigned act_altsetting;        /* active alternate setting */
        unsigned num_altsetting;        /* number of alternate settings */
 
-       struct usb_driver *driver;      /* driver */
        int minor;                      /* minor number this interface is bound to */
        struct device dev;              /* interface specific device info */
        struct class_device *class_dev;
@@ -286,7 +285,11 @@
 /* for drivers using iso endpoints */
 extern int usb_get_current_frame_number (struct usb_device *usb_dev);
 
-/* used these for multi-interface device registration */
+struct usb_driver;
+
+/* used by drivers that control several related interfaces during
+ * probe()/disconnect(), or need other fancy driver binding models
+ */
 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);
@@ -451,8 +454,6 @@
        const struct usb_device_id *id_table;
 
        struct device_driver driver;
-
-       struct semaphore serialize;
 };
 #define        to_usb_driver(d) container_of(d, struct usb_driver, driver)
 
--- 1.37/drivers/usb/core/message.c     Tue Sep 23 11:18:09 2003
+++ edited/drivers/usb/core/message.c   Mon Oct  6 11:39:36 2003
@@ -810,6 +810,7 @@
                        dev_dbg (&dev->dev, "unregistering interface %s\n",
                                interface->dev.bus_id);
                        device_del(&interface->dev);
+                       interface->dev.bus = 0;
                }
                dev->actconfig = 0;
                if (dev->state == USB_STATE_CONFIGURED)
@@ -1118,8 +1119,10 @@
                        desc = &intf->altsetting [0].desc;
                        usb_enable_interface(dev, intf);
 
+                       /* multi-interface drivers (cdc etc) might
+                        * already have claimed this interface (i>0).
+                        */
                        intf->dev.parent = &dev->dev;
-                       intf->dev.driver = NULL;
                        intf->dev.bus = &usb_bus_type;
                        intf->dev.dma_mask = dev->dev.dma_mask;
                        sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
--- 1.143/drivers/usb/core/usb.c        Thu Sep 25 03:59:51 2003
+++ edited/drivers/usb/core/usb.c       Tue Oct  7 11:01:17 2003
@@ -80,7 +80,7 @@
 
 static int usb_generic_driver_data;
 
-/* needs to be called with BKL held */
+/* called from driver core, with usb bus writelock */
 int usb_probe_interface(struct device *dev)
 {
        struct usb_interface * intf = to_usb_interface(dev);
@@ -88,41 +88,36 @@
        const struct usb_device_id *id;
        int error = -ENODEV;
 
-       dev_dbg(dev, "%s\n", __FUNCTION__);
-
        if (!driver->probe)
                return error;
 
        id = usb_match_id (intf, driver->id_table);
        if (id) {
-               dev_dbg (dev, "%s - got id\n", __FUNCTION__);
-               down (&driver->serialize);
+               dev_dbg (dev, "%s - id match\n", __FUNCTION__);
                error = driver->probe (intf, id);
-               up (&driver->serialize);
-       }
-       if (!error)
-               intf->driver = driver;
+       } else
+               dev_dbg(dev, "%s - no match\n", __FUNCTION__);
 
        return error;
 }
 
+/* called from driver core, with usb bus writelock */
 int usb_unbind_interface(struct device *dev)
 {
        struct usb_interface *intf = to_usb_interface(dev);
        struct usb_driver *driver = to_usb_driver(dev->driver);
 
-       down(&driver->serialize);
-
        /* release all urbs for this interface */
        usb_disable_interface(interface_to_usbdev(intf), intf);
 
-       if (intf->driver && intf->driver->disconnect)
-               intf->driver->disconnect(intf);
+       if (dev->driver && driver->disconnect)
+               driver->disconnect(intf);
 
-       /* force a release and re-initialize the interface */
-       usb_driver_release_interface(driver, intf);
-
-       up(&driver->serialize);
+       /* reset other interface state */
+       usb_set_interface(interface_to_usbdev(intf),
+                       intf->altsetting[0].desc.bInterfaceNumber,
+                       0);
+       usb_set_intfdata(intf, NULL);
 
        return 0;
 }
@@ -152,8 +147,6 @@
        new_driver->driver.probe = usb_probe_interface;
        new_driver->driver.remove = usb_unbind_interface;
 
-       init_MUTEX(&new_driver->serialize);
-
        retval = driver_register(&new_driver->driver);
 
        if (!retval) {
@@ -252,8 +245,8 @@
 
 /**
  * usb_driver_claim_interface - bind a driver to an interface
- * @driver: the driver to be bound
- * @iface: the interface to which it will be bound
+ * @driver: the driver to be bound (must be registered)
+ * @iface: the interface to which it will be bound (from active config)
  * @priv: driver data associated with that interface
  *
  * This is used by usb device drivers that need to claim more than one
@@ -264,26 +257,25 @@
  * 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.
+ *
+ * Drivers that can control multiple interfaces -- class drivers like
+ * CDC, audio, and video, and some vendor-specific drivers -- use this
+ * when probe()ing the main interface, to claim the other interfaces.
+ * Any other users need to hold the driver core writelock for this bus.
  */
 int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface 
*iface, void* priv)
 {
        if (!iface || !driver)
                return -EINVAL;
-
-       /* this is mainly to lock against usbfs */
-       lock_kernel();
-       if (iface->driver) {
-               unlock_kernel();
-               err ("%s driver booted %s off interface %p",
-                       driver->name, iface->driver->name, iface);
+       if (iface->dev.driver)
                return -EBUSY;
-       } else {
-           dbg("%s driver claimed interface %p", driver->name, iface);
-       }
 
-       iface->driver = driver;
+       iface->dev.driver = &driver->driver;
        usb_set_intfdata(iface, priv);
-       unlock_kernel();
+
+       /* interfaces may not be registered yet in sysfs */
+       if (iface->dev.bus)
+               device_bind_driver(&iface->dev);
        return 0;
 }
 
@@ -291,20 +283,17 @@
  * usb_interface_claimed - returns true iff an interface is claimed
  * @iface: the interface being checked
  *
- * This should be used by drivers to check other interfaces to see if
+ * This could be used by drivers to check other interfaces to see if
  * they are available or not.  If another driver has claimed the interface,
- * they may not claim it.  Otherwise it's OK to claim it using
- * usb_driver_claim_interface().
+ * they may not claim it.  However, success here is not a guarantee that
+ * usb_driver_claim_interface() won't report a failure later.
  *
  * Returns true (nonzero) iff the interface is claimed, else false (zero).
  */
-int usb_interface_claimed(struct usb_interface *iface)
+int __deprecated usb_interface_claimed(struct usb_interface *iface)
 {
-       if (!iface)
-               return 0;
-
-       return (iface->driver != NULL);
-} /* usb_interface_claimed() */
+       return (iface != NULL) && (iface->dev.driver != NULL);
+}
 
 /**
  * usb_driver_release_interface - unbind a driver from an interface
@@ -312,29 +301,25 @@
  * @iface: the interface from which it will be unbound
  *
  * In addition to unbinding the driver, this re-initializes the interface
- * by selecting altsetting 0, the default alternate setting.
- * 
- * This can be used by drivers to release an interface without waiting
- * for their disconnect() methods to be called.
- *
- * When the USB subsystem disconnect()s a driver from some interface,
- * it automatically invokes this method for that interface.  That
- * means that even drivers that used usb_driver_claim_interface()
- * usually won't need to call this.
+ * by selecting altsetting 0 (the default setting).  Unbinding includes
+ * calling the driver disconnect() method for that interface, and removing
+ * sysfs records of the binding.
  *
  * This call is synchronous, and may not be used in an interrupt context.
+ * The caller must own the driver core usb bus writelock.
  */
 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->driver && iface->driver != driver)
+       if (iface->dev.driver && iface->dev.driver != &driver->driver)
                return;
 
-       usb_set_interface(interface_to_usbdev(iface),
-                       iface->altsetting[0].desc.bInterfaceNumber,
-                       0);
-       usb_set_intfdata(iface, NULL);
-       iface->driver = NULL;
+dev_dbg(&iface->dev, "release 1\n");
+
+       /* trigger usb_unbind_interface(), and do sysfs magic */
+       device_release_driver(&iface->dev);
+
+dev_dbg(&iface->dev, "release 2\n");
 }
 
 /**

Reply via email to