diff options
| -rw-r--r-- | drivers/base/Makefile | 5 | ||||
| -rw-r--r-- | drivers/base/driver.c | 70 | ||||
| -rw-r--r-- | include/linux/device.h | 31 |
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; |
