diff options
Diffstat (limited to 'drivers/base/core.c')
| -rw-r--r-- | drivers/base/core.c | 215 |
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); |
