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