summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Mochel <mochel@osdl.org>2002-05-28 20:01:51 -0700
committerPatrick Mochel <mochel@osdl.org>2002-05-28 20:01:51 -0700
commit403892ab93deb909dc1cd16693b2dccb531aa2cd (patch)
tree04a3960dd2e0d3857fb5bdc3db2f8a5ca7e79795
parente1e5aa9b53be641a87ed3cb98871bb0abdf3e4f2 (diff)
Device Model: Implement centralized device/driver binding
- on device registration, all drivers of bus are iterated over - bus's bind callback is called to match device to driver - if successful, driver's probe callback is called - on device removal, driver's remove callback is called - on driver registration, list of devices is iterated over (and same thing happens)
-rw-r--r--drivers/base/base.h3
-rw-r--r--drivers/base/core.c125
-rw-r--r--drivers/base/driver.c2
-rw-r--r--include/linux/device.h2
4 files changed, 125 insertions, 7 deletions
diff --git a/drivers/base/base.h b/drivers/base/base.h
index d00e85db3924..aed2c93ba12c 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -17,3 +17,6 @@ extern int device_make_dir(struct device * dev);
extern void device_remove_dir(struct device * dev);
extern int device_bus_link(struct device * dev);
+
+extern int driver_bind(struct device_driver * drv);
+extern void driver_unbind(struct device_driver * drv);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index b163f48aaa5f..5f41eb4f2ef2 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -23,6 +23,120 @@ int (*platform_notify_remove)(struct device * dev) = NULL;
spinlock_t device_lock = SPIN_LOCK_UNLOCKED;
+
+/**
+ * found_match - do actual binding of device to driver
+ * @dev: device
+ * @drv: driver
+ *
+ * We're here because the bus's bind callback returned success for this
+ * pair. We call the driver's probe callback to verify they're really a
+ * match made in heaven.
+ *
+ * In the future, we may want to notify userspace of the binding. (But,
+ * we might not want to do it here).
+ *
+ * We may also want to create a symlink in the driver's directory to the
+ * device's physical directory.
+ */
+static int found_match(struct device * dev, struct device_driver * drv)
+{
+ int error = 0;
+
+ dev->driver = get_driver(drv);
+ if (drv->probe)
+ if (drv->probe(dev))
+ goto ProbeFailed;
+
+ pr_debug("bound device '%s' to driver '%s'\n",
+ dev->bus_id,drv->name);
+
+ write_lock(&drv->lock);
+ list_add_tail(&dev->driver_list,&drv->devices);
+ write_unlock(&drv->lock);
+
+ goto Done;
+
+ ProbeFailed:
+ put_driver(drv);
+ dev->driver = NULL;
+ Done:
+ return error;
+}
+
+/**
+ * bind_device - try to associated device with a driver
+ * @drv: current driver to try
+ * @data: device in disguise
+ *
+ * This function is used as a callback to bus_for_each_drv.
+ * It calls the bus's ::bind callback to check if the driver supports
+ * the device. If so, it calls the found_match() function above to
+ * take care of all the details.
+ */
+static int do_device_bind(struct device_driver * drv, void * data)
+{
+ struct device * dev = (struct device *)data;
+ int error = 0;
+
+ if (!dev->driver) {
+ if (drv->bus->bind && drv->bus->bind(dev,drv))
+ error = found_match(dev,drv);
+ }
+ return error;
+}
+
+static int device_bind(struct device * dev)
+{
+ int error = 0;
+ if (dev->bus)
+ error = bus_for_each_drv(dev->bus,dev,do_device_bind);
+ return error;
+}
+
+static void device_unbind(struct device * dev)
+{
+ /* unbind from driver */
+ if (dev->driver && dev->driver->remove)
+ dev->driver->remove(dev,REMOVE_NOTIFY);
+}
+
+static int do_driver_bind(struct device * dev, void * data)
+{
+ struct device_driver * drv = (struct device_driver *)data;
+ int error = 0;
+
+ if (!dev->driver) {
+ if (dev->bus->bind && dev->bus->bind(dev,drv))
+ error = found_match(dev,drv);
+ }
+ return error;
+}
+
+int driver_bind(struct device_driver * drv)
+{
+ return bus_for_each_dev(drv->bus,drv,do_driver_bind);
+}
+
+static int do_driver_unbind(struct device * dev, void * data)
+{
+ struct device_driver * drv = (struct device_driver *)data;
+ lock_device(dev);
+ if (dev->driver == drv) {
+ dev->driver = NULL;
+ unlock_device(dev);
+ if (drv->remove)
+ drv->remove(dev,REMOVE_NOTIFY);
+ } else
+ unlock_device(dev);
+ return 0;
+}
+
+void driver_unbind(struct device_driver * drv)
+{
+// driver_for_each_dev(drv,drv,do_driver_unbind);
+}
+
/**
* device_register - register a device
* @dev: pointer to the device structure
@@ -72,6 +186,9 @@ int device_register(struct device *dev)
bus_add_device(dev);
+ /* bind to driver */
+ device_bind(dev);
+
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
@@ -104,15 +221,9 @@ void put_device(struct device * dev)
if (platform_notify_remove)
platform_notify_remove(dev);
+ device_unbind(dev);
bus_remove_device(dev);
- /* Tell the driver to clean up after itself.
- * Note that we likely didn't allocate the device,
- * so this is the driver's chance to free that up...
- */
- if (dev->driver && dev->driver->remove)
- dev->driver->remove(dev,REMOVE_FREE_RESOURCES);
-
/* remove the driverfs directory */
device_remove_dir(dev);
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 13a7038c4826..a3e1e078385d 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -38,6 +38,7 @@ int driver_register(struct device_driver * drv)
list_add(&drv->bus_list,&drv->bus->drivers);
write_unlock(&drv->bus->lock);
driver_make_dir(drv);
+ driver_bind(drv);
put_driver(drv);
return 0;
}
@@ -55,6 +56,7 @@ void put_driver(struct device_driver * drv)
if (drv->bus) {
pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name);
+ driver_unbind(drv);
write_lock(&drv->bus->lock);
list_del_init(&drv->bus_list);
write_unlock(&drv->bus->lock);
diff --git a/include/linux/device.h b/include/linux/device.h
index b8729d952190..dd88f8b7cace 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -68,6 +68,8 @@ struct bus_type {
struct driver_dir_entry dir;
struct driver_dir_entry device_dir;
struct driver_dir_entry driver_dir;
+
+ int (*bind) (struct device * dev, struct device_driver * drv);
};