lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Message-ID: <0fafcb83-0b58-4dbd-8c13-7bdec1c78abc@oracle.com> Date: Sun, 6 Jul 2025 14:58:20 +0530 From: ALOK TIWARI <alok.a.tiwari@...cle.com> To: Kurt Borja <kuurtb@...il.com>, Hans de Goede <hdegoede@...hat.com>, Ilpo Järvinen <ilpo.jarvinen@...ux.intel.com>, Thomas Weißschuh <linux@...ssschuh.net>, Joshua Grisham <josh@...huagrisham.com>, Mark Pearson <mpearson-lenovo@...ebb.ca>, Armin Wolf <W_Armin@....de>, Mario Limonciello <mario.limonciello@....com> Cc: Antheas Kapenekakis <lkml@...heas.dev>, "Derek J. Clark" <derekjohn.clark@...il.com>, Prasanth Ksr <prasanth.ksr@...l.com>, Jorge Lopez <jorge.lopez2@...com>, platform-driver-x86@...r.kernel.org, linux-kernel@...r.kernel.org, Dell.Client.Kernel@...l.com Subject: Re: [PATCH v5 2/6] platform/x86: firmware_attributes_class: Add high level API for the attributes interface On 7/5/2025 9:03 AM, Kurt Borja wrote: > Add high level API to aid in the creation of attribute groups attached > to the `attrs_kobj` (per ABI specification). > > This new API lets users configure each group, either statically or > dynamically through a (per type) data struct and then create this group > through the generic fwat_create_group() macro. > > Signed-off-by: Kurt Borja <kuurtb@...il.com> > --- > drivers/platform/x86/firmware_attributes_class.c | 532 +++++++++++++++++++++++ > drivers/platform/x86/firmware_attributes_class.h | 335 ++++++++++++++ > 2 files changed, 867 insertions(+) > > diff --git a/drivers/platform/x86/firmware_attributes_class.c b/drivers/platform/x86/firmware_attributes_class.c > index 290364202cce64bb0e9046e0b2bbb8d85e2cbc6f..96473b3b1a2a87cf21a6e2a9a14d72ae322c94ae 100644 > --- a/drivers/platform/x86/firmware_attributes_class.c > +++ b/drivers/platform/x86/firmware_attributes_class.c > @@ -10,13 +10,67 @@ > #include <linux/module.h> > #include <linux/slab.h> > #include <linux/types.h> > +#include <linux/string_choices.h> > #include "firmware_attributes_class.h" > > +#define FWAT_TYPE_NONE -1 > + > +#define to_fwat_bool_data(_c) \ > + container_of_const(_c, struct fwat_bool_data, group) > +#define to_fwat_enum_data(_c) \ > + container_of_const(_c, struct fwat_enum_data, group) > +#define to_fwat_int_data(_c) \ > + container_of_const(_c, struct fwat_int_data, group) > +#define to_fwat_str_data(_c) \ > + container_of_const(_c, struct fwat_str_data, group) > + > +struct fwat_attribute { > + struct attribute attr; > + ssize_t (*show)(struct kobject *kobj, struct fwat_attribute *attr, > + char *buf); > + ssize_t (*store)(struct kobject *kobj, struct fwat_attribute *attr, > + const char *buf, size_t count); > + int type; > +}; > + > +#define to_fwat_attribute(_a) \ > + container_of_const(_a, struct fwat_attribute, attr) > + > +#define __FWAT_ATTR(_name, _mode, _show, _store, _type) \ > + { \ > + .attr = { .name = __stringify(_name), .mode = _mode }, \ > + .show = _show, .store = _store, .type = _type, \ > + } > + > +#define FWAT_ATTR_RO(_prefix, _name, _show, _type) \ > + static struct fwat_attribute fwat_##_prefix##_##_name##_attr = \ > + __FWAT_ATTR(_name, 0444, _show, NULL, _type) > + > +#define FWAT_ATTR_RW(_prefix, _name, _show, _store, _type) \ > + static struct fwat_attribute fwat_##_prefix##_##_name##_attr = \ > + __FWAT_ATTR(_name, 0644, _show, _store, _type) > + > +struct fwat_group { > + const struct fwat_group_data *data; > + struct device *dev; > + struct kobject kobj; > +}; > + > +#define kobj_to_fwat_group(_k) \ > + container_of_const(_k, struct fwat_group, kobj) > + > const struct class firmware_attributes_class = { > .name = "firmware-attributes", > }; > EXPORT_SYMBOL_GPL(firmware_attributes_class); > > +static const char * const fwat_type_labels[] = { > + [fwat_group_boolean] = "boolean", > + [fwat_group_enumeration] = "enumeration", > + [fwat_group_integer] = "integer", > + [fwat_group_string] = "string", > +}; > + > static void fwat_device_release(struct device *dev) > { > struct fwat_device *fadev = to_fwat_device(dev); > @@ -24,6 +78,483 @@ static void fwat_device_release(struct device *dev) > kfree(fadev); > } > > +static ssize_t > +type_show(struct kobject *kobj, struct fwat_attribute *attr, char *buf) > +{ > + return sysfs_emit(buf, "%s\n", fwat_type_labels[attr->type]); > +} > + > +static ssize_t > +display_name_show(struct kobject *kobj, struct fwat_attribute *attr, char *buf) > +{ > + struct fwat_group *group = kobj_to_fwat_group(kobj); > + const char *disp_name = group->data->display_name; > + > + if (!disp_name) > + return -EOPNOTSUPP; > + > + return sysfs_emit(buf, "%s\n", disp_name); > +} > + > +static ssize_t > +display_name_language_code_show(struct kobject *kobj, struct fwat_attribute *attr, > + char *buf) > +{ > + struct fwat_group *group = kobj_to_fwat_group(kobj); > + const char *lang_code = group->data->language_code; > + > + if (!lang_code) > + return -EOPNOTSUPP; > + > + return sysfs_emit(buf, "%s\n", lang_code); > +} > + > +static ssize_t > +bool_group_show(struct kobject *kobj, struct fwat_attribute *attr, char *buf) > +{ > + const struct fwat_group *group = kobj_to_fwat_group(kobj); > + const struct fwat_bool_data *data = to_fwat_bool_data(group->data); > + bool val; > + int ret; > + > + /* show_override does not affect current_value */ > + if (data->group.show_override && attr->type != fwat_bool_current_value) > + return data->group.show_override(group->dev, attr->type, buf); > + > + switch (attr->type) { > + case fwat_bool_current_value: > + ret = data->read(group->dev, data->group.id, &val); > + if (ret < 0) > + return ret; > + break; > + case fwat_bool_default_value: > + val = data->default_val; > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + return sysfs_emit(buf, "%s\n", str_yes_no(val)); > +} > + > +static ssize_t > +bool_group_store(struct kobject *kobj, struct fwat_attribute *attr, const char *buf, > + size_t count) > +{ > + const struct fwat_group *group = kobj_to_fwat_group(kobj); > + const struct fwat_bool_data *data = to_fwat_bool_data(group->data); > + bool val; > + int ret; > + > + ret = kstrtobool(buf, &val); > + if (ret) > + return ret; > + > + ret = data->write(group->dev, data->group.id, val); > + if (ret) > + return ret; > + > + return count; > +} > + > +static ssize_t > +enum_group_show(struct kobject *kobj, struct fwat_attribute *attr, char *buf) > +{ > + const struct fwat_group *group = kobj_to_fwat_group(kobj); > + const struct fwat_enum_data *data = to_fwat_enum_data(group->data); > + int val_idx, sz = 0; > + int ret; > + > + /* show_override does not affect current_value */ > + if (data->group.show_override && attr->type != fwat_enum_current_value) > + return data->group.show_override(group->dev, attr->type, buf); > + > + switch (attr->type) { > + case fwat_enum_current_value: > + ret = data->read(group->dev, data->group.id, &val_idx); > + if (ret < 0) > + return ret; > + break; > + case fwat_enum_default_value: > + val_idx = data->default_idx; > + break; > + case fwat_enum_possible_values: > + sz += sysfs_emit_at(buf, sz, "%s", data->possible_vals[0]); > + for (unsigned int i = 1; data->possible_vals[i]; i++) > + sz += sysfs_emit_at(buf, sz, ";%s", data->possible_vals[i]); > + sz += sysfs_emit_at(buf, sz, "\n"); > + return sz; > + default: > + return -EOPNOTSUPP; > + } > + > + return sysfs_emit(buf, "%s\n", data->possible_vals[val_idx]); > +} > + > +static ssize_t > +enum_group_store(struct kobject *kobj, struct fwat_attribute *attr, const char *buf, > + size_t count) > +{ > + const struct fwat_group *group = kobj_to_fwat_group(kobj); > + const struct fwat_enum_data *data = to_fwat_enum_data(group->data); > + int val_idx; > + int ret; > + > + val_idx = __sysfs_match_string(data->possible_vals, -1, buf); > + if (val_idx < 0) > + return val_idx; > + > + ret = data->write(group->dev, data->group.id, val_idx); > + if (ret) > + return ret; > + > + return count; > +} > + > +static ssize_t > +int_group_show(struct kobject *kobj, struct fwat_attribute *attr, char *buf) > +{ > + const struct fwat_group *group = kobj_to_fwat_group(kobj); > + const struct fwat_int_data *data = to_fwat_int_data(group->data); > + long val; > + int ret; > + > + /* show_override does not affect current_value */ > + if (data->group.show_override && attr->type != fwat_int_current_value) > + return data->group.show_override(group->dev, attr->type, buf); > + > + switch (attr->type) { > + case fwat_int_current_value: > + ret = data->read(group->dev, data->group.id, &val); > + if (ret < 0) > + return ret; > + break; > + case fwat_int_default_value: > + val = data->default_val; > + break; > + case fwat_int_min_value: > + val = data->min_val; > + break; > + case fwat_int_max_value: > + val = data->max_val; > + break; > + case fwat_int_scalar_increment: > + val = data->increment; > + break; > + default: > + return -EOPNOTSUPP; > + } > + > + return sysfs_emit(buf, "%ld\n", val); > +} > + > +static ssize_t > +int_group_store(struct kobject *kobj, struct fwat_attribute *attr, const char *buf, > + size_t count) > +{ > + const struct fwat_group *group = kobj_to_fwat_group(kobj); > + const struct fwat_int_data *data = to_fwat_int_data(group->data); > + long val; > + int ret; > + > + ret = kstrtol(buf, 0, &val); > + if (ret) > + return ret; > + > + ret = data->write(group->dev, data->group.id, val); > + if (ret) > + return ret; > + > + return count; > +} > + > +static ssize_t > +str_group_show(struct kobject *kobj, struct fwat_attribute *attr, char *buf) > +{ > + const struct fwat_group *group = kobj_to_fwat_group(kobj); > + const struct fwat_str_data *data = to_fwat_str_data(group->data); > + const char *val; > + long len; > + int ret; > + > + /* show_override does not affect current_value */ > + if (data->group.show_override && attr->type != fwat_bool_current_value) fwat_bool_current_value ? or str > + return data->group.show_override(group->dev, attr->type, buf); > + > + switch (attr->type) { > + case fwat_str_current_value: > + ret = data->read(group->dev, data->group.id, &val); > + if (ret < 0) > + return ret; > + break; > + case fwat_str_default_value: > + val = data->default_val; > + break; > + case fwat_str_min_length: > + len = data->min_len; > + return sysfs_emit(buf, "%ld\n", len); > + case fwat_str_max_length: > + len = data->max_len; > + return sysfs_emit(buf, "%ld\n", len); > + default: > + return -EOPNOTSUPP; > + } > + > + return sysfs_emit(buf, "%s\n", val); > +} > + > +static ssize_t > +str_group_store(struct kobject *kobj, struct fwat_attribute *attr, const char *buf, > + size_t count) > +{ > + const struct fwat_group *group = kobj_to_fwat_group(kobj); > + const struct fwat_str_data *data = to_fwat_str_data(group->data); > + int ret; > + > + ret = data->write(group->dev, data->group.id, buf); > + if (ret) > + return ret; > + > + return count; > +} > + > +FWAT_ATTR_RO(all, display_name, display_name_show, FWAT_TYPE_NONE); > +FWAT_ATTR_RO(all, display_name_language_code, display_name_language_code_show, FWAT_TYPE_NONE); > + > +FWAT_ATTR_RO(bool, type, type_show, fwat_group_boolean); > +FWAT_ATTR_RW(bool, current_value, bool_group_show, bool_group_store, fwat_bool_current_value); > +FWAT_ATTR_RO(bool, default_value, bool_group_show, fwat_bool_default_value); > + > +FWAT_ATTR_RO(enum, type, type_show, fwat_group_enumeration); > +FWAT_ATTR_RW(enum, current_value, enum_group_show, enum_group_store, fwat_enum_current_value); > +FWAT_ATTR_RO(enum, default_value, enum_group_show, fwat_enum_default_value); > +FWAT_ATTR_RO(enum, possible_values, enum_group_show, fwat_enum_possible_values); > + > +FWAT_ATTR_RO(int, type, type_show, fwat_group_integer); > +FWAT_ATTR_RW(int, current_value, int_group_show, int_group_store, fwat_int_current_value); > +FWAT_ATTR_RO(int, default_value, int_group_show, fwat_int_default_value); > +FWAT_ATTR_RO(int, min_value, int_group_show, fwat_int_min_value); > +FWAT_ATTR_RO(int, max_value, int_group_show, fwat_int_max_value); > +FWAT_ATTR_RO(int, scalar_increment, int_group_show, fwat_int_scalar_increment); > + > +FWAT_ATTR_RO(str, type, type_show, fwat_group_string); > +FWAT_ATTR_RW(str, current_value, str_group_show, str_group_store, fwat_int_current_value); wrong enum fwat_int_current_value -> fwat_str_current_value > +FWAT_ATTR_RO(str, default_value, str_group_show, fwat_str_default_value); > +FWAT_ATTR_RO(str, min_length, str_group_show, fwat_str_min_length); > +FWAT_ATTR_RO(str, max_length, str_group_show, fwat_str_max_length); > + > +static struct attribute *fwat_bool_attrs[] = { > + &fwat_bool_type_attr.attr, > + &fwat_all_display_name_attr.attr, > + &fwat_all_display_name_language_code_attr.attr, > + &fwat_bool_current_value_attr.attr, > + &fwat_bool_default_value_attr.attr, > + NULL > +}; > + > +static struct attribute *fwat_enum_attrs[] = { > + &fwat_enum_type_attr.attr, > + &fwat_all_display_name_attr.attr, > + &fwat_all_display_name_language_code_attr.attr, > + &fwat_enum_current_value_attr.attr, > + &fwat_enum_default_value_attr.attr, > + &fwat_enum_possible_values_attr.attr, > + NULL > +}; > + > +static struct attribute *fwat_int_attrs[] = { > + &fwat_int_type_attr.attr, > + &fwat_all_display_name_attr.attr, > + &fwat_all_display_name_language_code_attr.attr, > + &fwat_int_current_value_attr.attr, > + &fwat_int_default_value_attr.attr, > + &fwat_int_min_value_attr.attr, > + &fwat_int_max_value_attr.attr, > + &fwat_int_scalar_increment_attr.attr, > + NULL > +}; > + > +static struct attribute *fwat_str_attrs[] = { > + &fwat_str_type_attr.attr, > + &fwat_all_display_name_attr.attr, > + &fwat_all_display_name_language_code_attr.attr, > + &fwat_str_current_value_attr.attr, > + &fwat_str_default_value_attr.attr, > + &fwat_str_min_length_attr.attr, > + &fwat_str_max_length_attr.attr, > + NULL > +}; > + > +static umode_t fwat_attr_visible(struct kobject *kobj, struct attribute *attr, int n) > +{ > + struct fwat_attribute *fwat_attr = to_fwat_attribute(attr); > + struct fwat_group *group = kobj_to_fwat_group(kobj); > + const struct fwat_group_data *data = group->data; > + > + /* The `type` attribute is always first */ > + if (n == 0) > + return attr->mode; > + > + if (attr == &fwat_all_display_name_attr.attr) > + return data->display_name ? attr->mode : 0; > + > + if (attr == &fwat_all_display_name_language_code_attr.attr) > + return data->language_code ? attr->mode : 0; > + > + /* The `current_value` attribute always has type == 0 */ > + if (!fwat_attr->type) > + return data->mode; > + > + return test_bit(fwat_attr->type, &data->fattrs) ? attr->mode : 0; > +} > + > +static umode_t fwat_group_visible(struct kobject *kobj) > +{ > + return true; > +} > + > +DEFINE_SYSFS_GROUP_VISIBLE(fwat); > + > +static const struct attribute_group fwat_bool_group = { > + .attrs = fwat_bool_attrs, > + .is_visible = SYSFS_GROUP_VISIBLE(fwat), > +}; > +__ATTRIBUTE_GROUPS(fwat_bool); > + > +static const struct attribute_group fwat_enum_group = { > + .attrs = fwat_enum_attrs, > + .is_visible = SYSFS_GROUP_VISIBLE(fwat), > +}; > +__ATTRIBUTE_GROUPS(fwat_enum); > + > +static const struct attribute_group fwat_int_group = { > + .attrs = fwat_int_attrs, > + .is_visible = SYSFS_GROUP_VISIBLE(fwat), > +}; > +__ATTRIBUTE_GROUPS(fwat_int); > + > +static const struct attribute_group fwat_str_group = { > + .attrs = fwat_str_attrs, > + .is_visible = SYSFS_GROUP_VISIBLE(fwat), > +}; > +__ATTRIBUTE_GROUPS(fwat_str); > + > +static ssize_t > +fwat_attr_sysfs_show(struct kobject *kobj, struct attribute *attr, char *buf) > +{ > + struct fwat_attribute *fwat_attr = to_fwat_attribute(attr); > + > + if (!fwat_attr->show) > + return -EOPNOTSUPP; > + > + return fwat_attr->show(kobj, fwat_attr, buf); > +} > + > +static ssize_t > +fwat_attr_sysfs_store(struct kobject *kobj, struct attribute *attr, const char *buf, > + size_t count) > +{ > + struct fwat_attribute *fwat_attr = to_fwat_attribute(attr); > + > + if (!fwat_attr->show) > + return -EOPNOTSUPP; check for fwat_attr->store ? > + > + return fwat_attr->store(kobj, fwat_attr, buf, count); > +} > + > +static void fwat_group_release(struct kobject *kobj) > +{ > + struct fwat_group *group = kobj_to_fwat_group(kobj); > + > + kfree(group); > +} > + > +static const struct sysfs_ops fwat_attr_sysfs_ops = { > + .show = fwat_attr_sysfs_show, > + .store = fwat_attr_sysfs_store, > +}; > + > +static const struct kobj_type fwat_boolean_ktype = { > + .sysfs_ops = &fwat_attr_sysfs_ops, > + .release = fwat_group_release, > + .default_groups = fwat_bool_groups, > +}; > + > +static const struct kobj_type fwat_enumeration_ktype = { > + .sysfs_ops = &fwat_attr_sysfs_ops, > + .release = fwat_group_release, > + .default_groups = fwat_enum_groups, > +}; > + > +static const struct kobj_type fwat_integer_ktype = { > + .sysfs_ops = &fwat_attr_sysfs_ops, > + .release = fwat_group_release, > + .default_groups = fwat_int_groups, > +}; > + > +static const struct kobj_type fwat_string_ktype = { > + .sysfs_ops = &fwat_attr_sysfs_ops, > + .release = fwat_group_release, > + .default_groups = fwat_str_groups, > +}; > + > +static int __fwat_create_group(struct fwat_device *fadev, const struct kobj_type *ktype, > + const struct fwat_group_data *data) > +{ > + struct fwat_group *group; > + int ret; > + > + group = kzalloc(sizeof(*group), GFP_KERNEL); > + if (!group) > + return -ENOMEM; > + > + group->dev = &fadev->dev; > + group->data = data; > + > + group->kobj.kset = fadev->attrs_kset; > + ret = kobject_init_and_add(&group->kobj, ktype, NULL, "%s", data->name); > + if (ret) { > + kobject_put(&group->kobj); > + return ret; > + } > + > + kobject_uevent(&group->kobj, KOBJ_ADD); > + > + return 0; > +} > + > +static void fwat_remove_auto_groups(struct fwat_device *fadev) > +{ > + struct kobject *pos, *n; > + > + list_for_each_entry_safe(pos, n, &fadev->attrs_kset->list, entry) > + kobject_put(pos); > +} > + > +int fwat_create_bool_group(struct fwat_device *fadev, const struct fwat_bool_data *data) > +{ > + return __fwat_create_group(fadev, &fwat_boolean_ktype, &data->group); > +} > +EXPORT_SYMBOL_GPL(fwat_create_bool_group); > + > +int fwat_create_enum_group(struct fwat_device *fadev, const struct fwat_enum_data *data) > +{ > + return __fwat_create_group(fadev, &fwat_enumeration_ktype, &data->group); > +} > +EXPORT_SYMBOL_GPL(fwat_create_enum_group); > + > +int fwat_create_int_group(struct fwat_device *fadev, const struct fwat_int_data *data) > +{ > + return __fwat_create_group(fadev, &fwat_integer_ktype, &data->group); > +} > +EXPORT_SYMBOL_GPL(fwat_create_int_group); > + > +int fwat_create_str_group(struct fwat_device *fadev, const struct fwat_str_data *data) > +{ > + return __fwat_create_group(fadev, &fwat_string_ktype, &data->group); > +} > +EXPORT_SYMBOL_GPL(fwat_create_str_group); > + > /** > * fwat_device_register - Create and register a firmware-attributes class > * device > @@ -89,6 +620,7 @@ void fwat_device_unregister(struct fwat_device *fadev) > if (!fadev) > return; > > + fwat_remove_auto_groups(fadev); > sysfs_remove_groups(&fadev->attrs_kset->kobj, fadev->groups); > kset_unregister(fadev->attrs_kset); > device_unregister(&fadev->dev); > diff --git a/drivers/platform/x86/firmware_attributes_class.h b/drivers/platform/x86/firmware_attributes_class.h > index 048fd0904f767357ef856e687ec4cf3260016ec6..e8868ce05b595eda94a98975428391b9f9341e3d 100644 > --- a/drivers/platform/x86/firmware_attributes_class.h > +++ b/drivers/platform/x86/firmware_attributes_class.h > @@ -10,6 +10,7 @@ > #include <linux/device/class.h> > #include <linux/kobject.h> > #include <linux/sysfs.h> > +#include <linux/list.h> > > extern const struct class firmware_attributes_class; > > @@ -27,6 +28,340 @@ struct fwat_device { > > #define to_fwat_device(_d) container_of_const(_d, struct fwat_device, dev) > > +enum fwat_group_type { > + fwat_group_boolean, > + fwat_group_enumeration, > + fwat_group_integer, > + fwat_group_string, > +}; > + > +enum fwat_bool_attrs { > + fwat_bool_current_value, > + fwat_bool_default_value, > + fwat_bool_attrs_last > +}; > + > +#define FWAT_BOOL_CURRENT_VALUE BIT(fwat_bool_current_value) > +#define FWAT_BOOL_DEFAULT_VALUE BIT(fwat_bool_default_value) > +#define FWAT_BOOL_ALL_ATTRS GENMASK(fwat_bool_attrs_last, 0) > + > +enum fwat_enum_attrs { > + fwat_enum_current_value, > + fwat_enum_default_value, > + fwat_enum_possible_values, > + fwat_enum_attrs_last > +}; > + > +#define FWAT_ENUM_CURRENT_VALUE BIT(fwat_enum_current_value) > +#define FWAT_ENUM_DEFAULT_VALUE BIT(fwat_enum_default_value) > +#define FWAT_ENUM_POSSIBLE_VALUES BIT(fwat_enum_possible_values) > +#define FWAT_ENUM_ALL_ATTRS GENMASK(fwat_enum_attrs_last, 0) > + > +enum fwat_int_attrs { > + fwat_int_current_value, > + fwat_int_default_value, > + fwat_int_min_value, > + fwat_int_max_value, > + fwat_int_scalar_increment, > + fwat_int_attrs_last > +}; > + > +#define FWAT_INT_CURRENT_VALUE BIT(fwat_int_current_value) > +#define FWAT_INT_DEFAULT_VALUE BIT(fwat_int_default_value) > +#define FWAT_INT_MIN_VALUE BIT(fwat_int_min_value) > +#define FWAT_INT_MAX_VALUE BIT(fwat_int_max_value) > +#define FWAT_INT_SCALAR_INCREMENT BIT(fwat_int_scalar_increment) > +#define FWAT_INT_ALL_ATTRS GENMASK(fwat_int_attrs_last, 0) > + > +enum fwat_str_attrs { > + fwat_str_current_value, > + fwat_str_default_value, > + fwat_str_min_length, > + fwat_str_max_length, > + fwat_str_attrs_last > +}; > + > +#define FWAT_STR_CURRENT_VALUE BIT(fwat_str_current_value) > +#define FWAT_STR_DEFAULT_VALUE BIT(fwat_str_default_value) > +#define FWAT_STR_MIN_LENGTH BIT(fwat_str_min_length) > +#define FWAT_STR_MAX_LENGTH BIT(fwat_str_max_length) > +#define FWAT_STR_ALL_ATTRS GENMASK(fwat_str_attrs_last, 0) > + > +static_assert(fwat_bool_current_value == 0); > +static_assert(fwat_enum_current_value == 0); > +static_assert(fwat_int_current_value == 0); > +static_assert(fwat_str_current_value == 0); > + > +/** > + * struct fwat_group_data - Data struct common between group types > + * @id: Group ID defined by the user. > + * @name: Name of the group. > + * @display_name: Name showed in the display_name attribute. (Optional) > + * @language_code: Language code showed in the display_name_language_code > + * attribute. (Optional) > + * @mode: Mode for the current_value attribute. All other attributes will have > + * 0444 permissions. > + * @fattrs: Bitmap of selected attributes for this group type. > + * @show_override: Custom show method for attributes in this group, except for > + * the current_value attribute, for which the a `read` callback > + * will still be used. (Optional) > + * > + * NOTE: This struct is not meant to be defined directly. It is supposed to be > + * embedded and defined as part of fwat_[type]_data structs. > + */ > +struct fwat_group_data { > + long id; > + umode_t mode; > + const char *name; > + const char *display_name; > + const char *language_code; > + unsigned long fattrs; > + ssize_t (*show_override)(struct device *dev, int type, char *buf); > +}; > + > +/** > + * struct fwat_bool_data - Data struct for the boolean group type > + * @read: Read callback for the current_value attribute. > + * @write: Write callback for the current_value attribute. > + * @default_val: Default value. > + * @group: Group data. > + */ > +struct fwat_bool_data { > + int (*read)(struct device *dev, long id, bool *val); > + int (*write)(struct device *dev, long id, bool val); > + bool default_val; > + struct fwat_group_data group; > +}; > + > +/** > + * struct fwat_enum_data - Data struct for the enumeration group type > + * @read: Read callback for the current_value attribute. > + * @write: Write callback for the current_value attribute. > + * @default_idx: Index of the default value in the @possible_vals array. > + * @possible_vals: Array of possible value strings for this group type. > + * @group: Group data. > + * > + * NOTE: The `val_idx` argument in the @write callback is guaranteed to be a > + * valid (within bounds) index. However, the user is in charge of writing > + * valid indexes to the `*val_idx` argument of the @read callback. > + * Failing to do so may result in an OOB access. > + */ > +struct fwat_enum_data { > + int (*read)(struct device *dev, long id, int *val_idx); > + int (*write)(struct device *dev, long id, int val_idx); > + int default_idx; > + const char * const *possible_vals; > + struct fwat_group_data group; > +}; > + > +/** > + * struct fwat_int_data - Data struct for the integer group type > + * @read: Read callback for the current_value attribute. > + * @write: Write callback for the current_value attribute. > + * @default_val: Default value. > + * @min_val: Minimum value. > + * @max_val: Maximum value. > + * @increment: Scalar increment for this value. > + * @group: Group data. > + * > + * NOTE: The @min_val, @max_val, @increment constraints are merely informative. > + * These values are not enforced in any of the callbacks. > + */ > +struct fwat_int_data { > + int (*read)(struct device *dev, long id, long *val); > + int (*write)(struct device *dev, long id, long val); > + long default_val; > + long min_val; > + long max_val; > + long increment; > + struct fwat_group_data group; > +}; > + > +/** > + * struct fwat_str_data - Data struct for the string group type > + * @read: Read callback for the current_value attribute. > + * @write: Write callback for the current_value attribute. > + * @default_val: Default value. > + * @min_len: Minimum string length. > + * @max_len: Maximum string length. > + * @group: Group data. > + * > + * NOTE: The @min_len, @max_len constraints are merely informative. These > + * values are not enforced in any of the callbacks. > + */ > +struct fwat_str_data { > + int (*read)(struct device *dev, long id, const char **buf); > + int (*write)(struct device *dev, long id, const char *buf); > + const char *default_val; > + long min_len; > + long max_len; > + struct fwat_group_data group; > +}; > + > +#define __FWAT_GROUP(_name, _disp_name, _mode, _fattrs) \ > + { .name = __stringify(_name), .display_name = _disp_name, .mode = _mode, .fattrs = _fattrs } > + > +/** > + * DEFINE_FWAT_BOOL_GROUP - Convenience macro to quickly define an static an static -> a static (all place) > + * struct fwat_bool_data instance > + * @_name: Name of the group. > + * @_disp_name: Name showed in the display_name attribute. (Optional) showed -> shown (all place) > + * @_def_val: Default value. > + * @_mode: Mode for the current_value attribute. All other attributes will have > + * 0444 permissions. > + * @_fattrs: Bitmap of selected attributes for this group type. > + * > + * `read` and `write` callbacks are required to be already defined as > + * `_name##_read` and `_name##_write` respectively. > + */ > +#define DEFINE_FWAT_BOOL_GROUP(_name, _disp_name, _def_val, _mode, _fattrs) \ > + static const struct fwat_bool_data _name##_group_data = { \ > + .read = _name##_read, \ > + .write = _name##_write, \ > + .default_val = _def_val, \ > + .group = __FWAT_GROUP(_name, _disp_name, _mode, _fattrs), \ > + } > + > +/** > + * DEFINE_FWAT_ENUM_GROUP - Convenience macro to quickly define an static > + * struct fwat_enum_data instance > + * @_name: Name of the group. > + * @_disp_name: Name showed in the display_name attribute. (Optional) > + * @_def_idx: Index of the default value in the @_poss_vals array. > + * @_poss_vals: Array of possible value strings for this group type. > + * @_mode: Mode for the current_value attribute. All other attributes will have > + * 0444 permissions. > + * @_fattrs: Bitmap of selected attributes for this group type. > + * > + * `read` and `write` callbacks are required to be already defined as > + * `_name##_read` and `_name##_write` respectively. > + * > + * NOTE: The `val_idx` argument in the `write` callback is guaranteed to be a > + * valid (within bounds) index. However, the user is in charge of writing > + * valid indexes to the `*val_idx` argument of the `read` callback. > + * Failing to do so may result in an OOB access. > + */ > +#define DEFINE_FWAT_ENUM_GROUP(_name, _disp_name, _poss_vals, _def_idx, _mode, _fattrs) \ > + static const struct fwat_enum_data _name##_group_data = { \ > + .read = _name##_read, \ > + .write = _name##_write, \ > + .default_idx = _def_idx, \ > + .possible_vals = _poss_vals, \ > + .group = __FWAT_GROUP(_name, _disp_name, _mode, _fattrs), \ > + } > + > +/** > + * DEFINE_FWAT_INT_GROUP - Convenience macro to quickly define an static > + * struct fwat_int_data instance > + * @_name: Name of the group. > + * @_disp_name: Name showed in the display_name attribute. (Optional) > + * @_def_val: Default value. > + * @_min: Minimum value. > + * @_max: Maximum value. > + * @_inc: Scalar increment for this value. > + * @_mode: Mode for the current_value attribute. All other attributes will have > + * 0444 permissions. > + * @_fattrs: Bitmap of selected attributes for this group type. > + * > + * `read` and `write` callbacks are required to be already defined as > + * `_name##_read` and `_name##_write` respectively. > + * > + * NOTE: The @_min, @_max, @_inc constraints are merely informative. These > + * values are not enforced in any of the callbacks. > + */ > +#define DEFINE_FWAT_INT_GROUP(_name, _disp_name, _def_val, _min, _max, _inc, _mode, _fattrs) \ > + static const struct fwat_int_data _name##_group_data = { \ > + .read = _name##_read, \ > + .write = _name##_write, \ > + .default_val = _def_val, \ > + .min_val = _min, \ > + .max_val = _max, \ > + .increment = _inc, \ > + .group = __FWAT_GROUP(_name, _disp_name, _mode, _fattrs), \ > + } > + > +/** > + * DEFINE_FWAT_STR_GROUP - Convenience macro to quickly define an static > + * struct fwat_str_data instance > + * @_name: Name of the group. > + * @_disp_name: Name showed in the display_name attribute. (Optional) > + * @_def_val: Default value. > + * @_min: Minimum string length. > + * @_max: Maximum string length. > + * @_mode: Mode for the current_value attribute. All other attributes will have > + * 0444 permissions. > + * @_fattrs: Bitmap of selected attributes for this group type. > + * > + * `read` and `write` callbacks are required to be already defined as > + * `_name##_read` and `_name##_write` respectively. > + * > + * NOTE: The @_min, @_max constraints are merely informative. These values are > + * not enforced in any of the callbacks. > + */ > +#define DEFINE_FWAT_STR_GROUP(_name, _disp_name, _def_val, _min, _max, _mode, _fattrs) \ > + static const struct fwat_str_data _name##_group_data = { \ > + .read = _name##_read, \ > + .write = _name##_write, \ > + .default_val = _def_val, \ > + .min_len = _min, \ > + .max_len = _max, \ > + .group = __FWAT_GROUP(_name, _disp_name, _mode, _fattrs), \ > + } > + > +int fwat_create_bool_group(struct fwat_device *fadev, > + const struct fwat_bool_data *data); > +int fwat_create_enum_group(struct fwat_device *fadev, > + const struct fwat_enum_data *data); > +int fwat_create_int_group(struct fwat_device *fadev, > + const struct fwat_int_data *data); > +int fwat_create_str_group(struct fwat_device *fadev, > + const struct fwat_str_data *data); > + > +/** > + * fwat_create_group - Convenience generic macro to create a group > + * @_dev: fwat_device > + * @_data: One of fwat_{bool,enum,int,str}_data instance > + * > + * This macro (and associated functions) creates a sysfs group under the > + * 'attributes' directory, which is located in the class device root directory. > + * > + * See Documentation/ABI/testing/sysfs-class-firmware-attributes for details. > + * > + * The @_data associated with this group may be created either statically, > + * through DEFINE_FWAT_*_GROUP macros or dynamically, in which case the user > + * would have allocate and fill the struct manually. The dynamic approach should > + * be preferred when group constraints and/or visibility is decided dynamically. > + * > + * Example: > + * > + * static int stat_read(...){...}; > + * static int stat_write(...){...}; > + * > + * DEFINE_FWAT_(BOOL|ENUM|INT|STR)_GROUP(stat, ...); > + * > + * static int create_groups(struct fwat_device *fadev) > + * { > + * struct fwat_enum_data *dyn_group_data; > + * > + * dyn_group_data = kzalloc(...); > + * // Fill the data > + * ... > + * fwat_create_group(fadev, &stat_group_data); > + * fwat_create_group(fadev, &dyn_group_data); > + * fwat_create_group(...); > + * ... > + * } > + * > + * Return: 0 on success, -errno on failure > + */ > +#define fwat_create_group(_dev, _data) \ > + _Generic((_data), \ > + const struct fwat_bool_data * : fwat_create_bool_group, \ > + const struct fwat_enum_data * : fwat_create_enum_group, \ > + const struct fwat_int_data * : fwat_create_int_group, \ > + const struct fwat_str_data * : fwat_create_str_group) \ > + (_dev, _data) > + > struct fwat_device * __must_check > fwat_device_register(struct device *parent, const char *name, void *drvdata, > const struct attribute_group **groups); > Thanks Alok
Powered by blists - more mailing lists