Commit 487680ca authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://ldm.bkbits.net/linux-2.5-core

into home.transmeta.com:/home/torvalds/v2.5/linux
parents d103bdbf 879a1092
......@@ -1114,7 +1114,7 @@ int ecard_register_driver(struct ecard_driver *drv)
void ecard_remove_driver(struct ecard_driver *drv)
{
remove_driver(&drv->drv);
driver_unregister(&drv->drv);
}
static int ecard_match(struct device *_dev, struct device_driver *_drv)
......
......@@ -8,6 +8,7 @@
extern struct list_head global_device_list;
extern spinlock_t device_lock;
extern struct semaphore device_sem;
extern struct device * get_device_locked(struct device *);
......@@ -20,6 +21,9 @@ extern void device_remove_dir(struct device * dev);
extern int bus_make_dir(struct bus_type * bus);
extern void bus_remove_dir(struct bus_type * bus);
extern int bus_add_driver(struct device_driver *);
extern void bus_remove_driver(struct device_driver *);
extern int driver_make_dir(struct device_driver * drv);
extern void driver_remove_dir(struct device_driver * drv);
......@@ -48,9 +52,6 @@ extern int interface_add(struct device_class *, struct device *);
extern void interface_remove(struct device_class *, struct device *);
extern int driver_attach(struct device_driver * drv);
extern void driver_detach(struct device_driver * drv);
#ifdef CONFIG_HOTPLUG
extern int dev_hotplug(struct device *dev, const char *action);
#else
......
......@@ -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);
......@@ -10,27 +10,29 @@ static LIST_HEAD(class_list);
int devclass_add_driver(struct device_driver * drv)
{
if (drv->devclass) {
pr_debug("Registering driver %s:%s with class %s\n",
drv->bus->name,drv->name,drv->devclass->name);
spin_lock(&device_lock);
list_add_tail(&drv->class_list,&drv->devclass->drivers);
spin_unlock(&device_lock);
struct device_class * cls = get_devclass(drv->devclass);
if (cls) {
down_write(&cls->rwsem);
pr_debug("device class %s: adding driver %s:%s\n",
cls->name,drv->bus->name,drv->name);
list_add_tail(&drv->class_list,&cls->drivers);
devclass_drv_link(drv);
up_write(&cls->rwsem);
}
return 0;
}
void devclass_remove_driver(struct device_driver * drv)
{
if (drv->devclass) {
pr_debug("Removing driver %s:%s:%s\n",
drv->devclass->name,drv->bus->name,drv->name);
spin_lock(&device_lock);
struct device_class * cls = drv->devclass;
if (cls) {
down_write(&cls->rwsem);
pr_debug("device class %s: removing driver %s:%s\n",
cls->name,drv->bus->name,drv->name);
list_del_init(&drv->class_list);
spin_unlock(&device_lock);
devclass_drv_unlink(drv);
up_write(&cls->rwsem);
put_devclass(cls);
}
}
......@@ -38,9 +40,7 @@ void devclass_remove_driver(struct device_driver * drv)
static void enum_device(struct device_class * cls, struct device * dev)
{
u32 val;
spin_lock(&device_lock);
val = cls->devnum++;
spin_unlock(&device_lock);
dev->class_num = val;
devclass_dev_link(cls,dev);
}
......@@ -51,18 +51,44 @@ static void unenum_device(struct device_class * cls, struct device * dev)
dev->class_num = 0;
}
/**
* devclass_add_device - register device with device class
* @dev: device to be registered
*
* This is called when a device is either registered with the
* core, or after the a driver module is loaded and bound to
* the device.
* The class is determined by looking at @dev's driver, so one
* way or another, it must be bound to something. Once the
* class is determined, it's set to prevent against concurrent
* calls for the same device stomping on each other.
*
* /sbin/hotplug should be called once the device is added to
* class and all the interfaces.
*/
int devclass_add_device(struct device * dev)
{
struct device_class * cls = dev->driver->devclass;
struct device_class * cls;
int error = 0;
if (cls) {
pr_debug("adding device '%s' to class '%s'\n",
dev->name,cls->name);
if (cls->add_device)
error = cls->add_device(dev);
if (!error) {
enum_device(cls,dev);
interface_add(cls,dev);
if (dev->driver) {
cls = get_devclass(dev->driver->devclass);
if (cls) {
down_write(&cls->rwsem);
pr_debug("device class %s: adding device %s\n",
cls->name,dev->name);
if (cls->add_device)
error = cls->add_device(dev);
if (!error) {
enum_device(cls,dev);
interface_add(cls,dev);
}
/* notify userspace (call /sbin/hotplug) here */
up_write(&cls->rwsem);
if (error)
put_devclass(cls);
}
}
return error;
......@@ -70,40 +96,74 @@ int devclass_add_device(struct device * dev)
void devclass_remove_device(struct device * dev)
{
struct device_class * cls = dev->driver->devclass;
if (cls) {
pr_debug("removing device '%s' from class '%s'\n",
dev->name,cls->name);
interface_remove(cls,dev);
unenum_device(cls,dev);
if (cls->remove_device)
cls->remove_device(dev);
struct device_class * cls;
if (dev->driver) {
cls = dev->driver->devclass;
if (cls) {
down_write(&cls->rwsem);
pr_debug("device class %s: removing device %s\n",
cls->name,dev->name);
interface_remove(cls,dev);
unenum_device(cls,dev);
if (cls->remove_device)
cls->remove_device(dev);
up_write(&cls->rwsem);
put_devclass(cls);
}
}
}
struct device_class * get_devclass(struct device_class * cls)
{
struct device_class * ret = cls;
spin_lock(&device_lock);
if (cls && cls->present && atomic_read(&cls->refcount) > 0)
atomic_inc(&cls->refcount);
else
ret = NULL;
spin_unlock(&device_lock);
return ret;
}
void put_devclass(struct device_class * cls)
{
if (atomic_dec_and_lock(&cls->refcount,&device_lock)) {
list_del_init(&cls->node);
spin_unlock(&device_lock);
devclass_remove_dir(cls);
}
}
int devclass_register(struct device_class * cls)
{
INIT_LIST_HEAD(&cls->drivers);
INIT_LIST_HEAD(&cls->intf_list);
pr_debug("registering device class '%s'\n",cls->name);
init_rwsem(&cls->rwsem);
atomic_set(&cls->refcount,2);
cls->present = 1;
pr_debug("device class '%s': registering\n",cls->name);
spin_lock(&device_lock);
list_add_tail(&cls->node,&class_list);
spin_unlock(&device_lock);
devclass_make_dir(cls);
put_devclass(cls);
return 0;
}
void devclass_unregister(struct device_class * cls)
{
pr_debug("unregistering device class '%s'\n",cls->name);
devclass_remove_dir(cls);
spin_lock(&device_lock);
list_del_init(&class_list);
cls->present = 0;
spin_unlock(&device_lock);
pr_debug("device class '%s': unregistering\n",cls->name);
put_devclass(cls);
}
EXPORT_SYMBOL(devclass_register);
EXPORT_SYMBOL(devclass_unregister);
EXPORT_SYMBOL(get_devclass);
EXPORT_SYMBOL(put_devclass);
......@@ -19,136 +19,12 @@ LIST_HEAD(global_device_list);
int (*platform_notify)(struct device * dev) = NULL;
int (*platform_notify_remove)(struct device * dev) = NULL;
DECLARE_MUTEX(device_sem);
spinlock_t device_lock = SPIN_LOCK_UNLOCKED;
#define to_dev(node) container_of(node,struct device,driver_list)
static int probe(struct device * dev, struct device_driver * drv)
{
dev->driver = drv;
return drv->probe ? drv->probe(dev) : 0;
}
static void attach(struct device * dev)
{
spin_lock(&device_lock);
list_add_tail(&dev->driver_list,&dev->driver->devices);
spin_unlock(&device_lock);
devclass_add_device(dev);
}
/**
* found_match - do actual binding of device to driver
* @dev: device
* @drv: driver
*
* We're here because the bus's match callback returned success for this
* pair. We call the driver's probe callback to verify they're really a
* match made in heaven.
*
* In the future, we may want to notify userspace of the binding. (But,
* we might not want to do it here).
*
* We may also want to create a symlink in the driver's directory to the
* device's physical directory.
*/
static int found_match(struct device * dev, struct device_driver * drv)
{
int error = 0;
if (!(error = probe(dev,get_driver(drv)))) {
pr_debug("bound device '%s' to driver '%s'\n",
dev->bus_id,drv->name);
attach(dev);
} else {
put_driver(drv);
dev->driver = NULL;
}
return error;
}
/**
* device_attach - try to associated device with a driver
* @drv: current driver to try
* @data: device in disguise
*
* This function is used as a callback to bus_for_each_drv.
* It calls the bus's match callback to check if the driver supports
* the device. If so, it calls the found_match() function above to
* take care of all the details.
*/
static int do_device_attach(struct device_driver * drv, void * data)
{
struct device * dev = (struct device *)data;
int error = 0;
if (drv->bus->match && drv->bus->match(dev,drv))
error = found_match(dev,drv);
return error;
}
static int device_attach(struct device * dev)
{
int error = 0;
if (!dev->driver) {
if (dev->bus)
error = bus_for_each_drv(dev->bus,dev,do_device_attach);
} else
attach(dev);
return error;
}
static void device_detach(struct device * dev)
{
struct device_driver * drv = dev->driver;
if (drv) {
devclass_remove_device(dev);
if (drv && drv->remove)
drv->remove(dev);
dev->driver = NULL;
}
}
static int do_driver_attach(struct device * dev, void * data)
{
struct device_driver * drv = (struct device_driver *)data;
int error = 0;
if (!dev->driver) {
if (dev->bus->match && dev->bus->match(dev,drv))
error = found_match(dev,drv);
}
return error;
}
int driver_attach(struct device_driver * drv)
{
return bus_for_each_dev(drv->bus,drv,do_driver_attach);
}
void driver_detach(struct device_driver * drv)
{
struct list_head * node;
struct device * prev = NULL;
spin_lock(&device_lock);
list_for_each(node,&drv->devices) {
struct device * dev = get_device_locked(to_dev(node));
if (dev) {
if (prev)
list_del_init(&prev->driver_list);
spin_unlock(&device_lock);
device_detach(dev);
if (prev)
put_device(prev);
prev = dev;
spin_lock(&device_lock);
}
}
spin_unlock(&device_lock);
}
int device_add(struct device *dev)
{
int error;
......@@ -156,14 +32,14 @@ int device_add(struct device *dev)
if (!dev || !strlen(dev->bus_id))
return -EINVAL;
spin_lock(&device_lock);
dev->present = 1;
down(&device_sem);
dev->state = DEVICE_REGISTERED;
if (dev->parent) {
list_add_tail(&dev->g_list,&dev->parent->g_list);
list_add_tail(&dev->node,&dev->parent->children);
} else
list_add_tail(&dev->g_list,&global_device_list);
spin_unlock(&device_lock);
up(&device_sem);
pr_debug("DEV: registering device: ID = '%s', name = %s\n",
dev->bus_id, dev->name);
......@@ -173,9 +49,6 @@ int device_add(struct device *dev)
bus_add_device(dev);
/* bind to driver */
device_attach(dev);
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
......@@ -183,12 +56,13 @@ int device_add(struct device *dev)
/* notify userspace of device entry */
dev_hotplug(dev, "add");
devclass_add_device(dev);
register_done:
if (error) {
spin_lock(&device_lock);
up(&device_sem);
list_del_init(&dev->g_list);
list_del_init(&dev->node);
spin_unlock(&device_lock);
up(&device_sem);
}
return error;
}
......@@ -203,6 +77,7 @@ void device_initialize(struct device *dev)
INIT_LIST_HEAD(&dev->intf_list);
spin_lock_init(&dev->lock);
atomic_set(&dev->refcount,1);
dev->state = DEVICE_INITIALIZED;
if (dev->parent)
get_device(dev->parent);
}
......@@ -234,22 +109,15 @@ int device_register(struct device *dev)
return error;
}
struct device * get_device_locked(struct device * dev)
struct device * get_device(struct device * dev)
{
struct device * ret = dev;
if (dev && dev->present && atomic_read(&dev->refcount) > 0)
down(&device_sem);
if (device_present(dev) && atomic_read(&dev->refcount) > 0)
atomic_inc(&dev->refcount);
else
ret = NULL;
return ret;
}
struct device * get_device(struct device * dev)
{
struct device * ret;
spin_lock(&device_lock);
ret = get_device_locked(dev);
spin_unlock(&device_lock);
up(&device_sem);
return ret;
}
......@@ -259,34 +127,23 @@ struct device * get_device(struct device * dev)
*/
void put_device(struct device * dev)
{
struct device * parent;
if (!atomic_dec_and_lock(&dev->refcount,&device_lock))
down(&device_sem);
if (!atomic_dec_and_test(&dev->refcount)) {
up(&device_sem);
return;
parent = dev->parent;
dev->parent = NULL;
spin_unlock(&device_lock);
}
list_del_init(&dev->node);
list_del_init(&dev->g_list);
up(&device_sem);
BUG_ON(dev->present);
BUG_ON((dev->state != DEVICE_GONE));
if (dev->release)
dev->release(dev);
if (parent)
put_device(parent);
device_del(dev);
}
void device_del(struct device * dev)
{
spin_lock(&device_lock);
dev->present = 0;
list_del_init(&dev->node);
list_del_init(&dev->g_list);
list_del_init(&dev->bus_list);
list_del_init(&dev->driver_list);
spin_unlock(&device_lock);
pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n",
dev->bus_id,dev->name);
struct device * parent = dev->parent;
/* Notify the platform of the removal, in case they
* need to do anything...
......@@ -297,11 +154,16 @@ void device_del(struct device * dev)
/* notify userspace that this device is about to disappear */
dev_hotplug (dev, "remove");
device_detach(dev);
bus_remove_device(dev);
/* remove the driverfs directory */
device_remove_dir(dev);
if (dev->release)
dev->release(dev);
if (parent)
put_device(parent);
}
/**
......@@ -315,22 +177,15 @@ void device_del(struct device * dev)
*/
void device_unregister(struct device * dev)
{
device_del(dev);
put_device(dev);
}
static int __init device_init(void)
{
int error;
down(&device_sem);
dev->state = DEVICE_GONE;
up(&device_sem);
error = init_driverfs_fs();
if (error)
panic("DEV: could not initialize driverfs");
return 0;
pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n",
dev->bus_id,dev->name);
put_device(dev);
}
core_initcall(device_init);
EXPORT_SYMBOL(device_register);
EXPORT_SYMBOL(device_unregister);
EXPORT_SYMBOL(get_device);
......
......@@ -16,29 +16,61 @@ int driver_for_each_dev(struct device_driver * drv, void * data,
int (*callback)(struct device *, void * ))
{
struct list_head * node;
struct device * prev = NULL;
int error = 0;
get_driver(drv);
spin_lock(&device_lock);
list_for_each(node,&drv->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;
drv = get_driver(drv);
if (drv) {
down_read(&drv->bus->rwsem);
list_for_each(node,&drv->devices) {
struct device * dev = get_device(to_dev(node));
if (dev) {
error = callback(dev,data);
put_device(dev);
if (error)
break;
}
}
up_read(&drv->bus->rwsem);
put_driver(drv);
}
spin_unlock(&device_lock);
put_driver(drv);
return error;
}
struct device_driver * get_driver(struct device_driver * drv)
{
struct device_driver * ret = drv;
spin_lock(&device_lock);
if (drv && drv->present && atomic_read(&drv->refcount) > 0)
atomic_inc(&drv->refcount);
else
ret = NULL;
spin_unlock(&device_lock);
return ret;
}
void remove_driver(struct device_driver * drv)
{
BUG();
}
/**
* put_driver - decrement driver's refcount and clean up if necessary
* @drv: driver in question
*/
void put_driver(struct device_driver * drv)
{
struct bus_type * bus = drv->bus;
if (!atomic_dec_and_lock(&drv->refcount,&device_lock))
return;
spin_unlock(&device_lock);
BUG_ON(drv->present);
bus_remove_driver(drv);
if (drv->release)
drv->release(drv);
put_bus(bus);
}
/**
* driver_register - register driver with bus
* @drv: driver to register
......@@ -50,54 +82,29 @@ 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);
pr_debug("driver %s:%s: registering\n",drv->bus->name,drv->name);
get_bus(drv->bus);
atomic_set(&drv->refcount,2);
rwlock_init(&drv->lock);
INIT_LIST_HEAD(&drv->devices);
spin_lock(&device_lock);
list_add(&drv->bus_list,&drv->bus->drivers);
spin_unlock(&device_lock);
driver_make_dir(drv);
driver_attach(drv);
drv->present = 1;
bus_add_driver(drv);
put_driver(drv);
return 0;
}
static void __remove_driver(struct device_driver * drv)
{
pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name);
driver_detach(drv);
driver_remove_dir(drv);
if (drv->release)
drv->release(drv);
put_bus(drv->bus);
}
void remove_driver(struct device_driver * drv)
void driver_unregister(struct device_driver * drv)
{
spin_lock(&device_lock);
atomic_set(&drv->refcount,0);
list_del_init(&drv->bus_list);
drv->present = 0;
spin_unlock(&device_lock);
__remove_driver(drv);
}
/**
* 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;
list_del_init(&drv->bus_list);
spin_unlock(&device_lock);
__remove_driver(drv);
pr_debug("driver %s:%s: unregistering\n",drv->bus->name,drv->name);
put_driver(drv);
}
EXPORT_SYMBOL(driver_for_each_dev);
EXPORT_SYMBOL(driver_register);
EXPORT_SYMBOL(driver_unregister);
EXPORT_SYMBOL(get_driver);
EXPORT_SYMBOL(put_driver);
EXPORT_SYMBOL(remove_driver);
......@@ -19,7 +19,7 @@ static DEVICE_ATTR(name,S_IRUGO,device_read_name,NULL);
static ssize_t
device_read_power(struct device * dev, char * page, size_t count, loff_t off)
{
return off ? 0 : sprintf(page,"%d\n",dev->current_state);
return off ? 0 : sprintf(page,"%d\n",dev->power_state);
}
static ssize_t
......
......@@ -8,6 +8,8 @@
*
*/
#define DEBUG 0
#include <linux/device.h>
#include <linux/module.h>
#include "base.h"
......@@ -28,34 +30,21 @@
int device_suspend(u32 state, u32 level)
{
struct list_head * node;
struct device * prev = NULL;
int error = 0;
if(level == SUSPEND_POWER_DOWN)
printk(KERN_EMERG "Shutting down devices\n");
else
printk(KERN_EMERG "Suspending devices\n");
printk(KERN_EMERG "Suspending devices\n");
spin_lock(&device_lock);
down(&device_sem);
list_for_each(node,&global_device_list) {
struct device * dev = get_device_locked(to_dev(node));
if (dev) {
spin_unlock(&device_lock);
if(dev->driver) {
if(level == SUSPEND_POWER_DOWN) {
if(dev->driver->remove)
dev->driver->remove(dev);
} else if(dev->driver->suspend)
error = dev->driver->suspend(dev,state,level);
}
if (prev)
put_device(prev);
prev = dev;
spin_lock(&device_lock);
struct device * dev = to_dev(node);
if (device_present(dev) && dev->driver && dev->driver->suspend) {
pr_debug("suspending device %s\n",dev->name);
error = dev->driver->suspend(dev,state,level);
if (error)
printk(KERN_ERR "%s: suspend returned %d\n",dev->name,error);
}
}
spin_unlock(&device_lock);
up(&device_sem);
return error;
}
......@@ -70,33 +59,38 @@ int device_suspend(u32 state, u32 level)
void device_resume(u32 level)
{
struct list_head * node;
struct device * prev = NULL;
spin_lock(&device_lock);
down(&device_sem);
list_for_each_prev(node,&global_device_list) {
struct device * dev = get_device_locked(to_dev(node));
if (dev) {
spin_unlock(&device_lock);
if (dev->driver && dev->driver->resume)
dev->driver->resume(dev,level);
if (prev)
put_device(prev);
prev = dev;
spin_lock(&device_lock);
struct device * dev = to_dev(node);
if (device_present(dev) && dev->driver && dev->driver->resume) {
pr_debug("resuming device %s\n",dev->name);
dev->driver->resume(dev,level);
}
}
spin_unlock(&device_lock);
up(&device_sem);
printk(KERN_EMERG "Devices Resumed\n");
}
/**
* device_shutdown - call device_suspend with status set to shutdown, to
* cause all devices to remove themselves cleanly
* device_shutdown - call ->remove() on each device to shutdown.
*/
void device_shutdown(void)
{
device_suspend(4, SUSPEND_POWER_DOWN);
struct list_head * entry;
printk(KERN_EMERG "Shutting down devices\n");
down(&device_sem);
list_for_each(entry,&global_device_list) {
struct device * dev = to_dev(entry);
if (device_present(dev) && dev->driver && dev->driver->shutdown) {
pr_debug("shutting down %s\n",dev->name);
dev->driver->shutdown(dev);
}
}
up(&device_sem);
}
EXPORT_SYMBOL(device_suspend);
......
......@@ -10,6 +10,8 @@
* add themselves as children of the system bus.
*/
#define DEBUG 1
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
......@@ -76,8 +78,8 @@ int sys_register_root(struct sys_root * root)
*/
void sys_unegister_root(struct sys_root * root)
{
put_device(&root->sysdev);
put_device(&root->dev);
device_unregister(&root->sysdev);
device_unregister(&root->dev);
}
/**
......
......@@ -354,7 +354,7 @@ static int __init ps2_init(void)
static void __exit ps2_exit(void)
{
remove_driver(&ps2_driver.drv);
driver_unregister(&ps2_driver.drv);
}
module_init(ps2_init);
......
......@@ -141,7 +141,7 @@ pci_register_driver(struct pci_driver *drv)
void
pci_unregister_driver(struct pci_driver *drv)
{
remove_driver(&drv->driver);
driver_unregister(&drv->driver);
}
static struct pci_driver pci_compat_driver = {
......
......@@ -297,7 +297,7 @@ static int __init sa1111_drv_pcmcia_init(void)
static void __exit sa1111_drv_pcmcia_exit(void)
{
remove_driver(&pcmcia_driver.drv);
driver_unregister(&pcmcia_driver.drv);
}
module_init(sa1111_drv_pcmcia_init);
......
......@@ -861,8 +861,7 @@ static int scsi_debug_release(struct Scsi_Host * hpnt)
if (++num_releases == num_present) {
#ifdef DRIVERFS_SUPPORT
do_remove_driverfs_files();
remove_driver(&sdebug_driverfs_driver);
// driver_unregister(&sdebug_driverfs_driver);
driver_unregister(&sdebug_driverfs_driver);
#endif
vfree(fake_storep);
vfree(devInfop);
......
......@@ -1525,7 +1525,7 @@ static void __exit exit_sd(void)
vfree(sd_dsk_arr);
}
sd_template.dev_max = 0;
remove_driver(&sd_template.scsi_driverfs_driver);
driver_unregister(&sd_template.scsi_driverfs_driver);
unregister_reboot_notifier(&sd_notifier_block);
}
......
......@@ -1655,7 +1655,7 @@ exit_sg(void)
sg_dev_arr = NULL;
}
sg_template.dev_max = 0;
remove_driver(&sg_template.scsi_driverfs_driver);
driver_unregister(&sg_template.scsi_driverfs_driver);
}
static int
......
......@@ -856,7 +856,7 @@ static void __exit exit_sr(void)
kfree(scsi_CDs);
sr_template.dev_max = 0;
remove_driver(&sr_template.scsi_driverfs_driver);
driver_unregister(&sr_template.scsi_driverfs_driver);
}
module_init(init_sr);
......
......@@ -3992,7 +3992,7 @@ static void __exit exit_st(void)
kfree(scsi_tapes);
}
st_template.dev_max = 0;
remove_driver(&st_template.scsi_driverfs_driver);
driver_unregister(&st_template.scsi_driverfs_driver);
printk(KERN_INFO "st: Unloaded.\n");
}
......
......@@ -208,7 +208,7 @@ void usb_deregister(struct usb_driver *driver)
{
info("deregistering driver %s", driver->name);
remove_driver (&driver->driver);
driver_unregister (&driver->driver);
usbfs_update_special();
}
......@@ -1394,7 +1394,7 @@ static void __exit usb_exit(void)
if (nousb)
return;
remove_driver(&usb_generic_driver);
driver_unregister(&usb_generic_driver);
usb_major_cleanup();
usbfs_cleanup();
usb_hub_cleanup();
......
......@@ -413,7 +413,7 @@ static int __init ohci_hcd_sa1111_init (void)
static void __exit ohci_hcd_sa1111_cleanup (void)
{
remove_driver(&ohci_hcd_sa1111_driver.drv);
driver_unregister(&ohci_hcd_sa1111_driver.drv);
}
module_init (ohci_hcd_sa1111_init);
......
......@@ -509,11 +509,13 @@ static void put_mount(void)
DBG("driverfs: mount_count = %d\n",mount_count);
}
int __init init_driverfs_fs(void)
static int __init driverfs_init(void)
{
return register_filesystem(&driverfs_fs_type);
}
core_initcall(driverfs_init);
static struct dentry * get_dentry(struct dentry * parent, const char * name)
{
struct qstr qstr;
......
......@@ -48,14 +48,22 @@ enum {
RESUME_ENABLE,
};
enum device_state {
DEVICE_UNINITIALIZED = 0,
DEVICE_INITIALIZED = 1,
DEVICE_REGISTERED = 2,
DEVICE_GONE = 3,
};
struct device;
struct device_driver;
struct device_class;
struct bus_type {
char * name;
rwlock_t lock;
struct rw_semaphore rwsem;
atomic_t refcount;
u32 present;
struct list_head node;
struct list_head devices;
......@@ -73,14 +81,9 @@ struct bus_type {
extern int bus_register(struct bus_type * bus);
extern void bus_unregister(struct bus_type * bus);
static inline struct bus_type * get_bus(struct bus_type * bus)
{
BUG_ON(!atomic_read(&bus->refcount));
atomic_inc(&bus->refcount);
return bus;
}
extern 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,
......@@ -114,6 +117,7 @@ struct device_driver {
rwlock_t lock;
atomic_t refcount;
u32 present;
struct list_head bus_list;
struct list_head class_list;
......@@ -123,7 +127,7 @@ struct device_driver {
int (*probe) (struct device * dev);
int (*remove) (struct device * dev);
void (*shutdown) (struct device * dev);
int (*suspend) (struct device * dev, u32 state, u32 level);
int (*resume) (struct device * dev, u32 level);
......@@ -131,16 +135,10 @@ struct device_driver {
};
extern int driver_register(struct device_driver * drv);
extern void driver_unregister(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 struct device_driver * get_driver(struct device_driver * drv);
extern void put_driver(struct device_driver * drv);
extern void remove_driver(struct device_driver * drv);
......@@ -172,6 +170,11 @@ extern void driver_remove_file(struct device_driver *, struct driver_attribute *
*/
struct device_class {
char * name;
struct rw_semaphore rwsem;
atomic_t refcount;
u32 present;
u32 devnum;
struct list_head node;
......@@ -189,6 +192,9 @@ struct device_class {
extern int devclass_register(struct device_class *);
extern void devclass_unregister(struct device_class *);
extern struct device_class * get_devclass(struct device_class *);
extern void put_devclass(struct device_class *);
struct devclass_attribute {
struct attribute attr;
......@@ -289,8 +295,8 @@ struct device {
void *platform_data; /* Platform specific data (e.g. ACPI,
BIOS data relevant to device) */
u32 present;
u32 current_state; /* Current operating state. In
enum device_state state;
u32 power_state; /* Current operating state. In
ACPI-speak, this is D0-D3, D0
being fully functional, and D3
being off. */
......@@ -362,6 +368,11 @@ extern int (*platform_notify)(struct device * dev);
extern int (*platform_notify_remove)(struct device * dev);
static inline int device_present(struct device * dev)
{
return (dev && (dev->state == DEVICE_INITIALIZED || dev->state == DEVICE_REGISTERED));
}
/* device and bus locking helpers.
*
* FIXME: Is there anything else we need to do?
......
......@@ -65,6 +65,4 @@ driverfs_create_symlink(struct driver_dir_entry * parent,
extern void
driverfs_remove_file(struct driver_dir_entry *, const char * name);
extern int init_driverfs_fs(void);
#endif /* _DDFS_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment