[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260210191938.137234-3-i@rong.moe>
Date: Wed, 11 Feb 2026 03:19:37 +0800
From: Rong Zhang <i@...g.moe>
To: Mark Pearson <mpearson-lenovo@...ebb.ca>,
"Derek J. Clark" <derekjohn.clark@...il.com>,
Armin Wolf <W_Armin@....de>,
Hans de Goede <hansg@...nel.org>,
Ilpo Järvinen <ilpo.jarvinen@...ux.intel.com>
Cc: Rong Zhang <i@...g.moe>,
Kurt Borja <kuurtb@...il.com>,
platform-driver-x86@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [PATCH 2/2] platform/x86: lenovo-wmi-capdata: Add debugfs file for dumping capdata
The Lenovo GameZone/Other interfaces have some delicate divergences
among different devices. When making a bug report or adding support for
new devices/interfaces, capdata is the most important information to
cross-check with.
Add a debugfs file (lenovo_wmi/<device_name>/capdata), so that users can
dump capdata and include it in their reports.
Since `struct capdata01' is just an extension to `struct capdata00',
also converts the former to include the latter anonymously
(-fms-extensions, since v6.19). In this manner type casting won't be
confusing.
Signed-off-by: Rong Zhang <i@...g.moe>
---
drivers/platform/x86/lenovo/Kconfig | 1 +
drivers/platform/x86/lenovo/wmi-capdata.c | 121 ++++++++++++++++++++++
drivers/platform/x86/lenovo/wmi-capdata.h | 4 +-
3 files changed, 123 insertions(+), 3 deletions(-)
diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig
index f885127b007f1..8357971c76d80 100644
--- a/drivers/platform/x86/lenovo/Kconfig
+++ b/drivers/platform/x86/lenovo/Kconfig
@@ -236,6 +236,7 @@ config YT2_1380
config LENOVO_WMI_CAPDATA
tristate
depends on ACPI_WMI
+ depends on LENOVO_WMI_HELPERS
config LENOVO_WMI_EVENTS
tristate
diff --git a/drivers/platform/x86/lenovo/wmi-capdata.c b/drivers/platform/x86/lenovo/wmi-capdata.c
index ee1fb02d8e31e..ca478b45119bc 100644
--- a/drivers/platform/x86/lenovo/wmi-capdata.c
+++ b/drivers/platform/x86/lenovo/wmi-capdata.c
@@ -32,6 +32,7 @@
#include <linux/cleanup.h>
#include <linux/component.h>
#include <linux/container_of.h>
+#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
@@ -43,11 +44,13 @@
#include <linux/mutex_types.h>
#include <linux/notifier.h>
#include <linux/overflow.h>
+#include <linux/seq_file.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/wmi.h>
#include "wmi-capdata.h"
+#include "wmi-helpers.h"
#define LENOVO_CAPABILITY_DATA_00_GUID "362A3AFE-3D96-4665-8530-96DAD5BB300E"
#define LENOVO_CAPABILITY_DATA_01_GUID "7A8F5407-CB67-4D6E-B547-39B3BE018154"
@@ -88,6 +91,7 @@ struct lwmi_cd_priv {
struct notifier_block acpi_nb; /* ACPI events */
struct wmi_device *wdev;
struct cd_list *list;
+ struct dentry *debugfs_dir;
/*
* A capdata device may be a component master of another capdata device.
@@ -118,6 +122,8 @@ struct cd_list {
static struct wmi_driver lwmi_cd_driver;
+/* ======== Device components ======== */
+
/**
* lwmi_cd_match() - Match rule for the master driver.
* @dev: Pointer to the capability data parent device.
@@ -471,6 +477,116 @@ EXPORT_SYMBOL_NS_GPL(lwmi_cd01_get_data, "LENOVO_WMI_CAPDATA");
DEF_LWMI_CDXX_GET_DATA(cd_fan, LENOVO_FAN_TEST_DATA, struct capdata_fan);
EXPORT_SYMBOL_NS_GPL(lwmi_cd_fan_get_data, "LENOVO_WMI_CAPDATA");
+/* ======== debugfs ======== */
+
+/**
+ * lwmi_cd00_show() - Dump capdata00
+ * @s: Pointer to seq_file where the capdata00 is dumped.
+ * @cd00: Pointer to a capdata00 struct to be dumped.
+ */
+static void lwmi_cd00_show(struct seq_file *s, struct capdata00 *cd00)
+{
+ u8 dev = FIELD_GET(LWMI_ATTR_DEV_ID_MASK, cd00->id);
+ u8 feat = FIELD_GET(LWMI_ATTR_FEAT_ID_MASK, cd00->id);
+ u8 mode = FIELD_GET(LWMI_ATTR_MODE_ID_MASK, cd00->id);
+ u8 type = FIELD_GET(LWMI_ATTR_TYPE_ID_MASK, cd00->id);
+ bool extra = cd00->supported & ~(LWMI_SUPP_GET | LWMI_SUPP_SET | LWMI_SUPP_VALID);
+ bool get = cd00->supported & LWMI_SUPP_GET;
+ bool set = cd00->supported & LWMI_SUPP_SET;
+ bool valid = cd00->supported & LWMI_SUPP_VALID;
+
+ seq_printf(s, " id: 0x%08x [dev: %2u, feat: %2u, mode: %2u, type: %2u]\n",
+ cd00->id, dev, feat, mode, type);
+
+ seq_printf(s, " supported: 0x%08x [%c%c%c%c]\n", cd00->supported,
+ extra ? '+' : ' ',
+ get ? 'R' : ' ',
+ set ? 'W' : ' ',
+ valid ? 'V' : ' ');
+
+ seq_printf(s, " default_value: %u\n", cd00->default_value);
+}
+
+/**
+ * lwmi_cd01_show() - Dump capdata01
+ * @s: Pointer to seq_file where the capdata01 is dumped.
+ * @cd01: Pointer to a capdata01 struct to be dumped.
+ */
+static void lwmi_cd01_show(struct seq_file *s, struct capdata01 *cd01)
+{
+ /* capdata01 is an extension to capdata00. */
+ lwmi_cd00_show(s, (struct capdata00 *)cd01);
+
+ seq_printf(s, " step: %u\n", cd01->step);
+ seq_printf(s, " min_value: %u\n", cd01->min_value);
+ seq_printf(s, " max_value: %u\n", cd01->max_value);
+}
+
+/**
+ * lwmi_cd_fan_show() - Dump capdata_fan
+ * @s: Pointer to seq_file where the capdata_fan is dumped.
+ * @cd_fan: Pointer to a capdata_fan struct to be dumped.
+ */
+static void lwmi_cd_fan_show(struct seq_file *s, struct capdata_fan *cd_fan)
+{
+ seq_printf(s, " id: %u\n", cd_fan->id);
+ seq_printf(s, " min_rpm: %u\n", cd_fan->min_rpm);
+ seq_printf(s, " max_rpm: %u\n", cd_fan->max_rpm);
+}
+
+/**
+ * lwmi_cd_debugfs_show() - Dump capability data to debugfs
+ * @s: Pointer to seq_file where the capability data is dumped.
+ * @data: unused.
+ *
+ * Return: 0
+ */
+static int lwmi_cd_debugfs_show(struct seq_file *s, void *data)
+{
+ struct lwmi_cd_priv *priv = s->private;
+ u8 idx;
+
+ guard(mutex)(&priv->list->list_mutex);
+
+ /* lwmi_cd_alloc() ensured priv->list->type must be a valid type. */
+ for (idx = 0; idx < priv->list->count; idx++) {
+ seq_printf(s, "%s[%u]:\n", lwmi_cd_table[priv->list->type].name, idx);
+
+ if (priv->list->type == LENOVO_CAPABILITY_DATA_00)
+ lwmi_cd00_show(s, &priv->list->cd00[idx]);
+ else if (priv->list->type == LENOVO_CAPABILITY_DATA_01)
+ lwmi_cd01_show(s, &priv->list->cd01[idx]);
+ else if (priv->list->type == LENOVO_FAN_TEST_DATA)
+ lwmi_cd_fan_show(s, &priv->list->cd_fan[idx]);
+ }
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(lwmi_cd_debugfs);
+
+/**
+ * lwmi_cd_debugfs_add() - Create debugfs directory and files for a device
+ * @priv: lenovo-wmi-capdata driver data.
+ */
+static void lwmi_cd_debugfs_add(struct lwmi_cd_priv *priv)
+{
+ priv->debugfs_dir = lwmi_debugfs_create_dir(priv->wdev);
+
+ debugfs_create_file("capdata", 0444, priv->debugfs_dir, priv, &lwmi_cd_debugfs_fops);
+}
+
+/**
+ * lwmi_cd_debugfs_remove() - Remove debugfs directory for a device
+ * @priv: lenovo-wmi-capdata driver data.
+ */
+static void lwmi_cd_debugfs_remove(struct lwmi_cd_priv *priv)
+{
+ debugfs_remove_recursive(priv->debugfs_dir);
+ priv->debugfs_dir = NULL;
+}
+
+/* ======== WMI interface ======== */
+
/**
* lwmi_cd_cache() - Cache all WMI data block information
* @priv: lenovo-wmi-capdata driver data.
@@ -773,6 +889,8 @@ static int lwmi_cd_probe(struct wmi_device *wdev, const void *context)
dev_err(&wdev->dev, "failed to register %s: %d\n",
info->name, ret);
} else {
+ lwmi_cd_debugfs_add(priv);
+
dev_dbg(&wdev->dev, "registered %s with %u items\n",
info->name, priv->list->count);
}
@@ -783,6 +901,8 @@ static void lwmi_cd_remove(struct wmi_device *wdev)
{
struct lwmi_cd_priv *priv = dev_get_drvdata(&wdev->dev);
+ lwmi_cd_debugfs_remove(priv);
+
switch (priv->list->type) {
case LENOVO_CAPABILITY_DATA_00:
lwmi_cd_sub_master_del(priv);
@@ -822,6 +942,7 @@ static struct wmi_driver lwmi_cd_driver = {
module_wmi_driver(lwmi_cd_driver);
+MODULE_IMPORT_NS("LENOVO_WMI_HELPERS");
MODULE_DEVICE_TABLE(wmi, lwmi_cd_id_table);
MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@...il.com>");
MODULE_AUTHOR("Rong Zhang <i@...g.moe>");
diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h
index 8c1df3efcc553..034a6e48be071 100644
--- a/drivers/platform/x86/lenovo/wmi-capdata.h
+++ b/drivers/platform/x86/lenovo/wmi-capdata.h
@@ -30,9 +30,7 @@ struct capdata00 {
};
struct capdata01 {
- u32 id;
- u32 supported;
- u32 default_value;
+ struct capdata00;
u32 step;
u32 min_value;
u32 max_value;
--
2.51.0
Powered by blists - more mailing lists