From 403892ab93deb909dc1cd16693b2dccb531aa2cd Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 28 May 2002 20:01:51 -0700 Subject: 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) --- drivers/base/base.h | 3 ++ drivers/base/core.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++--- drivers/base/driver.c | 2 + include/linux/device.h | 2 + 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); }; -- cgit v1.2.3