* [PATCH] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models
@ 2026-05-24 12:15 Shaposhnikov Daniil
2026-05-25 10:16 ` Ilpo Järvinen
` (3 more replies)
0 siblings, 4 replies; 11+ messages in thread
From: Shaposhnikov Daniil @ 2026-05-24 12:15 UTC (permalink / raw)
To: hansg
Cc: ilpo.jarvinen, ayman.bagabas, platform-driver-x86, linux-kernel,
Shaposhnikov Daniil
Newer Huawei laptops (e.g. FLMH-XX / MateBook 14 2024) no longer support
the legacy WMI interface for Fn-lock control. Instead, they expose direct
ACPI methods \GFRS and \SFRS (Get/Set Fn key Reversal Status) which
communicate with the EC via registers 0x6B (read) and 0x6C (write).
Add huawei_acpi_fn_lock_get() and huawei_acpi_fn_lock_set() helpers that
use acpi_evaluate_object() to call these methods. Both
huawei_wmi_fn_lock_get() and huawei_wmi_fn_lock_set() now probe for
\GFRS/\SFRS via acpi_has_method() first and fall back to the legacy WMI
path if not present.
Tested on: HUAWEI FLMH-XX (MateBook 14 2024),
CachyOS (kernel 7.0.9-1-cachyos).
Signed-off-by: Shaposhnikov Daniil <2minesweeper2@gmail.com>
---
drivers/platform/x86/huawei-wmi.c | 95 +++++++++++++++++++++++++++++++
1 file changed, 95 insertions(+)
diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c
index 93cca17fdf58..19cd8f1a8e33 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -527,11 +527,101 @@ static void huawei_wmi_battery_exit(struct device *dev)
/* Fn lock */
+/*
+ * Newer Huawei models (e.g. HUAWEI FLMH-XX / MateBook 14 2024) use direct
+ * ACPI methods \GFRS / \SFRS (Get/Set Fn key Reversal Status) to control
+ * Fn-lock via EC registers 0x6B (read) and 0x6C (write).
+ *
+ * GFRS response buffer layout:
+ * byte[0] = STAT (0 = success)
+ * byte[1] = 0x01 (fn-lock off) or 0x02 (fn-lock on)
+ *
+ * SFRS argument layout (CreateByteField(Arg0, 0x02, FRSR)):
+ * Value is read from byte[2] of the integer argument, so it must be
+ * passed as (value << 16):
+ * (1 << 16) = fn-lock off (writes 0x55 to EC 0x6C)
+ * (2 << 16) = fn-lock on (writes 0x5A to EC 0x6C)
+ */
+
+static int huawei_acpi_fn_lock_get(int *on)
+{
+ union acpi_object acpi_arg, *obj;
+ struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg };
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+ u8 val;
+
+ acpi_arg.type = ACPI_TYPE_INTEGER;
+ acpi_arg.integer.value = 0;
+
+ status = acpi_evaluate_object(NULL, "\\GFRS", &arg_list, &output);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ obj = output.pointer;
+ if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 2) {
+ kfree(obj);
+ return -ENODATA;
+ }
+
+ /* byte[0] = STAT (0 = success), byte[1] = 1 (off) or 2 (on) */
+ if (obj->buffer.pointer[0] != 0) {
+ kfree(obj);
+ return -EIO;
+ }
+
+ val = obj->buffer.pointer[1];
+ if (val != 1 && val != 2) {
+ kfree(obj);
+ return -ENODATA;
+ }
+
+ if (on)
+ *on = val - 1; /* 1→0 (off), 2→1 (on) */
+
+ kfree(obj);
+ return 0;
+}
+
+static int huawei_acpi_fn_lock_set(int on)
+{
+ union acpi_object acpi_arg, *obj;
+ struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg };
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+ int ret = 0;
+
+ /*
+ * SFRS reads byte[2] of its argument via CreateByteField(Arg0, 0x02).
+ * on=0 → FRSR=1 → EC gets 0x55 (fn-lock off)
+ * on=1 → FRSR=2 → EC gets 0x5A (fn-lock on)
+ */
+ acpi_arg.type = ACPI_TYPE_INTEGER;
+ acpi_arg.integer.value = (u64)(on + 1) << 16;
+
+ status = acpi_evaluate_object(NULL, "\\SFRS", &arg_list, &output);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ obj = output.pointer;
+ if (obj && obj->type == ACPI_TYPE_BUFFER &&
+ obj->buffer.length >= 1 && obj->buffer.pointer[0] != 0)
+ ret = -EIO;
+
+ kfree(obj);
+ return ret;
+}
+
static int huawei_wmi_fn_lock_get(int *on)
{
u8 ret[0x100] = { 0 };
int err, i;
+ /* Newer models: use direct ACPI \GFRS method */
+ if (acpi_has_method(NULL, "\\GFRS"))
+ return huawei_acpi_fn_lock_get(on);
+
+ /* Legacy WMI fallback */
err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
if (err)
return err;
@@ -550,6 +640,11 @@ static int huawei_wmi_fn_lock_set(int on)
{
union hwmi_arg arg;
+ /* Newer models: use direct ACPI \SFRS method */
+ if (acpi_has_method(NULL, "\\SFRS"))
+ return huawei_acpi_fn_lock_set(on);
+
+ /* Legacy WMI fallback */
arg.cmd = FN_LOCK_SET;
arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models
2026-05-24 12:15 [PATCH] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models Shaposhnikov Daniil
@ 2026-05-25 10:16 ` Ilpo Järvinen
2026-05-25 12:17 ` [PATCH v2 0/1] " Shaposhnikov Daniil
` (2 subsequent siblings)
3 siblings, 0 replies; 11+ messages in thread
From: Ilpo Järvinen @ 2026-05-25 10:16 UTC (permalink / raw)
To: Shaposhnikov Daniil
Cc: Hans de Goede, ayman.bagabas, platform-driver-x86, LKML
[-- Attachment #1: Type: text/plain, Size: 5210 bytes --]
On Sun, 24 May 2026, Shaposhnikov Daniil wrote:
> Newer Huawei laptops (e.g. FLMH-XX / MateBook 14 2024) no longer support
> the legacy WMI interface for Fn-lock control. Instead, they expose direct
> ACPI methods \GFRS and \SFRS (Get/Set Fn key Reversal Status) which
> communicate with the EC via registers 0x6B (read) and 0x6C (write).
>
> Add huawei_acpi_fn_lock_get() and huawei_acpi_fn_lock_set() helpers that
> use acpi_evaluate_object() to call these methods. Both
> huawei_wmi_fn_lock_get() and huawei_wmi_fn_lock_set() now probe for
> \GFRS/\SFRS via acpi_has_method() first and fall back to the legacy WMI
> path if not present.
>
> Tested on: HUAWEI FLMH-XX (MateBook 14 2024),
> CachyOS (kernel 7.0.9-1-cachyos).
>
> Signed-off-by: Shaposhnikov Daniil <2minesweeper2@gmail.com>
> ---
> drivers/platform/x86/huawei-wmi.c | 95 +++++++++++++++++++++++++++++++
> 1 file changed, 95 insertions(+)
>
> diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c
> index 93cca17fdf58..19cd8f1a8e33 100644
> --- a/drivers/platform/x86/huawei-wmi.c
> +++ b/drivers/platform/x86/huawei-wmi.c
> @@ -527,11 +527,101 @@ static void huawei_wmi_battery_exit(struct device *dev)
>
> /* Fn lock */
>
> +/*
> + * Newer Huawei models (e.g. HUAWEI FLMH-XX / MateBook 14 2024) use direct
> + * ACPI methods \GFRS / \SFRS (Get/Set Fn key Reversal Status) to control
> + * Fn-lock via EC registers 0x6B (read) and 0x6C (write).
> + *
> + * GFRS response buffer layout:
> + * byte[0] = STAT (0 = success)
> + * byte[1] = 0x01 (fn-lock off) or 0x02 (fn-lock on)
> + *
> + * SFRS argument layout (CreateByteField(Arg0, 0x02, FRSR)):
> + * Value is read from byte[2] of the integer argument, so it must be
> + * passed as (value << 16):
> + * (1 << 16) = fn-lock off (writes 0x55 to EC 0x6C)
> + * (2 << 16) = fn-lock on (writes 0x5A to EC 0x6C)
> + */
> +
> +static int huawei_acpi_fn_lock_get(int *on)
> +{
> + union acpi_object acpi_arg, *obj;
> + struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg };
> + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> + acpi_status status;
> + u8 val;
> +
> + acpi_arg.type = ACPI_TYPE_INTEGER;
> + acpi_arg.integer.value = 0;
> +
> + status = acpi_evaluate_object(NULL, "\\GFRS", &arg_list, &output);
> + if (ACPI_FAILURE(status))
> + return -EIO;
> +
> + obj = output.pointer;
You must use __free() here to avoid duplicating kfree() (move the
variable declaration mid-function).
> + if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 2) {
> + kfree(obj);
> + return -ENODATA;
> + }
> +
> + /* byte[0] = STAT (0 = success), byte[1] = 1 (off) or 2 (on) */
> + if (obj->buffer.pointer[0] != 0) {
> + kfree(obj);
> + return -EIO;
> + }
> +
> + val = obj->buffer.pointer[1];
> + if (val != 1 && val != 2) {
These magic literals should be replaced with using named defines.
> + kfree(obj);
> + return -ENODATA;
> + }
> +
> + if (on)
> + *on = val - 1; /* 1→0 (off), 2→1 (on) */
This too is unnecessarily cryptic (on a non-performance critical path).
One option would be to convert these two if blocks into a single
switch/case. You need to duplicate the if (on) to both case blocks but
that doesn't look too bad.
> +
> + kfree(obj);
> + return 0;
> +}
> +
> +static int huawei_acpi_fn_lock_set(int on)
> +{
> + union acpi_object acpi_arg, *obj;
> + struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg };
> + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> + acpi_status status;
> + int ret = 0;
> +
> + /*
> + * SFRS reads byte[2] of its argument via CreateByteField(Arg0, 0x02).
> + * on=0 → FRSR=1 → EC gets 0x55 (fn-lock off)
> + * on=1 → FRSR=2 → EC gets 0x5A (fn-lock on)
> + */
> + acpi_arg.type = ACPI_TYPE_INTEGER;
> + acpi_arg.integer.value = (u64)(on + 1) << 16;
Please make a named define instead.
The cast looks unnecessary.
> +
> + status = acpi_evaluate_object(NULL, "\\SFRS", &arg_list, &output);
> + if (ACPI_FAILURE(status))
> + return -EIO;
> +
> + obj = output.pointer;
As with above, please use __free()...
> + if (obj && obj->type == ACPI_TYPE_BUFFER &&
> + obj->buffer.length >= 1 && obj->buffer.pointer[0] != 0)
> + ret = -EIO;
...This can then return immediately.
> +
> + kfree(obj);
> + return ret;
> +}
> +
> static int huawei_wmi_fn_lock_get(int *on)
> {
> u8 ret[0x100] = { 0 };
> int err, i;
>
> + /* Newer models: use direct ACPI \GFRS method */
> + if (acpi_has_method(NULL, "\\GFRS"))
> + return huawei_acpi_fn_lock_get(on);
> +
> + /* Legacy WMI fallback */
> err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
> if (err)
> return err;
> @@ -550,6 +640,11 @@ static int huawei_wmi_fn_lock_set(int on)
> {
> union hwmi_arg arg;
>
> + /* Newer models: use direct ACPI \SFRS method */
> + if (acpi_has_method(NULL, "\\SFRS"))
> + return huawei_acpi_fn_lock_set(on);
> +
> + /* Legacy WMI fallback */
> arg.cmd = FN_LOCK_SET;
> arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
>
>
--
i.
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 0/1] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models
2026-05-24 12:15 [PATCH] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models Shaposhnikov Daniil
2026-05-25 10:16 ` Ilpo Järvinen
@ 2026-05-25 12:17 ` Shaposhnikov Daniil
2026-05-25 12:17 ` [PATCH v2 1/1] " Shaposhnikov Daniil
2026-05-25 13:00 ` [PATCH v3 0/1] " Shaposhnikov Daniil
2026-05-25 18:38 ` [PATCH] " Ayman Bagabas
3 siblings, 1 reply; 11+ messages in thread
From: Shaposhnikov Daniil @ 2026-05-25 12:17 UTC (permalink / raw)
To: hansg, ilpo.jarvinen
Cc: platform-driver-x86, linux-kernel, Shaposhnikov Daniil
Changes in v2:
Added linux/cleanup.h include for __free support
Used __free(kfree) macro to avoid duplicated kfree calls
Replaced magic literals with FN_LOCK_ACPI_OFF and FN_LOCK_ACPI_ON macro
Used switch/case in huawei_acpi_fn_lock_get to ehance readability
Removed unnecessary cast in SFRS argument construction
Shaposhnikov Daniil (1):
platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer
models
drivers/platform/x86/huawei-wmi.c | 98 +++++++++++++++++++++++++++++++
1 file changed, 98 insertions(+)
--
2.54.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 1/1] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models
2026-05-25 12:17 ` [PATCH v2 0/1] " Shaposhnikov Daniil
@ 2026-05-25 12:17 ` Shaposhnikov Daniil
2026-05-25 12:33 ` Ilpo Järvinen
0 siblings, 1 reply; 11+ messages in thread
From: Shaposhnikov Daniil @ 2026-05-25 12:17 UTC (permalink / raw)
To: hansg, ilpo.jarvinen
Cc: platform-driver-x86, linux-kernel, Shaposhnikov Daniil
Newer Huawei laptops (e.g. FLMH-XX / MateBook 14 2024) no longer support
the legacy WMI interface for Fn-lock control. Instead, they expose direct
ACPI methods \GFRS and \SFRS (Get/Set Fn key Reversal Status) which
communicate with the EC via registers 0x6B (read) and 0x6C (write).
Add huawei_acpi_fn_lock_get() and huawei_acpi_fn_lock_set() helpers that
use acpi_evaluate_object() to call these methods. Both
huawei_wmi_fn_lock_get() and huawei_wmi_fn_lock_set() now probe for
\GFRS/\SFRS via acpi_has_method() first and fall back to the legacy WMI
path if not present.
Tested on: HUAWEI FLMH-XX (MateBook 14 2024),
CachyOS (kernel 7.0.9-1-cachyos).
Signed-off-by: Shaposhnikov Daniil <2minesweeper2@gmail.com>
---
drivers/platform/x86/huawei-wmi.c | 98 +++++++++++++++++++++++++++++++
1 file changed, 98 insertions(+)
diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c
index 93cca17fdf58..006aa31eb777 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
*/
#include <linux/acpi.h>
+#include <linux/cleanup.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/dmi.h>
@@ -527,11 +528,103 @@ static void huawei_wmi_battery_exit(struct device *dev)
/* Fn lock */
+/* GFRS byte[1] / SFRS byte[2] (FRSR) fn-lock state values */
+#define FN_LOCK_ACPI_OFF 1
+#define FN_LOCK_ACPI_ON 2
+
+/*
+ * Newer Huawei models (e.g. HUAWEI FLMH-XX / MateBook 14 2024) use direct
+ * ACPI methods \GFRS / \SFRS (Get/Set Fn key Reversal Status) to control
+ * Fn-lock via EC registers 0x6B (read) and 0x6C (write).
+ *
+ * GFRS response buffer layout:
+ * byte[0] = STAT (0 = success)
+ * byte[1] = FN_LOCK_ACPI_OFF (fn-lock off) or FN_LOCK_ACPI_ON (fn-lock on)
+ *
+ * SFRS argument layout (CreateByteField(Arg0, 0x02, FRSR)):
+ * Value is read from byte[2] of the integer argument, so it must be
+ * passed as (value << 16):
+ * (FN_LOCK_ACPI_OFF << 16) = fn-lock off (writes 0x55 to EC 0x6C)
+ * (FN_LOCK_ACPI_ON << 16) = fn-lock on (writes 0x5A to EC 0x6C)
+ */
+
+static int huawei_acpi_fn_lock_get(int *on)
+{
+ union acpi_object acpi_arg;
+ struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg };
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+
+ acpi_arg.type = ACPI_TYPE_INTEGER;
+ acpi_arg.integer.value = 0;
+
+ status = acpi_evaluate_object(NULL, "\\GFRS", &arg_list, &output);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ union acpi_object *obj __free(kfree) = output.pointer;
+
+ if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 2)
+ return -ENODATA;
+
+ /* byte[0] = STAT (0 = success), byte[1] = fn-lock state */
+ if (obj->buffer.pointer[0] != 0)
+ return -EIO;
+
+ switch (obj->buffer.pointer[1]) {
+ case FN_LOCK_ACPI_OFF:
+ if (on)
+ *on = 0;
+ break;
+ case FN_LOCK_ACPI_ON:
+ if (on)
+ *on = 1;
+ break;
+ default:
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+static int huawei_acpi_fn_lock_set(int on)
+{
+ union acpi_object acpi_arg;
+ struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg };
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+
+ /*
+ * SFRS reads byte[2] of its argument via CreateByteField(Arg0, 0x02).
+ * on=0 → FRSR=FN_LOCK_ACPI_OFF → EC gets 0x55 (fn-lock off)
+ * on=1 → FRSR=FN_LOCK_ACPI_ON → EC gets 0x5A (fn-lock on)
+ */
+ acpi_arg.type = ACPI_TYPE_INTEGER;
+ acpi_arg.integer.value = (on ? FN_LOCK_ACPI_ON : FN_LOCK_ACPI_OFF) << 16;
+
+ status = acpi_evaluate_object(NULL, "\\SFRS", &arg_list, &output);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ union acpi_object *obj __free(kfree) = output.pointer;
+
+ if (obj && obj->type == ACPI_TYPE_BUFFER &&
+ obj->buffer.length >= 1 && obj->buffer.pointer[0] != 0)
+ return -EIO;
+
+ return 0;
+}
+
static int huawei_wmi_fn_lock_get(int *on)
{
u8 ret[0x100] = { 0 };
int err, i;
+ /* Newer models: use direct ACPI \GFRS method */
+ if (acpi_has_method(NULL, "\\GFRS"))
+ return huawei_acpi_fn_lock_get(on);
+
+ /* Legacy WMI fallback */
err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
if (err)
return err;
@@ -550,6 +643,11 @@ static int huawei_wmi_fn_lock_set(int on)
{
union hwmi_arg arg;
+ /* Newer models: use direct ACPI \SFRS method */
+ if (acpi_has_method(NULL, "\\SFRS"))
+ return huawei_acpi_fn_lock_set(on);
+
+ /* Legacy WMI fallback */
arg.cmd = FN_LOCK_SET;
arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v2 1/1] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models
2026-05-25 12:17 ` [PATCH v2 1/1] " Shaposhnikov Daniil
@ 2026-05-25 12:33 ` Ilpo Järvinen
0 siblings, 0 replies; 11+ messages in thread
From: Ilpo Järvinen @ 2026-05-25 12:33 UTC (permalink / raw)
To: Shaposhnikov Daniil; +Cc: Hans de Goede, platform-driver-x86, LKML
[-- Attachment #1: Type: text/plain, Size: 5246 bytes --]
On Mon, 25 May 2026, Shaposhnikov Daniil wrote:
> Newer Huawei laptops (e.g. FLMH-XX / MateBook 14 2024) no longer support
> the legacy WMI interface for Fn-lock control. Instead, they expose direct
> ACPI methods \GFRS and \SFRS (Get/Set Fn key Reversal Status) which
> communicate with the EC via registers 0x6B (read) and 0x6C (write).
>
> Add huawei_acpi_fn_lock_get() and huawei_acpi_fn_lock_set() helpers that
> use acpi_evaluate_object() to call these methods. Both
> huawei_wmi_fn_lock_get() and huawei_wmi_fn_lock_set() now probe for
> \GFRS/\SFRS via acpi_has_method() first and fall back to the legacy WMI
> path if not present.
>
> Tested on: HUAWEI FLMH-XX (MateBook 14 2024),
> CachyOS (kernel 7.0.9-1-cachyos).
>
> Signed-off-by: Shaposhnikov Daniil <2minesweeper2@gmail.com>
> ---
> drivers/platform/x86/huawei-wmi.c | 98 +++++++++++++++++++++++++++++++
> 1 file changed, 98 insertions(+)
>
> diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c
> index 93cca17fdf58..006aa31eb777 100644
> --- a/drivers/platform/x86/huawei-wmi.c
> +++ b/drivers/platform/x86/huawei-wmi.c
> @@ -6,6 +6,7 @@
> */
>
> #include <linux/acpi.h>
> +#include <linux/cleanup.h>
> #include <linux/debugfs.h>
> #include <linux/delay.h>
> #include <linux/dmi.h>
> @@ -527,11 +528,103 @@ static void huawei_wmi_battery_exit(struct device *dev)
>
> /* Fn lock */
>
> +/* GFRS byte[1] / SFRS byte[2] (FRSR) fn-lock state values */
> +#define FN_LOCK_ACPI_OFF 1
> +#define FN_LOCK_ACPI_ON 2
> +
> +/*
> + * Newer Huawei models (e.g. HUAWEI FLMH-XX / MateBook 14 2024) use direct
> + * ACPI methods \GFRS / \SFRS (Get/Set Fn key Reversal Status) to control
> + * Fn-lock via EC registers 0x6B (read) and 0x6C (write).
> + *
> + * GFRS response buffer layout:
> + * byte[0] = STAT (0 = success)
> + * byte[1] = FN_LOCK_ACPI_OFF (fn-lock off) or FN_LOCK_ACPI_ON (fn-lock on)
> + *
> + * SFRS argument layout (CreateByteField(Arg0, 0x02, FRSR)):
> + * Value is read from byte[2] of the integer argument, so it must be
> + * passed as (value << 16):
> + * (FN_LOCK_ACPI_OFF << 16) = fn-lock off (writes 0x55 to EC 0x6C)
> + * (FN_LOCK_ACPI_ON << 16) = fn-lock on (writes 0x5A to EC 0x6C)
> + */
> +
> +static int huawei_acpi_fn_lock_get(int *on)
> +{
> + union acpi_object acpi_arg;
> + struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg };
> + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> + acpi_status status;
> +
> + acpi_arg.type = ACPI_TYPE_INTEGER;
> + acpi_arg.integer.value = 0;
> +
> + status = acpi_evaluate_object(NULL, "\\GFRS", &arg_list, &output);
> + if (ACPI_FAILURE(status))
> + return -EIO;
> +
> + union acpi_object *obj __free(kfree) = output.pointer;
> +
> + if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 2)
> + return -ENODATA;
> +
> + /* byte[0] = STAT (0 = success), byte[1] = fn-lock state */
> + if (obj->buffer.pointer[0] != 0)
Could you also name this 0 with a define (it's also used in set side).
> + return -EIO;
> +
> + switch (obj->buffer.pointer[1]) {
> + case FN_LOCK_ACPI_OFF:
> + if (on)
> + *on = 0;
> + break;
> + case FN_LOCK_ACPI_ON:
> + if (on)
> + *on = 1;
> + break;
> + default:
> + return -ENODATA;
> + }
> +
> + return 0;
> +}
> +
> +static int huawei_acpi_fn_lock_set(int on)
> +{
> + union acpi_object acpi_arg;
> + struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg };
> + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> + acpi_status status;
> +
> + /*
> + * SFRS reads byte[2] of its argument via CreateByteField(Arg0, 0x02).
> + * on=0 → FRSR=FN_LOCK_ACPI_OFF → EC gets 0x55 (fn-lock off)
> + * on=1 → FRSR=FN_LOCK_ACPI_ON → EC gets 0x5A (fn-lock on)
> + */
> + acpi_arg.type = ACPI_TYPE_INTEGER;
> + acpi_arg.integer.value = (on ? FN_LOCK_ACPI_ON : FN_LOCK_ACPI_OFF) << 16;
> +
> + status = acpi_evaluate_object(NULL, "\\SFRS", &arg_list, &output);
> + if (ACPI_FAILURE(status))
> + return -EIO;
> +
> + union acpi_object *obj __free(kfree) = output.pointer;
> +
> + if (obj && obj->type == ACPI_TYPE_BUFFER &&
> + obj->buffer.length >= 1 && obj->buffer.pointer[0] != 0)
Now that I look this again, is the logic wrong way around here? This
results in returning 0 in many of what I'd assume to be error cases.
> + return -EIO;
> +
> + return 0;
> +}
> +
> static int huawei_wmi_fn_lock_get(int *on)
> {
> u8 ret[0x100] = { 0 };
> int err, i;
>
> + /* Newer models: use direct ACPI \GFRS method */
> + if (acpi_has_method(NULL, "\\GFRS"))
> + return huawei_acpi_fn_lock_get(on);
> +
> + /* Legacy WMI fallback */
> err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
> if (err)
> return err;
> @@ -550,6 +643,11 @@ static int huawei_wmi_fn_lock_set(int on)
> {
> union hwmi_arg arg;
>
> + /* Newer models: use direct ACPI \SFRS method */
> + if (acpi_has_method(NULL, "\\SFRS"))
> + return huawei_acpi_fn_lock_set(on);
> +
> + /* Legacy WMI fallback */
> arg.cmd = FN_LOCK_SET;
> arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
>
>
--
i.
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 0/1] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models
2026-05-24 12:15 [PATCH] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models Shaposhnikov Daniil
2026-05-25 10:16 ` Ilpo Järvinen
2026-05-25 12:17 ` [PATCH v2 0/1] " Shaposhnikov Daniil
@ 2026-05-25 13:00 ` Shaposhnikov Daniil
2026-05-25 13:00 ` [PATCH v3 1/1] " Shaposhnikov Daniil
2026-05-25 18:38 ` [PATCH] " Ayman Bagabas
3 siblings, 1 reply; 11+ messages in thread
From: Shaposhnikov Daniil @ 2026-05-25 13:00 UTC (permalink / raw)
To: hansg, ilpo.jarvinen
Cc: platform-driver-x86, linux-kernel, Shaposhnikov Daniil
Changes in v3:
Fixed and improved error handling in huawei_acpi_fn_lock_set
Added FN_LOCK_ACPI_STAT_OK macro to describe successful STAT byte value
Shaposhnikov Daniil (1):
platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer
models
drivers/platform/x86/huawei-wmi.c | 101 ++++++++++++++++++++++++++++++
1 file changed, 101 insertions(+)
--
2.54.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 1/1] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models
2026-05-25 13:00 ` [PATCH v3 0/1] " Shaposhnikov Daniil
@ 2026-05-25 13:00 ` Shaposhnikov Daniil
0 siblings, 0 replies; 11+ messages in thread
From: Shaposhnikov Daniil @ 2026-05-25 13:00 UTC (permalink / raw)
To: hansg, ilpo.jarvinen
Cc: platform-driver-x86, linux-kernel, Shaposhnikov Daniil
Newer Huawei laptops (e.g. FLMH-XX / MateBook 14 2024) no longer support
the legacy WMI interface for Fn-lock control. Instead, they expose direct
ACPI methods \GFRS and \SFRS (Get/Set Fn key Reversal Status) which
communicate with the EC via registers 0x6B (read) and 0x6C (write).
Add huawei_acpi_fn_lock_get() and huawei_acpi_fn_lock_set() helpers that
use acpi_evaluate_object() to call these methods. Both
huawei_wmi_fn_lock_get() and huawei_wmi_fn_lock_set() now probe for
\GFRS/\SFRS via acpi_has_method() first and fall back to the legacy WMI
path if not present.
Tested on: HUAWEI FLMH-XX (MateBook 14 2024),
CachyOS (kernel 7.0.9-1-cachyos).
Signed-off-by: Shaposhnikov Daniil <2minesweeper2@gmail.com>
---
drivers/platform/x86/huawei-wmi.c | 101 ++++++++++++++++++++++++++++++
1 file changed, 101 insertions(+)
diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c
index 93cca17fdf58..8b0a52d66771 100644
--- a/drivers/platform/x86/huawei-wmi.c
+++ b/drivers/platform/x86/huawei-wmi.c
@@ -6,6 +6,7 @@
*/
#include <linux/acpi.h>
+#include <linux/cleanup.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/dmi.h>
@@ -527,11 +528,106 @@ static void huawei_wmi_battery_exit(struct device *dev)
/* Fn lock */
+/* GFRS byte[1] / SFRS byte[2] (FRSR) fn-lock state values */
+#define FN_LOCK_ACPI_OFF 1
+#define FN_LOCK_ACPI_ON 2
+#define FN_LOCK_ACPI_STAT_OK 0
+
+/*
+ * Newer Huawei models (e.g. HUAWEI FLMH-XX / MateBook 14 2024) use direct
+ * ACPI methods \GFRS / \SFRS (Get/Set Fn key Reversal Status) to control
+ * Fn-lock via EC registers 0x6B (read) and 0x6C (write).
+ *
+ * GFRS response buffer layout:
+ * byte[0] = STAT (FN_LOCK_ACPI_STAT_OK = success)
+ * byte[1] = FN_LOCK_ACPI_OFF (fn-lock off) or FN_LOCK_ACPI_ON (fn-lock on)
+ *
+ * SFRS argument layout (CreateByteField(Arg0, 0x02, FRSR)):
+ * Value is read from byte[2] of the integer argument, so it must be
+ * passed as (value << 16):
+ * (FN_LOCK_ACPI_OFF << 16) = fn-lock off (writes 0x55 to EC 0x6C)
+ * (FN_LOCK_ACPI_ON << 16) = fn-lock on (writes 0x5A to EC 0x6C)
+ */
+
+static int huawei_acpi_fn_lock_get(int *on)
+{
+ union acpi_object acpi_arg;
+ struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg };
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+
+ acpi_arg.type = ACPI_TYPE_INTEGER;
+ acpi_arg.integer.value = 0;
+
+ status = acpi_evaluate_object(NULL, "\\GFRS", &arg_list, &output);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ union acpi_object *obj __free(kfree) = output.pointer;
+
+ if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 2)
+ return -ENODATA;
+
+ /* byte[0] = STAT, byte[1] = fn-lock state */
+ if (obj->buffer.pointer[0] != FN_LOCK_ACPI_STAT_OK)
+ return -EIO;
+
+ switch (obj->buffer.pointer[1]) {
+ case FN_LOCK_ACPI_OFF:
+ if (on)
+ *on = 0;
+ break;
+ case FN_LOCK_ACPI_ON:
+ if (on)
+ *on = 1;
+ break;
+ default:
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+static int huawei_acpi_fn_lock_set(int on)
+{
+ union acpi_object acpi_arg;
+ struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg };
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+
+ /*
+ * SFRS reads byte[2] of its argument via CreateByteField(Arg0, 0x02).
+ * on=0 → FRSR=FN_LOCK_ACPI_OFF → EC gets 0x55 (fn-lock off)
+ * on=1 → FRSR=FN_LOCK_ACPI_ON → EC gets 0x5A (fn-lock on)
+ */
+ acpi_arg.type = ACPI_TYPE_INTEGER;
+ acpi_arg.integer.value = (on ? FN_LOCK_ACPI_ON : FN_LOCK_ACPI_OFF) << 16;
+
+ status = acpi_evaluate_object(NULL, "\\SFRS", &arg_list, &output);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ union acpi_object *obj __free(kfree) = output.pointer;
+
+ if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 1)
+ return -ENODATA;
+
+ if (obj->buffer.pointer[0] != FN_LOCK_ACPI_STAT_OK)
+ return -EIO;
+
+ return 0;
+}
+
static int huawei_wmi_fn_lock_get(int *on)
{
u8 ret[0x100] = { 0 };
int err, i;
+ /* Newer models: use direct ACPI \GFRS method */
+ if (acpi_has_method(NULL, "\\GFRS"))
+ return huawei_acpi_fn_lock_get(on);
+
+ /* Legacy WMI fallback */
err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
if (err)
return err;
@@ -550,6 +646,11 @@ static int huawei_wmi_fn_lock_set(int on)
{
union hwmi_arg arg;
+ /* Newer models: use direct ACPI \SFRS method */
+ if (acpi_has_method(NULL, "\\SFRS"))
+ return huawei_acpi_fn_lock_set(on);
+
+ /* Legacy WMI fallback */
arg.cmd = FN_LOCK_SET;
arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models
2026-05-24 12:15 [PATCH] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models Shaposhnikov Daniil
` (2 preceding siblings ...)
2026-05-25 13:00 ` [PATCH v3 0/1] " Shaposhnikov Daniil
@ 2026-05-25 18:38 ` Ayman Bagabas
2026-05-25 19:22 ` Superstes
[not found] ` <CALOxGBVJzk4GX2xrG3N1Djy1UZhMXMdcYKeJQuBqcf289SO32w@mail.gmail.com>
3 siblings, 2 replies; 11+ messages in thread
From: Ayman Bagabas @ 2026-05-25 18:38 UTC (permalink / raw)
To: Shaposhnikov Daniil
Cc: hansg, ilpo.jarvinen, platform-driver-x86, linux-kernel
>
> Newer Huawei laptops (e.g. FLMH-XX / MateBook 14 2024) no longer support
> the legacy WMI interface for Fn-lock control. Instead, they expose direct
> ACPI methods \GFRS and \SFRS (Get/Set Fn key Reversal Status) which
> communicate with the EC via registers 0x6B (read) and 0x6C (write).
>
> Add huawei_acpi_fn_lock_get() and huawei_acpi_fn_lock_set() helpers that
> use acpi_evaluate_object() to call these methods. Both
> huawei_wmi_fn_lock_get() and huawei_wmi_fn_lock_set() now probe for
> \GFRS/\SFRS via acpi_has_method() first and fall back to the legacy WMI
> path if not present.
Thank you for working on this. Could you open an issue and/or send a PR
to https://github.com/aymanbagabas/Huawei-WMI ?
>
> Tested on: HUAWEI FLMH-XX (MateBook 14 2024),
> CachyOS (kernel 7.0.9-1-cachyos).
>
> Signed-off-by: Shaposhnikov Daniil <2minesweeper2@gmail.com>
> ---
> drivers/platform/x86/huawei-wmi.c | 95 +++++++++++++++++++++++++++++++
> 1 file changed, 95 insertions(+)
>
> diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c
> index 93cca17fdf58..19cd8f1a8e33 100644
> --- a/drivers/platform/x86/huawei-wmi.c
> +++ b/drivers/platform/x86/huawei-wmi.c
> @@ -527,11 +527,101 @@ static void huawei_wmi_battery_exit(struct device *dev)
>
> /* Fn lock */
>
> +/*
> + * Newer Huawei models (e.g. HUAWEI FLMH-XX / MateBook 14 2024) use direct
> + * ACPI methods \GFRS / \SFRS (Get/Set Fn key Reversal Status) to control
> + * Fn-lock via EC registers 0x6B (read) and 0x6C (write).
> + *
> + * GFRS response buffer layout:
> + * byte[0] = STAT (0 = success)
> + * byte[1] = 0x01 (fn-lock off) or 0x02 (fn-lock on)
> + *
> + * SFRS argument layout (CreateByteField(Arg0, 0x02, FRSR)):
> + * Value is read from byte[2] of the integer argument, so it must be
> + * passed as (value << 16):
> + * (1 << 16) = fn-lock off (writes 0x55 to EC 0x6C)
> + * (2 << 16) = fn-lock on (writes 0x5A to EC 0x6C)
> + */
This is very specific to the FLMH-XX model. Can you confirm this is the
case for other newer models?
> +
> +static int huawei_acpi_fn_lock_get(int *on)
> +{
> + union acpi_object acpi_arg, *obj;
> + struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg };
> + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> + acpi_status status;
> + u8 val;
> +
> + acpi_arg.type = ACPI_TYPE_INTEGER;
> + acpi_arg.integer.value = 0;
> +
> + status = acpi_evaluate_object(NULL, "\\GFRS", &arg_list, &output);
> + if (ACPI_FAILURE(status))
> + return -EIO;
> +
> + obj = output.pointer;
> + if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 2) {
> + kfree(obj);
> + return -ENODATA;
> + }
> +
> + /* byte[0] = STAT (0 = success), byte[1] = 1 (off) or 2 (on) */
> + if (obj->buffer.pointer[0] != 0) {
> + kfree(obj);
> + return -EIO;
> + }
> +
> + val = obj->buffer.pointer[1];
> + if (val != 1 && val != 2) {
> + kfree(obj);
> + return -ENODATA;
> + }
> +
> + if (on)
> + *on = val - 1; /* 1→0 (off), 2→1 (on) */
> +
> + kfree(obj);
> + return 0;
> +}
> +
> +static int huawei_acpi_fn_lock_set(int on)
> +{
> + union acpi_object acpi_arg, *obj;
> + struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg };
> + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> + acpi_status status;
> + int ret = 0;
> +
> + /*
> + * SFRS reads byte[2] of its argument via CreateByteField(Arg0, 0x02).
> + * on=0 → FRSR=1 → EC gets 0x55 (fn-lock off)
> + * on=1 → FRSR=2 → EC gets 0x5A (fn-lock on)
> + */
> + acpi_arg.type = ACPI_TYPE_INTEGER;
> + acpi_arg.integer.value = (u64)(on + 1) << 16;
> +
> + status = acpi_evaluate_object(NULL, "\\SFRS", &arg_list, &output);
> + if (ACPI_FAILURE(status))
> + return -EIO;
> +
> + obj = output.pointer;
> + if (obj && obj->type == ACPI_TYPE_BUFFER &&
> + obj->buffer.length >= 1 && obj->buffer.pointer[0] != 0)
> + ret = -EIO;
> +
> + kfree(obj);
> + return ret;
> +}
> +
> static int huawei_wmi_fn_lock_get(int *on)
> {
> u8 ret[0x100] = { 0 };
> int err, i;
>
> + /* Newer models: use direct ACPI \GFRS method */
> + if (acpi_has_method(NULL, "\\GFRS"))
> + return huawei_acpi_fn_lock_get(on);
Wouldn't it be better to put this behavior behind a quirk instead
of enforcing it on the whole module? Keep in mind that this is
also used by Honor devices that have the same WMI method.
> +
> + /* Legacy WMI fallback */
> err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
> if (err)
> return err;
> @@ -550,6 +640,11 @@ static int huawei_wmi_fn_lock_set(int on)
> {
> union hwmi_arg arg;
>
> + /* Newer models: use direct ACPI \SFRS method */
> + if (acpi_has_method(NULL, "\\SFRS"))
> + return huawei_acpi_fn_lock_set(on);
> +
> + /* Legacy WMI fallback */
> arg.cmd = FN_LOCK_SET;
> arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
>
> --
> 2.54.0
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models
2026-05-25 18:38 ` [PATCH] " Ayman Bagabas
@ 2026-05-25 19:22 ` Superstes
[not found] ` <CALOxGBVJzk4GX2xrG3N1Djy1UZhMXMdcYKeJQuBqcf289SO32w@mail.gmail.com>
1 sibling, 0 replies; 11+ messages in thread
From: Superstes @ 2026-05-25 19:22 UTC (permalink / raw)
To: Ayman Bagabas; +Cc: hansg, ilpo.jarvinen, platform-driver-x86, linux-kernel
>> Thank you for working on this. Could you open an issue and/or send a PR to https://github.com/aymanbagabas/Huawei-WMI ?
Yes, I can send PR in the next two-three days.
>> This is very specific to the FLMH-XX model. Can you confirm this is the case for other newer models?
No, it is the latest model in the Matebook 14 series, I've just bought
it recently and faced multiple issues, which I try to resolve. I can't
found any information about that issues from another users, but i
think that it can be related with other recent laptop based on Intel
Core Ultra processors, it will be good to have someone with following
models:
MateBook Fold (2025)
MateBook X Pro (2024)
MateBook GT 14 (2024)
MateBook D 14 (2024)
>> Wouldn't it be better to put this behavior behind a quirk instead of enforcing it on the whole module?
I haven't found a better place to make such changes. But you can
suggest something. But I think that my workaround is safe because it
calls very specific ACPI objects and uses default fallbacks.
By the way, maybe You, or Ilpo knows a good place to implement board
specific i2c acpi fixes? It Looks like there is a race condition
situation which leads to arbitration loss when kernel tries to
initialize multiple devices on the i2c bus. To fix this I've disabled
light sensor and made systemd service to remove and enable the
i2c_hid_acpi module, but I haven't found a place where to implement a
quirk that fixes that issue.
пн, 25 мая 2026 г. в 23:38, Ayman Bagabas <ayman.bagabas@gmail.com>:
>
> >
> > Newer Huawei laptops (e.g. FLMH-XX / MateBook 14 2024) no longer support
> > the legacy WMI interface for Fn-lock control. Instead, they expose direct
> > ACPI methods \GFRS and \SFRS (Get/Set Fn key Reversal Status) which
> > communicate with the EC via registers 0x6B (read) and 0x6C (write).
> >
> > Add huawei_acpi_fn_lock_get() and huawei_acpi_fn_lock_set() helpers that
> > use acpi_evaluate_object() to call these methods. Both
> > huawei_wmi_fn_lock_get() and huawei_wmi_fn_lock_set() now probe for
> > \GFRS/\SFRS via acpi_has_method() first and fall back to the legacy WMI
> > path if not present.
>
> Thank you for working on this. Could you open an issue and/or send a PR
> to https://github.com/aymanbagabas/Huawei-WMI ?
>
> >
> > Tested on: HUAWEI FLMH-XX (MateBook 14 2024),
> > CachyOS (kernel 7.0.9-1-cachyos).
> >
> > Signed-off-by: Shaposhnikov Daniil <2minesweeper2@gmail.com>
> > ---
> > drivers/platform/x86/huawei-wmi.c | 95 +++++++++++++++++++++++++++++++
> > 1 file changed, 95 insertions(+)
> >
> > diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c
> > index 93cca17fdf58..19cd8f1a8e33 100644
> > --- a/drivers/platform/x86/huawei-wmi.c
> > +++ b/drivers/platform/x86/huawei-wmi.c
> > @@ -527,11 +527,101 @@ static void huawei_wmi_battery_exit(struct device *dev)
> >
> > /* Fn lock */
> >
> > +/*
> > + * Newer Huawei models (e.g. HUAWEI FLMH-XX / MateBook 14 2024) use direct
> > + * ACPI methods \GFRS / \SFRS (Get/Set Fn key Reversal Status) to control
> > + * Fn-lock via EC registers 0x6B (read) and 0x6C (write).
> > + *
> > + * GFRS response buffer layout:
> > + * byte[0] = STAT (0 = success)
> > + * byte[1] = 0x01 (fn-lock off) or 0x02 (fn-lock on)
> > + *
> > + * SFRS argument layout (CreateByteField(Arg0, 0x02, FRSR)):
> > + * Value is read from byte[2] of the integer argument, so it must be
> > + * passed as (value << 16):
> > + * (1 << 16) = fn-lock off (writes 0x55 to EC 0x6C)
> > + * (2 << 16) = fn-lock on (writes 0x5A to EC 0x6C)
> > + */
>
> This is very specific to the FLMH-XX model. Can you confirm this is the
> case for other newer models?
>
> > +
> > +static int huawei_acpi_fn_lock_get(int *on)
> > +{
> > + union acpi_object acpi_arg, *obj;
> > + struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg };
> > + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> > + acpi_status status;
> > + u8 val;
> > +
> > + acpi_arg.type = ACPI_TYPE_INTEGER;
> > + acpi_arg.integer.value = 0;
> > +
> > + status = acpi_evaluate_object(NULL, "\\GFRS", &arg_list, &output);
> > + if (ACPI_FAILURE(status))
> > + return -EIO;
> > +
> > + obj = output.pointer;
> > + if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 2) {
> > + kfree(obj);
> > + return -ENODATA;
> > + }
> > +
> > + /* byte[0] = STAT (0 = success), byte[1] = 1 (off) or 2 (on) */
> > + if (obj->buffer.pointer[0] != 0) {
> > + kfree(obj);
> > + return -EIO;
> > + }
> > +
> > + val = obj->buffer.pointer[1];
> > + if (val != 1 && val != 2) {
> > + kfree(obj);
> > + return -ENODATA;
> > + }
> > +
> > + if (on)
> > + *on = val - 1; /* 1→0 (off), 2→1 (on) */
> > +
> > + kfree(obj);
> > + return 0;
> > +}
> > +
> > +static int huawei_acpi_fn_lock_set(int on)
> > +{
> > + union acpi_object acpi_arg, *obj;
> > + struct acpi_object_list arg_list = { .count = 1, .pointer = &acpi_arg };
> > + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> > + acpi_status status;
> > + int ret = 0;
> > +
> > + /*
> > + * SFRS reads byte[2] of its argument via CreateByteField(Arg0, 0x02).
> > + * on=0 → FRSR=1 → EC gets 0x55 (fn-lock off)
> > + * on=1 → FRSR=2 → EC gets 0x5A (fn-lock on)
> > + */
> > + acpi_arg.type = ACPI_TYPE_INTEGER;
> > + acpi_arg.integer.value = (u64)(on + 1) << 16;
> > +
> > + status = acpi_evaluate_object(NULL, "\\SFRS", &arg_list, &output);
> > + if (ACPI_FAILURE(status))
> > + return -EIO;
> > +
> > + obj = output.pointer;
> > + if (obj && obj->type == ACPI_TYPE_BUFFER &&
> > + obj->buffer.length >= 1 && obj->buffer.pointer[0] != 0)
> > + ret = -EIO;
> > +
> > + kfree(obj);
> > + return ret;
> > +}
> > +
> > static int huawei_wmi_fn_lock_get(int *on)
> > {
> > u8 ret[0x100] = { 0 };
> > int err, i;
> >
> > + /* Newer models: use direct ACPI \GFRS method */
> > + if (acpi_has_method(NULL, "\\GFRS"))
> > + return huawei_acpi_fn_lock_get(on);
>
> Wouldn't it be better to put this behavior behind a quirk instead
> of enforcing it on the whole module? Keep in mind that this is
> also used by Honor devices that have the same WMI method.
>
> > +
> > + /* Legacy WMI fallback */
> > err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
> > if (err)
> > return err;
> > @@ -550,6 +640,11 @@ static int huawei_wmi_fn_lock_set(int on)
> > {
> > union hwmi_arg arg;
> >
> > + /* Newer models: use direct ACPI \SFRS method */
> > + if (acpi_has_method(NULL, "\\SFRS"))
> > + return huawei_acpi_fn_lock_set(on);
> > +
> > + /* Legacy WMI fallback */
> > arg.cmd = FN_LOCK_SET;
> > arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
> >
> > --
> > 2.54.0
> >
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models
[not found] ` <CALOxGBVJzk4GX2xrG3N1Djy1UZhMXMdcYKeJQuBqcf289SO32w@mail.gmail.com>
@ 2026-05-26 8:58 ` Ilpo Järvinen
2026-05-26 9:09 ` Superstes
0 siblings, 1 reply; 11+ messages in thread
From: Ilpo Järvinen @ 2026-05-26 8:58 UTC (permalink / raw)
To: Superstes; +Cc: Ayman Bagabas, Hans de Goede, platform-driver-x86, LKML
[-- Attachment #1: Type: text/plain, Size: 8875 bytes --]
On Tue, 26 May 2026, Superstes wrote:
> >> Thank you for working on this. Could you open an issue and/or send a PR
> to https://github.com/aymanbagabas/Huawei-WMI ?
> Yes, I can send PR in the next two-three days.
> >> This is very specific to the FLMH-XX model. Can you confirm this is the case for
> other newer models?No, it is the latest model in the Matebook 14 series, I've just
> bought it recently and faced multiple issues, which I try to resolve. I can't found
> any information about that issues from another users, but i think that it can be
> related with other recent laptop based on Intel Core Ultra processors, it will be
> good to have someone with following models:
> MateBook Fold (2025)
> MateBook X Pro (2024)
> MateBook GT 14 (2024)
> MateBook D 14 (2024)
> >> Wouldn't it be better to put this behavior behind a quirk instead of enforcing it
> on the whole module?
> I haven't found a better place to make such changes. But you can suggest something.
> But I think that my workaround is safe because it calls very specific ACPI objects
> and uses default fallbacks.
>
> By the way, maybe You, or Ilpo knows a good place to implement board specific i2c
> acpi fixes? It Looks like there is a race condition situation which leads to
> arbitration loss when kernel tries to initialize multiple devices on the i2c bus. To
> fix this I've disabled light sensor and made systemd service to remove and enable
> the i2c_hid_acpi module, but I haven't found a place where to implement a quirk that
> fixes that issue.
Do you mean you'd want to serialize .probe()s of those devices?
--
i.
> пн, 25 мая 2026 г. в 23:38, Ayman Bagabas <ayman.bagabas@gmail.com>:
> >
> > Newer Huawei laptops (e.g. FLMH-XX / MateBook 14 2024) no longer
> support
> > the legacy WMI interface for Fn-lock control. Instead, they expose
> direct
> > ACPI methods \GFRS and \SFRS (Get/Set Fn key Reversal Status) which
> > communicate with the EC via registers 0x6B (read) and 0x6C (write).
> >
> > Add huawei_acpi_fn_lock_get() and huawei_acpi_fn_lock_set() helpers
> that
> > use acpi_evaluate_object() to call these methods. Both
> > huawei_wmi_fn_lock_get() and huawei_wmi_fn_lock_set() now probe for
> > \GFRS/\SFRS via acpi_has_method() first and fall back to the legacy WMI
> > path if not present.
>
> Thank you for working on this. Could you open an issue and/or send a PR
> to https://github.com/aymanbagabas/Huawei-WMI ?
>
> >
> > Tested on: HUAWEI FLMH-XX (MateBook 14 2024),
> > CachyOS (kernel 7.0.9-1-cachyos).
> >
> > Signed-off-by: Shaposhnikov Daniil <2minesweeper2@gmail.com>
> > ---
> > drivers/platform/x86/huawei-wmi.c | 95 +++++++++++++++++++++++++++++++
> > 1 file changed, 95 insertions(+)
> >
> > diff --git a/drivers/platform/x86/huawei-wmi.c
> b/drivers/platform/x86/huawei-wmi.c
> > index 93cca17fdf58..19cd8f1a8e33 100644
> > --- a/drivers/platform/x86/huawei-wmi.c
> > +++ b/drivers/platform/x86/huawei-wmi.c
> > @@ -527,11 +527,101 @@ static void huawei_wmi_battery_exit(struct
> device *dev)
> >
> > /* Fn lock */
> >
> > +/*
> > + * Newer Huawei models (e.g. HUAWEI FLMH-XX / MateBook 14 2024) use
> direct
> > + * ACPI methods \GFRS / \SFRS (Get/Set Fn key Reversal Status) to
> control
> > + * Fn-lock via EC registers 0x6B (read) and 0x6C (write).
> > + *
> > + * GFRS response buffer layout:
> > + * byte[0] = STAT (0 = success)
> > + * byte[1] = 0x01 (fn-lock off) or 0x02 (fn-lock on)
> > + *
> > + * SFRS argument layout (CreateByteField(Arg0, 0x02, FRSR)):
> > + * Value is read from byte[2] of the integer argument, so it must be
> > + * passed as (value << 16):
> > + * (1 << 16) = fn-lock off (writes 0x55 to EC 0x6C)
> > + * (2 << 16) = fn-lock on (writes 0x5A to EC 0x6C)
> > + */
>
> This is very specific to the FLMH-XX model. Can you confirm this is the
> case for other newer models?
>
> > +
> > +static int huawei_acpi_fn_lock_get(int *on)
> > +{
> > + union acpi_object acpi_arg, *obj;
> > + struct acpi_object_list arg_list = { .count = 1, .pointer =
> &acpi_arg };
> > + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> > + acpi_status status;
> > + u8 val;
> > +
> > + acpi_arg.type = ACPI_TYPE_INTEGER;
> > + acpi_arg.integer.value = 0;
> > +
> > + status = acpi_evaluate_object(NULL, "\\GFRS", &arg_list,
> &output);
> > + if (ACPI_FAILURE(status))
> > + return -EIO;
> > +
> > + obj = output.pointer;
> > + if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length
> < 2) {
> > + kfree(obj);
> > + return -ENODATA;
> > + }
> > +
> > + /* byte[0] = STAT (0 = success), byte[1] = 1 (off) or 2 (on) */
> > + if (obj->buffer.pointer[0] != 0) {
> > + kfree(obj);
> > + return -EIO;
> > + }
> > +
> > + val = obj->buffer.pointer[1];
> > + if (val != 1 && val != 2) {
> > + kfree(obj);
> > + return -ENODATA;
> > + }
> > +
> > + if (on)
> > + *on = val - 1; /* 1→0 (off), 2→1 (on) */
> > +
> > + kfree(obj);
> > + return 0;
> > +}
> > +
> > +static int huawei_acpi_fn_lock_set(int on)
> > +{
> > + union acpi_object acpi_arg, *obj;
> > + struct acpi_object_list arg_list = { .count = 1, .pointer =
> &acpi_arg };
> > + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> > + acpi_status status;
> > + int ret = 0;
> > +
> > + /*
> > + * SFRS reads byte[2] of its argument via CreateByteField(Arg0,
> 0x02).
> > + * on=0 → FRSR=1 → EC gets 0x55 (fn-lock off)
> > + * on=1 → FRSR=2 → EC gets 0x5A (fn-lock on)
> > + */
> > + acpi_arg.type = ACPI_TYPE_INTEGER;
> > + acpi_arg.integer.value = (u64)(on + 1) << 16;
> > +
> > + status = acpi_evaluate_object(NULL, "\\SFRS", &arg_list,
> &output);
> > + if (ACPI_FAILURE(status))
> > + return -EIO;
> > +
> > + obj = output.pointer;
> > + if (obj && obj->type == ACPI_TYPE_BUFFER &&
> > + obj->buffer.length >= 1 && obj->buffer.pointer[0] != 0)
> > + ret = -EIO;
> > +
> > + kfree(obj);
> > + return ret;
> > +}
> > +
> > static int huawei_wmi_fn_lock_get(int *on)
> > {
> > u8 ret[0x100] = { 0 };
> > int err, i;
> >
> > + /* Newer models: use direct ACPI \GFRS method */
> > + if (acpi_has_method(NULL, "\\GFRS"))
> > + return huawei_acpi_fn_lock_get(on);
>
> Wouldn't it be better to put this behavior behind a quirk instead
> of enforcing it on the whole module? Keep in mind that this is
> also used by Honor devices that have the same WMI method.
>
> > +
> > + /* Legacy WMI fallback */
> > err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
> > if (err)
> > return err;
> > @@ -550,6 +640,11 @@ static int huawei_wmi_fn_lock_set(int on)
> > {
> > union hwmi_arg arg;
> >
> > + /* Newer models: use direct ACPI \SFRS method */
> > + if (acpi_has_method(NULL, "\\SFRS"))
> > + return huawei_acpi_fn_lock_set(on);
> > +
> > + /* Legacy WMI fallback */
> > arg.cmd = FN_LOCK_SET;
> > arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
> >
> > --
> > 2.54.0
> >
>
>
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models
2026-05-26 8:58 ` Ilpo Järvinen
@ 2026-05-26 9:09 ` Superstes
0 siblings, 0 replies; 11+ messages in thread
From: Superstes @ 2026-05-26 9:09 UTC (permalink / raw)
To: Ilpo Järvinen
Cc: Ayman Bagabas, Hans de Goede, platform-driver-x86, LKML
>> Do you mean you'd want to serialize .probe()s of those devices?
I don't really know how to fix this by now, I think serializing i2c
devices initialization can help.
It should be well tested(booting, waking from sleep\hibernation) to
confirm that just serializing works, maybe other workarounds should be
used.
вт, 26 мая 2026 г. в 13:58, Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>:
>
> On Tue, 26 May 2026, Superstes wrote:
>
> > >> Thank you for working on this. Could you open an issue and/or send a PR
> > to https://github.com/aymanbagabas/Huawei-WMI ?
> > Yes, I can send PR in the next two-three days.
> > >> This is very specific to the FLMH-XX model. Can you confirm this is the case for
> > other newer models?No, it is the latest model in the Matebook 14 series, I've just
> > bought it recently and faced multiple issues, which I try to resolve. I can't found
> > any information about that issues from another users, but i think that it can be
> > related with other recent laptop based on Intel Core Ultra processors, it will be
> > good to have someone with following models:
> > MateBook Fold (2025)
> > MateBook X Pro (2024)
> > MateBook GT 14 (2024)
> > MateBook D 14 (2024)
> > >> Wouldn't it be better to put this behavior behind a quirk instead of enforcing it
> > on the whole module?
> > I haven't found a better place to make such changes. But you can suggest something.
> > But I think that my workaround is safe because it calls very specific ACPI objects
> > and uses default fallbacks.
> >
> > By the way, maybe You, or Ilpo knows a good place to implement board specific i2c
> > acpi fixes? It Looks like there is a race condition situation which leads to
> > arbitration loss when kernel tries to initialize multiple devices on the i2c bus. To
> > fix this I've disabled light sensor and made systemd service to remove and enable
> > the i2c_hid_acpi module, but I haven't found a place where to implement a quirk that
> > fixes that issue.
>
> Do you mean you'd want to serialize .probe()s of those devices?
>
> --
> i.
>
> > пн, 25 мая 2026 г. в 23:38, Ayman Bagabas <ayman.bagabas@gmail.com>:
> > >
> > > Newer Huawei laptops (e.g. FLMH-XX / MateBook 14 2024) no longer
> > support
> > > the legacy WMI interface for Fn-lock control. Instead, they expose
> > direct
> > > ACPI methods \GFRS and \SFRS (Get/Set Fn key Reversal Status) which
> > > communicate with the EC via registers 0x6B (read) and 0x6C (write).
> > >
> > > Add huawei_acpi_fn_lock_get() and huawei_acpi_fn_lock_set() helpers
> > that
> > > use acpi_evaluate_object() to call these methods. Both
> > > huawei_wmi_fn_lock_get() and huawei_wmi_fn_lock_set() now probe for
> > > \GFRS/\SFRS via acpi_has_method() first and fall back to the legacy WMI
> > > path if not present.
> >
> > Thank you for working on this. Could you open an issue and/or send a PR
> > to https://github.com/aymanbagabas/Huawei-WMI ?
> >
> > >
> > > Tested on: HUAWEI FLMH-XX (MateBook 14 2024),
> > > CachyOS (kernel 7.0.9-1-cachyos).
> > >
> > > Signed-off-by: Shaposhnikov Daniil <2minesweeper2@gmail.com>
> > > ---
> > > drivers/platform/x86/huawei-wmi.c | 95 +++++++++++++++++++++++++++++++
> > > 1 file changed, 95 insertions(+)
> > >
> > > diff --git a/drivers/platform/x86/huawei-wmi.c
> > b/drivers/platform/x86/huawei-wmi.c
> > > index 93cca17fdf58..19cd8f1a8e33 100644
> > > --- a/drivers/platform/x86/huawei-wmi.c
> > > +++ b/drivers/platform/x86/huawei-wmi.c
> > > @@ -527,11 +527,101 @@ static void huawei_wmi_battery_exit(struct
> > device *dev)
> > >
> > > /* Fn lock */
> > >
> > > +/*
> > > + * Newer Huawei models (e.g. HUAWEI FLMH-XX / MateBook 14 2024) use
> > direct
> > > + * ACPI methods \GFRS / \SFRS (Get/Set Fn key Reversal Status) to
> > control
> > > + * Fn-lock via EC registers 0x6B (read) and 0x6C (write).
> > > + *
> > > + * GFRS response buffer layout:
> > > + * byte[0] = STAT (0 = success)
> > > + * byte[1] = 0x01 (fn-lock off) or 0x02 (fn-lock on)
> > > + *
> > > + * SFRS argument layout (CreateByteField(Arg0, 0x02, FRSR)):
> > > + * Value is read from byte[2] of the integer argument, so it must be
> > > + * passed as (value << 16):
> > > + * (1 << 16) = fn-lock off (writes 0x55 to EC 0x6C)
> > > + * (2 << 16) = fn-lock on (writes 0x5A to EC 0x6C)
> > > + */
> >
> > This is very specific to the FLMH-XX model. Can you confirm this is the
> > case for other newer models?
> >
> > > +
> > > +static int huawei_acpi_fn_lock_get(int *on)
> > > +{
> > > + union acpi_object acpi_arg, *obj;
> > > + struct acpi_object_list arg_list = { .count = 1, .pointer =
> > &acpi_arg };
> > > + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> > > + acpi_status status;
> > > + u8 val;
> > > +
> > > + acpi_arg.type = ACPI_TYPE_INTEGER;
> > > + acpi_arg.integer.value = 0;
> > > +
> > > + status = acpi_evaluate_object(NULL, "\\GFRS", &arg_list,
> > &output);
> > > + if (ACPI_FAILURE(status))
> > > + return -EIO;
> > > +
> > > + obj = output.pointer;
> > > + if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length
> > < 2) {
> > > + kfree(obj);
> > > + return -ENODATA;
> > > + }
> > > +
> > > + /* byte[0] = STAT (0 = success), byte[1] = 1 (off) or 2 (on) */
> > > + if (obj->buffer.pointer[0] != 0) {
> > > + kfree(obj);
> > > + return -EIO;
> > > + }
> > > +
> > > + val = obj->buffer.pointer[1];
> > > + if (val != 1 && val != 2) {
> > > + kfree(obj);
> > > + return -ENODATA;
> > > + }
> > > +
> > > + if (on)
> > > + *on = val - 1; /* 1→0 (off), 2→1 (on) */
> > > +
> > > + kfree(obj);
> > > + return 0;
> > > +}
> > > +
> > > +static int huawei_acpi_fn_lock_set(int on)
> > > +{
> > > + union acpi_object acpi_arg, *obj;
> > > + struct acpi_object_list arg_list = { .count = 1, .pointer =
> > &acpi_arg };
> > > + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
> > > + acpi_status status;
> > > + int ret = 0;
> > > +
> > > + /*
> > > + * SFRS reads byte[2] of its argument via CreateByteField(Arg0,
> > 0x02).
> > > + * on=0 → FRSR=1 → EC gets 0x55 (fn-lock off)
> > > + * on=1 → FRSR=2 → EC gets 0x5A (fn-lock on)
> > > + */
> > > + acpi_arg.type = ACPI_TYPE_INTEGER;
> > > + acpi_arg.integer.value = (u64)(on + 1) << 16;
> > > +
> > > + status = acpi_evaluate_object(NULL, "\\SFRS", &arg_list,
> > &output);
> > > + if (ACPI_FAILURE(status))
> > > + return -EIO;
> > > +
> > > + obj = output.pointer;
> > > + if (obj && obj->type == ACPI_TYPE_BUFFER &&
> > > + obj->buffer.length >= 1 && obj->buffer.pointer[0] != 0)
> > > + ret = -EIO;
> > > +
> > > + kfree(obj);
> > > + return ret;
> > > +}
> > > +
> > > static int huawei_wmi_fn_lock_get(int *on)
> > > {
> > > u8 ret[0x100] = { 0 };
> > > int err, i;
> > >
> > > + /* Newer models: use direct ACPI \GFRS method */
> > > + if (acpi_has_method(NULL, "\\GFRS"))
> > > + return huawei_acpi_fn_lock_get(on);
> >
> > Wouldn't it be better to put this behavior behind a quirk instead
> > of enforcing it on the whole module? Keep in mind that this is
> > also used by Honor devices that have the same WMI method.
> >
> > > +
> > > + /* Legacy WMI fallback */
> > > err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100);
> > > if (err)
> > > return err;
> > > @@ -550,6 +640,11 @@ static int huawei_wmi_fn_lock_set(int on)
> > > {
> > > union hwmi_arg arg;
> > >
> > > + /* Newer models: use direct ACPI \SFRS method */
> > > + if (acpi_has_method(NULL, "\\SFRS"))
> > > + return huawei_acpi_fn_lock_set(on);
> > > +
> > > + /* Legacy WMI fallback */
> > > arg.cmd = FN_LOCK_SET;
> > > arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on.
> > >
> > > --
> > > 2.54.0
> > >
> >
> >
> >
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-05-26 9:09 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-24 12:15 [PATCH] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models Shaposhnikov Daniil
2026-05-25 10:16 ` Ilpo Järvinen
2026-05-25 12:17 ` [PATCH v2 0/1] " Shaposhnikov Daniil
2026-05-25 12:17 ` [PATCH v2 1/1] " Shaposhnikov Daniil
2026-05-25 12:33 ` Ilpo Järvinen
2026-05-25 13:00 ` [PATCH v3 0/1] " Shaposhnikov Daniil
2026-05-25 13:00 ` [PATCH v3 1/1] " Shaposhnikov Daniil
2026-05-25 18:38 ` [PATCH] " Ayman Bagabas
2026-05-25 19:22 ` Superstes
[not found] ` <CALOxGBVJzk4GX2xrG3N1Djy1UZhMXMdcYKeJQuBqcf289SO32w@mail.gmail.com>
2026-05-26 8:58 ` Ilpo Järvinen
2026-05-26 9:09 ` Superstes
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.