summaryrefslogtreecommitdiff
path: root/drivers/base/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/core.c')
-rw-r--r--drivers/base/core.c215
1 files changed, 35 insertions, 180 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 83c31723d844..bd82a792d877 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -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);