=================================================================== sysfs: add support for attribute arrays so it is possible to create a number of similar attributes all sharing the same show and store handlers: ..//0 ..//1 ... ..// Number of attributes can be specified at run-time. Signed-off-by: Dmitry Torokhov fs/sysfs/Makefile | 2 fs/sysfs/array.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sysfs.h | 20 +++++- 3 files changed, 182 insertions(+), 2 deletions(-) Index: dtor/fs/sysfs/array.c =================================================================== --- /dev/null +++ dtor/fs/sysfs/array.c @@ -0,0 +1,162 @@ +/* + * fs/sysfs/array.c - Operations for adding/removing arrays of attributes. + * + * Copyright (c) 2005 Dmitry Torokhov + * + * This file is released under the GPL v2. + * + */ + +#include +#include +#include +#include "sysfs.h" + +struct attr_element { + struct attribute attr; + unsigned int idx; + char name[4]; +}; +#define to_attr_element(e) container_of(e, struct attr_element, attr) + +struct attr_array { + const struct enumerated_attr *base_attr; + unsigned int n_elements; + struct kobject kobj; + struct attr_element elements[0]; +}; +#define to_attr_array(obj) container_of(obj, struct attr_array, kobj) + +static ssize_t +attr_array_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct attr_element *element = to_attr_element(attr); + struct attr_array *array = to_attr_array(kobj); + ssize_t ret = 0; + + if (array->base_attr->show) + ret = array->base_attr->show(kobj->parent, element->idx, buf); + + return ret; +} + +static ssize_t +attr_array_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct attr_element *element = to_attr_element(attr); + struct attr_array *array = to_attr_array(kobj); + ssize_t ret = 0; + + if (array->base_attr->store) + ret = array->base_attr->store(kobj->parent, element->idx, buf, count); + + return ret; +} + +static struct sysfs_ops attr_array_sysfs_ops = { + .show = attr_array_show, + .store = attr_array_store, +}; + +static void attr_array_release(struct kobject *kobj) +{ + kfree(to_attr_array(kobj)); +} + +static struct kobj_type ktype_attr_array = { + .release = attr_array_release, + .sysfs_ops = &attr_array_sysfs_ops, +}; + + +static int create_array_element(struct attr_array *array, + unsigned int idx) +{ + struct attr_element *element = &array->elements[idx]; + + snprintf(element->name, sizeof(element->name), "%d", idx); + element->idx = idx; + element->attr = array->base_attr->attr; + element->attr.name = element->name; + + return sysfs_create_file(&array->kobj, &element->attr); +} + +static inline void remove_array_element(struct attr_array *array, + unsigned int idx) +{ + sysfs_remove_file(&array->kobj, &array->elements[idx].attr); +} + +int sysfs_create_array(struct kobject *kobj, + const struct enumerated_attr *attr, + unsigned int n_elements) +{ + struct attr_array *array; + size_t size; + unsigned int i; + int error; + + BUG_ON(!kobj || !kobj->dentry); + BUG_ON(!attr_name(*attr)); + + if (n_elements > 999) + return -EINVAL; + + size = sizeof(struct attr_array) + + sizeof(struct attr_element) * n_elements; + + array = kmalloc(size, GFP_KERNEL); + if (!array) + return -ENOMEM; + + memset(array, 0, size); + array->base_attr = attr; + array->n_elements = n_elements; + + array->kobj.ktype = &ktype_attr_array; + array->kobj.parent = kobj; + kobject_set_name(&array->kobj, attr_name(*attr)); + + error = kobject_register(&array->kobj); + if (error) + goto fail; + + for (i = 0; i < n_elements; i++) { + error = create_array_element(array, i); + if (error) { + while (--i) + remove_array_element(array, i); + goto fail; + } + } + + return 0; + + fail: + kfree(array); + return error; +} + +void sysfs_remove_array(struct kobject *kobj, + const struct enumerated_attr *attr) +{ + struct attr_array *array; + struct dentry *dir; + unsigned int i; + + dir = sysfs_get_dentry(kobj->dentry, attr_name(*attr)); + + if (dir) { + array = to_attr_array(to_kobj(dir)); + for (i = 0; i < array->n_elements; i++) + remove_array_element(array, i); + kobject_unregister(&array->kobj); + } + + dput(dir); +} + +EXPORT_SYMBOL_GPL(sysfs_create_array); +EXPORT_SYMBOL_GPL(sysfs_remove_array); Index: dtor/include/linux/sysfs.h =================================================================== --- dtor.orig/include/linux/sysfs.h +++ dtor/include/linux/sysfs.h @@ -26,7 +26,12 @@ struct attribute_group { struct attribute ** attrs; }; +struct enumerated_attr { + struct attribute attr; + ssize_t (*show)(struct kobject *, unsigned int index, char *); + ssize_t (*store)(struct kobject *, unsigned int index, const char *, size_t); +}; /** * Use these macros to make defining attributes easier. See include/linux/device.h @@ -102,7 +107,7 @@ sysfs_update_file(struct kobject *, cons extern void sysfs_remove_file(struct kobject *, const struct attribute *); -extern int +extern int sysfs_create_link(struct kobject * kobj, struct kobject * target, char * name); extern void @@ -114,6 +119,9 @@ int sysfs_remove_bin_file(struct kobject int sysfs_create_group(struct kobject *, const struct attribute_group *); void sysfs_remove_group(struct kobject *, const struct attribute_group *); +int sysfs_create_array(struct kobject *, const struct enumerated_attr *, unsigned int); +void sysfs_remove_array(struct kobject *, const struct enumerated_attr *); + #else /* CONFIG_SYSFS */ static inline int sysfs_create_dir(struct kobject * k) @@ -177,6 +185,16 @@ static inline void sysfs_remove_group(st ; } +static inline int sysfs_create_array(struct kobject * k, const struct enumerated_attr * a, unsigned int n) +{ + return 0; +} + +static inline void sysfs_remove_array(struct kobject * k, const struct enumerated_attr * a) +{ + ; +} + #endif /* CONFIG_SYSFS */ #endif /* _SYSFS_H_ */ Index: dtor/fs/sysfs/Makefile =================================================================== --- dtor.orig/fs/sysfs/Makefile +++ dtor/fs/sysfs/Makefile @@ -3,4 +3,4 @@ # obj-y := inode.o file.o dir.o symlink.o mount.o bin.o \ - group.o + group.o array.o