=================================================================== sysfs: add support for arrays of groups of attributes: ..//0/ ..//0/ ..//1/ ..//1/ ... ../// ../// Signed-off-by: Dmitry Torokhov fs/sysfs/Makefile | 2 fs/sysfs/group-array.c | 206 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sysfs.h | 18 ++++ 3 files changed, 225 insertions(+), 1 deletion(-) 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 array.o + group.o array.o group-array.o Index: dtor/include/linux/sysfs.h =================================================================== --- dtor.orig/include/linux/sysfs.h +++ dtor/include/linux/sysfs.h @@ -33,6 +33,11 @@ struct enumerated_attr { ssize_t (*store)(struct kobject *, unsigned int index, const char *, size_t); }; +struct attribute_group_array { + char * name; + struct enumerated_attr * attrs; +}; + /** * Use these macros to make defining attributes easier. See include/linux/device.h * for examples.. @@ -122,6 +127,9 @@ void sysfs_remove_group(struct kobject * int sysfs_create_array(struct kobject *, const struct enumerated_attr *, unsigned int); void sysfs_remove_array(struct kobject *, const struct enumerated_attr *); +int sysfs_create_group_array(struct kobject *, const struct attribute_group_array *, unsigned int); +void sysfs_remove_group_array(struct kobject *, const struct attribute_group_array *); + #else /* CONFIG_SYSFS */ static inline int sysfs_create_dir(struct kobject * k) @@ -195,6 +203,16 @@ static inline void sysfs_remove_array(st ; } +static inline int sysfs_create_group_array(struct kobject * k, const struct attribute_group_array * a, unsigned int n) +{ + return 0; +} + +static inline void sysfs_remove_group_array(struct kobject * k, const struct attribute_group_array * a) +{ + ; +} + #endif /* CONFIG_SYSFS */ #endif /* _SYSFS_H_ */ Index: dtor/fs/sysfs/group-array.c =================================================================== --- /dev/null +++ dtor/fs/sysfs/group-array.c @@ -0,0 +1,206 @@ +/* + * fs/sysfs/group-array.c - Operations for adding/removing arrays of groups + * of attributes. + * + * Copyright (c) 2005 Dmitry Torokhov + * + * This file is released under the GPL v2. + * + */ + +#include +#include +#include +#include "sysfs.h" + +#define to_enumerated_attr(obj) container_of(obj, struct enumerated_attr, attr) + +struct attr_group { + struct kobject kobj; + struct kobject *attr_owner; + unsigned int idx; +}; +#define to_attr_group(obj) container_of(obj, struct attr_group, kobj) + +struct attr_array { + struct kobject kobj; + unsigned int n_elements; + struct enumerated_attr *attrs; +}; +#define to_attr_array(obj) container_of(obj, struct attr_array, kobj) + +static ssize_t +attr_group_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct enumerated_attr *eattr = to_enumerated_attr(attr); + struct attr_group *group = to_attr_group(kobj); + ssize_t ret = 0; + + if (eattr->show) + ret = eattr->show(group->attr_owner, group->idx, buf); + + return ret; +} + +static ssize_t +attr_group_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct enumerated_attr *eattr = to_enumerated_attr(attr); + struct attr_group *group = to_attr_group(kobj); + ssize_t ret = 0; + + if (eattr->store) + ret = eattr->store(group->attr_owner, group->idx, buf, count); + + return ret; +} + +static struct sysfs_ops attr_group_sysfs_ops = { + .show = attr_group_show, + .store = attr_group_store, +}; + +static void attr_group_release(struct kobject *kobj) +{ + struct attr_group *group = to_attr_group(kobj); + + kobject_put(group->attr_owner); + kfree(group); +} + +static struct kobj_type ktype_attr_group = { + .release = attr_group_release, + .sysfs_ops = &attr_group_sysfs_ops, +}; + +static void attr_array_release(struct kobject *kobj) +{ + kfree(to_attr_array(kobj)); +} + +static struct kobj_type ktype_attr_array = { + .release = attr_array_release, +}; + +static int create_group(struct attr_array *array, unsigned int idx) +{ + struct attr_group *group; + struct enumerated_attr *eattr, *eattr2; + int err; + + group = kmalloc(sizeof(struct attr_group), GFP_KERNEL); + if (!group) + return -ENOMEM; + + memset(group, 0, sizeof(struct attr_group)); + kobject_set_name(&group->kobj, "%d", idx); + group->kobj.ktype = &ktype_attr_group; + group->kobj.parent = &array->kobj; + group->attr_owner = kobject_get(array->kobj.parent); + group->idx = idx; + + err = kobject_register(&group->kobj); + if (err) + goto fail1; + + for (eattr = array->attrs; attr_name(*eattr); eattr++) { + err = sysfs_create_file(&group->kobj, &eattr->attr); + if (err) + goto fail2; + } + + return 0; + + fail2: for (eattr2 = array->attrs; eattr2 != eattr; eattr2++) + sysfs_remove_file(&group->kobj, &eattr2->attr); + fail1: kfree(group); + return err; +} + +static void remove_group(struct attr_array *array, unsigned int idx) +{ + struct attr_group *group; + struct enumerated_attr *eattr; + struct dentry *dir; + char name[4]; + + snprintf(name, sizeof(name), "%d", idx); + dir = sysfs_get_dentry(array->kobj.dentry, name); + if (dir) { + group = to_attr_group(to_kobj(dir)); + for (eattr = array->attrs; attr_name(*eattr); eattr++) + sysfs_remove_file(&group->kobj, &eattr->attr); + kobject_unregister(&group->kobj); + } + + dput(dir); +} + +int sysfs_create_group_array(struct kobject *kobj, + const struct attribute_group_array *a, + unsigned int n_elements) +{ + struct attr_array *array; + unsigned int i; + int error; + + BUG_ON(!kobj || !kobj->dentry); + BUG_ON(!a->name); + + if (n_elements > 999) + return -EINVAL; + + array = kmalloc(sizeof(struct attr_array), GFP_KERNEL); + if (!array) + return -ENOMEM; + + memset(array, 0, sizeof(struct attr_array)); + array->attrs = a->attrs; + array->n_elements = n_elements; + + array->kobj.ktype = &ktype_attr_array; + array->kobj.parent = kobj; + kobject_set_name(&array->kobj, a->name); + + error = kobject_register(&array->kobj); + if (error) + goto fail; + + for (i = 0; i < n_elements; i++) { + error = create_group(array, i); + if (error) { + while (--i) + remove_group(array, i); + goto fail; + } + } + + return 0; + + fail: + kfree(array); + return error; +} + +void sysfs_remove_group_array(struct kobject *kobj, + const struct attribute_group_array *a) +{ + struct attr_array *array; + struct dentry *dir; + unsigned int i; + + dir = sysfs_get_dentry(kobj->dentry, a->name); + + if (dir) { + array = to_attr_array(to_kobj(dir)); + for (i = 0; i < array->n_elements; i++) + remove_group(array, i); + kobject_unregister(&array->kobj); + } + + dput(dir); +} + +EXPORT_SYMBOL_GPL(sysfs_create_group_array); +EXPORT_SYMBOL_GPL(sysfs_remove_group_array);