summaryrefslogtreecommitdiff
path: root/drivers/base
diff options
context:
space:
mode:
authorPatrick Mochel <mochel@osdl.org>2002-10-16 03:12:42 -0700
committerPatrick Mochel <mochel@osdl.org>2002-10-16 03:12:42 -0700
commitd9b24a116f72056c7ac8bb2cdcd9dfd84ce85513 (patch)
tree3db188acd706ad3de937f6477d2353de67db780b /drivers/base
parentf21c8fc316b1cf3c537656508fd463522d58cdf3 (diff)
driver model: add per-class rwsem and protect list accesses with it.
This is similar to struct bus_type's rwsem, though classes aren't doing anything nearly as fancy with it. We just make sure to take it when ever we add or remove any devices or drivers.
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/class.c80
1 files changed, 55 insertions, 25 deletions
diff --git a/drivers/base/class.c b/drivers/base/class.c
index 21860a1380f7..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,22 +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;
int error = 0;
if (dev->driver) {
- cls = dev->driver->devclass;
+ cls = get_devclass(dev->driver->devclass);
if (cls) {
- pr_debug("adding device '%s' to class '%s'\n",
- dev->name,cls->name);
+ 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;
@@ -74,14 +96,21 @@ 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);
+ }
}
}
@@ -111,6 +140,7 @@ int devclass_register(struct device_class * cls)
{
INIT_LIST_HEAD(&cls->drivers);
INIT_LIST_HEAD(&cls->intf_list);
+ init_rwsem(&cls->rwsem);
atomic_set(&cls->refcount,2);
cls->present = 1;
pr_debug("device class '%s': registering\n",cls->name);