Commit 007aa553 authored by Patrick Mochel's avatar Patrick Mochel

driver model: simplify device/driver binding.

- move list walking and matching to bus.c (since it's a function of the bus driver)
- do specialized walks of the bus's lists when binding; no more callbacks being passed
  to bus_for_each_*.
- take rwsem when adding and removing both devices and drivers. lists of each are now
  fully protected by that rwsem. It's also taken before we walk each list.
- move calls of device_{de,at}tach() to bus_{add,remove}_device() and calls of 
  driver_{de,at}tach() to bus_{add,remove}_driver().
parent 5f330849
...@@ -20,6 +20,9 @@ extern void device_remove_dir(struct device * dev); ...@@ -20,6 +20,9 @@ extern void device_remove_dir(struct device * dev);
extern int bus_make_dir(struct bus_type * bus); extern int bus_make_dir(struct bus_type * bus);
extern void bus_remove_dir(struct bus_type * bus); extern void bus_remove_dir(struct bus_type * bus);
extern int bus_add_driver(struct device_driver *);
extern void bus_remove_driver(struct device_driver *);
extern int driver_make_dir(struct device_driver * drv); extern int driver_make_dir(struct device_driver * drv);
extern void driver_remove_dir(struct device_driver * drv); extern void driver_remove_dir(struct device_driver * drv);
...@@ -48,9 +51,6 @@ extern int interface_add(struct device_class *, struct device *); ...@@ -48,9 +51,6 @@ extern int interface_add(struct device_class *, struct device *);
extern void interface_remove(struct device_class *, struct device *); extern void interface_remove(struct device_class *, struct device *);
extern int driver_attach(struct device_driver * drv);
extern void driver_detach(struct device_driver * drv);
#ifdef CONFIG_HOTPLUG #ifdef CONFIG_HOTPLUG
extern int dev_hotplug(struct device *dev, const char *action); extern int dev_hotplug(struct device *dev, const char *action);
#else #else
......
...@@ -94,6 +94,104 @@ int bus_for_each_drv(struct bus_type * bus, void * data, ...@@ -94,6 +94,104 @@ int bus_for_each_drv(struct bus_type * bus, void * data,
return error; return error;
} }
static void attach(struct device * dev)
{
pr_debug("bound device '%s' to driver '%s'\n",
dev->bus_id,dev->driver->name);
list_add_tail(&dev->driver_list,&dev->driver->devices);
}
static int bus_match(struct device * dev, struct device_driver * drv)
{
int error = 0;
if (dev->bus->match(dev,drv)) {
dev->driver = drv;
if (drv->probe) {
if (!(error = drv->probe(dev)))
attach(dev);
else
dev->driver = NULL;
}
}
return error;
}
static int device_attach(struct device * dev)
{
struct bus_type * bus = dev->bus;
struct list_head * entry;
int error = 0;
if (dev->driver) {
attach(dev);
return 0;
}
if (!bus->match)
return 0;
list_for_each(entry,&bus->drivers) {
struct device_driver * drv =
get_driver(container_of(entry,struct device_driver,bus_list));
if (!drv)
continue;
error = bus_match(dev,drv);
put_driver(drv);
if (!error)
break;
}
return error;
}
static int driver_attach(struct device_driver * drv)
{
struct bus_type * bus = drv->bus;
struct list_head * entry;
int error = 0;
if (!bus->match)
return 0;
list_for_each(entry,&bus->devices) {
struct device * dev = container_of(entry,struct device,bus_list);
if (get_device(dev)) {
if (!bus_match(dev,drv) && dev->driver)
devclass_add_device(dev);
put_device(dev);
}
}
return error;
}
static void detach(struct device * dev, struct device_driver * drv)
{
if (drv) {
list_del_init(&dev->driver_list);
devclass_remove_device(dev);
if (drv->remove)
drv->remove(dev);
dev->driver = NULL;
}
}
static void device_detach(struct device * dev)
{
detach(dev,dev->driver);
}
static void driver_detach(struct device_driver * drv)
{
struct list_head * entry;
list_for_each(entry,&drv->devices) {
struct device * dev = container_of(entry,struct device,driver_list);
if (get_device(dev)) {
detach(dev,drv);
put_device(dev);
}
}
}
/** /**
* bus_add_device - add device to bus * bus_add_device - add device to bus
* @dev: device being added * @dev: device being added
...@@ -105,11 +203,12 @@ int bus_for_each_drv(struct bus_type * bus, void * data, ...@@ -105,11 +203,12 @@ int bus_for_each_drv(struct bus_type * bus, void * data,
*/ */
int bus_add_device(struct device * dev) int bus_add_device(struct device * dev)
{ {
if (dev->bus) { struct bus_type * bus = get_bus(dev->bus);
if (bus) {
down_write(&dev->bus->rwsem); down_write(&dev->bus->rwsem);
pr_debug("registering %s with bus '%s'\n",dev->bus_id,dev->bus->name); pr_debug("bus %s: add device %s\n",bus->name,dev->bus_id);
get_bus(dev->bus);
list_add_tail(&dev->bus_list,&dev->bus->devices); list_add_tail(&dev->bus_list,&dev->bus->devices);
device_attach(dev);
up_write(&dev->bus->rwsem); up_write(&dev->bus->rwsem);
device_bus_link(dev); device_bus_link(dev);
} }
...@@ -127,13 +226,40 @@ void bus_remove_device(struct device * dev) ...@@ -127,13 +226,40 @@ void bus_remove_device(struct device * dev)
{ {
if (dev->bus) { if (dev->bus) {
down_write(&dev->bus->rwsem); down_write(&dev->bus->rwsem);
list_del_init(&dev->bus_list); pr_debug("bus %s: remove device %s\n",dev->bus->name,dev->bus_id);
device_remove_symlink(&dev->bus->device_dir,dev->bus_id); device_remove_symlink(&dev->bus->device_dir,dev->bus_id);
device_detach(dev);
list_del_init(&dev->bus_list);
up_write(&dev->bus->rwsem); up_write(&dev->bus->rwsem);
put_bus(dev->bus); put_bus(dev->bus);
} }
} }
int bus_add_driver(struct device_driver * drv)
{
struct bus_type * bus = get_bus(drv->bus);
if (bus) {
down_write(&bus->rwsem);
pr_debug("bus %s: add driver %s\n",bus->name,drv->name);
list_add_tail(&drv->bus_list,&bus->drivers);
driver_attach(drv);
up_write(&bus->rwsem);
driver_make_dir(drv);
}
return 0;
}
void bus_remove_driver(struct device_driver * drv)
{
if (drv->bus) {
down_write(&drv->bus->rwsem);
pr_debug("bus %s: remove driver %s\n",drv->bus->name,drv->name);
driver_detach(drv);
list_del_init(&drv->bus_list);
up_write(&drv->bus->rwsem);
}
}
struct bus_type * get_bus(struct bus_type * bus) struct bus_type * get_bus(struct bus_type * bus)
{ {
struct bus_type * ret = bus; struct bus_type * ret = bus;
......
...@@ -23,132 +23,6 @@ spinlock_t device_lock = SPIN_LOCK_UNLOCKED; ...@@ -23,132 +23,6 @@ spinlock_t device_lock = SPIN_LOCK_UNLOCKED;
#define to_dev(node) container_of(node,struct device,driver_list) #define to_dev(node) container_of(node,struct device,driver_list)
static int probe(struct device * dev, struct device_driver * drv)
{
dev->driver = drv;
return drv->probe ? drv->probe(dev) : 0;
}
static void attach(struct device * dev)
{
spin_lock(&device_lock);
list_add_tail(&dev->driver_list,&dev->driver->devices);
spin_unlock(&device_lock);
devclass_add_device(dev);
}
/**
* found_match - do actual binding of device to driver
* @dev: device
* @drv: driver
*
* We're here because the bus's match 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;
if (!(error = probe(dev,get_driver(drv)))) {
pr_debug("bound device '%s' to driver '%s'\n",
dev->bus_id,drv->name);
attach(dev);
} else {
put_driver(drv);
dev->driver = NULL;
}
return error;
}
/**
* device_attach - 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 match 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_attach(struct device_driver * drv, void * data)
{
struct device * dev = (struct device *)data;
int error = 0;
if (drv->bus->match && drv->bus->match(dev,drv))
error = found_match(dev,drv);
return error;
}
static int device_attach(struct device * dev)
{
int error = 0;
if (!dev->driver) {
if (dev->bus)
error = bus_for_each_drv(dev->bus,dev,do_device_attach);
} else
attach(dev);
return error;
}
static void device_detach(struct device * dev)
{
struct device_driver * drv = dev->driver;
if (drv) {
devclass_remove_device(dev);
if (drv && drv->remove)
drv->remove(dev);
dev->driver = NULL;
}
}
static int do_driver_attach(struct device * dev, void * data)
{
struct device_driver * drv = (struct device_driver *)data;
int error = 0;
if (!dev->driver) {
if (dev->bus->match && dev->bus->match(dev,drv))
error = found_match(dev,drv);
}
return error;
}
int driver_attach(struct device_driver * drv)
{
return bus_for_each_dev(drv->bus,drv,do_driver_attach);
}
void driver_detach(struct device_driver * drv)
{
struct list_head * node;
struct device * prev = NULL;
spin_lock(&device_lock);
list_for_each(node,&drv->devices) {
struct device * dev = get_device_locked(to_dev(node));
if (dev) {
if (prev)
list_del_init(&prev->driver_list);
spin_unlock(&device_lock);
device_detach(dev);
if (prev)
put_device(prev);
prev = dev;
spin_lock(&device_lock);
}
}
spin_unlock(&device_lock);
}
int device_add(struct device *dev) int device_add(struct device *dev)
{ {
int error; int error;
...@@ -173,9 +47,6 @@ int device_add(struct device *dev) ...@@ -173,9 +47,6 @@ int device_add(struct device *dev)
bus_add_device(dev); bus_add_device(dev);
/* bind to driver */
device_attach(dev);
/* notify platform of device entry */ /* notify platform of device entry */
if (platform_notify) if (platform_notify)
platform_notify(dev); platform_notify(dev);
...@@ -183,6 +54,7 @@ int device_add(struct device *dev) ...@@ -183,6 +54,7 @@ int device_add(struct device *dev)
/* notify userspace of device entry */ /* notify userspace of device entry */
dev_hotplug(dev, "add"); dev_hotplug(dev, "add");
devclass_add_device(dev);
register_done: register_done:
if (error) { if (error) {
spin_lock(&device_lock); spin_lock(&device_lock);
...@@ -284,7 +156,6 @@ void device_del(struct device * dev) ...@@ -284,7 +156,6 @@ void device_del(struct device * dev)
/* notify userspace that this device is about to disappear */ /* notify userspace that this device is about to disappear */
dev_hotplug (dev, "remove"); dev_hotplug (dev, "remove");
device_detach(dev);
bus_remove_device(dev); bus_remove_device(dev);
/* remove the driverfs directory */ /* remove the driverfs directory */
......
...@@ -66,11 +66,9 @@ void put_driver(struct device_driver * drv) ...@@ -66,11 +66,9 @@ void put_driver(struct device_driver * drv)
struct bus_type * bus = drv->bus; struct bus_type * bus = drv->bus;
if (!atomic_dec_and_lock(&drv->refcount,&device_lock)) if (!atomic_dec_and_lock(&drv->refcount,&device_lock))
return; return;
list_del_init(&drv->bus_list);
spin_unlock(&device_lock); spin_unlock(&device_lock);
BUG_ON(drv->present); BUG_ON(drv->present);
driver_detach(drv); bus_remove_driver(drv);
driver_remove_dir(drv);
if (drv->release) if (drv->release)
drv->release(drv); drv->release(drv);
put_bus(bus); put_bus(bus);
...@@ -94,11 +92,7 @@ int driver_register(struct device_driver * drv) ...@@ -94,11 +92,7 @@ int driver_register(struct device_driver * drv)
rwlock_init(&drv->lock); rwlock_init(&drv->lock);
INIT_LIST_HEAD(&drv->devices); INIT_LIST_HEAD(&drv->devices);
drv->present = 1; drv->present = 1;
spin_lock(&device_lock); bus_add_driver(drv);
list_add(&drv->bus_list,&drv->bus->drivers);
spin_unlock(&device_lock);
driver_make_dir(drv);
driver_attach(drv);
put_driver(drv); put_driver(drv);
return 0; return 0;
} }
......
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