* [PATCH v1] platform/x86: asus-wmi: add TUF keyboard RGB readback support
@ 2026-09-25 20:07 Bartu Alev
2026-09-25 20:25 ` Denis Benato
2026-09-26 0:56 ` [PATCH v2 0/2] platform/x86: asus-wmi: Fix TUF keyboard shutdown naming and add RGB readback Bartu Alev
0 siblings, 2 replies; 6+ messages in thread
From: Bartu Alev @ 2026-09-25 20:07 UTC (permalink / raw)
To: linux-kernel
Cc: platform-driver-x86, Hans de Goede, Ilpo Järvinen,
Luke D . Jones, Denis Benato, Bartu Alev
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.
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.
Assisted-by: GLM-5.3
Signed-off-by: Bartu Alev <bartualev@gmail.com>
---
- 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
+
/* Bootup sound control */
#define ASUS_WMI_DEVID_BOOT_SOUND 0x00130022
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH v1] platform/x86: asus-wmi: add TUF keyboard RGB readback support
2026-09-25 20:07 [PATCH v1] platform/x86: asus-wmi: add TUF keyboard RGB readback support Bartu Alev
@ 2026-09-25 20:25 ` Denis Benato
2026-09-26 0:56 ` [PATCH v2 0/2] platform/x86: asus-wmi: Fix TUF keyboard shutdown naming and add RGB readback Bartu Alev
1 sibling, 0 replies; 6+ messages in thread
From: Denis Benato @ 2026-09-25 20:25 UTC (permalink / raw)
To: Bartu Alev, linux-kernel
Cc: platform-driver-x86, Hans de Goede, Ilpo Järvinen, Luke D . Jones
Il 25 settembre 2026 22:07:44 CEST, Bartu Alev <bartualev@gmail.com> 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@gmail.com>
>---
>- 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!
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v2 0/2] platform/x86: asus-wmi: Fix TUF keyboard shutdown naming and add RGB readback
2026-09-25 20:07 [PATCH v1] platform/x86: asus-wmi: add TUF keyboard RGB readback support Bartu Alev
2026-09-25 20:25 ` Denis Benato
@ 2026-09-26 0:56 ` Bartu Alev
2026-09-26 0:56 ` [PATCH v2 1/2] platform/x86: asus-wmi: fix TUF keyboard power state shutdown naming Bartu Alev
2026-09-26 0:56 ` [PATCH v2 2/2] platform/x86: asus-wmi: add TUF keyboard RGB readback support Bartu Alev
1 sibling, 2 replies; 6+ messages in thread
From: Bartu Alev @ 2026-09-26 0:56 UTC (permalink / raw)
To: platform-driver-x86
Cc: linux-kernel, Hans de Goede, Ilpo Järvinen, Luke D . Jones,
Denis Benato, Bartu Alev
This series fixes the historical misnomer of the TUF keyboard
shutdown power-state flag and adds sysfs readback support for the
TUF RGB mode and power states.
Patch 1 relabels the fourth state field of kbd_rgb_state_index from
"keyboard" to "shutdown": BIT(7) gates the keyboard backlight during
the ACPI S5 power-off sequence (A/B verified on FA507NV: bit set =
keyboard lights up white at power-off, clear = dark shutdown).
Patch 2 adds readback support via DSTS 0x0010005B, which evaluates
the DSDT method EC0.KBLS() and returns the live EC RAM state as a
16-byte buffer (mode, R/G/B, speed, power flags). Both kbd_rgb_mode
and kbd_rgb_state become readable.
Re: the cmd field question raised during review of v1 - the KBLS
buffer carries no command byte. "Immediate (0xb3) vs save (0xb4)" is
a property of the write verb, consumed by the SMM handler; neither
the EC firmware nor the ERM2 mirror retains it, and both verbs result
in the same applied state (verified by writing with cmd=0 and cmd=1
and reading back identical semantics). The leading '1' in the output
is therefore synthetic, chosen as the canonical input form so that
readback round-trips with what userspace writes.
Changes in v2:
- Split into a 2-patch series:
* patch 1: isolated naming fix with a Fixes: tag for -stable
* patch 2: the readback feature
- Renamed the new DEVID to ASUS_WMI_DEVID_TUF_RGB_READBACK to avoid
confusion with ASUS_WMI_DEVID_TUF_RGB_STATE.
- Documented the synthetic cmd field in the patch 2 commit message
and here.
Bartu Alev (2):
platform/x86: asus-wmi: fix TUF keyboard power state shutdown naming
platform/x86: asus-wmi: add TUF keyboard RGB readback support
drivers/platform/x86/asus-wmi.c | 87 ++++++++++++++++++++--
include/linux/platform_data/x86/asus-wmi.h | 3 +
2 files changed, 84 insertions(+), 6 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 1/2] platform/x86: asus-wmi: fix TUF keyboard power state shutdown naming
2026-09-26 0:56 ` [PATCH v2 0/2] platform/x86: asus-wmi: Fix TUF keyboard shutdown naming and add RGB readback Bartu Alev
@ 2026-09-26 0:56 ` Bartu Alev
2026-09-26 0:56 ` [PATCH v2 2/2] platform/x86: asus-wmi: add TUF keyboard RGB readback support Bartu Alev
1 sibling, 0 replies; 6+ messages in thread
From: Bartu Alev @ 2026-09-26 0:56 UTC (permalink / raw)
To: platform-driver-x86
Cc: linux-kernel, Hans de Goede, Ilpo Järvinen, Luke D . Jones,
Denis Benato, Bartu Alev, stable, Denis Benato
The fourth state flag of kbd_rgb_state_index is currently named
"keyboard" and was historically assumed to have no effect. In hardware,
this bit (BIT(7) in the WMI DEVS payload) gates whether the keyboard
backlight remains illuminated during the ACPI S5 power-off sequence.
Hardware A/B testing on ASUS TUF Gaming A15 (FA507NV) confirms that
setting this bit causes the keyboard to light up white upon power-off,
while clearing it ensures a dark, clean shutdown.
Relabel this field from "keyboard" to "shutdown" to accurately describe
its hardware function and align it with userspace tooling (asusctl,
g-helper).
Fixes: 61f64515299e ("platform/x86: asus-wmi: Implement TUF laptop keyboard power states")
Cc: stable@vger.kernel.org
Suggested-by: Denis Benato <denis.benato@linux.dev>
Signed-off-by: Bartu Alev <bartualev@gmail.com>
---
drivers/platform/x86/asus-wmi.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index a65090429ca7..db6ee1974838 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1119,10 +1119,10 @@ 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 +1135,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 */
@@ -1149,7 +1149,7 @@ static ssize_t kbd_rgb_state_store(struct device *dev,
static DEVICE_ATTR_WO(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,
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v2 2/2] platform/x86: asus-wmi: add TUF keyboard RGB readback support
2026-09-26 0:56 ` [PATCH v2 0/2] platform/x86: asus-wmi: Fix TUF keyboard shutdown naming and add RGB readback Bartu Alev
2026-09-26 0:56 ` [PATCH v2 1/2] platform/x86: asus-wmi: fix TUF keyboard power state shutdown naming Bartu Alev
@ 2026-09-26 0:56 ` Bartu Alev
2026-09-26 1:41 ` Denis Benato
1 sibling, 1 reply; 6+ messages in thread
From: Bartu Alev @ 2026-09-26 0:56 UTC (permalink / raw)
To: platform-driver-x86
Cc: linux-kernel, Hans de Goede, Ilpo Järvinen, Luke D . Jones,
Denis Benato, Bartu Alev, Denis Benato
TUF Gaming laptops expose kbd_rgb_mode and kbd_rgb_state as write-only
attributes (DEVICE_ATTR_WO), preventing userspace from querying the
active hardware configuration.
Add readback support by querying ASUS_WMI_DEVID_TUF_RGB_READBACK
(0x0010005B) via the WMI DSTS method. On supported platforms this
evaluates the DSDT method EC0.KBLS(), which returns a 16-byte buffer
containing the active lighting mode, RGB color channels, effect speed
and power-state flags.
Introduce kbd_rgb_read_status() to evaluate and validate the buffer,
and convert both attributes to DEVICE_ATTR_RW. Map the hardware speed
codes (0xe1, 0xeb, 0xf5) to their sysfs indices (0, 1, 2).
The command field is not part of the status buffer: "immediate vs
save-to-flash" is a property of the write verb (0xb3/0xb4), not of
readable state, and the EC mirror is updated identically by both.
Readback therefore emits a synthetic leading '1' - the canonical
input form userspace writes - so that output matches input.
Suggested-by: Denis Benato <denis.benato@linux.dev>
Signed-off-by: Bartu Alev <bartualev@gmail.com>
---
drivers/platform/x86/asus-wmi.c | 79 +++++++++++++++++++++-
include/linux/platform_data/x86/asus-wmi.h | 3 +
2 files changed, 80 insertions(+), 2 deletions(-)
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index db6ee1974838..fe1dcc7701ad 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_READBACK,
+ 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,6 +1166,30 @@ 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)
@@ -1146,7 +1221,7 @@ 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 shutdown");
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index b5ed8c83ace1..1447c7f354bc 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 readback*/
+#define ASUS_WMI_DEVID_TUF_RGB_READBACK 0x0010005B
+
/* Bootup sound control */
#define ASUS_WMI_DEVID_BOOT_SOUND 0x00130022
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH v2 2/2] platform/x86: asus-wmi: add TUF keyboard RGB readback support
2026-09-26 0:56 ` [PATCH v2 2/2] platform/x86: asus-wmi: add TUF keyboard RGB readback support Bartu Alev
@ 2026-09-26 1:41 ` Denis Benato
0 siblings, 0 replies; 6+ messages in thread
From: Denis Benato @ 2026-09-26 1:41 UTC (permalink / raw)
To: Bartu Alev, platform-driver-x86
Cc: linux-kernel, Hans de Goede, Ilpo Järvinen, Luke D . Jones,
Denis Benato
On 9/26/26 02:56, Bartu Alev wrote:
> TUF Gaming laptops expose kbd_rgb_mode and kbd_rgb_state as write-only
> attributes (DEVICE_ATTR_WO), preventing userspace from querying the
> active hardware configuration.
>
> Add readback support by querying ASUS_WMI_DEVID_TUF_RGB_READBACK
> (0x0010005B) via the WMI DSTS method. On supported platforms this
> evaluates the DSDT method EC0.KBLS(), which returns a 16-byte buffer
> containing the active lighting mode, RGB color channels, effect speed
> and power-state flags.
>
> Introduce kbd_rgb_read_status() to evaluate and validate the buffer,
> and convert both attributes to DEVICE_ATTR_RW. Map the hardware speed
> codes (0xe1, 0xeb, 0xf5) to their sysfs indices (0, 1, 2).
>
> The command field is not part of the status buffer: "immediate vs
> save-to-flash" is a property of the write verb (0xb3/0xb4), not of
> readable state, and the EC mirror is updated identically by both.
> Readback therefore emits a synthetic leading '1' - the canonical
> input form userspace writes - so that output matches input.
>
> Suggested-by: Denis Benato <denis.benato@linux.dev>
> Signed-off-by: Bartu Alev <bartualev@gmail.com>
> ---
> drivers/platform/x86/asus-wmi.c | 79 +++++++++++++++++++++-
> include/linux/platform_data/x86/asus-wmi.h | 3 +
> 2 files changed, 80 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
> index db6ee1974838..fe1dcc7701ad 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_READBACK,
> + 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;
> +
-ENODEV or -ENOTSUPP ? Which one is better suited for these kind of things?
If we go with two separate sysfs attrs you don't register the read one,
otherwise I am not sure.
> + 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);
We had this discussion in discord so I want to update everyone reading:
the status returned is the current one and both cmd=0 and cmd=1 on write
update the current status.
Therefore this is an asymmetry that doesn't really need to be,
what if we introduce another sysfs that is RO? Ilpo?
> +}
> 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,6 +1166,30 @@ 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)
> @@ -1146,7 +1221,7 @@ 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 shutdown");
> diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
> index b5ed8c83ace1..1447c7f354bc 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
>
The pre-existing one should probably be renamed to make clear
it's write only and it is a command... In its own patch.
ASUS_WMI_DEVID_TUF_RGB_CMD probably?
> +/* TUF laptop RGB keyboard status readback*/
> +#define ASUS_WMI_DEVID_TUF_RGB_READBACK 0x0010005B
> +
ASUS_WMI_DEVID_TUF_RGB_READ_STATUS ?
> /* Bootup sound control */
> #define ASUS_WMI_DEVID_BOOT_SOUND 0x00130022
>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-09-26 1:41 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-25 20:07 [PATCH v1] platform/x86: asus-wmi: add TUF keyboard RGB readback support Bartu Alev
2026-09-25 20:25 ` Denis Benato
2026-09-26 0:56 ` [PATCH v2 0/2] platform/x86: asus-wmi: Fix TUF keyboard shutdown naming and add RGB readback Bartu Alev
2026-09-26 0:56 ` [PATCH v2 1/2] platform/x86: asus-wmi: fix TUF keyboard power state shutdown naming Bartu Alev
2026-09-26 0:56 ` [PATCH v2 2/2] platform/x86: asus-wmi: add TUF keyboard RGB readback support Bartu Alev
2026-09-26 1:41 ` Denis Benato
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®