Re: [PATCH v1] platform/x86: asus-wmi: add TUF keyboard RGB readback support
From: Denis Benato
Date: Fri Sep 25 2026 - 16:25:28 EST
Il 25 settembre 2026 22:07:44 CEST, Bartu Alev <bartualev@xxxxxxxxx> ha scritto:
>TUF Gaming laptops support setting keyboard RGB lighting modes and
>power states via kbd_rgb_mode and kbd_rgb_state, but both attributes
>are currently write-only (DEVICE_ATTR_WO). Consequently, userspace
>utilities have no way to query the active hardware configuration.
>
>Add readback support by querying the TUF status device ID 0x0010005B
>through the WMI DSTS method. On supported platforms, this evaluates the
>ACPI DSDT method EC0.KBLS(), which returns a 16-byte status buffer
>containing the active lighting mode, RGB color channels, effect speed,
>and power-state bitmask.
>
>Introduce kbd_rgb_read_status() to retrieve and validate the KBLS
>buffer. Convert kbd_rgb_mode and kbd_rgb_state to DEVICE_ATTR_RW. Map the
>hardware speed codes (0xe1, 0xeb, 0xf5) to their sysfs indices (0, 1, 2).
>Since the status buffer reports the active state rather than an action
>command, emit a synthetic leading '1' to maintain format symmetry with
>the input format expected by userspace.
>
1 or 0 is for immediate vs save to flash... Which one of the two is the reported one?
>Additionally, relabel the fourth field in kbd_rgb_state_index from
>"keyboard" to "shutdown". When the interface was originally introduced,
>the purpose of BIT(7) was unknown and noted as having no effect. In
>hardware, this bit gates whether the keyboard LED remains powered during
>the ACPI S5 sequence. Relabeling it accurately reflects its
>actual hardware behavior.
>
Good finding.
This however goes in its own.patch with an appropriate Fixes tag.
>Assisted-by: GLM-5.3
>Signed-off-by: Bartu Alev <bartualev@xxxxxxxxx>
>---
>- Tested on ASUS TUF Gaming A15 (FA507NV) running Linux 7.2.6
>
> drivers/platform/x86/asus-wmi.c | 87 ++++++++++++++++++++--
> include/linux/platform_data/x86/asus-wmi.h | 3 +
> 2 files changed, 84 insertions(+), 6 deletions(-)
>
>diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
>index a65090429ca7..36eff5adac52 100644
>--- a/drivers/platform/x86/asus-wmi.c
>+++ b/drivers/platform/x86/asus-wmi.c
>@@ -1046,7 +1046,58 @@ static ssize_t gpu_mux_mode_store(struct device *dev,
> static DEVICE_ATTR_RW(gpu_mux_mode);
> #endif /* IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) */
>
>+static int kbd_rgb_read_status(u8 data[16])
>+{
>+ int err;
>+
>+ err = asus_wmi_evaluate_method_buf(ASUS_WMI_METHODID_DSTS,
>+ ASUS_WMI_DEVID_TUF_RGB_STATUS,
>+ 0, data, 16);
>+
>+ if (err)
>+ return err < 0 ? err : -ENODEV;
>+
>+ /* DUBF[0] is a constant 1 set by the AML: anything else is not KBLS */
>+ if (data[0] != 1)
>+ return -ENODEV;
>+
>+ return 0;
>+}
>+
> /* TUF Laptop Keyboard RGB Modes **********************************************/
>+static ssize_t kbd_rgb_mode_show(struct device *dev,
>+ struct device_attribute *attr,
>+ char *buf)
>+{
>+ u8 data[16] = {};
>+ u32 speed;
>+ int err;
>+
>+ err = kbd_rgb_read_status(data);
>+ if (err)
>+ return err;
>+
>+ /* Map hardware speed codes back to sysfs index:
>+ * 0xe1 -> 0 (slow), 0xeb -> 1 (normal), 0xf5 -> 2 (fast)
>+ */
>+ switch (data[5]) {
>+ case 0xe1:
>+ speed = 0;
>+ break;
>+ case 0xeb:
>+ speed = 1;
>+ break;
>+ case 0xf5:
>+ speed = 2;
>+ break;
>+ default:
>+ speed = 1;
>+ break;
>+ }
>+
>+ return sysfs_emit(buf, "1 %d %d %d %d %d\n",
>+ data[1], data[2], data[3], data[4], speed);
>+}
> static ssize_t kbd_rgb_mode_store(struct device *dev,
> struct device_attribute *attr,
> const char *buf, size_t count)
>@@ -1099,7 +1150,7 @@ static ssize_t kbd_rgb_mode_store(struct device *dev,
>
> return count;
> }
>-static DEVICE_ATTR_WO(kbd_rgb_mode);
>+static DEVICE_ATTR_RW(kbd_rgb_mode);
>
> static DEVICE_STRING_ATTR_RO(kbd_rgb_mode_index, 0444,
> "cmd mode red green blue speed");
>@@ -1115,14 +1166,38 @@ static const struct attribute_group kbd_rgb_mode_group = {
> };
>
> /* TUF Laptop Keyboard RGB State **********************************************/
>+static ssize_t kbd_rgb_state_show(struct device *dev,
>+ struct device_attribute *attr,
>+ char *buf)
>+{
>+ u8 data[16] = {};
>+ u8 flags;
>+ int err;
>+
>+ err = kbd_rgb_read_status(data);
>+ if (err)
>+ return err;
>+
>+ /*
>+ * data[6] power-state bitmask:
>+ * BIT(1) boot, BIT(3) awake, BIT(5) sleep, BIT(7) shutdown
>+ */
>+ flags = data[6];
>+
>+ return sysfs_emit(buf, "1 %d %d %d %d\n",
>+ !!(flags & BIT(1)),
>+ !!(flags & BIT(3)),
>+ !!(flags & BIT(5)),
>+ !!(flags & BIT(7)));
>+}
> static ssize_t kbd_rgb_state_store(struct device *dev,
> struct device_attribute *attr,
> const char *buf, size_t count)
> {
>- u32 flags, cmd, boot, awake, sleep, keyboard;
>+ u32 flags, cmd, boot, awake, sleep, shutdown;
> int err;
>
>- if (sscanf(buf, "%d %d %d %d %d", &cmd, &boot, &awake, &sleep, &keyboard) != 5)
>+ if (sscanf(buf, "%d %d %d %d %d", &cmd, &boot, &awake, &sleep, &shutdown) != 5)
> return -EINVAL;
>
> if (cmd)
>@@ -1135,7 +1210,7 @@ static ssize_t kbd_rgb_state_store(struct device *dev,
> flags |= BIT(3);
> if (sleep)
> flags |= BIT(5);
>- if (keyboard)
>+ if (shutdown)
> flags |= BIT(7);
>
> /* 0xbd is the required default arg0 for the method. Nothing happens otherwise */
>@@ -1146,10 +1221,10 @@ static ssize_t kbd_rgb_state_store(struct device *dev,
>
> return count;
> }
>-static DEVICE_ATTR_WO(kbd_rgb_state);
>+static DEVICE_ATTR_RW(kbd_rgb_state);
>
> static DEVICE_STRING_ATTR_RO(kbd_rgb_state_index, 0444,
>- "cmd boot awake sleep keyboard");
>+ "cmd boot awake sleep shutdown");
>
> static struct attribute *kbd_rgb_state_attrs[] = {
> &dev_attr_kbd_rgb_state.attr,
>diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
>index b5ed8c83ace1..a6eb8f8cf159 100644
>--- a/include/linux/platform_data/x86/asus-wmi.h
>+++ b/include/linux/platform_data/x86/asus-wmi.h
>@@ -161,6 +161,9 @@
> /* TUF laptop RGB power/state */
> #define ASUS_WMI_DEVID_TUF_RGB_STATE 0x00100057
>
>+/* TUF laptop RGB keyboard status */
>+#define ASUS_WMI_DEVID_TUF_RGB_STATUS 0x0010005B
State versus status... Uhm...
>+
> /* Bootup sound control */
> #define ASUS_WMI_DEVID_BOOT_SOUND 0x00130022
>
Hi,
Thanks for this!