summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/base/bus.c87
-rw-r--r--include/linux/device.h7
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;