summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/kernel/ecard.c2
-rw-r--r--drivers/base/base.h7
-rw-r--r--drivers/base/bus.c241
-rw-r--r--drivers/base/class.c132
-rw-r--r--drivers/base/core.c215
-rw-r--r--drivers/base/driver.c107
-rw-r--r--drivers/base/interface.c2
-rw-r--r--drivers/base/power.c68
-rw-r--r--drivers/base/sys.c6
-rw-r--r--drivers/input/serio/sa1111ps2.c2
-rw-r--r--drivers/pci/pci-driver.c2
-rw-r--r--drivers/pcmcia/sa1111_generic.c2
-rw-r--r--drivers/scsi/scsi_debug.c3
-rw-r--r--drivers/scsi/sd.c2
-rw-r--r--drivers/scsi/sg.c2
-rw-r--r--drivers/scsi/sr.c2
-rw-r--r--drivers/scsi/st.c2
-rw-r--r--drivers/usb/core/usb.c4
-rw-r--r--drivers/usb/host/ohci-sa1111.c2
-rw-r--r--fs/driverfs/inode.c4
-rw-r--r--include/linux/device.h49
-rw-r--r--include/linux/driverfs_fs.h2
22 files changed, 466 insertions, 392 deletions
diff --git a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c
index 4cb68c31b86e..2b291c31ff8b 100644
--- a/arch/arm/kernel/ecard.c
+++ b/arch/arm/kernel/ecard.c
@@ -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)
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 0c05ae058971..3f294f8cafff 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -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
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 7daffbfd9913..49683a1d3898 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -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);
diff --git a/drivers/base/class.c b/drivers/base/class.c
index dfef9793871a..7328f3231a65 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -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);
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);
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 221e525736bd..4bf4a005b918 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -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);
diff --git a/drivers/base/interface.c b/drivers/base/interface.c
index a585b729c5c4..8a5c6e6bc226 100644
--- a/drivers/base/interface.c
+++ b/drivers/base/interface.c
@@ -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
diff --git a/drivers/base/power.c b/drivers/base/power.c
index c98984dfd8ab..176be19b72d1 100644
--- a/drivers/base/power.c
+++ b/drivers/base/power.c
@@ -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);
diff --git a/drivers/base/sys.c b/drivers/base/sys.c
index 2f8e10cf1ae8..cd681d1190bd 100644
--- a/drivers/base/sys.c
+++ b/drivers/base/sys.c
@@ -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);
}
/**
diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c
index 502d3c90b864..01dc3c0681f2 100644
--- a/drivers/input/serio/sa1111ps2.c
+++ b/drivers/input/serio/sa1111ps2.c
@@ -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);
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 040cc588981b..3ef7d9eb6e84 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -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 = {
diff --git a/drivers/pcmcia/sa1111_generic.c b/drivers/pcmcia/sa1111_generic.c
index 9bf1a6c392d4..7afea0c7b3aa 100644
--- a/drivers/pcmcia/sa1111_generic.c
+++ b/drivers/pcmcia/sa1111_generic.c
@@ -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);
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index c8ad7724307c..d32b1a65d6ee 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -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);
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 32300bedc779..02121a6fd6d5 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1492,7 +1492,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);
}
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 53acfd9194d5..4e7682ec0565 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -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
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 5c0a4bd0a9df..14c54d55c764 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -831,7 +831,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);
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 2a8161b18c04..43c05cb65e7b 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -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");
}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 556dbacb7c23..e31a270ce931 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -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();
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
index 98996cd62d6e..e5b729e5ad39 100644
--- a/drivers/usb/host/ohci-sa1111.c
+++ b/drivers/usb/host/ohci-sa1111.c
@@ -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);
diff --git a/fs/driverfs/inode.c b/fs/driverfs/inode.c
index 0f35e27e669c..b4b5aa9f6a55 100644
--- a/fs/driverfs/inode.c
+++ b/fs/driverfs/inode.c
@@ -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;
diff --git a/include/linux/device.h b/include/linux/device.h
index 80a63939f924..3dd9a4081298 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -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?
diff --git a/include/linux/driverfs_fs.h b/include/linux/driverfs_fs.h
index d859f8c2e041..b4270e947a1e 100644
--- a/include/linux/driverfs_fs.h
+++ b/include/linux/driverfs_fs.h
@@ -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_ */