All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API
@ 2025-06-22  0:04 Kurt Borja
  2025-06-22  0:04 ` [PATCH v3 1/6] platform/x86: firmware_attributes_class: Add device initialization methods Kurt Borja
                   ` (6 more replies)
  0 siblings, 7 replies; 12+ messages in thread
From: Kurt Borja @ 2025-06-22  0:04 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Thomas Weißschuh,
	Joshua Grisham, Mark Pearson, Armin Wolf, Mario Limonciello
  Cc: Antheas Kapenekakis, Derek J. Clark, Prasanth Ksr, Jorge Lopez,
	platform-driver-x86, linux-kernel, Dell.Client.Kernel, Kurt Borja

Hi all,

I apologize for taking so long. I've been a bit busy these last few
weeks.

After my discussion with Joshua on v2, I realized the API I made was not
ergonomic at all and it didn't exactly respond to driver needs. In this
version I tried a completely different approach and IMO it's much much
better now.

First of all I adopted standard sysfs terminology for everything. A
"firmware attribute" is just an attribute_group under the attributes/
directory so everything related to this concept is just called "group"
now. Everything refered as properties in the previous patch are now just
plain "attributes".

This new API revolves around the `fwat_{bool,enum,int,str}_data`
structs. These hold all the metadata a "firmware_attribute" of that
given type needs.

These structs also hold `read` and `write` callbacks for the
current_value attribute, because obviously that value is always dynamic.
However the rest of attributes (default_value, display_name, min, max,
etc) are constant.

In the simple case this metadata structs can be defined statically with
DEFINE_FWAT_{BOOL,ENUM,INT,STR}_GROUP() macros. However most users of
this class obtain this values dynamically so you can also define this
structs dynamically.

In the end all groups (static and dynamic) will be created using
fwat_create_group() after registering the class device.

Let me know what you think, your feedback is very appreciated :)

I do have one question for anyone interested. Should constraints over
the current_value (such as min, max, increment, etc.) be enforced at the
show/store level? i.e. before values reach read/write callbacks.

Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
Changes in v3:

[Patch 1]
- Fixed UAF in fwat_device_unregister(). Device was unregistered after
  freeing fadev.

[Patch 2]
- Patch 2 was completely replaced. A new approach for the API is taken,
  based on Joshua's suggestions.

- Link to v2: https://lore.kernel.org/r/20250517-fw-attrs-api-v2-0-fa1ab045a01c@gmail.com

Changes in v2:

[Patch 1]
 - Include kdev_t.h header

[Patch 2]
 - Use one line comments in fwat_create_attrs()
 - Check propagate errors in fwat_create_attrs()
 - Add `mode` to fwat_attr_config and related macros to let users
   configure the `current_value` attribute mode
 - Use defined structs in fwat_attr_ops instead of anonymous ones
 - Move fwat_attr_type from config to ops

[Patch 5]
 - Just transition to new API without chaing ABI

- Link to v1: https://lore.kernel.org/r/20250509-fw-attrs-api-v1-0-258afed65bfa@gmail.com

---
Kurt Borja (5):
      platform/x86: firmware_attributes_class: Add high level API for the attributes interface
      platform/x86: firmware_attributes_class: Move header to include directory
      platform/x86: samsung-galaxybook: Transition new firmware_attributes API
      Documentation: ABI: Update sysfs-class-firmware-attributes documentation
      MAINTAINERS: Add FIRMWARE ATTRIBUTES CLASS entry

Thomas Weißschuh (1):
      platform/x86: firmware_attributes_class: Add device initialization methods

 .../ABI/testing/sysfs-class-firmware-attributes    |   1 +
 MAINTAINERS                                        |   8 +
 drivers/platform/x86/dell/dell-wmi-sysman/sysman.c |   2 +-
 drivers/platform/x86/firmware_attributes_class.c   | 615 ++++++++++++++++++++-
 drivers/platform/x86/firmware_attributes_class.h   |  12 -
 drivers/platform/x86/hp/hp-bioscfg/bioscfg.c       |   2 +-
 drivers/platform/x86/lenovo/think-lmi.c            |   2 +-
 drivers/platform/x86/samsung-galaxybook.c          | 246 +++------
 include/linux/firmware_attributes_class.h          | 389 +++++++++++++
 9 files changed, 1077 insertions(+), 200 deletions(-)
---
base-commit: 73f0f2b52c5ea67b3140b23f58d8079d158839c8
change-id: 20250326-fw-attrs-api-0eea7c0225b6
-- 
 ~ Kurt


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v3 1/6] platform/x86: firmware_attributes_class: Add device initialization methods
  2025-06-22  0:04 [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API Kurt Borja
@ 2025-06-22  0:04 ` Kurt Borja
  2025-06-22  0:04 ` [PATCH v3 2/6] platform/x86: firmware_attributes_class: Add high level API for the attributes interface Kurt Borja
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Kurt Borja @ 2025-06-22  0:04 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Thomas Weißschuh,
	Joshua Grisham, Mark Pearson, Armin Wolf, Mario Limonciello
  Cc: Antheas Kapenekakis, Derek J. Clark, Prasanth Ksr, Jorge Lopez,
	platform-driver-x86, linux-kernel, Dell.Client.Kernel, Kurt Borja

From: Thomas Weißschuh <linux@weissschuh.net>

Currently each user of firmware_attributes_class has to manually set up
kobjects, devices, etc.

Provide this infrastructure out-of-the-box through the newly introduced
fwat_device_register().

Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
Co-developed-by: Kurt Borja <kuurtb@gmail.com>
Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
 drivers/platform/x86/firmware_attributes_class.c | 162 +++++++++++++++++++++++
 drivers/platform/x86/firmware_attributes_class.h |  44 ++++++
 2 files changed, 206 insertions(+)

diff --git a/drivers/platform/x86/firmware_attributes_class.c b/drivers/platform/x86/firmware_attributes_class.c
index 736e96c186d9dc6d945517f090e9af903e93bbf4..27edb48636c8a91125b0a335411ebafd9818630c 100644
--- a/drivers/platform/x86/firmware_attributes_class.c
+++ b/drivers/platform/x86/firmware_attributes_class.c
@@ -2,7 +2,13 @@
 
 /* Firmware attributes class helper module */
 
+#include <linux/device.h>
+#include <linux/device/class.h>
+#include <linux/kdev_t.h>
+#include <linux/kobject.h>
 #include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
 #include "firmware_attributes_class.h"
 
 const struct class firmware_attributes_class = {
@@ -10,6 +16,160 @@ const struct class firmware_attributes_class = {
 };
 EXPORT_SYMBOL_GPL(firmware_attributes_class);
 
+static ssize_t fwat_attrs_kobj_show(struct kobject *kobj, struct attribute *attr,
+				    char *buf)
+{
+	const struct fwat_attribute *fattr = to_fwat_attribute(attr);
+	struct fwat_device *fadev = to_fwat_device(kobj);
+
+	if (!fattr->show)
+		return -ENOENT;
+
+	return fattr->show(fadev->dev, fattr, buf);
+}
+
+static ssize_t fwat_attrs_kobj_store(struct kobject *kobj, struct attribute *attr,
+				     const char *buf, size_t count)
+{
+	const struct fwat_attribute *fattr = to_fwat_attribute(attr);
+	struct fwat_device *fadev = to_fwat_device(kobj);
+
+	if (!fattr->store)
+		return -ENOENT;
+
+	return fattr->store(fadev->dev, fattr, buf, count);
+}
+
+static const struct sysfs_ops fwat_attrs_kobj_ops = {
+	.show	= fwat_attrs_kobj_show,
+	.store	= fwat_attrs_kobj_store,
+};
+
+static void fwat_attrs_kobj_release(struct kobject *kobj)
+{
+	struct fwat_device *fadev = to_fwat_device(kobj);
+
+	kfree(fadev);
+}
+
+static const struct kobj_type fwat_attrs_ktype = {
+	.sysfs_ops	= &fwat_attrs_kobj_ops,
+	.release	= fwat_attrs_kobj_release,
+};
+
+/**
+ * fwat_device_register - Create and register a firmware-attributes class
+ *			  device
+ * @parent: Parent device
+ * @name: Name of the class device
+ * @data: Drvdata of the class device
+ * @groups: Extra groups for the class device (Optional)
+ *
+ * Return: pointer to the new fwat_device on success, ERR_PTR on failure
+ */
+struct fwat_device *
+fwat_device_register(struct device *parent, const char *name, void *data,
+		     const struct attribute_group **groups)
+{
+	struct fwat_device *fadev;
+	struct device *dev;
+	int ret;
+
+	if (!parent || !name)
+		return ERR_PTR(-EINVAL);
+
+	fadev = kzalloc(sizeof(*fadev), GFP_KERNEL);
+	if (!fadev)
+		return ERR_PTR(-ENOMEM);
+
+	dev = device_create(&firmware_attributes_class, parent, MKDEV(0, 0),
+			    data, "%s", name);
+	if (IS_ERR(dev)) {
+		kfree(fadev);
+		return ERR_CAST(dev);
+	}
+
+	ret = kobject_init_and_add(&fadev->attrs_kobj, &fwat_attrs_ktype, &dev->kobj,
+				   "attributes");
+	if (ret)
+		goto out_kobj_put;
+
+	if (groups) {
+		ret = device_add_groups(dev, groups);
+		if (ret)
+			goto out_kobj_unregister;
+	}
+
+	fadev->dev = dev;
+	fadev->groups = groups;
+
+	kobject_uevent(&fadev->attrs_kobj, KOBJ_ADD);
+
+	return fadev;
+
+out_kobj_unregister:
+	kobject_del(&fadev->attrs_kobj);
+
+out_kobj_put:
+	kobject_put(&fadev->attrs_kobj);
+	device_unregister(dev);
+
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(fwat_device_register);
+
+void fwat_device_unregister(struct fwat_device *fadev)
+{
+	struct device *dev = fadev->dev;
+
+	if (!fadev)
+		return;
+
+	device_remove_groups(dev, fadev->groups);
+	kobject_del(&fadev->attrs_kobj);
+	kobject_put(&fadev->attrs_kobj);
+	device_unregister(dev);
+}
+EXPORT_SYMBOL_GPL(fwat_device_unregister);
+
+static void devm_fwat_device_release(void *data)
+{
+	struct fwat_device *fadev = data;
+
+	fwat_device_unregister(fadev);
+}
+
+/**
+ * devm_fwat_device_register - Create and register a firmware-attributes class
+ *			       device
+ * @parent: Parent device
+ * @name: Name of the class device
+ * @data: Drvdata of the class device
+ * @groups: Extra groups for the class device (Optional)
+ *
+ * Device managed version of fwat_device_register().
+ *
+ * Return: pointer to the new fwat_device on success, ERR_PTR on failure
+ */
+struct fwat_device *
+devm_fwat_device_register(struct device *parent, const char *name, void *data,
+			  const struct attribute_group **groups)
+{
+	struct fwat_device *fadev;
+	int ret;
+
+	fadev = fwat_device_register(parent, name, data, groups);
+	if (IS_ERR(fadev))
+		return fadev;
+
+	ret = devm_add_action_or_reset(parent, devm_fwat_device_release, fadev);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return fadev;
+}
+EXPORT_SYMBOL_GPL(devm_fwat_device_register);
+
 static __init int fw_attributes_class_init(void)
 {
 	return class_register(&firmware_attributes_class);
@@ -23,5 +183,7 @@ static __exit void fw_attributes_class_exit(void)
 module_exit(fw_attributes_class_exit);
 
 MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");
+MODULE_AUTHOR("Thomas Weißschuh <linux@weissschuh.net>");
+MODULE_AUTHOR("Kurt Borja <kuurtb@gmail.com>");
 MODULE_DESCRIPTION("Firmware attributes class helper module");
 MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/firmware_attributes_class.h b/drivers/platform/x86/firmware_attributes_class.h
index d27abe54fcf9812a2f0868eec5426bbc8e7eb21c..ad94bf91e5af30a2b8feb9abf224ee6f0d17600a 100644
--- a/drivers/platform/x86/firmware_attributes_class.h
+++ b/drivers/platform/x86/firmware_attributes_class.h
@@ -5,8 +5,52 @@
 #ifndef FW_ATTR_CLASS_H
 #define FW_ATTR_CLASS_H
 
+#include <linux/container_of.h>
+#include <linux/device.h>
 #include <linux/device/class.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
 
 extern const struct class firmware_attributes_class;
 
+/**
+ * struct fwat_device - The firmware-attributes device
+ * @dev: The class device.
+ * @attrs_kobj: The "attributes" root kobject.
+ * @groups: Sysfs groups attached to the @attrs_kobj.
+ */
+struct fwat_device {
+	struct device *dev;
+	struct kobject attrs_kobj;
+	const struct attribute_group **groups;
+};
+
+#define to_fwat_device(_k)	container_of_const(_k, struct fwat_device, attrs_kobj)
+
+/**
+ * struct fwat_attribute - The firmware-attributes's custom attribute
+ * @attr: Embedded struct attribute.
+ * @show: Show method called by the "attributes" kobject's ktype.
+ * @store: Store method called by the "attributes" kobject's ktype.
+ */
+struct fwat_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct device *dev, const struct fwat_attribute *attr,
+			char *buf);
+	ssize_t (*store)(struct device *dev, const struct fwat_attribute *attr,
+			 const char *buf, size_t count);
+};
+
+#define to_fwat_attribute(_a) container_of_const(_a, struct fwat_attribute, attr)
+
+struct fwat_device * __must_check
+fwat_device_register(struct device *parent, const char *name, void *data,
+		     const struct attribute_group **groups);
+
+void fwat_device_unregister(struct fwat_device *fwadev);
+
+struct fwat_device * __must_check
+devm_fwat_device_register(struct device *parent, const char *name, void *data,
+			  const struct attribute_group **groups);
+
 #endif /* FW_ATTR_CLASS_H */

-- 
2.50.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 2/6] platform/x86: firmware_attributes_class: Add high level API for the attributes interface
  2025-06-22  0:04 [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API Kurt Borja
  2025-06-22  0:04 ` [PATCH v3 1/6] platform/x86: firmware_attributes_class: Add device initialization methods Kurt Borja
@ 2025-06-22  0:04 ` Kurt Borja
  2025-06-22  0:04 ` [PATCH v3 3/6] platform/x86: firmware_attributes_class: Move header to include directory Kurt Borja
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Kurt Borja @ 2025-06-22  0:04 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Thomas Weißschuh,
	Joshua Grisham, Mark Pearson, Armin Wolf, Mario Limonciello
  Cc: Antheas Kapenekakis, Derek J. Clark, Prasanth Ksr, Jorge Lopez,
	platform-driver-x86, linux-kernel, Dell.Client.Kernel, Kurt Borja

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@gmail.com>
---
 drivers/platform/x86/firmware_attributes_class.c | 453 ++++++++++++++++++++++-
 drivers/platform/x86/firmware_attributes_class.h | 333 +++++++++++++++++
 2 files changed, 785 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/firmware_attributes_class.c b/drivers/platform/x86/firmware_attributes_class.c
index 27edb48636c8a91125b0a335411ebafd9818630c..034f9254240b048f58c97c18062db03f771f8139 100644
--- a/drivers/platform/x86/firmware_attributes_class.c
+++ b/drivers/platform/x86/firmware_attributes_class.c
@@ -9,13 +9,88 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/types.h>
+#include <linux/string_choices.h>
 #include "firmware_attributes_class.h"
 
+#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_group {
+	struct attribute_group group;
+	struct list_head node;
+};
+
+struct fwat_group_config {
+	int type;
+	unsigned int total_attrs;
+	const char * const *attr_labels;
+	ssize_t (*show)(struct device *dev, const struct fwat_attribute *attr,
+			char *buf);
+	ssize_t (*store)(struct device *dev, const struct fwat_attribute *attr,
+			 const char *buf, size_t count);
+};
+
+struct fwat_attribute_ext {
+	struct fwat_attribute fattr;
+	const struct fwat_group_data *data;
+	unsigned int type;
+};
+
+#define to_fwat_attribute_ext(_f) \
+	container_of_const(_f, struct fwat_attribute_ext, fattr)
+
+#define fwat_attribute_ext_init(_name, _mode, _show, _store, _idx, _type, _config) \
+	((struct fwat_attribute_ext){				\
+		.fattr = __ATTR(_name, _mode, _show, _store),	\
+		.config = _config,				\
+		.idx = _idx,					\
+		.type = _type,					\
+	 })
+
 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 const char * const fwat_bool_labels[] = {
+	[fwat_bool_current_value]			= "current_value",
+	[fwat_bool_default_value]			= "default_value",
+};
+
+static const char * const fwat_enum_labels[] = {
+	[fwat_enum_current_value]			= "current_value",
+	[fwat_enum_default_value]			= "default_value",
+	[fwat_enum_possible_values]			= "possible_values",
+};
+
+static const char * const fwat_int_labels[] = {
+	[fwat_int_current_value]			= "current_value",
+	[fwat_int_default_value]			= "default_value",
+	[fwat_int_min_value]				= "min_value",
+	[fwat_int_max_value]				= "max_value",
+	[fwat_int_scalar_increment]			= "scalar_increment",
+};
+
+static const char * const fwat_str_labels[] = {
+	[fwat_str_current_value]			= "current_value",
+	[fwat_str_default_value]			= "default_value",
+	[fwat_str_min_length]				= "min_length",
+	[fwat_str_max_length]				= "max_length",
+};
+
 static ssize_t fwat_attrs_kobj_show(struct kobject *kobj, struct attribute *attr,
 				    char *buf)
 {
@@ -57,6 +132,379 @@ static const struct kobj_type fwat_attrs_ktype = {
 	.release	= fwat_attrs_kobj_release,
 };
 
+static ssize_t fwat_type_show(struct device *dev, const struct fwat_attribute *attr,
+			      char *buf)
+{
+	const struct fwat_attribute_ext *ext = to_fwat_attribute_ext(attr);
+
+	return sysfs_emit(buf, "%s\n", fwat_type_labels[ext->type]);
+}
+
+static ssize_t
+fwat_display_name_show(struct device *dev, const struct fwat_attribute *attr,
+		       char *buf)
+{
+	const struct fwat_attribute_ext *ext = to_fwat_attribute_ext(attr);
+	const struct fwat_group_data *data = ext->data;
+
+	if (!data->display_name)
+		return -EOPNOTSUPP;
+
+	return sysfs_emit(buf, "%s\n", data->display_name);
+}
+
+static ssize_t
+fwat_language_code_show(struct device *dev, const struct fwat_attribute *attr,
+			char *buf)
+{
+	const struct fwat_attribute_ext *ext = to_fwat_attribute_ext(attr);
+	const struct fwat_group_data *data = ext->data;
+
+	if (!data->language_code)
+		return -EOPNOTSUPP;
+
+	return sysfs_emit(buf, "%s\n", data->language_code);
+}
+
+static ssize_t
+boolean_group_show(struct device *dev, const struct fwat_attribute *attr,
+		   char *buf)
+{
+	const struct fwat_attribute_ext *ext = to_fwat_attribute_ext(attr);
+	const struct fwat_bool_data *data = to_fwat_bool_data(ext->data);
+	bool val;
+	int ret;
+
+	switch (ext->type) {
+	case fwat_bool_current_value:
+		ret = data->read(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
+enumeration_group_show(struct device *dev, const struct fwat_attribute *attr,
+		       char *buf)
+{
+	const struct fwat_attribute_ext *ext = to_fwat_attribute_ext(attr);
+	const struct fwat_enum_data *data = to_fwat_enum_data(ext->data);
+	int val_idx, sz = 0;
+	int ret;
+
+	switch (ext->type) {
+	case fwat_enum_current_value:
+		ret = data->read(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
+integer_group_show(struct device *dev, const struct fwat_attribute *attr,
+		   char *buf)
+{
+	const struct fwat_attribute_ext *ext = to_fwat_attribute_ext(attr);
+	const struct fwat_int_data *data = to_fwat_int_data(ext->data);
+	long val;
+	int ret;
+
+	switch (ext->type) {
+	case fwat_int_current_value:
+		ret = data->read(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
+string_group_show(struct device *dev, const struct fwat_attribute *attr,
+		  char *buf)
+{
+	const struct fwat_attribute_ext *ext = to_fwat_attribute_ext(attr);
+	const struct fwat_str_data *data = to_fwat_str_data(ext->data);
+	const char *val;
+	long len;
+	int ret;
+
+	switch (ext->type) {
+	case fwat_str_current_value:
+		ret = data->read(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
+boolean_group_store(struct device *dev, const struct fwat_attribute *attr,
+		    const char *buf, size_t count)
+{
+	const struct fwat_attribute_ext *ext = to_fwat_attribute_ext(attr);
+	const struct fwat_bool_data *data = to_fwat_bool_data(ext->data);
+	bool val;
+	int ret;
+
+	ret = kstrtobool(buf, &val);
+	if (ret)
+		return ret;
+
+	ret = data->write(dev, data->group.id, val);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static ssize_t
+enumeration_group_store(struct device *dev, const struct fwat_attribute *attr,
+			const char *buf, size_t count)
+{
+	const struct fwat_attribute_ext *ext = to_fwat_attribute_ext(attr);
+	const struct fwat_enum_data *data = to_fwat_enum_data(ext->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(dev, data->group.id, val_idx);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static ssize_t
+integer_group_store(struct device *dev, const struct fwat_attribute *attr,
+		    const char *buf, size_t count)
+{
+	const struct fwat_attribute_ext *ext = to_fwat_attribute_ext(attr);
+	const struct fwat_int_data *data = to_fwat_int_data(ext->data);
+	long val;
+	int ret;
+
+	ret = kstrtol(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	ret = data->write(dev, data->group.id, val);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static ssize_t
+string_group_store(struct device *dev, const struct fwat_attribute *attr,
+		   const char *buf, size_t count)
+{
+	const struct fwat_attribute_ext *ext = to_fwat_attribute_ext(attr);
+	const struct fwat_str_data *data = to_fwat_str_data(ext->data);
+	int ret;
+
+	ret = data->write(dev, data->group.id, buf);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static int fwat_add_group(struct fwat_device *fadev, const struct fwat_group_data *data,
+			  const struct fwat_group_config *config)
+{
+	struct fwat_attribute_ext *fattrs;
+	struct fwat_group *group;
+	struct attribute **attrs;
+	unsigned int bit, i = 0;
+	size_t nattrs;
+	int ret;
+
+	nattrs = bitmap_weight(&data->fattrs, config->total_attrs);
+	if (data->display_name)
+		nattrs++;
+	if (data->language_code)
+		nattrs++;
+
+	attrs = devm_kcalloc(fadev->dev, sizeof(*attrs), nattrs + 2, GFP_KERNEL);
+	if (!attrs)
+		return -ENOMEM;
+	fattrs = devm_kcalloc(fadev->dev, sizeof(*fattrs), nattrs + 1, GFP_KERNEL);
+	if (!fattrs)
+		return -ENOMEM;
+
+	fattrs[i].fattr.attr.name = "type";
+	fattrs[i].fattr.attr.mode = 0444;
+	fattrs[i].fattr.show = fwat_type_show;
+	fattrs[i].data = data;
+	fattrs[i].type = config->type;
+	attrs[i] = &fattrs[i].fattr.attr;
+	i++;
+
+	if (data->display_name) {
+		fattrs[i].fattr.attr.name = "display_name";
+		fattrs[i].fattr.attr.mode = 0444;
+		fattrs[i].fattr.show = fwat_display_name_show;
+		fattrs[i].data = data;
+		fattrs[i].type = config->type;
+		attrs[i] = &fattrs[i].fattr.attr;
+		i++;
+	}
+
+	if (data->language_code) {
+		fattrs[i].fattr.attr.name = "display_name_language_code";
+		fattrs[i].fattr.attr.mode = 0444;
+		fattrs[i].fattr.show = fwat_language_code_show;
+		fattrs[i].data = data;
+		fattrs[i].type = config->type;
+		attrs[i] = &fattrs[i].fattr.attr;
+		i++;
+	}
+
+	for_each_set_bit(bit, &data->fattrs, config->total_attrs) {
+		fattrs[i].fattr.attr.name = config->attr_labels[bit];
+		/* current_value is always at bit 0 and uses data->mode */
+		fattrs[i].fattr.attr.mode = bit ? 0444 : data->mode;
+		fattrs[i].fattr.show = config->show;
+		fattrs[i].fattr.store = config->store;
+		fattrs[i].data = data;
+		fattrs[i].type = bit;
+		attrs[i] = &fattrs[i].fattr.attr;
+		i++;
+	}
+
+	group = devm_kzalloc(fadev->dev, sizeof(*group), GFP_KERNEL);
+	if (!group)
+		return -ENOMEM;
+	group->group.name = data->name;
+	group->group.attrs = attrs;
+	ret = sysfs_create_group(&fadev->attrs_kobj, &group->group);
+	if (ret)
+		return ret;
+	list_add(&group->node, &fadev->auto_groups);
+
+	kobject_uevent(&fadev->attrs_kobj, KOBJ_CHANGE);
+
+	return 0;
+}
+
+static void fwat_remove_auto_groups(struct fwat_device *fadev)
+{
+	struct fwat_group *pos;
+
+	list_for_each_entry(pos, &fadev->auto_groups, node)
+		sysfs_remove_group(&fadev->attrs_kobj, &pos->group);
+}
+
+int fwat_create_bool_group(struct fwat_device *fadev, const struct fwat_bool_data *data)
+{
+	struct fwat_group_config config = {
+		.type = FWAT_GROUP_BOOLEAN,
+		.total_attrs = fwat_bool_attrs_last,
+		.attr_labels = fwat_bool_labels,
+		.show = boolean_group_show,
+		.store = boolean_group_store,
+	};
+
+	return fwat_add_group(fadev, &data->group, &config);
+}
+EXPORT_SYMBOL_GPL(fwat_create_bool_group);
+
+int fwat_create_enum_group(struct fwat_device *fadev, const struct fwat_enum_data *data)
+{
+	struct fwat_group_config config = {
+		.type = FWAT_GROUP_ENUMERATION,
+		.total_attrs = fwat_enum_attrs_last,
+		.attr_labels = fwat_enum_labels,
+		.show = enumeration_group_show,
+		.store = enumeration_group_store,
+	};
+
+	return fwat_add_group(fadev, &data->group, &config);
+}
+EXPORT_SYMBOL_GPL(fwat_create_enum_group);
+
+int fwat_create_int_group(struct fwat_device *fadev, const struct fwat_int_data *data)
+{
+	struct fwat_group_config config = {
+		.type = FWAT_GROUP_INTEGER,
+		.total_attrs = fwat_int_attrs_last,
+		.attr_labels = fwat_int_labels,
+		.show = integer_group_show,
+		.store = integer_group_store,
+	};
+
+	return fwat_add_group(fadev, &data->group, &config);
+}
+EXPORT_SYMBOL_GPL(fwat_create_int_group);
+
+int fwat_create_str_group(struct fwat_device *fadev, const struct fwat_str_data *data)
+{
+	struct fwat_group_config config = {
+		.type = FWAT_GROUP_STRING,
+		.total_attrs = fwat_str_attrs_last,
+		.attr_labels = fwat_str_labels,
+		.show = string_group_show,
+		.store = string_group_store,
+	};
+
+	return fwat_add_group(fadev, &data->group, &config);
+}
+EXPORT_SYMBOL_GPL(fwat_create_str_group);
+
 /**
  * fwat_device_register - Create and register a firmware-attributes class
  *			  device
@@ -102,6 +550,7 @@ fwat_device_register(struct device *parent, const char *name, void *data,
 
 	fadev->dev = dev;
 	fadev->groups = groups;
+	INIT_LIST_HEAD(&fadev->auto_groups);
 
 	kobject_uevent(&fadev->attrs_kobj, KOBJ_ADD);
 
@@ -120,11 +569,13 @@ EXPORT_SYMBOL_GPL(fwat_device_register);
 
 void fwat_device_unregister(struct fwat_device *fadev)
 {
-	struct device *dev = fadev->dev;
+	struct device *dev;
 
 	if (!fadev)
 		return;
 
+	dev = fadev->dev;
+	fwat_remove_auto_groups(fadev);
 	device_remove_groups(dev, fadev->groups);
 	kobject_del(&fadev->attrs_kobj);
 	kobject_put(&fadev->attrs_kobj);
diff --git a/drivers/platform/x86/firmware_attributes_class.h b/drivers/platform/x86/firmware_attributes_class.h
index ad94bf91e5af30a2b8feb9abf224ee6f0d17600a..6b089e61cfb74c4bf1d196d374bc6c271b14f211 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;
 
@@ -23,6 +24,7 @@ struct fwat_device {
 	struct device *dev;
 	struct kobject attrs_kobj;
 	const struct attribute_group **groups;
+	struct list_head auto_groups;
 };
 
 #define to_fwat_device(_k)	container_of_const(_k, struct fwat_device, attrs_kobj)
@@ -43,6 +45,337 @@ struct fwat_attribute {
 
 #define to_fwat_attribute(_a) container_of_const(_a, struct fwat_attribute, attr)
 
+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
+ * @idx: 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)
+ * @fattrs: Bitmap of selected attributes for this group type.
+ * @mode: Mode for the current_value attribute. All other attributes will have
+ *        0444 permissions.
+ *
+ * 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;
+	const char *name;
+	const char *display_name;
+	const char *language_code;
+	unsigned long fattrs;
+	umode_t mode;
+};
+
+/**
+ * struct fwat_bool_data - Data struct for the boolean group type
+ * @default_val: Default value.
+ * @read: Read callback for the current_value attribute.
+ * @write: Write callback for the current_value attribute.
+ * @group: Group data.
+ */
+struct fwat_bool_data {
+	bool default_val;
+	int (*read)(struct device *dev, long id, bool *val);
+	int (*write)(struct device *dev, long id, bool val);
+	struct fwat_group_data group;
+};
+
+/**
+ * struct fwat_enum_data - Data struct for the enumeration group type
+ * @default_idx: Index of the default value in the @possible_vals array.
+ * @possible_vals: Array of possible value strings for this group type.
+ * @read: Read callback for the current_value attribute.
+ * @write: Write callback for the current_value attribute.
+ * @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 default_idx;
+	const char * const *possible_vals;
+	int (*read)(struct device *dev, long id, int *val_idx);
+	int (*write)(struct device *dev, long id, int val_idx);
+	struct fwat_group_data group;
+};
+
+/**
+ * struct fwat_int_data - Data struct for the integer group type
+ * @default_val: Default value.
+ * @min_val: Minimum value.
+ * @max_val: Maximum value.
+ * @increment: Scalar increment for this value.
+ * @read: Read callback for the current_value attribute.
+ * @write: Write callback for the current_value attribute.
+ * @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 {
+	long default_val;
+	long min_val;
+	long max_val;
+	long increment;
+	int (*read)(struct device *dev, long id, long *val);
+	int (*write)(struct device *dev, long id, long val);
+	struct fwat_group_data group;
+};
+
+/**
+ * struct fwat_str_data - Data struct for the string group type
+ * @default_val: Default value.
+ * @min_len: Minimum string length.
+ * @max_len: Maximum string length.
+ * @read: Read callback for the current_value attribute.
+ * @write: Write callback for the current_value attribute.
+ * @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 {
+	const char *default_val;
+	long min_len;
+	long max_len;
+	int (*read)(struct device *dev, long id, const char **val);
+	int (*write)(struct device *dev, long id, const char *val);
+	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
+ *                          struct fwat_bool_data instance
+ * @_name: Name of the group.
+ * @_disp_name: Name showed in the display_name attribute. (Optional)
+ * @_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;
+ *	int ret;
+ *	...
+ *	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 *data,
 		     const struct attribute_group **groups);

-- 
2.50.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 3/6] platform/x86: firmware_attributes_class: Move header to include directory
  2025-06-22  0:04 [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API Kurt Borja
  2025-06-22  0:04 ` [PATCH v3 1/6] platform/x86: firmware_attributes_class: Add device initialization methods Kurt Borja
  2025-06-22  0:04 ` [PATCH v3 2/6] platform/x86: firmware_attributes_class: Add high level API for the attributes interface Kurt Borja
@ 2025-06-22  0:04 ` Kurt Borja
  2025-06-22  4:28   ` Kurt Borja
  2025-06-22  0:04 ` [PATCH v3 4/6] platform/x86: samsung-galaxybook: Transition new firmware_attributes API Kurt Borja
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 12+ messages in thread
From: Kurt Borja @ 2025-06-22  0:04 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Thomas Weißschuh,
	Joshua Grisham, Mark Pearson, Armin Wolf, Mario Limonciello
  Cc: Antheas Kapenekakis, Derek J. Clark, Prasanth Ksr, Jorge Lopez,
	platform-driver-x86, linux-kernel, Dell.Client.Kernel, Kurt Borja

Move firmware_attributes_class.h to include/linux/ to avoid hardcoding
paths inside drivers/platform/x86/.

Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
 drivers/platform/x86/dell/dell-wmi-sysman/sysman.c                  | 2 +-
 drivers/platform/x86/firmware_attributes_class.c                    | 2 +-
 drivers/platform/x86/hp/hp-bioscfg/bioscfg.c                        | 2 +-
 drivers/platform/x86/lenovo/think-lmi.c                             | 2 +-
 drivers/platform/x86/samsung-galaxybook.c                           | 2 +-
 {drivers/platform/x86 => include/linux}/firmware_attributes_class.h | 0
 6 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c
index d00389b860e4ea0655c740c78bc3751f323b6370..3aec09987ab145508ed05b02e61a6d94edf79484 100644
--- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c
+++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c
@@ -12,8 +12,8 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/wmi.h>
+#include <linux/firmware_attributes_class.h>
 #include "dell-wmi-sysman.h"
-#include "../../firmware_attributes_class.h"
 
 #define MAX_TYPES  4
 #include <linux/nls.h>
diff --git a/drivers/platform/x86/firmware_attributes_class.c b/drivers/platform/x86/firmware_attributes_class.c
index 034f9254240b048f58c97c18062db03f771f8139..af39ed9ad2836147c98b4bb0b89e70e96ee34b71 100644
--- a/drivers/platform/x86/firmware_attributes_class.c
+++ b/drivers/platform/x86/firmware_attributes_class.c
@@ -10,7 +10,7 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/string_choices.h>
-#include "firmware_attributes_class.h"
+#include <linux/firmware_attributes_class.h>
 
 #define to_fwat_bool_data(_c) \
 	container_of_const(_c, struct fwat_bool_data, group)
diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
index 13237890fc92002e7e730b1c235ddf068a6737cd..2df31af8a3b4ac88710af1fae2d5dabbb3185f1d 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
@@ -12,7 +12,7 @@
 #include <linux/kernel.h>
 #include <linux/wmi.h>
 #include "bioscfg.h"
-#include "../../firmware_attributes_class.h"
+#include <linux/firmware_attributes_class.h>
 #include <linux/nls.h>
 #include <linux/errno.h>
 
diff --git a/drivers/platform/x86/lenovo/think-lmi.c b/drivers/platform/x86/lenovo/think-lmi.c
index 34a47269e3d34d2eda6b71af73892656cd2bf67d..f61a6287eb0ebe9ac4c0c9445c3b54c12b276691 100644
--- a/drivers/platform/x86/lenovo/think-lmi.c
+++ b/drivers/platform/x86/lenovo/think-lmi.c
@@ -20,7 +20,7 @@
 #include <linux/types.h>
 #include <linux/dmi.h>
 #include <linux/wmi.h>
-#include "../firmware_attributes_class.h"
+#include <linux/firmware_attributes_class.h>
 #include "think-lmi.h"
 
 static bool debug_support;
diff --git a/drivers/platform/x86/samsung-galaxybook.c b/drivers/platform/x86/samsung-galaxybook.c
index 5878a351993eb05a4c5c2c75b4915d972ce9becc..9a5a7b956a9f6a2738470e83ce93f4cccf4bf3b4 100644
--- a/drivers/platform/x86/samsung-galaxybook.c
+++ b/drivers/platform/x86/samsung-galaxybook.c
@@ -28,7 +28,7 @@
 #include <linux/uuid.h>
 #include <linux/workqueue.h>
 #include <acpi/battery.h>
-#include "firmware_attributes_class.h"
+#include <linux/firmware_attributes_class.h>
 
 #define DRIVER_NAME "samsung-galaxybook"
 
diff --git a/drivers/platform/x86/firmware_attributes_class.h b/include/linux/firmware_attributes_class.h
similarity index 100%
rename from drivers/platform/x86/firmware_attributes_class.h
rename to include/linux/firmware_attributes_class.h

-- 
2.50.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 4/6] platform/x86: samsung-galaxybook: Transition new firmware_attributes API
  2025-06-22  0:04 [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API Kurt Borja
                   ` (2 preceding siblings ...)
  2025-06-22  0:04 ` [PATCH v3 3/6] platform/x86: firmware_attributes_class: Move header to include directory Kurt Borja
@ 2025-06-22  0:04 ` Kurt Borja
  2025-06-22  0:04 ` [PATCH v3 5/6] Documentation: ABI: Update sysfs-class-firmware-attributes documentation Kurt Borja
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Kurt Borja @ 2025-06-22  0:04 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Thomas Weißschuh,
	Joshua Grisham, Mark Pearson, Armin Wolf, Mario Limonciello
  Cc: Antheas Kapenekakis, Derek J. Clark, Prasanth Ksr, Jorge Lopez,
	platform-driver-x86, linux-kernel, Dell.Client.Kernel, Kurt Borja

Transition to new firmware_attributes API.

Defining firmware_attributes groups statically through
DEFINE_FWAT_ENUM_GROUP() incurs in a minor ABI change. In particular the
display_name_language_code attribute is no longer created. Fortunately,
this doesn't break user-space compatibility, because this attribute is
not required, neither by the ABI specification nor by user-space tools.

Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
 drivers/platform/x86/samsung-galaxybook.c | 244 ++++++++----------------------
 1 file changed, 61 insertions(+), 183 deletions(-)

diff --git a/drivers/platform/x86/samsung-galaxybook.c b/drivers/platform/x86/samsung-galaxybook.c
index 9a5a7b956a9f6a2738470e83ce93f4cccf4bf3b4..d1db2a3f716aafc5a314f2bdc57f7c958a9ae346 100644
--- a/drivers/platform/x86/samsung-galaxybook.c
+++ b/drivers/platform/x86/samsung-galaxybook.c
@@ -36,8 +36,6 @@ struct samsung_galaxybook {
 	struct platform_device *platform;
 	struct acpi_device *acpi;
 
-	struct device *fw_attrs_dev;
-	struct kset *fw_attrs_kset;
 	/* block in case firmware attributes are updated in multiple threads */
 	struct mutex fw_attr_lock;
 
@@ -60,36 +58,6 @@ struct samsung_galaxybook {
 	u8 profile_performance_modes[PLATFORM_PROFILE_LAST];
 };
 
-enum galaxybook_fw_attr_id {
-	GB_ATTR_POWER_ON_LID_OPEN,
-	GB_ATTR_USB_CHARGING,
-	GB_ATTR_BLOCK_RECORDING,
-};
-
-static const char * const galaxybook_fw_attr_name[] = {
-	[GB_ATTR_POWER_ON_LID_OPEN] = "power_on_lid_open",
-	[GB_ATTR_USB_CHARGING]      = "usb_charging",
-	[GB_ATTR_BLOCK_RECORDING]   = "block_recording",
-};
-
-static const char * const galaxybook_fw_attr_desc[] = {
-	[GB_ATTR_POWER_ON_LID_OPEN] = "Power On Lid Open",
-	[GB_ATTR_USB_CHARGING]      = "USB Charging",
-	[GB_ATTR_BLOCK_RECORDING]   = "Block Recording",
-};
-
-#define GB_ATTR_LANGUAGE_CODE "en_US.UTF-8"
-
-struct galaxybook_fw_attr {
-	struct samsung_galaxybook *galaxybook;
-	enum galaxybook_fw_attr_id fw_attr_id;
-	struct attribute_group attr_group;
-	struct kobj_attribute display_name;
-	struct kobj_attribute current_value;
-	int (*get_value)(struct samsung_galaxybook *galaxybook, bool *value);
-	int (*set_value)(struct samsung_galaxybook *galaxybook, const bool value);
-};
-
 struct sawb {
 	u16 safn;
 	u16 sasb;
@@ -908,193 +876,106 @@ static int galaxybook_block_recording_init(struct samsung_galaxybook *galaxybook
 
 /* Firmware Attributes setup */
 
-static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
+static const char * const galaxybook_possible_vals[] = {
+	[false] = "0", [true] = "1", NULL
+};
+
+static int power_on_lid_open_read(struct device *dev, long id, int *val_idx)
 {
-	return sysfs_emit(buf, "enumeration\n");
-}
-
-static struct kobj_attribute fw_attr_type = __ATTR_RO(type);
-
-static ssize_t default_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
-{
-	return sysfs_emit(buf, "0\n");
-}
-
-static struct kobj_attribute fw_attr_default_value = __ATTR_RO(default_value);
-
-static ssize_t possible_values_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
-{
-	return sysfs_emit(buf, "0;1\n");
-}
-
-static struct kobj_attribute fw_attr_possible_values = __ATTR_RO(possible_values);
-
-static ssize_t display_name_language_code_show(struct kobject *kobj, struct kobj_attribute *attr,
-					       char *buf)
-{
-	return sysfs_emit(buf, "%s\n", GB_ATTR_LANGUAGE_CODE);
-}
-
-static struct kobj_attribute fw_attr_display_name_language_code =
-	__ATTR_RO(display_name_language_code);
-
-static ssize_t display_name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
-{
-	struct galaxybook_fw_attr *fw_attr =
-		container_of(attr, struct galaxybook_fw_attr, display_name);
-
-	return sysfs_emit(buf, "%s\n", galaxybook_fw_attr_desc[fw_attr->fw_attr_id]);
-}
-
-static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
-{
-	struct galaxybook_fw_attr *fw_attr =
-		container_of(attr, struct galaxybook_fw_attr, current_value);
-	bool value;
+	struct samsung_galaxybook *galaxybook = dev_get_drvdata(dev);
+	bool val;
 	int err;
 
-	err = fw_attr->get_value(fw_attr->galaxybook, &value);
+	err = power_on_lid_open_acpi_get(galaxybook, &val);
 	if (err)
 		return err;
 
-	return sysfs_emit(buf, "%u\n", value);
+	*val_idx = val;
+
+	return 0;
 }
 
-static ssize_t current_value_store(struct kobject *kobj, struct kobj_attribute *attr,
-				   const char *buf, size_t count)
+static int power_on_lid_open_write(struct device *dev, long id, int val_idx)
 {
-	struct galaxybook_fw_attr *fw_attr =
-		container_of(attr, struct galaxybook_fw_attr, current_value);
-	struct samsung_galaxybook *galaxybook = fw_attr->galaxybook;
-	bool value;
+	struct samsung_galaxybook *galaxybook = dev_get_drvdata(dev);
+
+	return power_on_lid_open_acpi_set(galaxybook, val_idx ? true : false);
+}
+
+DEFINE_FWAT_ENUM_GROUP(power_on_lid_open, "Power On Lid Open", galaxybook_possible_vals,
+		       false, 0644, FWAT_ENUM_ALL_ATTRS);
+
+static int usb_charging_read(struct device *dev, long id, int *val_idx)
+{
+	struct samsung_galaxybook *galaxybook = dev_get_drvdata(dev);
+	bool val;
 	int err;
 
-	if (!count)
-		return -EINVAL;
-
-	err = kstrtobool(buf, &value);
+	err = usb_charging_acpi_get(galaxybook, &val);
 	if (err)
 		return err;
 
-	guard(mutex)(&galaxybook->fw_attr_lock);
+	*val_idx = val;
 
-	err = fw_attr->set_value(galaxybook, value);
+	return 0;
+}
+
+static int usb_charging_write(struct device *dev, long id, int val_idx)
+{
+	struct samsung_galaxybook *galaxybook = dev_get_drvdata(dev);
+
+	return usb_charging_acpi_set(galaxybook, val_idx ? true : false);
+}
+
+DEFINE_FWAT_ENUM_GROUP(usb_charging, "USB Charging", galaxybook_possible_vals,
+		       false, 0644, FWAT_ENUM_ALL_ATTRS);
+
+static int block_recording_read(struct device *dev, long id, int *val_idx)
+{
+	struct samsung_galaxybook *galaxybook = dev_get_drvdata(dev);
+	bool val;
+	int err;
+
+	err = block_recording_acpi_get(galaxybook, &val);
 	if (err)
 		return err;
 
-	return count;
+	*val_idx = val;
+
+	return 0;
 }
 
-#define NUM_FW_ATTR_ENUM_ATTRS  6
-
-static int galaxybook_fw_attr_init(struct samsung_galaxybook *galaxybook,
-				   const enum galaxybook_fw_attr_id fw_attr_id,
-				   int (*get_value)(struct samsung_galaxybook *galaxybook,
-						    bool *value),
-				   int (*set_value)(struct samsung_galaxybook *galaxybook,
-						    const bool value))
+static int block_recording_write(struct device *dev, long id, int val_idx)
 {
-	struct galaxybook_fw_attr *fw_attr;
-	struct attribute **attrs;
 
-	fw_attr = devm_kzalloc(&galaxybook->platform->dev, sizeof(*fw_attr), GFP_KERNEL);
-	if (!fw_attr)
-		return -ENOMEM;
+	struct samsung_galaxybook *galaxybook = dev_get_drvdata(dev);
 
-	attrs = devm_kcalloc(&galaxybook->platform->dev, NUM_FW_ATTR_ENUM_ATTRS + 1,
-			     sizeof(*attrs), GFP_KERNEL);
-	if (!attrs)
-		return -ENOMEM;
-
-	attrs[0] = &fw_attr_type.attr;
-	attrs[1] = &fw_attr_default_value.attr;
-	attrs[2] = &fw_attr_possible_values.attr;
-	attrs[3] = &fw_attr_display_name_language_code.attr;
-
-	sysfs_attr_init(&fw_attr->display_name.attr);
-	fw_attr->display_name.attr.name = "display_name";
-	fw_attr->display_name.attr.mode = 0444;
-	fw_attr->display_name.show = display_name_show;
-	attrs[4] = &fw_attr->display_name.attr;
-
-	sysfs_attr_init(&fw_attr->current_value.attr);
-	fw_attr->current_value.attr.name = "current_value";
-	fw_attr->current_value.attr.mode = 0644;
-	fw_attr->current_value.show = current_value_show;
-	fw_attr->current_value.store = current_value_store;
-	attrs[5] = &fw_attr->current_value.attr;
-
-	attrs[6] = NULL;
-
-	fw_attr->galaxybook = galaxybook;
-	fw_attr->fw_attr_id = fw_attr_id;
-	fw_attr->attr_group.name = galaxybook_fw_attr_name[fw_attr_id];
-	fw_attr->attr_group.attrs = attrs;
-	fw_attr->get_value = get_value;
-	fw_attr->set_value = set_value;
-
-	return sysfs_create_group(&galaxybook->fw_attrs_kset->kobj, &fw_attr->attr_group);
+	return block_recording_acpi_set(galaxybook, val_idx ? true : false);
 }
 
-static void galaxybook_kset_unregister(void *data)
-{
-	struct kset *kset = data;
-
-	kset_unregister(kset);
-}
-
-static void galaxybook_fw_attrs_dev_unregister(void *data)
-{
-	struct device *fw_attrs_dev = data;
-
-	device_unregister(fw_attrs_dev);
-}
+DEFINE_FWAT_ENUM_GROUP(block_recording, "Block Recording", galaxybook_possible_vals,
+		       false, 0644, FWAT_ENUM_ALL_ATTRS);
 
 static int galaxybook_fw_attrs_init(struct samsung_galaxybook *galaxybook)
 {
+	struct fwat_device *fdev;
 	bool value;
 	int err;
 
-	err = devm_mutex_init(&galaxybook->platform->dev, &galaxybook->fw_attr_lock);
-	if (err)
-		return err;
-
-	galaxybook->fw_attrs_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0),
-						 NULL, "%s", DRIVER_NAME);
-	if (IS_ERR(galaxybook->fw_attrs_dev))
-		return PTR_ERR(galaxybook->fw_attrs_dev);
-
-	err = devm_add_action_or_reset(&galaxybook->platform->dev,
-				       galaxybook_fw_attrs_dev_unregister,
-				       galaxybook->fw_attrs_dev);
-	if (err)
-		return err;
-
-	galaxybook->fw_attrs_kset = kset_create_and_add("attributes", NULL,
-							&galaxybook->fw_attrs_dev->kobj);
-	if (!galaxybook->fw_attrs_kset)
-		return -ENOMEM;
-	err = devm_add_action_or_reset(&galaxybook->platform->dev,
-				       galaxybook_kset_unregister, galaxybook->fw_attrs_kset);
-	if (err)
-		return err;
+	fdev = devm_fwat_device_register(&galaxybook->platform->dev, DRIVER_NAME, galaxybook, NULL);
+	if (IS_ERR(fdev))
+		return PTR_ERR(fdev);
 
 	err = power_on_lid_open_acpi_get(galaxybook, &value);
 	if (!err) {
-		err = galaxybook_fw_attr_init(galaxybook,
-					      GB_ATTR_POWER_ON_LID_OPEN,
-					      &power_on_lid_open_acpi_get,
-					      &power_on_lid_open_acpi_set);
+		err = fwat_create_group(fdev, &power_on_lid_open_group_data);
 		if (err)
 			return err;
 	}
 
 	err = usb_charging_acpi_get(galaxybook, &value);
 	if (!err) {
-		err = galaxybook_fw_attr_init(galaxybook,
-					      GB_ATTR_USB_CHARGING,
-					      &usb_charging_acpi_get,
-					      &usb_charging_acpi_set);
+		err = fwat_create_group(fdev, &usb_charging_group_data);
 		if (err)
 			return err;
 	}
@@ -1107,10 +988,7 @@ static int galaxybook_fw_attrs_init(struct samsung_galaxybook *galaxybook)
 
 	galaxybook->has_block_recording = true;
 
-	return galaxybook_fw_attr_init(galaxybook,
-				       GB_ATTR_BLOCK_RECORDING,
-				       &block_recording_acpi_get,
-				       &block_recording_acpi_set);
+	return fwat_create_group(fdev, &block_recording_group_data);
 }
 
 /*

-- 
2.50.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 5/6] Documentation: ABI: Update sysfs-class-firmware-attributes documentation
  2025-06-22  0:04 [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API Kurt Borja
                   ` (3 preceding siblings ...)
  2025-06-22  0:04 ` [PATCH v3 4/6] platform/x86: samsung-galaxybook: Transition new firmware_attributes API Kurt Borja
@ 2025-06-22  0:04 ` Kurt Borja
  2025-06-22  0:04 ` [PATCH v3 6/6] MAINTAINERS: Add FIRMWARE ATTRIBUTES CLASS entry Kurt Borja
  2025-06-22  3:01 ` [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API Derek J. Clark
  6 siblings, 0 replies; 12+ messages in thread
From: Kurt Borja @ 2025-06-22  0:04 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Thomas Weißschuh,
	Joshua Grisham, Mark Pearson, Armin Wolf, Mario Limonciello
  Cc: Antheas Kapenekakis, Derek J. Clark, Prasanth Ksr, Jorge Lopez,
	platform-driver-x86, linux-kernel, Dell.Client.Kernel, Kurt Borja

Add a simple boolean type.

Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
 Documentation/ABI/testing/sysfs-class-firmware-attributes | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/ABI/testing/sysfs-class-firmware-attributes b/Documentation/ABI/testing/sysfs-class-firmware-attributes
index 2713efa509b465a39bf014180794bf487e5b42d6..64b8d5d49716e8387fee26e3e56910862f6a4f5c 100644
--- a/Documentation/ABI/testing/sysfs-class-firmware-attributes
+++ b/Documentation/ABI/testing/sysfs-class-firmware-attributes
@@ -18,6 +18,7 @@ Description:
 
 		The following are known types:
 
+			- boolean
 			- enumeration: a set of pre-defined valid values
 			- integer: a range of numerical values
 			- string

-- 
2.50.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3 6/6] MAINTAINERS: Add FIRMWARE ATTRIBUTES CLASS entry
  2025-06-22  0:04 [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API Kurt Borja
                   ` (4 preceding siblings ...)
  2025-06-22  0:04 ` [PATCH v3 5/6] Documentation: ABI: Update sysfs-class-firmware-attributes documentation Kurt Borja
@ 2025-06-22  0:04 ` Kurt Borja
  2025-06-22  3:01 ` [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API Derek J. Clark
  6 siblings, 0 replies; 12+ messages in thread
From: Kurt Borja @ 2025-06-22  0:04 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Thomas Weißschuh,
	Joshua Grisham, Mark Pearson, Armin Wolf, Mario Limonciello
  Cc: Antheas Kapenekakis, Derek J. Clark, Prasanth Ksr, Jorge Lopez,
	platform-driver-x86, linux-kernel, Dell.Client.Kernel, Kurt Borja

Add entry for the FIRMWARE ATTRIBUTES CLASS.

Signed-off-by: Kurt Borja <kuurtb@gmail.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index c14614613377df7f40565c6df50661fe3f510034..c799f603e9210e4703eeb1f0ac9d6b9e8bd469c5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9352,6 +9352,14 @@ F:	include/linux/firewire.h
 F:	include/uapi/linux/firewire*.h
 F:	tools/firewire/
 
+FIRMWARE ATTRIBUTES CLASS
+M:	Kurt Borja <kuurtb@gmail.com>
+L:	platform-driver-x86@vger.kernel.org
+S:	Maintained
+F:	drivers/platform/x86/firmware_attributes_class.c
+F:	include/linux/firmware_attributes_class.h
+K:	(devm_)?fwat_device_(un)?register
+
 FIRMWARE FRAMEWORK FOR ARMV8-A
 M:	Sudeep Holla <sudeep.holla@arm.com>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)

-- 
2.50.0


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API
  2025-06-22  0:04 [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API Kurt Borja
                   ` (5 preceding siblings ...)
  2025-06-22  0:04 ` [PATCH v3 6/6] MAINTAINERS: Add FIRMWARE ATTRIBUTES CLASS entry Kurt Borja
@ 2025-06-22  3:01 ` Derek J. Clark
  2025-06-22  3:27   ` Kurt Borja
  6 siblings, 1 reply; 12+ messages in thread
From: Derek J. Clark @ 2025-06-22  3:01 UTC (permalink / raw)
  To: Kurt Borja, Hans de Goede, Ilpo Järvinen,
	Thomas Weißschuh, Joshua Grisham, Mark Pearson, Armin Wolf,
	Mario Limonciello
  Cc: Antheas Kapenekakis, Prasanth Ksr, Jorge Lopez,
	platform-driver-x86, linux-kernel, Dell.Client.Kernel



On June 21, 2025 5:04:03 PM PDT, Kurt Borja <kuurtb@gmail.com> wrote:
>Hi all,
>
>I apologize for taking so long. I've been a bit busy these last few
>weeks.
>
>After my discussion with Joshua on v2, I realized the API I made was not
>ergonomic at all and it didn't exactly respond to driver needs. In this
>version I tried a completely different approach and IMO it's much much
>better now.
>
>First of all I adopted standard sysfs terminology for everything. A
>"firmware attribute" is just an attribute_group under the attributes/
>directory so everything related to this concept is just called "group"
>now. Everything refered as properties in the previous patch are now just
>plain "attributes".
>
>This new API revolves around the `fwat_{bool,enum,int,str}_data`
>structs. These hold all the metadata a "firmware_attribute" of that
>given type needs.
>
>These structs also hold `read` and `write` callbacks for the
>current_value attribute, because obviously that value is always dynamic.
>However the rest of attributes (default_value, display_name, min, max,
>etc) are constant.

Hi Kurt,

In the lenovo-wmi drivers the min/max for multiple attributes are actually dynamic based on if power is AC connected or on battery. Looking at patch 2 I might be able to do some pointer manipulation with the attribute's "data" member for those events to make this work, but it would be a lot easier if there was a simple way for me to call my own functions here instead. Perhaps a function pointer could be used to override the default method here? 

Cheers, 
Derek

>In the simple case this metadata structs can be defined statically with
>DEFINE_FWAT_{BOOL,ENUM,INT,STR}_GROUP() macros. However most users of
>this class obtain this values dynamically so you can also define this
>structs dynamically.
>
>In the end all groups (static and dynamic) will be created using
>fwat_create_group() after registering the class device.
>
>Let me know what you think, your feedback is very appreciated :)
>
>I do have one question for anyone interested. Should constraints over
>the current_value (such as min, max, increment, etc.) be enforced at the
>show/store level? i.e. before values reach read/write callbacks.
>
>Signed-off-by: Kurt Borja <kuurtb@gmail.com>
>---
>Changes in v3:
>
>[Patch 1]
>- Fixed UAF in fwat_device_unregister(). Device was unregistered after
>  freeing fadev.
>
>[Patch 2]
>- Patch 2 was completely replaced. A new approach for the API is taken,
>  based on Joshua's suggestions.
>
>- Link to v2: https://lore.kernel.org/r/20250517-fw-attrs-api-v2-0-fa1ab045a01c@gmail.com
>
>Changes in v2:
>
>[Patch 1]
> - Include kdev_t.h header
>
>[Patch 2]
> - Use one line comments in fwat_create_attrs()
> - Check propagate errors in fwat_create_attrs()
> - Add `mode` to fwat_attr_config and related macros to let users
>   configure the `current_value` attribute mode
> - Use defined structs in fwat_attr_ops instead of anonymous ones
> - Move fwat_attr_type from config to ops
>
>[Patch 5]
> - Just transition to new API without chaing ABI
>
>- Link to v1: https://lore.kernel.org/r/20250509-fw-attrs-api-v1-0-258afed65bfa@gmail.com
>
>---
>Kurt Borja (5):
>      platform/x86: firmware_attributes_class: Add high level API for the attributes interface
>      platform/x86: firmware_attributes_class: Move header to include directory
>      platform/x86: samsung-galaxybook: Transition new firmware_attributes API
>      Documentation: ABI: Update sysfs-class-firmware-attributes documentation
>      MAINTAINERS: Add FIRMWARE ATTRIBUTES CLASS entry
>
>Thomas Weißschuh (1):
>      platform/x86: firmware_attributes_class: Add device initialization methods
>
> .../ABI/testing/sysfs-class-firmware-attributes    |   1 +
> MAINTAINERS                                        |   8 +
> drivers/platform/x86/dell/dell-wmi-sysman/sysman.c |   2 +-
> drivers/platform/x86/firmware_attributes_class.c   | 615 ++++++++++++++++++++-
> drivers/platform/x86/firmware_attributes_class.h   |  12 -
> drivers/platform/x86/hp/hp-bioscfg/bioscfg.c       |   2 +-
> drivers/platform/x86/lenovo/think-lmi.c            |   2 +-
> drivers/platform/x86/samsung-galaxybook.c          | 246 +++------
> include/linux/firmware_attributes_class.h          | 389 +++++++++++++
> 9 files changed, 1077 insertions(+), 200 deletions(-)
>---
>base-commit: 73f0f2b52c5ea67b3140b23f58d8079d158839c8
>change-id: 20250326-fw-attrs-api-0eea7c0225b6

- Derek

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API
  2025-06-22  3:01 ` [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API Derek J. Clark
@ 2025-06-22  3:27   ` Kurt Borja
  2025-06-22  3:42     ` Derek J. Clark
  0 siblings, 1 reply; 12+ messages in thread
From: Kurt Borja @ 2025-06-22  3:27 UTC (permalink / raw)
  To: Derek J. Clark, Hans de Goede, Ilpo Järvinen,
	Thomas Weißschuh, Joshua Grisham, Mark Pearson, Armin Wolf,
	Mario Limonciello
  Cc: Antheas Kapenekakis, Prasanth Ksr, Jorge Lopez,
	platform-driver-x86, linux-kernel, Dell.Client.Kernel

On Sun Jun 22, 2025 at 12:01 AM -03, Derek J. Clark wrote:
>
>
> On June 21, 2025 5:04:03 PM PDT, Kurt Borja <kuurtb@gmail.com> wrote:
>>Hi all,
>>
>>I apologize for taking so long. I've been a bit busy these last few
>>weeks.
>>
>>After my discussion with Joshua on v2, I realized the API I made was not
>>ergonomic at all and it didn't exactly respond to driver needs. In this
>>version I tried a completely different approach and IMO it's much much
>>better now.
>>
>>First of all I adopted standard sysfs terminology for everything. A
>>"firmware attribute" is just an attribute_group under the attributes/
>>directory so everything related to this concept is just called "group"
>>now. Everything refered as properties in the previous patch are now just
>>plain "attributes".
>>
>>This new API revolves around the `fwat_{bool,enum,int,str}_data`
>>structs. These hold all the metadata a "firmware_attribute" of that
>>given type needs.
>>
>>These structs also hold `read` and `write` callbacks for the
>>current_value attribute, because obviously that value is always dynamic.
>>However the rest of attributes (default_value, display_name, min, max,
>>etc) are constant.
>
> Hi Kurt,
>
> In the lenovo-wmi drivers the min/max for multiple attributes are actually dynamic based on if power is AC connected or on battery. Looking at patch 2 I might be able to do some pointer manipulation with the attribute's "data" member for those events to make this work, but it would be a lot easier if there was a simple way for me to call my own functions here instead. Perhaps a function pointer could be used to override the default method here? 

Hi Derek,

All attributes in a given group have the same show method. Maybe we can
let users override this with their own show method, i.e. Add a

	ssize_t (*attr_show)(struct device *dev, const struct fwat_attribute *attr, const char *buf)

to struct fwat_group_data. That should be fairly simple to implement.

Did you have another solution in mind?


-- 
 ~ Kurt

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API
  2025-06-22  3:27   ` Kurt Borja
@ 2025-06-22  3:42     ` Derek J. Clark
  2025-06-22  4:27       ` Kurt Borja
  0 siblings, 1 reply; 12+ messages in thread
From: Derek J. Clark @ 2025-06-22  3:42 UTC (permalink / raw)
  To: Kurt Borja, Hans de Goede, Ilpo Järvinen,
	Thomas Weißschuh, Joshua Grisham, Mark Pearson, Armin Wolf,
	Mario Limonciello
  Cc: Antheas Kapenekakis, Prasanth Ksr, Jorge Lopez,
	platform-driver-x86, linux-kernel, Dell.Client.Kernel



On June 21, 2025 8:27:06 PM PDT, Kurt Borja <kuurtb@gmail.com> wrote:
>On Sun Jun 22, 2025 at 12:01 AM -03, Derek J. Clark wrote:
>>
>>
>> On June 21, 2025 5:04:03 PM PDT, Kurt Borja <kuurtb@gmail.com> wrote:
>>>Hi all,
>>>
>>>I apologize for taking so long. I've been a bit busy these last few
>>>weeks.
>>>
>>>After my discussion with Joshua on v2, I realized the API I made was not
>>>ergonomic at all and it didn't exactly respond to driver needs. In this
>>>version I tried a completely different approach and IMO it's much much
>>>better now.
>>>
>>>First of all I adopted standard sysfs terminology for everything. A
>>>"firmware attribute" is just an attribute_group under the attributes/
>>>directory so everything related to this concept is just called "group"
>>>now. Everything refered as properties in the previous patch are now just
>>>plain "attributes".
>>>
>>>This new API revolves around the `fwat_{bool,enum,int,str}_data`
>>>structs. These hold all the metadata a "firmware_attribute" of that
>>>given type needs.
>>>
>>>These structs also hold `read` and `write` callbacks for the
>>>current_value attribute, because obviously that value is always dynamic.
>>>However the rest of attributes (default_value, display_name, min, max,
>>>etc) are constant.
>>
>> Hi Kurt,
>>
>> In the lenovo-wmi drivers the min/max for multiple attributes are actually dynamic based on if power is AC connected or on battery. Looking at patch 2 I might be able to do some pointer manipulation with the attribute's "data" member for those events to make this work, but it would be a lot easier if there was a simple way for me to call my own functions here instead. Perhaps a function pointer could be used to override the default method here? 
>
>Hi Derek,
>
>All attributes in a given group have the same show method. Maybe we can
>let users override this with their own show method, i.e. Add a
>
>	ssize_t (*attr_show)(struct device *dev, const struct fwat_attribute *attr, const char *buf)
>
>to struct fwat_group_data. That should be fairly simple to implement.
>
>Did you have another solution in mind?
>
>

That should work, yeah. 
- Derek

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API
  2025-06-22  3:42     ` Derek J. Clark
@ 2025-06-22  4:27       ` Kurt Borja
  0 siblings, 0 replies; 12+ messages in thread
From: Kurt Borja @ 2025-06-22  4:27 UTC (permalink / raw)
  To: Derek J. Clark, Hans de Goede, Ilpo Järvinen,
	Thomas Weißschuh, Joshua Grisham, Mark Pearson, Armin Wolf,
	Mario Limonciello
  Cc: Antheas Kapenekakis, Prasanth Ksr, Jorge Lopez,
	platform-driver-x86, linux-kernel, Dell.Client.Kernel

On Sun Jun 22, 2025 at 12:42 AM -03, Derek J. Clark wrote:
>
>
> On June 21, 2025 8:27:06 PM PDT, Kurt Borja <kuurtb@gmail.com> wrote:
>>On Sun Jun 22, 2025 at 12:01 AM -03, Derek J. Clark wrote:
>>>
>>>
>>> On June 21, 2025 5:04:03 PM PDT, Kurt Borja <kuurtb@gmail.com> wrote:
>>>>Hi all,
>>>>
>>>>I apologize for taking so long. I've been a bit busy these last few
>>>>weeks.
>>>>
>>>>After my discussion with Joshua on v2, I realized the API I made was not
>>>>ergonomic at all and it didn't exactly respond to driver needs. In this
>>>>version I tried a completely different approach and IMO it's much much
>>>>better now.
>>>>
>>>>First of all I adopted standard sysfs terminology for everything. A
>>>>"firmware attribute" is just an attribute_group under the attributes/
>>>>directory so everything related to this concept is just called "group"
>>>>now. Everything refered as properties in the previous patch are now just
>>>>plain "attributes".
>>>>
>>>>This new API revolves around the `fwat_{bool,enum,int,str}_data`
>>>>structs. These hold all the metadata a "firmware_attribute" of that
>>>>given type needs.
>>>>
>>>>These structs also hold `read` and `write` callbacks for the
>>>>current_value attribute, because obviously that value is always dynamic.
>>>>However the rest of attributes (default_value, display_name, min, max,
>>>>etc) are constant.
>>>
>>> Hi Kurt,
>>>
>>> In the lenovo-wmi drivers the min/max for multiple attributes are actually dynamic based on if power is AC connected or on battery. Looking at patch 2 I might be able to do some pointer manipulation with the attribute's "data" member for those events to make this work, but it would be a lot easier if there was a simple way for me to call my own functions here instead. Perhaps a function pointer could be used to override the default method here? 
>>
>>Hi Derek,
>>
>>All attributes in a given group have the same show method. Maybe we can
>>let users override this with their own show method, i.e. Add a
>>
>>	ssize_t (*attr_show)(struct device *dev, const struct fwat_attribute *attr, const char *buf)
>>
>>to struct fwat_group_data. That should be fairly simple to implement.
>>
>>Did you have another solution in mind?
>>
>>
>
> That should work, yeah. 
> - Derek

Just realized that's exactly what you said :)

It was indeed easy to implement. It will be there for next version.


-- 
 ~ Kurt


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v3 3/6] platform/x86: firmware_attributes_class: Move header to include directory
  2025-06-22  0:04 ` [PATCH v3 3/6] platform/x86: firmware_attributes_class: Move header to include directory Kurt Borja
@ 2025-06-22  4:28   ` Kurt Borja
  0 siblings, 0 replies; 12+ messages in thread
From: Kurt Borja @ 2025-06-22  4:28 UTC (permalink / raw)
  To: Kurt Borja, Hans de Goede, Ilpo Järvinen,
	Thomas Weißschuh, Joshua Grisham, Mark Pearson, Armin Wolf,
	Mario Limonciello
  Cc: Antheas Kapenekakis, Derek J. Clark, Prasanth Ksr, Jorge Lopez,
	platform-driver-x86, linux-kernel, Dell.Client.Kernel

On Sat Jun 21, 2025 at 9:04 PM -03, Kurt Borja wrote:
> Move firmware_attributes_class.h to include/linux/ to avoid hardcoding
> paths inside drivers/platform/x86/.
>
> Signed-off-by: Kurt Borja <kuurtb@gmail.com>

I forgot to add:

Suggested-by: Joshua Grisham <josh@joshuagrisham.com>

> ---
>  drivers/platform/x86/dell/dell-wmi-sysman/sysman.c                  | 2 +-
>  drivers/platform/x86/firmware_attributes_class.c                    | 2 +-
>  drivers/platform/x86/hp/hp-bioscfg/bioscfg.c                        | 2 +-
>  drivers/platform/x86/lenovo/think-lmi.c                             | 2 +-
>  drivers/platform/x86/samsung-galaxybook.c                           | 2 +-
>  {drivers/platform/x86 => include/linux}/firmware_attributes_class.h | 0
>  6 files changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c
> index d00389b860e4ea0655c740c78bc3751f323b6370..3aec09987ab145508ed05b02e61a6d94edf79484 100644
> --- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c
> +++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c
> @@ -12,8 +12,8 @@
>  #include <linux/module.h>
>  #include <linux/kernel.h>
>  #include <linux/wmi.h>
> +#include <linux/firmware_attributes_class.h>
>  #include "dell-wmi-sysman.h"
> -#include "../../firmware_attributes_class.h"
>  
>  #define MAX_TYPES  4
>  #include <linux/nls.h>
> diff --git a/drivers/platform/x86/firmware_attributes_class.c b/drivers/platform/x86/firmware_attributes_class.c
> index 034f9254240b048f58c97c18062db03f771f8139..af39ed9ad2836147c98b4bb0b89e70e96ee34b71 100644
> --- a/drivers/platform/x86/firmware_attributes_class.c
> +++ b/drivers/platform/x86/firmware_attributes_class.c
> @@ -10,7 +10,7 @@
>  #include <linux/slab.h>
>  #include <linux/types.h>
>  #include <linux/string_choices.h>
> -#include "firmware_attributes_class.h"
> +#include <linux/firmware_attributes_class.h>
>  
>  #define to_fwat_bool_data(_c) \
>  	container_of_const(_c, struct fwat_bool_data, group)
> diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
> index 13237890fc92002e7e730b1c235ddf068a6737cd..2df31af8a3b4ac88710af1fae2d5dabbb3185f1d 100644
> --- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
> +++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
> @@ -12,7 +12,7 @@
>  #include <linux/kernel.h>
>  #include <linux/wmi.h>
>  #include "bioscfg.h"
> -#include "../../firmware_attributes_class.h"
> +#include <linux/firmware_attributes_class.h>
>  #include <linux/nls.h>
>  #include <linux/errno.h>
>  
> diff --git a/drivers/platform/x86/lenovo/think-lmi.c b/drivers/platform/x86/lenovo/think-lmi.c
> index 34a47269e3d34d2eda6b71af73892656cd2bf67d..f61a6287eb0ebe9ac4c0c9445c3b54c12b276691 100644
> --- a/drivers/platform/x86/lenovo/think-lmi.c
> +++ b/drivers/platform/x86/lenovo/think-lmi.c
> @@ -20,7 +20,7 @@
>  #include <linux/types.h>
>  #include <linux/dmi.h>
>  #include <linux/wmi.h>
> -#include "../firmware_attributes_class.h"
> +#include <linux/firmware_attributes_class.h>
>  #include "think-lmi.h"
>  
>  static bool debug_support;
> diff --git a/drivers/platform/x86/samsung-galaxybook.c b/drivers/platform/x86/samsung-galaxybook.c
> index 5878a351993eb05a4c5c2c75b4915d972ce9becc..9a5a7b956a9f6a2738470e83ce93f4cccf4bf3b4 100644
> --- a/drivers/platform/x86/samsung-galaxybook.c
> +++ b/drivers/platform/x86/samsung-galaxybook.c
> @@ -28,7 +28,7 @@
>  #include <linux/uuid.h>
>  #include <linux/workqueue.h>
>  #include <acpi/battery.h>
> -#include "firmware_attributes_class.h"
> +#include <linux/firmware_attributes_class.h>
>  
>  #define DRIVER_NAME "samsung-galaxybook"
>  
> diff --git a/drivers/platform/x86/firmware_attributes_class.h b/include/linux/firmware_attributes_class.h
> similarity index 100%
> rename from drivers/platform/x86/firmware_attributes_class.h
> rename to include/linux/firmware_attributes_class.h


-- 
 ~ Kurt


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2025-06-22  4:29 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-22  0:04 [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API Kurt Borja
2025-06-22  0:04 ` [PATCH v3 1/6] platform/x86: firmware_attributes_class: Add device initialization methods Kurt Borja
2025-06-22  0:04 ` [PATCH v3 2/6] platform/x86: firmware_attributes_class: Add high level API for the attributes interface Kurt Borja
2025-06-22  0:04 ` [PATCH v3 3/6] platform/x86: firmware_attributes_class: Move header to include directory Kurt Borja
2025-06-22  4:28   ` Kurt Borja
2025-06-22  0:04 ` [PATCH v3 4/6] platform/x86: samsung-galaxybook: Transition new firmware_attributes API Kurt Borja
2025-06-22  0:04 ` [PATCH v3 5/6] Documentation: ABI: Update sysfs-class-firmware-attributes documentation Kurt Borja
2025-06-22  0:04 ` [PATCH v3 6/6] MAINTAINERS: Add FIRMWARE ATTRIBUTES CLASS entry Kurt Borja
2025-06-22  3:01 ` [PATCH v3 0/6] platform/x86: firmware_attributes_class: Add a high level API Derek J. Clark
2025-06-22  3:27   ` Kurt Borja
2025-06-22  3:42     ` Derek J. Clark
2025-06-22  4:27       ` Kurt Borja

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.