Commit 403892ab authored by Patrick Mochel's avatar Patrick Mochel

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)
parent e1e5aa9b
......@@ -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);
......@@ -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);
......
......@@ -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);
......
......@@ -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);
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment