diff --git a/drivers/base/class.c b/drivers/base/class.c index 344b8cd7390156e5570f85166ad07e63867743f8..a3b006b6f2bad1088949838fbbec53b8fd145d77 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "base.h" #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) @@ -162,6 +163,51 @@ void class_unregister(struct class * cls) subsystem_unregister(&cls->subsys); } +static void class_create_release(struct class *cls) +{ + kfree(cls); +} + +static void class_device_create_release(struct class_device *class_dev) +{ + kfree(class_dev); +} + +struct class *class_create(struct module *owner, char *name) +{ + struct class *cls; + int retval; + + cls = kmalloc(sizeof(struct class), GFP_KERNEL); + if (!cls) { + retval = -ENOMEM; + goto error; + } + memset(cls, 0x00, sizeof(struct class)); + + cls->name = name; + cls->owner = owner; + cls->class_release = class_create_release; + cls->release = class_device_create_release; + + retval = class_register(cls); + if (retval) + goto error; + + return cls; + +error: + kfree(cls); + return ERR_PTR(retval); +} + +void class_destroy(struct class *cls) +{ + if ((cls == NULL) || (IS_ERR(cls))) + return; + + class_unregister(cls); +} /* Class Device Stuff */ @@ -375,7 +421,6 @@ static ssize_t show_dev(struct class_device *class_dev, char *buf) { return print_dev_t(buf, class_dev->devt); } -static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL); void class_device_initialize(struct class_device *class_dev) { @@ -412,7 +457,31 @@ int class_device_add(struct class_device *class_dev) if ((error = kobject_add(&class_dev->kobj))) goto register_done; - /* now take care of our own registration */ + /* add the needed attributes to this device */ + if (MAJOR(class_dev->devt)) { + struct class_device_attribute *attr; + attr = kmalloc(sizeof(*attr), GFP_KERNEL); + if (!attr) { + error = -ENOMEM; + kobject_del(&class_dev->kobj); + goto register_done; + } + memset(attr, sizeof(*attr), 0x00); + attr->attr.name = "dev"; + attr->attr.mode = S_IRUGO; + attr->attr.owner = parent->owner; + attr->show = show_dev; + attr->store = NULL; + class_device_create_file(class_dev, attr); + class_dev->devt_attr = attr; + } + + class_device_add_attrs(class_dev); + if (class_dev->dev) + sysfs_create_link(&class_dev->kobj, + &class_dev->dev->kobj, "device"); + + /* notify any interfaces this device is now here */ if (parent) { down(&parent->sem); list_add_tail(&class_dev->node, &parent->children); @@ -421,16 +490,8 @@ int class_device_add(struct class_device *class_dev) class_intf->add(class_dev); up(&parent->sem); } - - if (MAJOR(class_dev->devt)) - class_device_create_file(class_dev, &class_device_attr_dev); - - class_device_add_attrs(class_dev); - if (class_dev->dev) - sysfs_create_link(&class_dev->kobj, - &class_dev->dev->kobj, "device"); - kobject_hotplug(&class_dev->kobj, KOBJ_ADD); + register_done: if (error && parent) class_put(parent); @@ -444,6 +505,41 @@ int class_device_register(struct class_device *class_dev) return class_device_add(class_dev); } +struct class_device *class_device_create(struct class *cls, dev_t devt, + struct device *device, char *fmt, ...) +{ + va_list args; + struct class_device *class_dev = NULL; + int retval = -ENODEV; + + if (cls == NULL || IS_ERR(cls)) + goto error; + + class_dev = kmalloc(sizeof(struct class_device), GFP_KERNEL); + if (!class_dev) { + retval = -ENOMEM; + goto error; + } + memset(class_dev, 0x00, sizeof(struct class_device)); + + class_dev->devt = devt; + class_dev->dev = device; + class_dev->class = cls; + + va_start(args, fmt); + vsnprintf(class_dev->class_id, BUS_ID_SIZE, fmt, args); + va_end(args); + retval = class_device_register(class_dev); + if (retval) + goto error; + + return class_dev; + +error: + kfree(class_dev); + return ERR_PTR(retval); +} + void class_device_del(struct class_device *class_dev) { struct class * parent = class_dev->class; @@ -460,6 +556,11 @@ void class_device_del(struct class_device *class_dev) if (class_dev->dev) sysfs_remove_link(&class_dev->kobj, "device"); + if (class_dev->devt_attr) { + class_device_remove_file(class_dev, class_dev->devt_attr); + kfree(class_dev->devt_attr); + class_dev->devt_attr = NULL; + } class_device_remove_attrs(class_dev); kobject_hotplug(&class_dev->kobj, KOBJ_REMOVE); @@ -477,6 +578,24 @@ void class_device_unregister(struct class_device *class_dev) class_device_put(class_dev); } +void class_device_destroy(struct class *cls, dev_t devt) +{ + struct class_device *class_dev = NULL; + struct class_device *class_dev_tmp; + + down(&cls->sem); + list_for_each_entry(class_dev_tmp, &cls->children, node) { + if (class_dev_tmp->devt == devt) { + class_dev = class_dev_tmp; + break; + } + } + up(&cls->sem); + + if (class_dev) + class_device_unregister(class_dev); +} + int class_device_rename(struct class_device *class_dev, char *new_name) { int error = 0; @@ -576,6 +695,8 @@ EXPORT_SYMBOL_GPL(class_register); EXPORT_SYMBOL_GPL(class_unregister); EXPORT_SYMBOL_GPL(class_get); EXPORT_SYMBOL_GPL(class_put); +EXPORT_SYMBOL_GPL(class_create); +EXPORT_SYMBOL_GPL(class_destroy); EXPORT_SYMBOL_GPL(class_device_register); EXPORT_SYMBOL_GPL(class_device_unregister); @@ -584,6 +705,8 @@ EXPORT_SYMBOL_GPL(class_device_add); EXPORT_SYMBOL_GPL(class_device_del); EXPORT_SYMBOL_GPL(class_device_get); EXPORT_SYMBOL_GPL(class_device_put); +EXPORT_SYMBOL_GPL(class_device_create); +EXPORT_SYMBOL_GPL(class_device_destroy); EXPORT_SYMBOL_GPL(class_device_create_file); EXPORT_SYMBOL_GPL(class_device_remove_file); EXPORT_SYMBOL_GPL(class_device_create_bin_file); diff --git a/include/linux/device.h b/include/linux/device.h index fa9e6ca08f5af13b79cf5c44a5e53fca613ff70b..73250d01c01ffe2a9be04ce52631e82e61a8750a 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -143,6 +143,7 @@ extern void driver_remove_file(struct device_driver *, struct driver_attribute * */ struct class { const char * name; + struct module * owner; struct subsystem subsys; struct list_head children; @@ -185,6 +186,7 @@ struct class_device { struct kobject kobj; struct class * class; /* required */ dev_t devt; /* dev_t, creates the sysfs "dev" */ + struct class_device_attribute *devt_attr; struct device * dev; /* not necessary, but nice to have */ void * class_data; /* class-specific data */ @@ -245,6 +247,13 @@ struct class_interface { extern int class_interface_register(struct class_interface *); extern void class_interface_unregister(struct class_interface *); +extern struct class *class_create(struct module *owner, char *name); +extern void class_destroy(struct class *cls); +extern struct class_device *class_device_create(struct class *cls, dev_t devt, + struct device *device, char *fmt, ...) + __attribute__((format(printf,4,5))); +extern void class_device_destroy(struct class *cls, dev_t devt); + /* interface for class simple stuff */ extern struct class_simple *class_simple_create(struct module *owner, char *name); extern void class_simple_destroy(struct class_simple *cs);