Merge git://oss.sgi.com:8090/oss/git/rc-fixes-xfs-2.6
[pandora-kernel.git] / drivers / base / bus.c
index fa601b0..c314156 100644 (file)
@@ -133,6 +133,8 @@ static struct kobj_type ktype_bus = {
 decl_subsys(bus, &ktype_bus, NULL);
 
 
+#ifdef CONFIG_HOTPLUG
+
 /* Manually detach a device from its associated driver. */
 static int driver_helper(struct device *dev, void *data)
 {
@@ -152,7 +154,11 @@ static ssize_t driver_unbind(struct device_driver *drv,
 
        dev = bus_find_device(bus, NULL, (void *)buf, driver_helper);
        if (dev && dev->driver == drv) {
+               if (dev->parent)        /* Needed for USB */
+                       down(&dev->parent->sem);
                device_release_driver(dev);
+               if (dev->parent)
+                       up(&dev->parent->sem);
                err = count;
        }
        put_device(dev);
@@ -175,9 +181,13 @@ static ssize_t driver_bind(struct device_driver *drv,
 
        dev = bus_find_device(bus, NULL, (void *)buf, driver_helper);
        if (dev && dev->driver == NULL) {
+               if (dev->parent)        /* Needed for USB */
+                       down(&dev->parent->sem);
                down(&dev->sem);
                err = driver_probe_device(drv, dev);
                up(&dev->sem);
+               if (dev->parent)
+                       up(&dev->parent->sem);
        }
        put_device(dev);
        put_bus(bus);
@@ -185,6 +195,7 @@ static ssize_t driver_bind(struct device_driver *drv,
 }
 static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind);
 
+#endif
 
 static struct device * next_device(struct klist_iter * i)
 {
@@ -420,6 +431,26 @@ static void driver_remove_attrs(struct bus_type * bus, struct device_driver * dr
        }
 }
 
+#ifdef CONFIG_HOTPLUG
+/*
+ * Thanks to drivers making their tables __devinit, we can't allow manual
+ * bind and unbind from userspace unless CONFIG_HOTPLUG is enabled.
+ */
+static void add_bind_files(struct device_driver *drv)
+{
+       driver_create_file(drv, &driver_attr_unbind);
+       driver_create_file(drv, &driver_attr_bind);
+}
+
+static void remove_bind_files(struct device_driver *drv)
+{
+       driver_remove_file(drv, &driver_attr_bind);
+       driver_remove_file(drv, &driver_attr_unbind);
+}
+#else
+static inline void add_bind_files(struct device_driver *drv) {}
+static inline void remove_bind_files(struct device_driver *drv) {}
+#endif
 
 /**
  *     bus_add_driver - Add a driver to the bus.
@@ -449,8 +480,7 @@ int bus_add_driver(struct device_driver * drv)
                module_add_driver(drv->owner, drv);
 
                driver_add_attrs(bus, drv);
-               driver_create_file(drv, &driver_attr_unbind);
-               driver_create_file(drv, &driver_attr_bind);
+               add_bind_files(drv);
        }
        return error;
 }
@@ -468,8 +498,7 @@ int bus_add_driver(struct device_driver * drv)
 void bus_remove_driver(struct device_driver * drv)
 {
        if (drv->bus) {
-               driver_remove_file(drv, &driver_attr_bind);
-               driver_remove_file(drv, &driver_attr_unbind);
+               remove_bind_files(drv);
                driver_remove_attrs(drv->bus, drv);
                klist_remove(&drv->knode_bus);
                pr_debug("bus %s: remove driver %s\n", drv->bus->name, drv->name);
@@ -484,8 +513,13 @@ void bus_remove_driver(struct device_driver * drv)
 /* Helper for bus_rescan_devices's iter */
 static int bus_rescan_devices_helper(struct device *dev, void *data)
 {
-       if (!dev->driver)
+       if (!dev->driver) {
+               if (dev->parent)        /* Needed for USB */
+                       down(&dev->parent->sem);
                device_attach(dev);
+               if (dev->parent)
+                       up(&dev->parent->sem);
+       }
        return 0;
 }