diff options
| author | Greg Kroah-Hartman <greg@kroah.com> | 2003-04-28 09:05:37 -0700 |
|---|---|---|
| committer | Greg Kroah-Hartman <greg@kroah.com> | 2003-04-28 09:05:37 -0700 |
| commit | 28926b3d45f4850f1d2d246a2c01b6d24fbd227c (patch) | |
| tree | d35c1fab223537ebc0a22096d83ed2949405b595 /drivers/base | |
| parent | 5d4d8070c3d10a9a18894c671b247c6790e3355a (diff) | |
driver core: rework driver class structures and logic
Removes the device_class, devclass_attribute, and device_interface structures
and replaces them with class, class_device, and class_interface structures.
This allows us to have multiple class_device structures per device structures
which mirrors the ways things really are within the kernel. It also allows
class_device structures to be created later than struct devices as they
are naturally created much later in the initialization process of a device.
Diffstat (limited to 'drivers/base')
| -rw-r--r-- | drivers/base/Makefile | 4 | ||||
| -rw-r--r-- | drivers/base/base.h | 20 | ||||
| -rw-r--r-- | drivers/base/bus.c | 11 | ||||
| -rw-r--r-- | drivers/base/class.c | 472 | ||||
| -rw-r--r-- | drivers/base/core.c | 2 | ||||
| -rw-r--r-- | drivers/base/driver.c | 1 |
6 files changed, 307 insertions, 203 deletions
diff --git a/drivers/base/Makefile b/drivers/base/Makefile index e9c753888d48..354a9789d29d 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -1,8 +1,6 @@ # Makefile for the Linux device tree obj-y := core.o sys.o interface.o power.o bus.o \ - driver.o class.o intf.o platform.o \ + driver.o class.o platform.o \ cpu.o firmware.o init.o obj-$(CONFIG_NUMA) += node.o memblk.o -obj-y += fs/ -obj-$(CONFIG_HOTPLUG) += hotplug.o diff --git a/drivers/base/base.h b/drivers/base/base.h index 9180934e6c99..121bb0c84418 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -1,5 +1,4 @@ extern struct semaphore device_sem; -extern struct semaphore devclass_sem; extern int bus_add_device(struct device * dev); extern void bus_remove_device(struct device * dev); @@ -7,22 +6,3 @@ extern void bus_remove_device(struct device * dev); extern int bus_add_driver(struct device_driver *); extern void bus_remove_driver(struct device_driver *); -extern int devclass_add_device(struct device *); -extern void devclass_remove_device(struct device *); - -extern int devclass_add_driver(struct device_driver *); -extern void devclass_remove_driver(struct device_driver *); - -extern int interface_add_dev(struct device *); -extern void interface_remove_dev(struct device *); - - -#ifdef CONFIG_HOTPLUG -extern int class_hotplug(struct device *dev, const char *action); -#else -static inline int class_hotplug(struct device *dev, const char *action) -{ - return 0; -} -#endif - diff --git a/drivers/base/bus.c b/drivers/base/bus.c index e918f4db4840..3a8e54592af7 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -311,8 +311,7 @@ static int device_attach(struct device * dev) * Walk the list of devices that the bus has on it and try to match * the driver with each one. * If bus_match() returns 0 and the @dev->driver is set, we've found - * a compatible pair, so we call devclass_add_device() to add the - * device to the class. + * a compatible pair. * * Note that we ignore the error from bus_match(), since it's perfectly * valid for a driver not to bind to any devices. @@ -328,8 +327,7 @@ static void driver_attach(struct device_driver * drv) list_for_each(entry,&bus->devices.list) { struct device * dev = container_of(entry,struct device,bus_list); if (!dev->driver) { - if (!bus_match(dev,drv)) - devclass_add_device(dev); + bus_match(dev,drv); } } } @@ -351,7 +349,6 @@ void device_release_driver(struct device * dev) if (drv) { sysfs_remove_link(&drv->kobj,dev->kobj.name); list_del_init(&dev->driver_list); - devclass_remove_device(dev); if (drv->remove) drv->remove(dev); dev->driver = NULL; @@ -443,8 +440,7 @@ int bus_add_driver(struct device_driver * drv) } down_write(&bus->subsys.rwsem); - if (!(error = devclass_add_driver(drv))) - driver_attach(drv); + driver_attach(drv); up_write(&bus->subsys.rwsem); if (error) { @@ -471,7 +467,6 @@ void bus_remove_driver(struct device_driver * drv) down_write(&drv->bus->subsys.rwsem); pr_debug("bus %s: remove driver %s\n",drv->bus->name,drv->name); driver_detach(drv); - devclass_remove_driver(drv); up_write(&drv->bus->subsys.rwsem); kobject_unregister(&drv->kobj); put_bus(drv->bus); diff --git a/drivers/base/class.c b/drivers/base/class.c index 8e35b7884276..eda427f96134 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -1,5 +1,7 @@ /* * class.c - basic device class management + * + * Copyright (c) 2001-2003 Patrick Mochel <mochel@osdl.org> */ #undef DEBUG @@ -10,16 +12,14 @@ #include <linux/string.h> #include "base.h" -#define to_class_attr(_attr) container_of(_attr,struct devclass_attribute,attr) -#define to_class(obj) container_of(obj,struct device_class,subsys.kset.kobj) - -DECLARE_MUTEX(devclass_sem); +#define to_class_attr(_attr) container_of(_attr,struct class_attribute,attr) +#define to_class(obj) container_of(obj,struct class,subsys.kset.kobj) static ssize_t -devclass_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) +class_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) { - struct devclass_attribute * class_attr = to_class_attr(attr); - struct device_class * dc = to_class(kobj); + struct class_attribute * class_attr = to_class_attr(attr); + struct class * dc = to_class(kobj); ssize_t ret = 0; if (class_attr->show) @@ -28,11 +28,11 @@ devclass_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) } static ssize_t -devclass_attr_store(struct kobject * kobj, struct attribute * attr, - const char * buf, size_t count) +class_attr_store(struct kobject * kobj, struct attribute * attr, + const char * buf, size_t count) { - struct devclass_attribute * class_attr = to_class_attr(attr); - struct device_class * dc = to_class(kobj); + struct class_attribute * class_attr = to_class_attr(attr); + struct class * dc = to_class(kobj); ssize_t ret = 0; if (class_attr->store) @@ -41,242 +41,376 @@ devclass_attr_store(struct kobject * kobj, struct attribute * attr, } static struct sysfs_ops class_sysfs_ops = { - .show = devclass_attr_show, - .store = devclass_attr_store, + .show = class_attr_show, + .store = class_attr_store, }; -static struct kobj_type ktype_devclass = { +static struct kobj_type ktype_class = { .sysfs_ops = &class_sysfs_ops, }; -/* Classes can't use the kobject hotplug logic, as - * they do not add new kobjects to the system */ -static decl_subsys(class,&ktype_devclass,NULL); +/* Hotplug events for classes go to the class_obj subsys */ +static decl_subsys(class,&ktype_class,NULL); -static int devclass_dev_link(struct device_class * cls, struct device * dev) +int class_create_file(struct class * cls, struct class_attribute * attr) { - char linkname[16]; - snprintf(linkname,16,"%u",dev->class_num); - return sysfs_create_link(&cls->devices.kobj,&dev->kobj,linkname); + int error; + if (cls) { + error = sysfs_create_file(&cls->subsys.kset.kobj,&attr->attr); + } else + error = -EINVAL; + return error; } -static void devclass_dev_unlink(struct device_class * cls, struct device * dev) +void class_remove_file(struct class * cls, struct class_attribute * attr) { - char linkname[16]; - snprintf(linkname,16,"%u",dev->class_num); - sysfs_remove_link(&cls->devices.kobj,linkname); + if (cls) + sysfs_remove_file(&cls->subsys.kset.kobj,&attr->attr); } -static int devclass_drv_link(struct device_driver * drv) +struct class * class_get(struct class * cls) { - char name[KOBJ_NAME_LEN * 3]; - snprintf(name,KOBJ_NAME_LEN * 3,"%s:%s",drv->bus->name,drv->name); - return sysfs_create_link(&drv->devclass->drivers.kobj,&drv->kobj,name); + if (cls) + return container_of(subsys_get(&cls->subsys),struct class,subsys); + return NULL; } -static void devclass_drv_unlink(struct device_driver * drv) +void class_put(struct class * cls) { - char name[KOBJ_NAME_LEN * 3]; - snprintf(name,KOBJ_NAME_LEN * 3,"%s:%s",drv->bus->name,drv->name); - return sysfs_remove_link(&drv->devclass->drivers.kobj,name); + subsys_put(&cls->subsys); } +int class_register(struct class * cls) +{ + pr_debug("device class '%s': registering\n",cls->name); + + INIT_LIST_HEAD(&cls->children); + INIT_LIST_HEAD(&cls->interfaces); + + strncpy(cls->subsys.kset.kobj.name,cls->name,KOBJ_NAME_LEN); + subsys_set_kset(cls,class_subsys); + subsystem_register(&cls->subsys); + + return 0; +} -int devclass_create_file(struct device_class * cls, struct devclass_attribute * attr) +void class_unregister(struct class * cls) { - int error; - if (cls) { - error = sysfs_create_file(&cls->subsys.kset.kobj,&attr->attr); - } else - error = -EINVAL; + pr_debug("device class '%s': unregistering\n",cls->name); + subsystem_unregister(&cls->subsys); +} + +/* Class Device Stuff */ + +int class_device_create_file(struct class_device * class_dev, + struct class_device_attribute * attr) +{ + int error = -EINVAL; + if (class_dev) + error = sysfs_create_file(&class_dev->kobj, &attr->attr); return error; } -void devclass_remove_file(struct device_class * cls, struct devclass_attribute * attr) +void class_device_remove_file(struct class_device * class_dev, + struct class_device_attribute * attr) { - if (cls) - sysfs_remove_file(&cls->subsys.kset.kobj,&attr->attr); + if (class_dev) + sysfs_remove_file(&class_dev->kobj, &attr->attr); } +static int class_device_dev_link(struct class_device * class_dev) +{ + if (class_dev->dev) + return sysfs_create_link(&class_dev->kobj, + &class_dev->dev->kobj, "device"); + return 0; +} -int devclass_add_driver(struct device_driver * drv) +static void class_device_dev_unlink(struct class_device * class_dev) { - struct device_class * cls = get_devclass(drv->devclass); - int error = 0; + if (class_dev->dev) + sysfs_remove_link(&class_dev->kobj, "device"); +} - if (cls) { - down_write(&cls->subsys.rwsem); - pr_debug("device class %s: adding driver %s:%s\n", - cls->name,drv->bus->name,drv->name); - error = devclass_drv_link(drv); - - if (!error) - list_add_tail(&drv->class_list,&cls->drivers.list); - up_write(&cls->subsys.rwsem); - } - return error; +#define to_class_dev(obj) container_of(obj,struct class_device,kobj) +#define to_class_dev_attr(_attr) container_of(_attr,struct class_device_attribute,attr) + +static ssize_t +class_device_attr_show(struct kobject * kobj, struct attribute * attr, + char * buf) +{ + struct class_device_attribute * class_dev_attr = to_class_dev_attr(attr); + struct class_device * cd = to_class_dev(kobj); + ssize_t ret = 0; + + if (class_dev_attr->show) + ret = class_dev_attr->show(cd,buf); + return ret; } -void devclass_remove_driver(struct device_driver * drv) +static ssize_t +class_device_attr_store(struct kobject * kobj, struct attribute * attr, + const char * buf, size_t count) { - struct device_class * cls = drv->devclass; - if (cls) { - down_write(&cls->subsys.rwsem); - pr_debug("device class %s: removing driver %s:%s\n", - cls->name,drv->bus->name,drv->name); - list_del_init(&drv->class_list); - devclass_drv_unlink(drv); - up_write(&cls->subsys.rwsem); - put_devclass(cls); - } + struct class_device_attribute * class_dev_attr = to_class_dev_attr(attr); + struct class_device * cd = to_class_dev(kobj); + ssize_t ret = 0; + + if (class_dev_attr->store) + ret = class_dev_attr->store(cd,buf,count); + return ret; } +static struct sysfs_ops class_dev_sysfs_ops = { + .show = class_device_attr_show, + .store = class_device_attr_store, +}; + +static struct kobj_type ktype_class_device = { + .sysfs_ops = &class_dev_sysfs_ops, +}; -static void enum_device(struct device_class * cls, struct device * dev) +static int class_hotplug_filter(struct kset *kset, struct kobject *kobj) { - u32 val; - val = cls->devnum++; - dev->class_num = val; - devclass_dev_link(cls,dev); + struct kobj_type *ktype = get_ktype(kobj); + + if (ktype == &ktype_class_device) { + struct class_device *class_dev = to_class_dev(kobj); + if (class_dev->class) + return 1; + } + return 0; } -static void unenum_device(struct device_class * cls, struct device * dev) +static char *class_hotplug_name(struct kset *kset, struct kobject *kobj) { - devclass_dev_unlink(cls,dev); - dev->class_num = 0; + struct class_device *class_dev = to_class_dev(kobj); + + return class_dev->class->name; } -/** - * 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) +static int class_hotplug(struct kset *kset, struct kobject *kobj, char **envp, + int num_envp, char *buffer, int buffer_size) { - struct device_class * cls; - int error = 0; - - down(&devclass_sem); - if (dev->driver) { - cls = get_devclass(dev->driver->devclass); - - if (!cls) - goto Done; - - pr_debug("device class %s: adding device %s\n", - cls->name,dev->name); - if (cls->add_device) - error = cls->add_device(dev); - if (error) { - put_devclass(cls); - goto Done; + struct class_device *class_dev = to_class_dev(kobj); + int retval = 0; + + pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id); + if (class_dev->class->hotplug) { + /* have the bus specific function add its stuff */ + retval = class_dev->class->hotplug (class_dev, envp, num_envp, + buffer, buffer_size); + if (retval) { + pr_debug ("%s - hotplug() returned %d\n", + __FUNCTION__, retval); } + } - down_write(&cls->subsys.rwsem); - enum_device(cls,dev); - list_add_tail(&dev->class_list,&cls->devices.list); - /* notify userspace (call /sbin/hotplug) */ - class_hotplug (dev, "add"); + return retval; +} - up_write(&cls->subsys.rwsem); +static struct kset_hotplug_ops class_hotplug_ops = { + .filter = class_hotplug_filter, + .name = class_hotplug_name, + .hotplug = class_hotplug, +}; - interface_add_dev(dev); - } - Done: - up(&devclass_sem); - return error; +static decl_subsys(class_obj, &ktype_class_device, &class_hotplug_ops); + +void class_device_initialize(struct class_device *class_dev) +{ + kobject_init(&class_dev->kobj); + INIT_LIST_HEAD(&class_dev->node); } -void devclass_remove_device(struct device * dev) +int class_device_add(struct class_device *class_dev) { - struct device_class * cls; + struct class * parent; + struct class_interface * class_intf; + struct list_head * entry; + int error; - down(&devclass_sem); - if (dev->driver) { - cls = dev->driver->devclass; - if (!cls) - goto Done; + class_dev = class_device_get(class_dev); + if (!class_dev || !strlen(class_dev->class_id)) + return -EINVAL; + + parent = class_get(class_dev->class); + if (class_dev->dev) + get_device(class_dev->dev); + + pr_debug("CLASS: registering class device: ID = '%s'\n", + class_dev->class_id); + + /* first, register with generic layer. */ + strncpy(class_dev->kobj.name, class_dev->class_id, KOBJ_NAME_LEN); + kobj_set_kset_s(class_dev, class_subsys); + kobj_set_kset_s(class_dev, class_obj_subsys); + if (parent) + class_dev->kobj.parent = &parent->subsys.kset.kobj; + + if ((error = kobject_add(&class_dev->kobj))) + goto register_done; + + /* now take care of our own registration */ + if (parent) { + down_write(&parent->subsys.rwsem); + list_add_tail(&class_dev->node, &parent->children); + list_for_each(entry, &parent->interfaces) { + class_intf = container_of(entry, struct class_interface, node); + if (class_intf->add) + class_intf->add(class_dev); + } + up_write(&parent->subsys.rwsem); + } - interface_remove_dev(dev); + class_device_dev_link(class_dev); - down_write(&cls->subsys.rwsem); - pr_debug("device class %s: removing device %s\n", - cls->name,dev->name); + register_done: + if (error && parent) + class_put(parent); + class_device_put(class_dev); + return error; +} - unenum_device(cls,dev); +int class_device_register(struct class_device *class_dev) +{ + class_device_initialize(class_dev); + return class_device_add(class_dev); +} - list_del(&dev->class_list); +void class_device_del(struct class_device *class_dev) +{ + struct class * parent = class_dev->class; + struct class_interface * class_intf; + struct list_head * entry; + + if (parent) { + down_write(&parent->subsys.rwsem); + list_del_init(&class_dev->node); + list_for_each(entry, &parent->interfaces) { + class_intf = container_of(entry, struct class_interface, node); + if (class_intf->remove) + class_intf->remove(class_dev); + } + up_write(&parent->subsys.rwsem); + } - /* notify userspace (call /sbin/hotplug) */ - class_hotplug (dev, "remove"); + if (class_dev->dev) { + class_device_dev_unlink(class_dev); + put_device(class_dev->dev); + } + + kobject_del(&class_dev->kobj); - up_write(&cls->subsys.rwsem); + if (parent) + class_put(parent); +} - if (cls->remove_device) - cls->remove_device(dev); - put_devclass(cls); - } - Done: - up(&devclass_sem); +void class_device_unregister(struct class_device *class_dev) +{ + pr_debug("CLASS: Unregistering class device. ID = '%s'\n", + class_dev->class_id); + class_device_del(class_dev); + class_device_put(class_dev); } -struct device_class * get_devclass(struct device_class * cls) +struct class_device * class_device_get(struct class_device *class_dev) { - return cls ? container_of(subsys_get(&cls->subsys),struct device_class,subsys) : NULL; + if (class_dev) + return to_class_dev(kobject_get(&class_dev->kobj)); + return NULL; } -void put_devclass(struct device_class * cls) +void class_device_put(struct class_device *class_dev) { - subsys_put(&cls->subsys); + kobject_put(&class_dev->kobj); } -int devclass_register(struct device_class * cls) +int class_interface_register(struct class_interface *class_intf) { - pr_debug("device class '%s': registering\n",cls->name); - strncpy(cls->subsys.kset.kobj.name,cls->name,KOBJ_NAME_LEN); - subsys_set_kset(cls,class_subsys); - subsystem_register(&cls->subsys); + struct class * parent; + struct class_device * class_dev; + struct list_head * entry; + + if (!class_intf || !class_intf->class) + return -ENODEV; + + parent = class_get(class_intf->class); + if (!parent) + return -EINVAL; - snprintf(cls->devices.kobj.name,KOBJ_NAME_LEN,"devices"); - cls->devices.subsys = &cls->subsys; - kset_register(&cls->devices); + down_write(&parent->subsys.rwsem); + list_add_tail(&class_intf->node, &parent->interfaces); - snprintf(cls->drivers.kobj.name,KOBJ_NAME_LEN,"drivers"); - cls->drivers.subsys = &cls->subsys; - kset_register(&cls->drivers); + if (class_intf->add) { + list_for_each(entry, &parent->children) { + class_dev = container_of(entry, struct class_device, node); + class_intf->add(class_dev); + } + } + up_write(&parent->subsys.rwsem); return 0; } -void devclass_unregister(struct device_class * cls) +void class_interface_unregister(struct class_interface *class_intf) { - pr_debug("device class '%s': unregistering\n",cls->name); - kset_unregister(&cls->drivers); - kset_unregister(&cls->devices); - subsystem_unregister(&cls->subsys); + struct class * parent = class_intf->class; + struct list_head * entry; + + if (!parent) + return; + + down_write(&parent->subsys.rwsem); + list_del_init(&class_intf->node); + + if (class_intf->remove) { + list_for_each(entry, &parent->children) { + struct class_device *class_dev = container_of(entry, struct class_device, node); + class_intf->remove(class_dev); + } + } + up_write(&parent->subsys.rwsem); + + class_put(parent); } + + int __init classes_init(void) { - return subsystem_register(&class_subsys); -} + int retval; -EXPORT_SYMBOL(devclass_create_file); -EXPORT_SYMBOL(devclass_remove_file); -EXPORT_SYMBOL(devclass_register); -EXPORT_SYMBOL(devclass_unregister); -EXPORT_SYMBOL(get_devclass); -EXPORT_SYMBOL(put_devclass); + retval = subsystem_register(&class_subsys); + if (retval) + return retval; + + /* ick, this is ugly, the things we go through to keep from showing up + * in sysfs... */ + subsystem_init(&class_obj_subsys); + if (!class_obj_subsys.kset.subsys) + class_obj_subsys.kset.subsys = &class_obj_subsys; + return 0; +} +EXPORT_SYMBOL(class_create_file); +EXPORT_SYMBOL(class_remove_file); +EXPORT_SYMBOL(class_register); +EXPORT_SYMBOL(class_unregister); +EXPORT_SYMBOL(class_get); +EXPORT_SYMBOL(class_put); + +EXPORT_SYMBOL(class_device_register); +EXPORT_SYMBOL(class_device_unregister); +EXPORT_SYMBOL(class_device_initialize); +EXPORT_SYMBOL(class_device_add); +EXPORT_SYMBOL(class_device_del); +EXPORT_SYMBOL(class_device_get); +EXPORT_SYMBOL(class_device_put); +EXPORT_SYMBOL(class_device_create_file); +EXPORT_SYMBOL(class_device_remove_file); + +EXPORT_SYMBOL(class_interface_register); +EXPORT_SYMBOL(class_interface_unregister); diff --git a/drivers/base/core.c b/drivers/base/core.c index 0374b33562cd..8c34057ea3c3 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -185,7 +185,6 @@ void device_initialize(struct device *dev) INIT_LIST_HEAD(&dev->children); INIT_LIST_HEAD(&dev->driver_list); INIT_LIST_HEAD(&dev->bus_list); - INIT_LIST_HEAD(&dev->class_list); } /** @@ -235,7 +234,6 @@ int device_add(struct device *dev) if (platform_notify) platform_notify(dev); - devclass_add_device(dev); register_done: if (error && parent) put_device(parent); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index c843b73c66da..a7b123878ce4 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -82,7 +82,6 @@ void put_driver(struct device_driver * drv) int driver_register(struct device_driver * drv) { INIT_LIST_HEAD(&drv->devices); - INIT_LIST_HEAD(&drv->class_list); init_MUTEX_LOCKED(&drv->unload_sem); return bus_add_driver(drv); } |
