All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
To: Shaposhnikov Daniil <2minesweeper2@gmail.com>
Cc: Hans de Goede <hansg@kernel.org>,
	platform-driver-x86@vger.kernel.org,
	 LKML <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v2 1/1] platform/x86: huawei-wmi: add ACPI fallback for Fn-lock on newer models
Date: Mon, 25 May 2026 15:33:23 +0300 (EEST)	[thread overview]
Message-ID: <d6df277b-512e-65f2-dc19-4912f6da55c8@linux.intel.com> (raw)
In-Reply-To: <20260525121738.11354-2-2minesweeper2@gmail.com>

[-- 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.

  reply	other threads:[~2026-05-25 12:33 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d6df277b-512e-65f2-dc19-4912f6da55c8@linux.intel.com \
    --to=ilpo.jarvinen@linux.intel.com \
    --cc=2minesweeper2@gmail.com \
    --cc=hansg@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=platform-driver-x86@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.