diff options
| author | Linus Torvalds <torvalds@home.transmeta.com> | 2002-10-17 21:55:08 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2002-10-17 21:55:08 -0700 |
| commit | 487680ca16759a671984fcd00a514fde9d9f3b9a (patch) | |
| tree | fd4b8758c31a9606cf747efd958465eac0a8ebbc /drivers/base/bus.c | |
| parent | d103bdbf34d5ab76a92fc0d1a4aae7b1af5c8493 (diff) | |
| parent | 879a1092976b201f23c69c7159fe51be1c8c7bc2 (diff) | |
Merge bk://ldm.bkbits.net/linux-2.5-core
into home.transmeta.com:/home/torvalds/v2.5/linux
Diffstat (limited to 'drivers/base/bus.c')
| -rw-r--r-- | drivers/base/bus.c | 241 |
1 files changed, 193 insertions, 48 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 7daffbfd9913..49683a1d3898 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -43,28 +43,23 @@ int bus_for_each_dev(struct bus_type * bus, void * data, int (*callback)(struct device * dev, void * data)) { struct list_head * node; - struct device * prev = NULL; int error = 0; - get_bus(bus); - spin_lock(&device_lock); - list_for_each(node,&bus->devices) { - struct device * dev = get_device_locked(to_dev(node)); - if (dev) { - spin_unlock(&device_lock); - error = callback(dev,data); - if (prev) - put_device(prev); - prev = dev; - spin_lock(&device_lock); - if (error) - break; + bus = get_bus(bus); + if (bus) { + down_read(&bus->rwsem); + list_for_each(node,&bus->devices) { + struct device * dev = get_device(to_dev(node)); + if (dev) { + error = callback(dev,data); + put_device(dev); + if (error) + break; + } } + up_read(&bus->rwsem); + put_bus(bus); } - spin_unlock(&device_lock); - if (prev) - put_device(prev); - put_bus(bus); return error; } @@ -72,33 +67,126 @@ int bus_for_each_drv(struct bus_type * bus, void * data, int (*callback)(struct device_driver * drv, void * data)) { struct list_head * node; - struct device_driver * prev = NULL; int error = 0; - /* pin bus in memory */ - get_bus(bus); + bus = get_bus(bus); + if (bus) { + down_read(&bus->rwsem); + list_for_each(node,&bus->drivers) { + struct device_driver * drv = get_driver(to_drv(node)); + if (drv) { + error = callback(drv,data); + put_driver(drv); + if (error) + break; + } + } + up_read(&bus->rwsem); + put_bus(bus); + } + return error; +} - spin_lock(&device_lock); - list_for_each(node,&bus->drivers) { - struct device_driver * drv = get_driver(to_drv(node)); - if (drv) { - spin_unlock(&device_lock); - error = callback(drv,data); - if (prev) - put_driver(prev); - prev = drv; - spin_lock(&device_lock); - if (error) - break; +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; } } - spin_unlock(&device_lock); - if (prev) - put_driver(prev); - put_bus(bus); 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 (!dev->driver) { + 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, * next; + list_for_each_safe(entry,next,&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 * @dev: device being added @@ -110,12 +198,13 @@ int bus_for_each_drv(struct bus_type * bus, void * data, */ int bus_add_device(struct device * dev) { - if (dev->bus) { - pr_debug("registering %s with bus '%s'\n",dev->bus_id,dev->bus->name); - get_bus(dev->bus); - spin_lock(&device_lock); + struct bus_type * bus = get_bus(dev->bus); + if (bus) { + down_write(&dev->bus->rwsem); + pr_debug("bus %s: add device %s\n",bus->name,dev->bus_id); list_add_tail(&dev->bus_list,&dev->bus->devices); - spin_unlock(&device_lock); + device_attach(dev); + up_write(&dev->bus->rwsem); device_bus_link(dev); } return 0; @@ -131,17 +220,70 @@ int bus_add_device(struct device * dev) void bus_remove_device(struct device * dev) { if (dev->bus) { + down_write(&dev->bus->rwsem); + 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_detach(dev); + list_del_init(&dev->bus_list); + up_write(&dev->bus->rwsem); 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 * ret = bus; + spin_lock(&device_lock); + if (bus && bus->present && atomic_read(&bus->refcount)) + atomic_inc(&bus->refcount); + else + ret = NULL; + spin_unlock(&device_lock); + return ret; +} + +void put_bus(struct bus_type * bus) +{ + if (!atomic_dec_and_lock(&bus->refcount,&device_lock)) + return; + list_del_init(&bus->node); + spin_unlock(&device_lock); + BUG_ON(bus->present); + bus_remove_dir(bus); +} + int bus_register(struct bus_type * bus) { - rwlock_init(&bus->lock); + init_rwsem(&bus->rwsem); INIT_LIST_HEAD(&bus->devices); INIT_LIST_HEAD(&bus->drivers); atomic_set(&bus->refcount,2); + bus->present = 1; spin_lock(&device_lock); list_add_tail(&bus->node,&bus_driver_list); @@ -156,13 +298,14 @@ int bus_register(struct bus_type * bus) return 0; } -void put_bus(struct bus_type * bus) +void bus_unregister(struct bus_type * bus) { - if (!atomic_dec_and_lock(&bus->refcount,&device_lock)) - return; - list_del_init(&bus->node); + spin_lock(&device_lock); + bus->present = 0; spin_unlock(&device_lock); - bus_remove_dir(bus); + + pr_debug("bus %s: unregistering\n",bus->name); + put_bus(bus); } EXPORT_SYMBOL(bus_for_each_dev); @@ -170,4 +313,6 @@ EXPORT_SYMBOL(bus_for_each_drv); EXPORT_SYMBOL(bus_add_device); EXPORT_SYMBOL(bus_remove_device); EXPORT_SYMBOL(bus_register); +EXPORT_SYMBOL(bus_unregister); +EXPORT_SYMBOL(get_bus); EXPORT_SYMBOL(put_bus); |
