diff options
| -rw-r--r-- | drivers/base/bus.c | 87 | ||||
| -rw-r--r-- | include/linux/device.h | 7 |
2 files changed, 93 insertions, 1 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 0159a32a59db..febfbaabf00f 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -23,6 +23,91 @@ static struct driver_dir_entry bus_dir = { }; /** + * bus_for_each_dev - walk list of devices and do something to each + * @bus: bus in question + * @data: data for the callback + * @callback: caller-defined action to perform on each device + * + * Why do we do this? So we can guarantee proper locking and reference + * counting on devices as we touch each one. + * + * Algorithm: + * Take the bus lock and get the first node in the list. We increment + * the reference count and unlock the bus. If we have a device from a + * previous iteration, we decrement the reference count. + * After we call the callback, we get the next node in the list and loop. + * At the end, if @dev is not null, we still have it pinned, so we need + * to let it go. + */ +int bus_for_each_dev(struct bus_type * bus, void * data, + int (*callback)(struct device * dev, void * data)) +{ + struct device * next; + struct device * dev = NULL; + struct list_head * node; + int error = 0; + + get_bus(bus); + read_lock(&bus->lock); + node = bus->devices.next; + while (node != &bus->devices) { + next = list_entry(node,struct device,bus_list); + get_device(next); + read_unlock(&bus->lock); + + if (dev) + put_device(dev); + dev = next; + if ((error = callback(dev,data))) { + put_device(dev); + break; + } + read_lock(&bus->lock); + node = dev->bus_list.next; + } + read_unlock(&bus->lock); + if (dev) + put_device(dev); + put_bus(bus); + return error; +} + +int bus_for_each_drv(struct bus_type * bus, void * data, + int (*callback)(struct device_driver * drv, void * data)) +{ + struct device_driver * next; + struct device_driver * drv = NULL; + struct list_head * node; + int error = 0; + + /* pin bus in memory */ + get_bus(bus); + + read_lock(&bus->lock); + node = bus->drivers.next; + while (node != &bus->drivers) { + next = list_entry(node,struct device_driver,bus_list); + get_driver(next); + read_unlock(&bus->lock); + + if (drv) + put_driver(drv); + drv = next; + if ((error = callback(drv,data))) { + put_driver(drv); + break; + } + read_lock(&bus->lock); + node = drv->bus_list.next; + } + read_unlock(&bus->lock); + if (drv) + put_driver(drv); + put_bus(bus); + return error; +} + +/** * bus_add_device - add device to bus * @dev: device being added * @@ -119,6 +204,8 @@ static int __init bus_init(void) core_initcall(bus_init); +EXPORT_SYMBOL(bus_for_each_dev); +EXPORT_SYMBOL(bus_for_each_drv); EXPORT_SYMBOL(bus_add_device); EXPORT_SYMBOL(bus_remove_device); EXPORT_SYMBOL(bus_register); diff --git a/include/linux/device.h b/include/linux/device.h index 2ad1e9ff4f75..b8729d952190 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -54,7 +54,7 @@ enum { }; struct device; - +struct device_driver; struct bus_type { char * name; @@ -82,6 +82,11 @@ static inline struct bus_type * get_bus(struct bus_type * bus) extern void put_bus(struct bus_type * bus); +extern int bus_for_each_dev(struct bus_type * bus, void * data, + int (*callback)(struct device * dev, void * data)); +extern int bus_for_each_drv(struct bus_type * bus, void * data, + int (*callback)(struct device_driver * drv, void * data)); + struct device_driver { char * name; |
