summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/base/Makefile5
-rw-r--r--drivers/base/driver.c70
-rw-r--r--include/linux/device.h31
3 files changed, 104 insertions, 2 deletions
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index eae7bc965fcb..a20768fa9630 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -1,7 +1,8 @@
O_TARGET := base.o
-obj-y := core.o sys.o interface.o fs.o power.o bus.o
+obj-y := core.o sys.o interface.o fs.o power.o bus.o \
+ driver.o
-export-objs := core.o fs.o power.o sys.o bus.o
+export-objs := core.o fs.o power.o sys.o bus.o driver.o
include $(TOPDIR)/Rules.make
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
new file mode 100644
index 000000000000..13a7038c4826
--- /dev/null
+++ b/drivers/base/driver.c
@@ -0,0 +1,70 @@
+/*
+ * driver.c - centralized device driver management
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include "base.h"
+
+/**
+ * driver_make_dir - create a driverfs directory for a driver
+ * @drv: driver in question
+ */
+static int driver_make_dir(struct device_driver * drv)
+{
+ drv->dir.name = drv->name;
+ return device_create_dir(&drv->dir,&drv->bus->driver_dir);
+}
+
+/**
+ * driver_register - register driver with bus
+ * @drv: driver to register
+ *
+ * Add to bus's list of devices
+ */
+int driver_register(struct device_driver * drv)
+{
+ if (!drv->bus)
+ return -EINVAL;
+
+ pr_debug("Registering driver '%s' with bus '%s'\n",drv->name,drv->bus->name);
+
+ get_bus(drv->bus);
+ atomic_set(&drv->refcount,2);
+ rwlock_init(&drv->lock);
+ write_lock(&drv->bus->lock);
+ list_add(&drv->bus_list,&drv->bus->drivers);
+ write_unlock(&drv->bus->lock);
+ driver_make_dir(drv);
+ put_driver(drv);
+ return 0;
+}
+
+/**
+ * put_driver - decrement driver's refcount and clean up if necessary
+ * @drv: driver in question
+ */
+void put_driver(struct device_driver * drv)
+{
+ if (!atomic_dec_and_lock(&drv->refcount,&device_lock))
+ return;
+ spin_unlock(&device_lock);
+
+ if (drv->bus) {
+ pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name);
+
+ write_lock(&drv->bus->lock);
+ list_del_init(&drv->bus_list);
+ write_unlock(&drv->bus->lock);
+
+ driverfs_remove_dir(&drv->dir);
+ put_bus(drv->bus);
+ }
+ if (drv->release)
+ drv->release(drv);
+}
+
+EXPORT_SYMBOL(driver_register);
+EXPORT_SYMBOL(put_driver);
diff --git a/include/linux/device.h b/include/linux/device.h
index 078c65e02370..2ad1e9ff4f75 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -84,17 +84,48 @@ extern void put_bus(struct bus_type * bus);
struct device_driver {
+ char * name;
+ struct bus_type * bus;
+
+ rwlock_t lock;
+ atomic_t refcount;
+
+ list_t bus_list;
+ list_t devices;
+
+ struct driver_dir_entry dir;
+
int (*probe) (struct device * dev);
int (*remove) (struct device * dev, u32 flags);
int (*suspend) (struct device * dev, u32 state, u32 level);
int (*resume) (struct device * dev, u32 level);
+
+ void (*release) (struct device_driver * drv);
};
+
+
+extern int driver_register(struct device_driver * drv);
+
+static inline struct device_driver * get_driver(struct device_driver * drv)
+{
+ BUG_ON(!atomic_read(&drv->refcount));
+ atomic_inc(&drv->refcount);
+ return drv;
+}
+
+extern void put_driver(struct device_driver * drv);
+
+extern int driver_for_each_dev(struct device_driver * drv, void * data,
+ int (*callback)(struct device * dev, void * data));
+
+
struct device {
struct list_head g_list; /* node in depth-first order list */
struct list_head node; /* node in sibling list */
struct list_head bus_list; /* node in bus's list */
+ struct list_head driver_list;
struct list_head children;
struct device * parent;