* [PATCH v9 01/11] HID: asus: simplify RGB init sequence
2025-11-20 9:46 [PATCH v9 00/11] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling Antheas Kapenekakis
@ 2025-11-20 9:46 ` Antheas Kapenekakis
2025-11-20 9:46 ` [PATCH v9 02/11] HID: asus: initialize additional endpoints only for legacy devices Antheas Kapenekakis
` (9 subsequent siblings)
10 siblings, 0 replies; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 9:46 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis
Currently, RGB initialization forks depending on whether a device is
NKEY. However, in reality both initialization forks are the same, other
than the NKEY initialization initializing the LED_REPORT_ID1,
LED_REPORT_ID2 endpoints, and the non-NKEY initialization having a
functionality check which is skipped for the NKEY path.
Therefore, merge the if blocks, gate the ID1/ID2 initializations
behind the NKEY quirk instead, and introduce the functionality check
for NKEY devices (it is supported by them).
There should be no functional change with this patch.
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 52 ++++++++++++++++++------------------------
1 file changed, 22 insertions(+), 30 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index a444d41e53b6..415d067938e3 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -638,13 +638,20 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
unsigned char kbd_func;
int ret;
- if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
- /* Initialize keyboard */
- ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);
- if (ret < 0)
- return ret;
+ ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);
+ if (ret < 0)
+ return ret;
- /* The LED endpoint is initialised in two HID */
+ /* Get keyboard functions */
+ ret = asus_kbd_get_functions(hdev, &kbd_func, FEATURE_KBD_REPORT_ID);
+ if (ret < 0)
+ return ret;
+
+ /* Check for backlight support */
+ if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
+ return -ENODEV;
+
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID1);
if (ret < 0)
return ret;
@@ -652,34 +659,19 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID2);
if (ret < 0)
return ret;
+ }
- if (dmi_match(DMI_PRODUCT_FAMILY, "ProArt P16")) {
- ret = asus_kbd_disable_oobe(hdev);
- if (ret < 0)
- return ret;
- }
-
- if (drvdata->quirks & QUIRK_ROG_ALLY_XPAD) {
- intf = to_usb_interface(hdev->dev.parent);
- udev = interface_to_usbdev(intf);
- validate_mcu_fw_version(hdev,
- le16_to_cpu(udev->descriptor.idProduct));
- }
-
- } else {
- /* Initialize keyboard */
- ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);
- if (ret < 0)
- return ret;
-
- /* Get keyboard functions */
- ret = asus_kbd_get_functions(hdev, &kbd_func, FEATURE_KBD_REPORT_ID);
+ if (dmi_match(DMI_PRODUCT_FAMILY, "ProArt P16")) {
+ ret = asus_kbd_disable_oobe(hdev);
if (ret < 0)
return ret;
+ }
- /* Check for backlight support */
- if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
- return -ENODEV;
+ if (drvdata->quirks & QUIRK_ROG_ALLY_XPAD) {
+ intf = to_usb_interface(hdev->dev.parent);
+ udev = interface_to_usbdev(intf);
+ validate_mcu_fw_version(
+ hdev, le16_to_cpu(udev->descriptor.idProduct));
}
drvdata->kbd_backlight = devm_kzalloc(&hdev->dev,
--
2.52.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v9 02/11] HID: asus: initialize additional endpoints only for legacy devices
2025-11-20 9:46 [PATCH v9 00/11] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling Antheas Kapenekakis
2025-11-20 9:46 ` [PATCH v9 01/11] HID: asus: simplify RGB init sequence Antheas Kapenekakis
@ 2025-11-20 9:46 ` Antheas Kapenekakis
2025-11-20 9:46 ` [PATCH v9 03/11] HID: asus: use same report_id in response Antheas Kapenekakis
` (8 subsequent siblings)
10 siblings, 0 replies; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 9:46 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis
Currently, ID1/ID2 initializations are performed for all NKEY devices.
However, ID1 initializations are only required for RGB control and are
only supported for RGB capable devices. ID2 initializations are only
required for initializing the Anime display endpoint which is only
supported on devices with an Anime display. Both of these
initializations are out of scope for this driver (this is a brightness
control and keyboard shortcut driver) and they should not be performed
for devices that do not support them in any case.
At the same time, there are older NKEY devices that have only been
tested with these initializations in the kernel and it is not possible
to recheck them. There is a possibility that especially with the ID1
initialization, certain laptop models might have their shortcuts stop
working (currently unproven).
For an abundance of caution, only initialize ID1/ID2 for those older
NKEY devices by introducing a quirk for them and replacing the NKEY
quirk in the block that performs the inits with that.
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 415d067938e3..db2cc9922447 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -89,6 +89,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define QUIRK_ROG_NKEY_KEYBOARD BIT(11)
#define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12)
#define QUIRK_ROG_ALLY_XPAD BIT(13)
+#define QUIRK_ROG_NKEY_LEGACY BIT(14)
#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
QUIRK_NO_INIT_REPORTS | \
@@ -651,7 +652,7 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
if (!(kbd_func & SUPPORT_KBD_BACKLIGHT))
return -ENODEV;
- if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) {
+ if (drvdata->quirks & QUIRK_ROG_NKEY_LEGACY) {
ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID1);
if (ret < 0)
return ret;
@@ -1375,10 +1376,10 @@ static const struct hid_device_id asus_devices[] = {
QUIRK_USE_KBD_BACKLIGHT },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD),
- QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
+ QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_LEGACY },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2),
- QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
+ QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_LEGACY },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR),
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
--
2.52.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v9 03/11] HID: asus: use same report_id in response
2025-11-20 9:46 [PATCH v9 00/11] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling Antheas Kapenekakis
2025-11-20 9:46 ` [PATCH v9 01/11] HID: asus: simplify RGB init sequence Antheas Kapenekakis
2025-11-20 9:46 ` [PATCH v9 02/11] HID: asus: initialize additional endpoints only for legacy devices Antheas Kapenekakis
@ 2025-11-20 9:46 ` Antheas Kapenekakis
2025-11-20 9:46 ` [PATCH v9 04/11] HID: asus: fortify keyboard handshake Antheas Kapenekakis
` (7 subsequent siblings)
10 siblings, 0 replies; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 9:46 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis
Currently, asus_kbd_get_functions prods the device using feature
report report_id, but then is hardcoded to check the response through
FEATURE_KBD_REPORT_ID. This only works if report_id is that value
(currently true). So, use report_id in the response as well to
maintain functionality if that value changes in the future.
Reviewed-by: Denis Benato <benato.denis96@gmail.com>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index db2cc9922447..6de402d215d0 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -423,7 +423,7 @@ static int asus_kbd_get_functions(struct hid_device *hdev,
if (!readbuf)
return -ENOMEM;
- ret = hid_hw_raw_request(hdev, FEATURE_KBD_REPORT_ID, readbuf,
+ ret = hid_hw_raw_request(hdev, report_id, readbuf,
FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (ret < 0) {
--
2.52.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v9 04/11] HID: asus: fortify keyboard handshake
2025-11-20 9:46 [PATCH v9 00/11] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling Antheas Kapenekakis
` (2 preceding siblings ...)
2025-11-20 9:46 ` [PATCH v9 03/11] HID: asus: use same report_id in response Antheas Kapenekakis
@ 2025-11-20 9:46 ` Antheas Kapenekakis
2025-11-20 14:15 ` Denis Benato
2025-11-20 9:46 ` [PATCH v9 05/11] HID: asus: move vendor initialization to probe Antheas Kapenekakis
` (6 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 9:46 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis
Handshaking with an Asus device involves sending it a feature report
with the string "ASUS Tech.Inc." and then reading it back to verify the
handshake was successful, under the feature ID the interaction will
take place.
Currently, the driver only does the first part. Add the readback to
verify the handshake was successful. As this could cause breakages,
allow the verification to fail with a dmesg error until we verify
all devices work with it (they seem to).
Since the response is more than 16 bytes, increase the buffer size
to 64 as well to avoid overflow errors.
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 32 +++++++++++++++++++++++++++++---
1 file changed, 29 insertions(+), 3 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 6de402d215d0..5149dc7edfc5 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -48,7 +48,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define FEATURE_REPORT_ID 0x0d
#define INPUT_REPORT_ID 0x5d
#define FEATURE_KBD_REPORT_ID 0x5a
-#define FEATURE_KBD_REPORT_SIZE 16
+#define FEATURE_KBD_REPORT_SIZE 64
#define FEATURE_KBD_LED_REPORT_ID1 0x5d
#define FEATURE_KBD_LED_REPORT_ID2 0x5e
@@ -394,14 +394,40 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu
static int asus_kbd_init(struct hid_device *hdev, u8 report_id)
{
+ /*
+ * The handshake is first sent as a set_report, then retrieved
+ * from a get_report. They should be equal.
+ */
const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,
0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
+ u8 *readbuf;
int ret;
ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
- if (ret < 0)
- hid_err(hdev, "Asus failed to send init command: %d\n", ret);
+ if (ret < 0) {
+ hid_err(hdev, "Asus failed to send handshake: %d\n", ret);
+ return ret;
+ }
+
+ readbuf = kzalloc(FEATURE_KBD_REPORT_SIZE, GFP_KERNEL);
+ if (!readbuf)
+ return -ENOMEM;
+
+ ret = hid_hw_raw_request(hdev, report_id, readbuf,
+ FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT,
+ HID_REQ_GET_REPORT);
+ if (ret < 0) {
+ hid_err(hdev, "Asus failed to receive handshake ack: %d\n", ret);
+ } else if (memcmp(readbuf, buf, sizeof(buf)) != 0) {
+ hid_warn(hdev, "Asus handshake returned invalid response: %*ph\n",
+ FEATURE_KBD_REPORT_SIZE, readbuf);
+ /*
+ * Do not return error if handshake is wrong until this is
+ * verified to work for all devices.
+ */
+ }
+ kfree(readbuf);
return ret;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH v9 04/11] HID: asus: fortify keyboard handshake
2025-11-20 9:46 ` [PATCH v9 04/11] HID: asus: fortify keyboard handshake Antheas Kapenekakis
@ 2025-11-20 14:15 ` Denis Benato
2025-11-20 14:28 ` Antheas Kapenekakis
0 siblings, 1 reply; 26+ messages in thread
From: Denis Benato @ 2025-11-20 14:15 UTC (permalink / raw)
To: Antheas Kapenekakis, platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen
On 11/20/25 10:46, Antheas Kapenekakis wrote:
> Handshaking with an Asus device involves sending it a feature report
> with the string "ASUS Tech.Inc." and then reading it back to verify the
> handshake was successful, under the feature ID the interaction will
> take place.
>
> Currently, the driver only does the first part. Add the readback to
> verify the handshake was successful. As this could cause breakages,
> allow the verification to fail with a dmesg error until we verify
> all devices work with it (they seem to).
>
> Since the response is more than 16 bytes, increase the buffer size
> to 64 as well to avoid overflow errors.
>
> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
> drivers/hid/hid-asus.c | 32 +++++++++++++++++++++++++++++---
> 1 file changed, 29 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> index 6de402d215d0..5149dc7edfc5 100644
> --- a/drivers/hid/hid-asus.c
> +++ b/drivers/hid/hid-asus.c
> @@ -48,7 +48,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
> #define FEATURE_REPORT_ID 0x0d
> #define INPUT_REPORT_ID 0x5d
> #define FEATURE_KBD_REPORT_ID 0x5a
> -#define FEATURE_KBD_REPORT_SIZE 16
> +#define FEATURE_KBD_REPORT_SIZE 64
> #define FEATURE_KBD_LED_REPORT_ID1 0x5d
> #define FEATURE_KBD_LED_REPORT_ID2 0x5e
>
> @@ -394,14 +394,40 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu
>
> static int asus_kbd_init(struct hid_device *hdev, u8 report_id)
> {
> + /*
> + * The handshake is first sent as a set_report, then retrieved
> + * from a get_report. They should be equal.
> + */
> const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,
> 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
> + u8 *readbuf;
> int ret;
>
> ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
> - if (ret < 0)
> - hid_err(hdev, "Asus failed to send init command: %d\n", ret);
> + if (ret < 0) {
> + hid_err(hdev, "Asus failed to send handshake: %d\n", ret);
> + return ret;
> + }
> +
> + readbuf = kzalloc(FEATURE_KBD_REPORT_SIZE, GFP_KERNEL);
I see my suggestion to use __free here didn't materialize in code using
it even after Ilpo kindly wrote how to correctly use it.
I think you can move the readbuf assignment right below buf and
take into account what Ilpo said.
I don't expect new variables will be added here ever again,
but I agree with Ilpo that it's a good idea here to write code
accounting for that possibility.
It is my understanding that who proposes patches is expected to
resolve discussions when changes are proposed or to take into
account requested changes and submit a modified version.
> + if (!readbuf)
> + return -ENOMEM;
> +
> + ret = hid_hw_raw_request(hdev, report_id, readbuf,
> + FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT,
> + HID_REQ_GET_REPORT);
> + if (ret < 0) {
> + hid_err(hdev, "Asus failed to receive handshake ack: %d\n", ret);
> + } else if (memcmp(readbuf, buf, sizeof(buf)) != 0) {
> + hid_warn(hdev, "Asus handshake returned invalid response: %*ph\n",
> + FEATURE_KBD_REPORT_SIZE, readbuf);
> + /*
> + * Do not return error if handshake is wrong until this is
> + * verified to work for all devices.
> + */
> + }
>
> + kfree(readbuf);
> return ret;
> }
>
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v9 04/11] HID: asus: fortify keyboard handshake
2025-11-20 14:15 ` Denis Benato
@ 2025-11-20 14:28 ` Antheas Kapenekakis
2025-11-20 14:31 ` Denis Benato
2025-11-20 16:41 ` Ilpo Järvinen
0 siblings, 2 replies; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 14:28 UTC (permalink / raw)
To: Denis Benato
Cc: platform-driver-x86, linux-input, linux-kernel, Jiri Kosina,
Benjamin Tissoires, Corentin Chary, Luke D . Jones, Hans de Goede,
Ilpo Järvinen
On Thu, 20 Nov 2025 at 15:15, Denis Benato <benato.denis96@gmail.com> wrote:
>
>
> On 11/20/25 10:46, Antheas Kapenekakis wrote:
> > Handshaking with an Asus device involves sending it a feature report
> > with the string "ASUS Tech.Inc." and then reading it back to verify the
> > handshake was successful, under the feature ID the interaction will
> > take place.
> >
> > Currently, the driver only does the first part. Add the readback to
> > verify the handshake was successful. As this could cause breakages,
> > allow the verification to fail with a dmesg error until we verify
> > all devices work with it (they seem to).
> >
> > Since the response is more than 16 bytes, increase the buffer size
> > to 64 as well to avoid overflow errors.
> >
> > Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > ---
> > drivers/hid/hid-asus.c | 32 +++++++++++++++++++++++++++++---
> > 1 file changed, 29 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> > index 6de402d215d0..5149dc7edfc5 100644
> > --- a/drivers/hid/hid-asus.c
> > +++ b/drivers/hid/hid-asus.c
> > @@ -48,7 +48,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
> > #define FEATURE_REPORT_ID 0x0d
> > #define INPUT_REPORT_ID 0x5d
> > #define FEATURE_KBD_REPORT_ID 0x5a
> > -#define FEATURE_KBD_REPORT_SIZE 16
> > +#define FEATURE_KBD_REPORT_SIZE 64
> > #define FEATURE_KBD_LED_REPORT_ID1 0x5d
> > #define FEATURE_KBD_LED_REPORT_ID2 0x5e
> >
> > @@ -394,14 +394,40 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu
> >
> > static int asus_kbd_init(struct hid_device *hdev, u8 report_id)
> > {
> > + /*
> > + * The handshake is first sent as a set_report, then retrieved
> > + * from a get_report. They should be equal.
> > + */
> > const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,
> > 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
> > + u8 *readbuf;
> > int ret;
> >
> > ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
> > - if (ret < 0)
> > - hid_err(hdev, "Asus failed to send init command: %d\n", ret);
> > + if (ret < 0) {
> > + hid_err(hdev, "Asus failed to send handshake: %d\n", ret);
> > + return ret;
> > + }
> > +
> > + readbuf = kzalloc(FEATURE_KBD_REPORT_SIZE, GFP_KERNEL);
> I see my suggestion to use __free here didn't materialize in code using
> it even after Ilpo kindly wrote how to correctly use it.
>
> I think you can move the readbuf assignment right below buf and
> take into account what Ilpo said.
>
> I don't expect new variables will be added here ever again,
> but I agree with Ilpo that it's a good idea here to write code
> accounting for that possibility.
>
> It is my understanding that who proposes patches is expected to
> resolve discussions when changes are proposed or to take into
> account requested changes and submit a modified version.
It was ambiguous. I interpreted Ilpo's email as a dismissal
I will try to incorporate it if I do another revision. Although I do
not think it improves things in this case as the function does not
have multiple return statements.
> > + if (!readbuf)
> > + return -ENOMEM;
> > +
> > + ret = hid_hw_raw_request(hdev, report_id, readbuf,
> > + FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT,
> > + HID_REQ_GET_REPORT);
> > + if (ret < 0) {
> > + hid_err(hdev, "Asus failed to receive handshake ack: %d\n", ret);
> > + } else if (memcmp(readbuf, buf, sizeof(buf)) != 0) {
> > + hid_warn(hdev, "Asus handshake returned invalid response: %*ph\n",
> > + FEATURE_KBD_REPORT_SIZE, readbuf);
> > + /*
> > + * Do not return error if handshake is wrong until this is
> > + * verified to work for all devices.
> > + */
> > + }
> >
> > + kfree(readbuf);
> > return ret;
> > }
> >
>
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v9 04/11] HID: asus: fortify keyboard handshake
2025-11-20 14:28 ` Antheas Kapenekakis
@ 2025-11-20 14:31 ` Denis Benato
2025-11-20 16:41 ` Ilpo Järvinen
1 sibling, 0 replies; 26+ messages in thread
From: Denis Benato @ 2025-11-20 14:31 UTC (permalink / raw)
To: Antheas Kapenekakis
Cc: platform-driver-x86, linux-input, linux-kernel, Jiri Kosina,
Benjamin Tissoires, Corentin Chary, Luke D . Jones, Hans de Goede,
Ilpo Järvinen
On 11/20/25 15:28, Antheas Kapenekakis wrote:
> On Thu, 20 Nov 2025 at 15:15, Denis Benato <benato.denis96@gmail.com> wrote:
>>
>> On 11/20/25 10:46, Antheas Kapenekakis wrote:
>>> Handshaking with an Asus device involves sending it a feature report
>>> with the string "ASUS Tech.Inc." and then reading it back to verify the
>>> handshake was successful, under the feature ID the interaction will
>>> take place.
>>>
>>> Currently, the driver only does the first part. Add the readback to
>>> verify the handshake was successful. As this could cause breakages,
>>> allow the verification to fail with a dmesg error until we verify
>>> all devices work with it (they seem to).
>>>
>>> Since the response is more than 16 bytes, increase the buffer size
>>> to 64 as well to avoid overflow errors.
>>>
>>> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
>>> ---
>>> drivers/hid/hid-asus.c | 32 +++++++++++++++++++++++++++++---
>>> 1 file changed, 29 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
>>> index 6de402d215d0..5149dc7edfc5 100644
>>> --- a/drivers/hid/hid-asus.c
>>> +++ b/drivers/hid/hid-asus.c
>>> @@ -48,7 +48,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
>>> #define FEATURE_REPORT_ID 0x0d
>>> #define INPUT_REPORT_ID 0x5d
>>> #define FEATURE_KBD_REPORT_ID 0x5a
>>> -#define FEATURE_KBD_REPORT_SIZE 16
>>> +#define FEATURE_KBD_REPORT_SIZE 64
>>> #define FEATURE_KBD_LED_REPORT_ID1 0x5d
>>> #define FEATURE_KBD_LED_REPORT_ID2 0x5e
>>>
>>> @@ -394,14 +394,40 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu
>>>
>>> static int asus_kbd_init(struct hid_device *hdev, u8 report_id)
>>> {
>>> + /*
>>> + * The handshake is first sent as a set_report, then retrieved
>>> + * from a get_report. They should be equal.
>>> + */
>>> const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,
>>> 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
>>> + u8 *readbuf;
>>> int ret;
>>>
>>> ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
>>> - if (ret < 0)
>>> - hid_err(hdev, "Asus failed to send init command: %d\n", ret);
>>> + if (ret < 0) {
>>> + hid_err(hdev, "Asus failed to send handshake: %d\n", ret);
>>> + return ret;
>>> + }
>>> +
>>> + readbuf = kzalloc(FEATURE_KBD_REPORT_SIZE, GFP_KERNEL);
>> I see my suggestion to use __free here didn't materialize in code using
>> it even after Ilpo kindly wrote how to correctly use it.
>>
>> I think you can move the readbuf assignment right below buf and
>> take into account what Ilpo said.
>>
>> I don't expect new variables will be added here ever again,
>> but I agree with Ilpo that it's a good idea here to write code
>> accounting for that possibility.
>>
>> It is my understanding that who proposes patches is expected to
>> resolve discussions when changes are proposed or to take into
>> account requested changes and submit a modified version.
> It was ambiguous. I interpreted Ilpo's email as a dismissal
>
> I will try to incorporate it if I do another revision. Although I do
> not think it improves things in this case as the function does not
> have multiple return statements.
I will leave this decision to Ilpo, if he thinks there is no point in using
__free here I will add my Reviewed-by tag.
>>> + if (!readbuf)
>>> + return -ENOMEM;
>>> +
>>> + ret = hid_hw_raw_request(hdev, report_id, readbuf,
>>> + FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT,
>>> + HID_REQ_GET_REPORT);
>>> + if (ret < 0) {
>>> + hid_err(hdev, "Asus failed to receive handshake ack: %d\n", ret);
>>> + } else if (memcmp(readbuf, buf, sizeof(buf)) != 0) {
>>> + hid_warn(hdev, "Asus handshake returned invalid response: %*ph\n",
>>> + FEATURE_KBD_REPORT_SIZE, readbuf);
>>> + /*
>>> + * Do not return error if handshake is wrong until this is
>>> + * verified to work for all devices.
>>> + */
>>> + }
>>>
>>> + kfree(readbuf);
>>> return ret;
>>> }
>>>
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v9 04/11] HID: asus: fortify keyboard handshake
2025-11-20 14:28 ` Antheas Kapenekakis
2025-11-20 14:31 ` Denis Benato
@ 2025-11-20 16:41 ` Ilpo Järvinen
2025-11-20 21:54 ` Antheas Kapenekakis
1 sibling, 1 reply; 26+ messages in thread
From: Ilpo Järvinen @ 2025-11-20 16:41 UTC (permalink / raw)
To: Antheas Kapenekakis
Cc: Denis Benato, platform-driver-x86, linux-input, LKML, Jiri Kosina,
Benjamin Tissoires, Corentin Chary, Luke D . Jones, Hans de Goede
On Thu, 20 Nov 2025, Antheas Kapenekakis wrote:
> On Thu, 20 Nov 2025 at 15:15, Denis Benato <benato.denis96@gmail.com> wrote:
> >
> >
> > On 11/20/25 10:46, Antheas Kapenekakis wrote:
> > > Handshaking with an Asus device involves sending it a feature report
> > > with the string "ASUS Tech.Inc." and then reading it back to verify the
> > > handshake was successful, under the feature ID the interaction will
> > > take place.
> > >
> > > Currently, the driver only does the first part. Add the readback to
> > > verify the handshake was successful. As this could cause breakages,
> > > allow the verification to fail with a dmesg error until we verify
> > > all devices work with it (they seem to).
> > >
> > > Since the response is more than 16 bytes, increase the buffer size
> > > to 64 as well to avoid overflow errors.
> > >
> > > Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > > ---
> > > drivers/hid/hid-asus.c | 32 +++++++++++++++++++++++++++++---
> > > 1 file changed, 29 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> > > index 6de402d215d0..5149dc7edfc5 100644
> > > --- a/drivers/hid/hid-asus.c
> > > +++ b/drivers/hid/hid-asus.c
> > > @@ -48,7 +48,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
> > > #define FEATURE_REPORT_ID 0x0d
> > > #define INPUT_REPORT_ID 0x5d
> > > #define FEATURE_KBD_REPORT_ID 0x5a
> > > -#define FEATURE_KBD_REPORT_SIZE 16
> > > +#define FEATURE_KBD_REPORT_SIZE 64
> > > #define FEATURE_KBD_LED_REPORT_ID1 0x5d
> > > #define FEATURE_KBD_LED_REPORT_ID2 0x5e
> > >
> > > @@ -394,14 +394,40 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu
> > >
> > > static int asus_kbd_init(struct hid_device *hdev, u8 report_id)
> > > {
> > > + /*
> > > + * The handshake is first sent as a set_report, then retrieved
> > > + * from a get_report. They should be equal.
> > > + */
> > > const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,
> > > 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
> > > + u8 *readbuf;
> > > int ret;
> > >
> > > ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
> > > - if (ret < 0)
> > > - hid_err(hdev, "Asus failed to send init command: %d\n", ret);
> > > + if (ret < 0) {
> > > + hid_err(hdev, "Asus failed to send handshake: %d\n", ret);
> > > + return ret;
> > > + }
> > > +
> > > + readbuf = kzalloc(FEATURE_KBD_REPORT_SIZE, GFP_KERNEL);
> > I see my suggestion to use __free here didn't materialize in code using
> > it even after Ilpo kindly wrote how to correctly use it.
> >
> > I think you can move the readbuf assignment right below buf and
> > take into account what Ilpo said.
> >
> > I don't expect new variables will be added here ever again,
It's also about always doing the right thing so others will pick up the
pattern (for the cases when it's needed).
> > but I agree with Ilpo that it's a good idea here to write code
> > accounting for that possibility.
> >
> > It is my understanding that who proposes patches is expected to
> > resolve discussions when changes are proposed or to take into
> > account requested changes and submit a modified version.
>
> It was ambiguous. I interpreted Ilpo's email as a dismissal
I tried to explain how to use it, not to suggest cleanup.h shouldn't be
used.
> I will try to incorporate it if I do another revision. Although I do
> not think it improves things in this case as the function does not
> have multiple return statements.
>
> > > + if (!readbuf)
> > > + return -ENOMEM;
> > > +
> > > + ret = hid_hw_raw_request(hdev, report_id, readbuf,
> > > + FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT,
> > > + HID_REQ_GET_REPORT);
> > > + if (ret < 0) {
> > > + hid_err(hdev, "Asus failed to receive handshake ack: %d\n", ret);
> > > + } else if (memcmp(readbuf, buf, sizeof(buf)) != 0) {
> > > + hid_warn(hdev, "Asus handshake returned invalid response: %*ph\n",
> > > + FEATURE_KBD_REPORT_SIZE, readbuf);
> > > + /*
> > > + * Do not return error if handshake is wrong until this is
> > > + * verified to work for all devices.
> > > + */
> > > + }
> > >
> > > + kfree(readbuf);
> > > return ret;
> > > }
> > >
> >
>
--
i.
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v9 04/11] HID: asus: fortify keyboard handshake
2025-11-20 16:41 ` Ilpo Järvinen
@ 2025-11-20 21:54 ` Antheas Kapenekakis
2025-11-21 10:11 ` Ilpo Järvinen
0 siblings, 1 reply; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 21:54 UTC (permalink / raw)
To: Ilpo Järvinen
Cc: Denis Benato, platform-driver-x86, linux-input, LKML, Jiri Kosina,
Benjamin Tissoires, Corentin Chary, Luke D . Jones, Hans de Goede
On Thu, 20 Nov 2025 at 17:41, Ilpo Järvinen
<ilpo.jarvinen@linux.intel.com> wrote:
>
> On Thu, 20 Nov 2025, Antheas Kapenekakis wrote:
>
> > On Thu, 20 Nov 2025 at 15:15, Denis Benato <benato.denis96@gmail.com> wrote:
> > >
> > >
> > > On 11/20/25 10:46, Antheas Kapenekakis wrote:
> > > > Handshaking with an Asus device involves sending it a feature report
> > > > with the string "ASUS Tech.Inc." and then reading it back to verify the
> > > > handshake was successful, under the feature ID the interaction will
> > > > take place.
> > > >
> > > > Currently, the driver only does the first part. Add the readback to
> > > > verify the handshake was successful. As this could cause breakages,
> > > > allow the verification to fail with a dmesg error until we verify
> > > > all devices work with it (they seem to).
> > > >
> > > > Since the response is more than 16 bytes, increase the buffer size
> > > > to 64 as well to avoid overflow errors.
> > > >
> > > > Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > > > ---
> > > > drivers/hid/hid-asus.c | 32 +++++++++++++++++++++++++++++---
> > > > 1 file changed, 29 insertions(+), 3 deletions(-)
> > > >
> > > > diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> > > > index 6de402d215d0..5149dc7edfc5 100644
> > > > --- a/drivers/hid/hid-asus.c
> > > > +++ b/drivers/hid/hid-asus.c
> > > > @@ -48,7 +48,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
> > > > #define FEATURE_REPORT_ID 0x0d
> > > > #define INPUT_REPORT_ID 0x5d
> > > > #define FEATURE_KBD_REPORT_ID 0x5a
> > > > -#define FEATURE_KBD_REPORT_SIZE 16
> > > > +#define FEATURE_KBD_REPORT_SIZE 64
> > > > #define FEATURE_KBD_LED_REPORT_ID1 0x5d
> > > > #define FEATURE_KBD_LED_REPORT_ID2 0x5e
> > > >
> > > > @@ -394,14 +394,40 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu
> > > >
> > > > static int asus_kbd_init(struct hid_device *hdev, u8 report_id)
> > > > {
> > > > + /*
> > > > + * The handshake is first sent as a set_report, then retrieved
> > > > + * from a get_report. They should be equal.
> > > > + */
> > > > const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,
> > > > 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
> > > > + u8 *readbuf;
> > > > int ret;
> > > >
> > > > ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
> > > > - if (ret < 0)
> > > > - hid_err(hdev, "Asus failed to send init command: %d\n", ret);
> > > > + if (ret < 0) {
> > > > + hid_err(hdev, "Asus failed to send handshake: %d\n", ret);
> > > > + return ret;
> > > > + }
> > > > +
> > > > + readbuf = kzalloc(FEATURE_KBD_REPORT_SIZE, GFP_KERNEL);
> > > I see my suggestion to use __free here didn't materialize in code using
> > > it even after Ilpo kindly wrote how to correctly use it.
> > >
> > > I think you can move the readbuf assignment right below buf and
> > > take into account what Ilpo said.
> > >
> > > I don't expect new variables will be added here ever again,
>
> It's also about always doing the right thing so others will pick up the
> pattern (for the cases when it's needed).
>
> > > but I agree with Ilpo that it's a good idea here to write code
> > > accounting for that possibility.
> > >
> > > It is my understanding that who proposes patches is expected to
> > > resolve discussions when changes are proposed or to take into
> > > account requested changes and submit a modified version.
> >
> > It was ambiguous. I interpreted Ilpo's email as a dismissal
>
> I tried to explain how to use it, not to suggest cleanup.h shouldn't be
> used.
Ok, I'll wait a few days and do another revision, doing some rewording
as well. Hopefully that will cover everything. To that extent, try to
finish reviewing the latter part of the series before that revision.
I'm a bit concerned with introducing kfree here because I do not know
how to use it and it might regress, but it should be ok.
I'd rather push the init down instead of pulling it up. Referencing
other code samples for kfree it is acceptable to push the variable
definition down, right?
Antheas
> > I will try to incorporate it if I do another revision. Although I do
> > not think it improves things in this case as the function does not
> > have multiple return statements.
> >
> > > > + if (!readbuf)
> > > > + return -ENOMEM;
> > > > +
> > > > + ret = hid_hw_raw_request(hdev, report_id, readbuf,
> > > > + FEATURE_KBD_REPORT_SIZE, HID_FEATURE_REPORT,
> > > > + HID_REQ_GET_REPORT);
> > > > + if (ret < 0) {
> > > > + hid_err(hdev, "Asus failed to receive handshake ack: %d\n", ret);
> > > > + } else if (memcmp(readbuf, buf, sizeof(buf)) != 0) {
> > > > + hid_warn(hdev, "Asus handshake returned invalid response: %*ph\n",
> > > > + FEATURE_KBD_REPORT_SIZE, readbuf);
> > > > + /*
> > > > + * Do not return error if handshake is wrong until this is
> > > > + * verified to work for all devices.
> > > > + */
> > > > + }
> > > >
> > > > + kfree(readbuf);
> > > > return ret;
> > > > }
> > > >
> > >
> >
>
> --
> i.
>
>
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v9 04/11] HID: asus: fortify keyboard handshake
2025-11-20 21:54 ` Antheas Kapenekakis
@ 2025-11-21 10:11 ` Ilpo Järvinen
0 siblings, 0 replies; 26+ messages in thread
From: Ilpo Järvinen @ 2025-11-21 10:11 UTC (permalink / raw)
To: Antheas Kapenekakis
Cc: Denis Benato, platform-driver-x86, linux-input, LKML, Jiri Kosina,
Benjamin Tissoires, Corentin Chary, Luke D . Jones, Hans de Goede
[-- Attachment #1: Type: text/plain, Size: 5797 bytes --]
On Thu, 20 Nov 2025, Antheas Kapenekakis wrote:
> On Thu, 20 Nov 2025 at 17:41, Ilpo Järvinen
> <ilpo.jarvinen@linux.intel.com> wrote:
> >
> > On Thu, 20 Nov 2025, Antheas Kapenekakis wrote:
> >
> > > On Thu, 20 Nov 2025 at 15:15, Denis Benato <benato.denis96@gmail.com> wrote:
> > > >
> > > >
> > > > On 11/20/25 10:46, Antheas Kapenekakis wrote:
> > > > > Handshaking with an Asus device involves sending it a feature report
> > > > > with the string "ASUS Tech.Inc." and then reading it back to verify the
> > > > > handshake was successful, under the feature ID the interaction will
> > > > > take place.
> > > > >
> > > > > Currently, the driver only does the first part. Add the readback to
> > > > > verify the handshake was successful. As this could cause breakages,
> > > > > allow the verification to fail with a dmesg error until we verify
> > > > > all devices work with it (they seem to).
> > > > >
> > > > > Since the response is more than 16 bytes, increase the buffer size
> > > > > to 64 as well to avoid overflow errors.
> > > > >
> > > > > Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > > > > ---
> > > > > drivers/hid/hid-asus.c | 32 +++++++++++++++++++++++++++++---
> > > > > 1 file changed, 29 insertions(+), 3 deletions(-)
> > > > >
> > > > > diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> > > > > index 6de402d215d0..5149dc7edfc5 100644
> > > > > --- a/drivers/hid/hid-asus.c
> > > > > +++ b/drivers/hid/hid-asus.c
> > > > > @@ -48,7 +48,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
> > > > > #define FEATURE_REPORT_ID 0x0d
> > > > > #define INPUT_REPORT_ID 0x5d
> > > > > #define FEATURE_KBD_REPORT_ID 0x5a
> > > > > -#define FEATURE_KBD_REPORT_SIZE 16
> > > > > +#define FEATURE_KBD_REPORT_SIZE 64
> > > > > #define FEATURE_KBD_LED_REPORT_ID1 0x5d
> > > > > #define FEATURE_KBD_LED_REPORT_ID2 0x5e
> > > > >
> > > > > @@ -394,14 +394,40 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu
> > > > >
> > > > > static int asus_kbd_init(struct hid_device *hdev, u8 report_id)
> > > > > {
> > > > > + /*
> > > > > + * The handshake is first sent as a set_report, then retrieved
> > > > > + * from a get_report. They should be equal.
> > > > > + */
> > > > > const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54,
> > > > > 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 };
> > > > > + u8 *readbuf;
> > > > > int ret;
> > > > >
> > > > > ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
> > > > > - if (ret < 0)
> > > > > - hid_err(hdev, "Asus failed to send init command: %d\n", ret);
> > > > > + if (ret < 0) {
> > > > > + hid_err(hdev, "Asus failed to send handshake: %d\n", ret);
> > > > > + return ret;
> > > > > + }
> > > > > +
> > > > > + readbuf = kzalloc(FEATURE_KBD_REPORT_SIZE, GFP_KERNEL);
> > > > I see my suggestion to use __free here didn't materialize in code using
> > > > it even after Ilpo kindly wrote how to correctly use it.
> > > >
> > > > I think you can move the readbuf assignment right below buf and
> > > > take into account what Ilpo said.
> > > >
> > > > I don't expect new variables will be added here ever again,
> >
> > It's also about always doing the right thing so others will pick up the
> > pattern (for the cases when it's needed).
> >
> > > > but I agree with Ilpo that it's a good idea here to write code
> > > > accounting for that possibility.
> > > >
> > > > It is my understanding that who proposes patches is expected to
> > > > resolve discussions when changes are proposed or to take into
> > > > account requested changes and submit a modified version.
> > >
> > > It was ambiguous. I interpreted Ilpo's email as a dismissal
> >
> > I tried to explain how to use it, not to suggest cleanup.h shouldn't be
> > used.
>
> Ok, I'll wait a few days and do another revision, doing some rewording
> as well. Hopefully that will cover everything. To that extent, try to
> finish reviewing the latter part of the series before that revision.
>
> I'm a bit concerned with introducing kfree here because I do not know
> how to use it and it might regress, but it should be ok.
You probably meant to say __free(), there are other things that could be
put inside __free() beyond kfree (no need to ack if that was the case).
It's not rocket science, basically the compiler performs the freeing
function when the scope the variable is in goes away. For error handling
rollback, this avoids need to do the cleanup call manually and no path is
left accidently without cleanup.
For cases, where you want to preserve a pointer to the allocated thing,
there are additional wrappers that one uses when returning the pointer
out of function (only gotcha here is that it will NULL the local
variable in question, so the variable cannot be dereferenced after that
point; been there, done that and had to debug a boot issue :-)).
> I'd rather push the init down instead of pulling it up. Referencing
> other code samples for kfree it is acceptable to push the variable
> definition down, right?
Yes.
Mid-function definitions used to be forbidding on the compiler level
but it was changed exactly because adding this auto cleanup framework. So
while top definition is still the general requirement, using __free() is an
exception to that rule and should be defined at the spot where we make
the "alloc" to ensure the teardown order is exactly reverse of the alloc
order (the order you introduce variables is the reverse of the order in
which the compiler will perform each tear down).
--
i.
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v9 05/11] HID: asus: move vendor initialization to probe
2025-11-20 9:46 [PATCH v9 00/11] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling Antheas Kapenekakis
` (3 preceding siblings ...)
2025-11-20 9:46 ` [PATCH v9 04/11] HID: asus: fortify keyboard handshake Antheas Kapenekakis
@ 2025-11-20 9:46 ` Antheas Kapenekakis
2025-11-20 9:46 ` [PATCH v9 06/11] HID: asus: early return for ROG devices Antheas Kapenekakis
` (5 subsequent siblings)
10 siblings, 0 replies; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 9:46 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis
ROG NKEY devices have multiple HID endpoints, around 3-4. One of those
endpoints has a usage page of 0xff31, and is the one that emits keyboard
shortcuts and controls RGB/backlight. Currently, this driver places
the usage page check under asus_input_mapping and then inits backlight
in asus_input_configured which is unnecessarily complicated and prevents
probe from performing customizations on the vendor endpoint.
Simplify the logic by introducing an is_vendor variable into probe that
checks for usage page 0xff31. Then, use this variable to move backlight
initialization into probe instead of asus_input_configured, and remove
the backlight check from asus_input_mapping.
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 35 ++++++++++++++++++-----------------
1 file changed, 18 insertions(+), 17 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 5149dc7edfc5..3047bc54bf2e 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -47,6 +47,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define T100CHI_MOUSE_REPORT_ID 0x06
#define FEATURE_REPORT_ID 0x0d
#define INPUT_REPORT_ID 0x5d
+#define HID_USAGE_PAGE_VENDOR 0xff310000
#define FEATURE_KBD_REPORT_ID 0x5a
#define FEATURE_KBD_REPORT_SIZE 64
#define FEATURE_KBD_LED_REPORT_ID1 0x5d
@@ -126,7 +127,6 @@ struct asus_drvdata {
struct input_dev *tp_kbd_input;
struct asus_kbd_leds *kbd_backlight;
const struct asus_touchpad_info *tp;
- bool enable_backlight;
struct power_supply *battery;
struct power_supply_desc battery_desc;
int battery_capacity;
@@ -317,7 +317,7 @@ static int asus_e1239t_event(struct asus_drvdata *drvdat, u8 *data, int size)
static int asus_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
- if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 &&
+ if ((usage->hid & HID_USAGE_PAGE) == HID_USAGE_PAGE_VENDOR &&
(usage->hid & HID_USAGE) != 0x00 &&
(usage->hid & HID_USAGE) != 0xff && !usage->type) {
hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n",
@@ -942,11 +942,6 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
drvdata->input = input;
- if (drvdata->enable_backlight &&
- !asus_kbd_wmi_led_control_present(hdev) &&
- asus_kbd_register_leds(hdev))
- hid_warn(hdev, "Failed to initialize backlight.\n");
-
return 0;
}
@@ -1019,15 +1014,6 @@ static int asus_input_mapping(struct hid_device *hdev,
return -1;
}
- /*
- * Check and enable backlight only on devices with UsagePage ==
- * 0xff31 to avoid initializing the keyboard firmware multiple
- * times on devices with multiple HID descriptors but same
- * PID/VID.
- */
- if (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT)
- drvdata->enable_backlight = true;
-
set_bit(EV_REP, hi->input->evbit);
return 1;
}
@@ -1144,8 +1130,11 @@ static int __maybe_unused asus_reset_resume(struct hid_device *hdev)
static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
- int ret;
+ struct hid_report_enum *rep_enum;
struct asus_drvdata *drvdata;
+ struct hid_report *rep;
+ bool is_vendor = false;
+ int ret;
drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
if (drvdata == NULL) {
@@ -1229,12 +1218,24 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret;
}
+ /* Check for vendor for RGB init and handle generic devices properly. */
+ rep_enum = &hdev->report_enum[HID_INPUT_REPORT];
+ list_for_each_entry(rep, &rep_enum->report_list, list) {
+ if ((rep->application & HID_USAGE_PAGE) == HID_USAGE_PAGE_VENDOR)
+ is_vendor = true;
+ }
+
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
hid_err(hdev, "Asus hw start failed: %d\n", ret);
return ret;
}
+ if (is_vendor && (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) &&
+ !asus_kbd_wmi_led_control_present(hdev) &&
+ asus_kbd_register_leds(hdev))
+ hid_warn(hdev, "Failed to initialize backlight.\n");
+
/*
* Check that input registration succeeded. Checking that
* HID_CLAIMED_INPUT is set prevents a UAF when all input devices
--
2.52.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v9 06/11] HID: asus: early return for ROG devices
2025-11-20 9:46 [PATCH v9 00/11] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling Antheas Kapenekakis
` (4 preceding siblings ...)
2025-11-20 9:46 ` [PATCH v9 05/11] HID: asus: move vendor initialization to probe Antheas Kapenekakis
@ 2025-11-20 9:46 ` Antheas Kapenekakis
2025-11-20 13:29 ` Denis Benato
2025-11-20 9:46 ` [PATCH v9 07/11] platform/x86: asus-wmi: Add support for multiple kbd led handlers Antheas Kapenekakis
` (4 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 9:46 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis
Some ROG devices have a new dynamic backlight interface for control by
Windows. This interface does not create an ->input device, causing the
kernel to print an error message and to eject it. In addition, ROG
devices have proper HID names in their descriptors so renaming them is
not necessary.
Therefore, if a device is identified as ROG, early return from probe to
skip renaming and ->input checks.
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 3047bc54bf2e..6193c9483bec 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -1236,6 +1236,13 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
asus_kbd_register_leds(hdev))
hid_warn(hdev, "Failed to initialize backlight.\n");
+ /*
+ * For ROG keyboards, skip rename for consistency and ->input check as
+ * some devices do not have inputs.
+ */
+ if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD)
+ return 0;
+
/*
* Check that input registration succeeded. Checking that
* HID_CLAIMED_INPUT is set prevents a UAF when all input devices
--
2.52.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH v9 06/11] HID: asus: early return for ROG devices
2025-11-20 9:46 ` [PATCH v9 06/11] HID: asus: early return for ROG devices Antheas Kapenekakis
@ 2025-11-20 13:29 ` Denis Benato
2025-11-20 14:15 ` Antheas Kapenekakis
0 siblings, 1 reply; 26+ messages in thread
From: Denis Benato @ 2025-11-20 13:29 UTC (permalink / raw)
To: Antheas Kapenekakis, platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen
On 11/20/25 10:46, Antheas Kapenekakis wrote:
> Some ROG devices have a new dynamic backlight interface for control by
> Windows. This interface does not create an ->input device, causing the
> kernel to print an error message and to eject it. In addition, ROG
> devices have proper HID names in their descriptors so renaming them is
> not necessary.
Is this patchset supposed to work without the renaming, correct?
If so consider dropping the drop of renames, taking required time
to organize with Derek and resubmit when things are ready:
there is no point for the rename to stall the rest and quit renaming
is not urgent at all.
> Therefore, if a device is identified as ROG, early return from probe to
> skip renaming and ->input checks.
>
> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
> drivers/hid/hid-asus.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> index 3047bc54bf2e..6193c9483bec 100644
> --- a/drivers/hid/hid-asus.c
> +++ b/drivers/hid/hid-asus.c
> @@ -1236,6 +1236,13 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
> asus_kbd_register_leds(hdev))
> hid_warn(hdev, "Failed to initialize backlight.\n");
>
> + /*
> + * For ROG keyboards, skip rename for consistency and ->input check as
> + * some devices do not have inputs.
> + */
> + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD)
> + return 0;
> +
> /*
> * Check that input registration succeeded. Checking that
> * HID_CLAIMED_INPUT is set prevents a UAF when all input devices
Just for clarity is this supposed to fix this: https://gitlab.com/asus-linux/asusctl/-/issues/700 ?
This model works once in windows users disable that new feature.
Note: that kernel the person submitting the bug is using contains your v8
and asus-armoury.
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 06/11] HID: asus: early return for ROG devices
2025-11-20 13:29 ` Denis Benato
@ 2025-11-20 14:15 ` Antheas Kapenekakis
2025-11-20 14:29 ` Denis Benato
0 siblings, 1 reply; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 14:15 UTC (permalink / raw)
To: Denis Benato
Cc: platform-driver-x86, linux-input, linux-kernel, Jiri Kosina,
Benjamin Tissoires, Corentin Chary, Luke D . Jones, Hans de Goede,
Ilpo Järvinen
On Thu, 20 Nov 2025 at 14:29, Denis Benato <benato.denis96@gmail.com> wrote:
>
>
> On 11/20/25 10:46, Antheas Kapenekakis wrote:
> > Some ROG devices have a new dynamic backlight interface for control by
> > Windows. This interface does not create an ->input device, causing the
> > kernel to print an error message and to eject it. In addition, ROG
> > devices have proper HID names in their descriptors so renaming them is
> > not necessary.
> Is this patchset supposed to work without the renaming, correct?
>
> If so consider dropping the drop of renames, taking required time
> to organize with Derek and resubmit when things are ready:
> there is no point for the rename to stall the rest and quit renaming
> is not urgent at all.
I feel like two months is enough of a timeframe for a simple rename
fix to go in.
I do not want to have to reorder the checks just so the rename can
stay in _for now_. Skipping the ->input check is important for both
Xbox Ally/Z13 as it causes errors and the device to stay partially
uninitialized.
> > Therefore, if a device is identified as ROG, early return from probe to
> > skip renaming and ->input checks.
> >
> > Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > ---
> > drivers/hid/hid-asus.c | 7 +++++++
> > 1 file changed, 7 insertions(+)
> >
> > diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> > index 3047bc54bf2e..6193c9483bec 100644
> > --- a/drivers/hid/hid-asus.c
> > +++ b/drivers/hid/hid-asus.c
> > @@ -1236,6 +1236,13 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
> > asus_kbd_register_leds(hdev))
> > hid_warn(hdev, "Failed to initialize backlight.\n");
> >
> > + /*
> > + * For ROG keyboards, skip rename for consistency and ->input check as
> > + * some devices do not have inputs.
> > + */
> > + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD)
> > + return 0;
> > +
> > /*
> > * Check that input registration succeeded. Checking that
> > * HID_CLAIMED_INPUT is set prevents a UAF when all input devices
> Just for clarity is this supposed to fix this: https://gitlab.com/asus-linux/asusctl/-/issues/700 ?
> This model works once in windows users disable that new feature.
>
> Note: that kernel the person submitting the bug is using contains your v8
> and asus-armoury.
>
No. This user has a laptop that has at least a WMI implementation of
RGB controls (this is why you can see rgb settings). Since you did not
ask for logs, it is not clear if it also has a HID implementation that
is skipped due to e.g., a missing product ID. Very likely it is a bug
on the WMI implementation that is out of scope for this series.
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 06/11] HID: asus: early return for ROG devices
2025-11-20 14:15 ` Antheas Kapenekakis
@ 2025-11-20 14:29 ` Denis Benato
2025-11-20 14:43 ` Antheas Kapenekakis
0 siblings, 1 reply; 26+ messages in thread
From: Denis Benato @ 2025-11-20 14:29 UTC (permalink / raw)
To: Antheas Kapenekakis
Cc: platform-driver-x86, linux-input, linux-kernel, Jiri Kosina,
Benjamin Tissoires, Corentin Chary, Luke D . Jones, Hans de Goede,
Ilpo Järvinen
On 11/20/25 15:15, Antheas Kapenekakis wrote:
> On Thu, 20 Nov 2025 at 14:29, Denis Benato <benato.denis96@gmail.com> wrote:
>>
>> On 11/20/25 10:46, Antheas Kapenekakis wrote:
>>> Some ROG devices have a new dynamic backlight interface for control by
>>> Windows. This interface does not create an ->input device, causing the
>>> kernel to print an error message and to eject it. In addition, ROG
>>> devices have proper HID names in their descriptors so renaming them is
>>> not necessary.
>> Is this patchset supposed to work without the renaming, correct?
>>
>> If so consider dropping the drop of renames, taking required time
>> to organize with Derek and resubmit when things are ready:
>> there is no point for the rename to stall the rest and quit renaming
>> is not urgent at all.
> I feel like two months is enough of a timeframe for a simple rename
> fix to go in.
>
> I do not want to have to reorder the checks just so the rename can
> stay in _for now_. Skipping the ->input check is important for both
> Xbox Ally/Z13 as it causes errors and the device to stay partially
> uninitialized.
>
>>> Therefore, if a device is identified as ROG, early return from probe to
>>> skip renaming and ->input checks.
>>>
>>> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
>>> ---
>>> drivers/hid/hid-asus.c | 7 +++++++
>>> 1 file changed, 7 insertions(+)
>>>
>>> diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
>>> index 3047bc54bf2e..6193c9483bec 100644
>>> --- a/drivers/hid/hid-asus.c
>>> +++ b/drivers/hid/hid-asus.c
>>> @@ -1236,6 +1236,13 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
>>> asus_kbd_register_leds(hdev))
>>> hid_warn(hdev, "Failed to initialize backlight.\n");
>>>
>>> + /*
>>> + * For ROG keyboards, skip rename for consistency and ->input check as
>>> + * some devices do not have inputs.
>>> + */
>>> + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD)
>>> + return 0;
>>> +
>>> /*
>>> * Check that input registration succeeded. Checking that
>>> * HID_CLAIMED_INPUT is set prevents a UAF when all input devices
>> Just for clarity is this supposed to fix this: https://gitlab.com/asus-linux/asusctl/-/issues/700 ?
>> This model works once in windows users disable that new feature.
>>
>> Note: that kernel the person submitting the bug is using contains your v8
>> and asus-armoury.
>>
> No. This user has a laptop that has at least a WMI implementation of
> RGB controls (this is why you can see rgb settings). Since you did not
> ask for logs, it is not clear if it also has a HID implementation that
> is skipped due to e.g., a missing product ID. Very likely it is a bug
> on the WMI implementation that is out of scope for this series.
I will ask for logs, but I recall someone with the same model sent dmesg already,
I'll try to find it, but if this is true... Are we lending control of LEDs to a bugged WMI
implementation for this laptop?
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 06/11] HID: asus: early return for ROG devices
2025-11-20 14:29 ` Denis Benato
@ 2025-11-20 14:43 ` Antheas Kapenekakis
0 siblings, 0 replies; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 14:43 UTC (permalink / raw)
To: Denis Benato
Cc: platform-driver-x86, linux-input, linux-kernel, Jiri Kosina,
Benjamin Tissoires, Corentin Chary, Luke D . Jones, Hans de Goede,
Ilpo Järvinen
On Thu, 20 Nov 2025 at 15:29, Denis Benato <benato.denis96@gmail.com> wrote:
>
>
> On 11/20/25 15:15, Antheas Kapenekakis wrote:
> > On Thu, 20 Nov 2025 at 14:29, Denis Benato <benato.denis96@gmail.com> wrote:
> >>
> >> On 11/20/25 10:46, Antheas Kapenekakis wrote:
> >>> Some ROG devices have a new dynamic backlight interface for control by
> >>> Windows. This interface does not create an ->input device, causing the
> >>> kernel to print an error message and to eject it. In addition, ROG
> >>> devices have proper HID names in their descriptors so renaming them is
> >>> not necessary.
> >> Is this patchset supposed to work without the renaming, correct?
> >>
> >> If so consider dropping the drop of renames, taking required time
> >> to organize with Derek and resubmit when things are ready:
> >> there is no point for the rename to stall the rest and quit renaming
> >> is not urgent at all.
> > I feel like two months is enough of a timeframe for a simple rename
> > fix to go in.
> >
> > I do not want to have to reorder the checks just so the rename can
> > stay in _for now_. Skipping the ->input check is important for both
> > Xbox Ally/Z13 as it causes errors and the device to stay partially
> > uninitialized.
> >
> >>> Therefore, if a device is identified as ROG, early return from probe to
> >>> skip renaming and ->input checks.
> >>>
> >>> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> >>> ---
> >>> drivers/hid/hid-asus.c | 7 +++++++
> >>> 1 file changed, 7 insertions(+)
> >>>
> >>> diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> >>> index 3047bc54bf2e..6193c9483bec 100644
> >>> --- a/drivers/hid/hid-asus.c
> >>> +++ b/drivers/hid/hid-asus.c
> >>> @@ -1236,6 +1236,13 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
> >>> asus_kbd_register_leds(hdev))
> >>> hid_warn(hdev, "Failed to initialize backlight.\n");
> >>>
> >>> + /*
> >>> + * For ROG keyboards, skip rename for consistency and ->input check as
> >>> + * some devices do not have inputs.
> >>> + */
> >>> + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD)
> >>> + return 0;
> >>> +
> >>> /*
> >>> * Check that input registration succeeded. Checking that
> >>> * HID_CLAIMED_INPUT is set prevents a UAF when all input devices
> >> Just for clarity is this supposed to fix this: https://gitlab.com/asus-linux/asusctl/-/issues/700 ?
> >> This model works once in windows users disable that new feature.
> >>
> >> Note: that kernel the person submitting the bug is using contains your v8
> >> and asus-armoury.
> >>
> > No. This user has a laptop that has at least a WMI implementation of
> > RGB controls (this is why you can see rgb settings). Since you did not
> > ask for logs, it is not clear if it also has a HID implementation that
> > is skipped due to e.g., a missing product ID. Very likely it is a bug
> > on the WMI implementation that is out of scope for this series.
> I will ask for logs, but I recall someone with the same model sent dmesg already,
> I'll try to find it, but if this is true... Are we lending control of LEDs to a bugged WMI
> implementation for this laptop?
>
Yes, the asus-wmi driver is bugged in certain laptops. This does not
mean it should not be activated-the device has RGB. It means that it
should be fixed eventually.
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v9 07/11] platform/x86: asus-wmi: Add support for multiple kbd led handlers
2025-11-20 9:46 [PATCH v9 00/11] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling Antheas Kapenekakis
` (5 preceding siblings ...)
2025-11-20 9:46 ` [PATCH v9 06/11] HID: asus: early return for ROG devices Antheas Kapenekakis
@ 2025-11-20 9:46 ` Antheas Kapenekakis
2025-11-20 13:46 ` Denis Benato
2025-11-20 9:46 ` [PATCH v9 08/11] HID: asus: listen to the asus-wmi brightness device instead of creating one Antheas Kapenekakis
` (3 subsequent siblings)
10 siblings, 1 reply; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 9:46 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis
Some devices, such as the Z13 have multiple Aura devices connected
to them by USB. In addition, they might have a WMI interface for
RGB. In Windows, Armoury Crate exposes a unified brightness slider
for all of them, with 3 brightness levels.
Therefore, to be synergistic in Linux, and support existing tooling
such as UPower, allow adding listeners to the RGB device of the WMI
interface. If WMI does not exist, lazy initialize the interface.
Since hid-asus and asus-wmi can both interact with the led objects
including from an atomic context, protect the brightness access with a
spinlock and update the values from a workqueue. Use this workqueue to
also process WMI keyboard events, so they are handled asynchronously.
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/platform/x86/asus-wmi.c | 174 ++++++++++++++++++---
include/linux/platform_data/x86/asus-wmi.h | 17 ++
2 files changed, 167 insertions(+), 24 deletions(-)
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index e72a2b5d158e..5f23aedbf34f 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -36,6 +36,7 @@
#include <linux/rfkill.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/units.h>
@@ -258,6 +259,9 @@ struct asus_wmi {
int tpd_led_wk;
struct led_classdev kbd_led;
int kbd_led_wk;
+ bool kbd_led_notify;
+ bool kbd_led_avail;
+ bool kbd_led_registered;
struct led_classdev lightbar_led;
int lightbar_led_wk;
struct led_classdev micmute_led;
@@ -266,6 +270,7 @@ struct asus_wmi {
struct work_struct tpd_led_work;
struct work_struct wlan_led_work;
struct work_struct lightbar_led_work;
+ struct work_struct kbd_led_work;
struct asus_rfkill wlan;
struct asus_rfkill bluetooth;
@@ -1530,6 +1535,99 @@ static void asus_wmi_battery_exit(struct asus_wmi *asus)
/* LEDs ***********************************************************************/
+struct asus_hid_ref {
+ struct list_head listeners;
+ struct asus_wmi *asus;
+ /* Protects concurrent access from hid-asus and asus-wmi to leds */
+ spinlock_t lock;
+};
+
+static struct asus_hid_ref asus_ref = {
+ .listeners = LIST_HEAD_INIT(asus_ref.listeners),
+ .asus = NULL,
+ /*
+ * Protects .asus, .asus.kbd_led_{wk,notify}, and .listener refs. Other
+ * asus variables are read-only after .asus is set. Except the led cdev
+ * device if not kbd_led_avail. That becomes read-only after the
+ * first hid-asus listener registers and triggers the work queue. It is
+ * then not referenced again until unregistering, which happens after
+ * .asus ref is dropped. Since .asus needs to be accessed by hid-asus
+ * IRQs to check if forwarding events is possible, a spinlock is used.
+ */
+ .lock = __SPIN_LOCK_UNLOCKED(asus_ref.lock),
+};
+
+/*
+ * Allows registering hid-asus listeners that want to be notified of
+ * keyboard backlight changes.
+ */
+int asus_hid_register_listener(struct asus_hid_listener *bdev)
+{
+ struct asus_wmi *asus;
+
+ guard(spinlock_irqsave)(&asus_ref.lock);
+ list_add_tail(&bdev->list, &asus_ref.listeners);
+ asus = asus_ref.asus;
+ if (asus)
+ queue_work(asus->led_workqueue, &asus->kbd_led_work);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asus_hid_register_listener);
+
+/*
+ * Allows unregistering hid-asus listeners that were added with
+ * asus_hid_register_listener().
+ */
+void asus_hid_unregister_listener(struct asus_hid_listener *bdev)
+{
+ guard(spinlock_irqsave)(&asus_ref.lock);
+ list_del(&bdev->list);
+}
+EXPORT_SYMBOL_GPL(asus_hid_unregister_listener);
+
+static void do_kbd_led_set(struct led_classdev *led_cdev, int value);
+
+static void kbd_led_update_all(struct work_struct *work)
+{
+ struct asus_wmi *asus;
+ bool registered, notify;
+ int ret, value;
+
+ asus = container_of(work, struct asus_wmi, kbd_led_work);
+
+ scoped_guard(spinlock_irqsave, &asus_ref.lock) {
+ registered = asus->kbd_led_registered;
+ value = asus->kbd_led_wk;
+ notify = asus->kbd_led_notify;
+ }
+
+ if (!registered) {
+ /*
+ * This workqueue runs under asus-wmi, which means probe has
+ * completed and asus-wmi will keep running until it finishes.
+ * Therefore, we can safely register the LED without holding
+ * a spinlock.
+ */
+ ret = devm_led_classdev_register(&asus->platform_device->dev,
+ &asus->kbd_led);
+ if (!ret) {
+ scoped_guard(spinlock_irqsave, &asus_ref.lock)
+ asus->kbd_led_registered = true;
+ } else {
+ pr_warn("Failed to register keyboard backlight LED: %d\n", ret);
+ return;
+ }
+ }
+
+ if (value >= 0)
+ do_kbd_led_set(&asus->kbd_led, value);
+ if (notify) {
+ scoped_guard(spinlock_irqsave, &asus_ref.lock)
+ asus->kbd_led_notify = false;
+ led_classdev_notify_brightness_hw_changed(&asus->kbd_led, value);
+ }
+}
+
/*
* These functions actually update the LED's, and are called from a
* workqueue. By doing this as separate work rather than when the LED
@@ -1576,7 +1674,8 @@ static void kbd_led_update(struct asus_wmi *asus)
{
int ctrl_param = 0;
- ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
+ scoped_guard(spinlock_irqsave, &asus_ref.lock)
+ ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);
}
@@ -1609,14 +1708,23 @@ static int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
static void do_kbd_led_set(struct led_classdev *led_cdev, int value)
{
+ struct asus_hid_listener *listener;
struct asus_wmi *asus;
int max_level;
asus = container_of(led_cdev, struct asus_wmi, kbd_led);
max_level = asus->kbd_led.max_brightness;
- asus->kbd_led_wk = clamp_val(value, 0, max_level);
- kbd_led_update(asus);
+ scoped_guard(spinlock_irqsave, &asus_ref.lock)
+ asus->kbd_led_wk = clamp_val(value, 0, max_level);
+
+ if (asus->kbd_led_avail)
+ kbd_led_update(asus);
+
+ scoped_guard(spinlock_irqsave, &asus_ref.lock) {
+ list_for_each_entry(listener, &asus_ref.listeners, list)
+ listener->brightness_set(listener, asus->kbd_led_wk);
+ }
}
static void kbd_led_set(struct led_classdev *led_cdev,
@@ -1631,10 +1739,11 @@ static void kbd_led_set(struct led_classdev *led_cdev,
static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value)
{
- struct led_classdev *led_cdev = &asus->kbd_led;
-
- do_kbd_led_set(led_cdev, value);
- led_classdev_notify_brightness_hw_changed(led_cdev, asus->kbd_led_wk);
+ scoped_guard(spinlock_irqsave, &asus_ref.lock) {
+ asus->kbd_led_wk = value;
+ asus->kbd_led_notify = true;
+ }
+ queue_work(asus->led_workqueue, &asus->kbd_led_work);
}
static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
@@ -1644,10 +1753,18 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
asus = container_of(led_cdev, struct asus_wmi, kbd_led);
+ scoped_guard(spinlock_irqsave, &asus_ref.lock) {
+ if (!asus->kbd_led_avail)
+ return asus->kbd_led_wk;
+ }
+
retval = kbd_led_read(asus, &value, NULL);
if (retval < 0)
return retval;
+ scoped_guard(spinlock_irqsave, &asus_ref.lock)
+ asus->kbd_led_wk = value;
+
return value;
}
@@ -1759,7 +1876,9 @@ static int camera_led_set(struct led_classdev *led_cdev,
static void asus_wmi_led_exit(struct asus_wmi *asus)
{
- led_classdev_unregister(&asus->kbd_led);
+ scoped_guard(spinlock_irqsave, &asus_ref.lock)
+ asus_ref.asus = NULL;
+
led_classdev_unregister(&asus->tpd_led);
led_classdev_unregister(&asus->wlan_led);
led_classdev_unregister(&asus->lightbar_led);
@@ -1797,22 +1916,25 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
goto error;
}
- if (!kbd_led_read(asus, &led_val, NULL) && !dmi_check_system(asus_use_hid_led_dmi_ids)) {
- pr_info("using asus-wmi for asus::kbd_backlight\n");
- asus->kbd_led_wk = led_val;
- asus->kbd_led.name = "asus::kbd_backlight";
- asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
- asus->kbd_led.brightness_set = kbd_led_set;
- asus->kbd_led.brightness_get = kbd_led_get;
- asus->kbd_led.max_brightness = 3;
+ asus->kbd_led.name = "asus::kbd_backlight";
+ asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
+ asus->kbd_led.brightness_set = kbd_led_set;
+ asus->kbd_led.brightness_get = kbd_led_get;
+ asus->kbd_led.max_brightness = 3;
+ asus->kbd_led_avail = !kbd_led_read(asus, &led_val, NULL);
+ INIT_WORK(&asus->kbd_led_work, kbd_led_update_all);
+ if (asus->kbd_led_avail) {
+ asus->kbd_led_wk = led_val;
if (num_rgb_groups != 0)
asus->kbd_led.groups = kbd_rgb_mode_groups;
+ } else
+ asus->kbd_led_wk = -1;
- rv = led_classdev_register(&asus->platform_device->dev,
- &asus->kbd_led);
- if (rv)
- goto error;
+ scoped_guard(spinlock_irqsave, &asus_ref.lock) {
+ asus_ref.asus = asus;
+ if (asus->kbd_led_avail || !list_empty(&asus_ref.listeners))
+ queue_work(asus->led_workqueue, &asus->kbd_led_work);
}
if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_WIRELESS_LED)
@@ -4272,6 +4394,7 @@ static int asus_wmi_get_event_code(union acpi_object *obj)
static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
{
+ enum led_brightness led_value;
unsigned int key_value = 1;
bool autorelease = 1;
@@ -4288,19 +4411,22 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
return;
}
+ scoped_guard(spinlock_irqsave, &asus_ref.lock)
+ led_value = asus->kbd_led_wk;
+
if (code == NOTIFY_KBD_BRTUP) {
- kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
+ kbd_led_set_by_kbd(asus, led_value + 1);
return;
}
if (code == NOTIFY_KBD_BRTDWN) {
- kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1);
+ kbd_led_set_by_kbd(asus, led_value - 1);
return;
}
if (code == NOTIFY_KBD_BRTTOGGLE) {
- if (asus->kbd_led_wk == asus->kbd_led.max_brightness)
+ if (led_value == asus->kbd_led.max_brightness)
kbd_led_set_by_kbd(asus, 0);
else
- kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
+ kbd_led_set_by_kbd(asus, led_value + 1);
return;
}
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index 8a515179113d..1165039013b1 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -163,11 +163,20 @@ enum asus_ally_mcu_hack {
ASUS_WMI_ALLY_MCU_HACK_DISABLED,
};
+/* Used to notify hid-asus when asus-wmi changes keyboard backlight */
+struct asus_hid_listener {
+ struct list_head list;
+ void (*brightness_set)(struct asus_hid_listener *listener, int brightness);
+};
+
#if IS_REACHABLE(CONFIG_ASUS_WMI)
void set_ally_mcu_hack(enum asus_ally_mcu_hack status);
void set_ally_mcu_powersave(bool enabled);
int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval);
int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval);
+
+int asus_hid_register_listener(struct asus_hid_listener *cdev);
+void asus_hid_unregister_listener(struct asus_hid_listener *cdev);
#else
static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status)
{
@@ -184,6 +193,14 @@ static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
{
return -ENODEV;
}
+
+static inline int asus_hid_register_listener(struct asus_hid_listener *bdev)
+{
+ return -ENODEV;
+}
+static inline void asus_hid_unregister_listener(struct asus_hid_listener *bdev)
+{
+}
#endif
/* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */
--
2.52.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH v9 07/11] platform/x86: asus-wmi: Add support for multiple kbd led handlers
2025-11-20 9:46 ` [PATCH v9 07/11] platform/x86: asus-wmi: Add support for multiple kbd led handlers Antheas Kapenekakis
@ 2025-11-20 13:46 ` Denis Benato
2025-11-20 14:40 ` Antheas Kapenekakis
2025-11-20 16:38 ` Ilpo Järvinen
0 siblings, 2 replies; 26+ messages in thread
From: Denis Benato @ 2025-11-20 13:46 UTC (permalink / raw)
To: Antheas Kapenekakis, platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen
On 11/20/25 10:46, Antheas Kapenekakis wrote:
> Some devices, such as the Z13 have multiple Aura devices connected
> to them by USB. In addition, they might have a WMI interface for
> RGB. In Windows, Armoury Crate exposes a unified brightness slider
> for all of them, with 3 brightness levels.
>
> Therefore, to be synergistic in Linux, and support existing tooling
> such as UPower, allow adding listeners to the RGB device of the WMI
> interface. If WMI does not exist, lazy initialize the interface.
>
> Since hid-asus and asus-wmi can both interact with the led objects
> including from an atomic context, protect the brightness access with a
> spinlock and update the values from a workqueue. Use this workqueue to
> also process WMI keyboard events, so they are handled asynchronously.
>
> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
> drivers/platform/x86/asus-wmi.c | 174 ++++++++++++++++++---
> include/linux/platform_data/x86/asus-wmi.h | 17 ++
> 2 files changed, 167 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
> index e72a2b5d158e..5f23aedbf34f 100644
> --- a/drivers/platform/x86/asus-wmi.c
> +++ b/drivers/platform/x86/asus-wmi.c
> @@ -36,6 +36,7 @@
> #include <linux/rfkill.h>
> #include <linux/seq_file.h>
> #include <linux/slab.h>
> +#include <linux/spinlock.h>
> #include <linux/types.h>
> #include <linux/units.h>
>
> @@ -258,6 +259,9 @@ struct asus_wmi {
> int tpd_led_wk;
> struct led_classdev kbd_led;
> int kbd_led_wk;
> + bool kbd_led_notify;
> + bool kbd_led_avail;
> + bool kbd_led_registered;
> struct led_classdev lightbar_led;
> int lightbar_led_wk;
> struct led_classdev micmute_led;
> @@ -266,6 +270,7 @@ struct asus_wmi {
> struct work_struct tpd_led_work;
> struct work_struct wlan_led_work;
> struct work_struct lightbar_led_work;
> + struct work_struct kbd_led_work;
>
> struct asus_rfkill wlan;
> struct asus_rfkill bluetooth;
> @@ -1530,6 +1535,99 @@ static void asus_wmi_battery_exit(struct asus_wmi *asus)
>
> /* LEDs ***********************************************************************/
>
> +struct asus_hid_ref {
> + struct list_head listeners;
> + struct asus_wmi *asus;
> + /* Protects concurrent access from hid-asus and asus-wmi to leds */
> + spinlock_t lock;
> +};
> +
> +static struct asus_hid_ref asus_ref = {
> + .listeners = LIST_HEAD_INIT(asus_ref.listeners),
> + .asus = NULL,
> + /*
> + * Protects .asus, .asus.kbd_led_{wk,notify}, and .listener refs. Other
> + * asus variables are read-only after .asus is set. Except the led cdev
> + * device if not kbd_led_avail. That becomes read-only after the
> + * first hid-asus listener registers and triggers the work queue. It is
> + * then not referenced again until unregistering, which happens after
> + * .asus ref is dropped. Since .asus needs to be accessed by hid-asus
> + * IRQs to check if forwarding events is possible, a spinlock is used.
> + */
What are "That" and "It" referring to in this context?
Are you absolutely sure you want to begin a sentence with "Except"?
On "ref is dropped" I would continue with ": since .asus .....".
> + .lock = __SPIN_LOCK_UNLOCKED(asus_ref.lock),
> +};
> +
> +/*
> + * Allows registering hid-asus listeners that want to be notified of
> + * keyboard backlight changes.
> + */
> +int asus_hid_register_listener(struct asus_hid_listener *bdev)
> +{
> + struct asus_wmi *asus;
> +
> + guard(spinlock_irqsave)(&asus_ref.lock);
> + list_add_tail(&bdev->list, &asus_ref.listeners);
> + asus = asus_ref.asus;
> + if (asus)
> + queue_work(asus->led_workqueue, &asus->kbd_led_work);
Are you sure this has to be protected by the guard too?
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(asus_hid_register_listener);
> +
> +/*
> + * Allows unregistering hid-asus listeners that were added with
> + * asus_hid_register_listener().
> + */
> +void asus_hid_unregister_listener(struct asus_hid_listener *bdev)
> +{
> + guard(spinlock_irqsave)(&asus_ref.lock);
> + list_del(&bdev->list);
> +}
> +EXPORT_SYMBOL_GPL(asus_hid_unregister_listener);
> +
> +static void do_kbd_led_set(struct led_classdev *led_cdev, int value);
> +
> +static void kbd_led_update_all(struct work_struct *work)
> +{
> + struct asus_wmi *asus;
> + bool registered, notify;
> + int ret, value;
> +
> + asus = container_of(work, struct asus_wmi, kbd_led_work);
> +
> + scoped_guard(spinlock_irqsave, &asus_ref.lock) {
> + registered = asus->kbd_led_registered;
> + value = asus->kbd_led_wk;
> + notify = asus->kbd_led_notify;
> + }
> +
> + if (!registered) {
> + /*
> + * This workqueue runs under asus-wmi, which means probe has
> + * completed and asus-wmi will keep running until it finishes.
> + * Therefore, we can safely register the LED without holding
> + * a spinlock.
> + */
> + ret = devm_led_classdev_register(&asus->platform_device->dev,
> + &asus->kbd_led);
> + if (!ret) {
> + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> + asus->kbd_led_registered = true;
> + } else {
> + pr_warn("Failed to register keyboard backlight LED: %d\n", ret);
> + return;
> + }
> + }
> +
> + if (value >= 0)
> + do_kbd_led_set(&asus->kbd_led, value);
> + if (notify) {
> + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> + asus->kbd_led_notify = false;
> + led_classdev_notify_brightness_hw_changed(&asus->kbd_led, value);
> + }
> +}
> +
> /*
> * These functions actually update the LED's, and are called from a
> * workqueue. By doing this as separate work rather than when the LED
> @@ -1576,7 +1674,8 @@ static void kbd_led_update(struct asus_wmi *asus)
> {
> int ctrl_param = 0;
>
> - ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
> + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> + ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
> asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);
> }
>
> @@ -1609,14 +1708,23 @@ static int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
>
> static void do_kbd_led_set(struct led_classdev *led_cdev, int value)
> {
> + struct asus_hid_listener *listener;
> struct asus_wmi *asus;
> int max_level;
>
> asus = container_of(led_cdev, struct asus_wmi, kbd_led);
> max_level = asus->kbd_led.max_brightness;
>
> - asus->kbd_led_wk = clamp_val(value, 0, max_level);
> - kbd_led_update(asus);
> + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> + asus->kbd_led_wk = clamp_val(value, 0, max_level);
> +
> + if (asus->kbd_led_avail)
> + kbd_led_update(asus);
> +
> + scoped_guard(spinlock_irqsave, &asus_ref.lock) {
> + list_for_each_entry(listener, &asus_ref.listeners, list)
> + listener->brightness_set(listener, asus->kbd_led_wk);
> + }
> }
>
> static void kbd_led_set(struct led_classdev *led_cdev,
> @@ -1631,10 +1739,11 @@ static void kbd_led_set(struct led_classdev *led_cdev,
>
> static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value)
> {
> - struct led_classdev *led_cdev = &asus->kbd_led;
> -
> - do_kbd_led_set(led_cdev, value);
> - led_classdev_notify_brightness_hw_changed(led_cdev, asus->kbd_led_wk);
> + scoped_guard(spinlock_irqsave, &asus_ref.lock) {
> + asus->kbd_led_wk = value;
> + asus->kbd_led_notify = true;
> + }
> + queue_work(asus->led_workqueue, &asus->kbd_led_work);
> }
>
> static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
> @@ -1644,10 +1753,18 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
>
> asus = container_of(led_cdev, struct asus_wmi, kbd_led);
>
> + scoped_guard(spinlock_irqsave, &asus_ref.lock) {
> + if (!asus->kbd_led_avail)
> + return asus->kbd_led_wk;
> + }
> +
> retval = kbd_led_read(asus, &value, NULL);
> if (retval < 0)
> return retval;
>
> + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> + asus->kbd_led_wk = value;
> +
> return value;
> }
>
> @@ -1759,7 +1876,9 @@ static int camera_led_set(struct led_classdev *led_cdev,
>
> static void asus_wmi_led_exit(struct asus_wmi *asus)
> {
> - led_classdev_unregister(&asus->kbd_led);
> + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> + asus_ref.asus = NULL;
> +
> led_classdev_unregister(&asus->tpd_led);
> led_classdev_unregister(&asus->wlan_led);
> led_classdev_unregister(&asus->lightbar_led);
> @@ -1797,22 +1916,25 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
> goto error;
> }
>
> - if (!kbd_led_read(asus, &led_val, NULL) && !dmi_check_system(asus_use_hid_led_dmi_ids)) {
> - pr_info("using asus-wmi for asus::kbd_backlight\n");
> - asus->kbd_led_wk = led_val;
> - asus->kbd_led.name = "asus::kbd_backlight";
> - asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
> - asus->kbd_led.brightness_set = kbd_led_set;
> - asus->kbd_led.brightness_get = kbd_led_get;
> - asus->kbd_led.max_brightness = 3;
> + asus->kbd_led.name = "asus::kbd_backlight";
> + asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
> + asus->kbd_led.brightness_set = kbd_led_set;
> + asus->kbd_led.brightness_get = kbd_led_get;
> + asus->kbd_led.max_brightness = 3;
> + asus->kbd_led_avail = !kbd_led_read(asus, &led_val, NULL);
> + INIT_WORK(&asus->kbd_led_work, kbd_led_update_all);
>
> + if (asus->kbd_led_avail) {
> + asus->kbd_led_wk = led_val;
> if (num_rgb_groups != 0)
> asus->kbd_led.groups = kbd_rgb_mode_groups;
> + } else
> + asus->kbd_led_wk = -1;
>
> - rv = led_classdev_register(&asus->platform_device->dev,
> - &asus->kbd_led);
> - if (rv)
> - goto error;
> + scoped_guard(spinlock_irqsave, &asus_ref.lock) {
> + asus_ref.asus = asus;
> + if (asus->kbd_led_avail || !list_empty(&asus_ref.listeners))
> + queue_work(asus->led_workqueue, &asus->kbd_led_work);
> }
>
> if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_WIRELESS_LED)
> @@ -4272,6 +4394,7 @@ static int asus_wmi_get_event_code(union acpi_object *obj)
>
> static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
> {
> + enum led_brightness led_value;
> unsigned int key_value = 1;
> bool autorelease = 1;
>
> @@ -4288,19 +4411,22 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
> return;
> }
>
> + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> + led_value = asus->kbd_led_wk;
> +
> if (code == NOTIFY_KBD_BRTUP) {
> - kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
> + kbd_led_set_by_kbd(asus, led_value + 1);
> return;
> }
> if (code == NOTIFY_KBD_BRTDWN) {
> - kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1);
> + kbd_led_set_by_kbd(asus, led_value - 1);
> return;
> }
> if (code == NOTIFY_KBD_BRTTOGGLE) {
> - if (asus->kbd_led_wk == asus->kbd_led.max_brightness)
> + if (led_value == asus->kbd_led.max_brightness)
> kbd_led_set_by_kbd(asus, 0);
> else
> - kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
> + kbd_led_set_by_kbd(asus, led_value + 1);
> return;
> }
>
> diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
> index 8a515179113d..1165039013b1 100644
> --- a/include/linux/platform_data/x86/asus-wmi.h
> +++ b/include/linux/platform_data/x86/asus-wmi.h
> @@ -163,11 +163,20 @@ enum asus_ally_mcu_hack {
> ASUS_WMI_ALLY_MCU_HACK_DISABLED,
> };
>
> +/* Used to notify hid-asus when asus-wmi changes keyboard backlight */
> +struct asus_hid_listener {
> + struct list_head list;
> + void (*brightness_set)(struct asus_hid_listener *listener, int brightness);
> +};
> +
> #if IS_REACHABLE(CONFIG_ASUS_WMI)
> void set_ally_mcu_hack(enum asus_ally_mcu_hack status);
> void set_ally_mcu_powersave(bool enabled);
> int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval);
> int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval);
> +
> +int asus_hid_register_listener(struct asus_hid_listener *cdev);
> +void asus_hid_unregister_listener(struct asus_hid_listener *cdev);
> #else
> static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status)
> {
> @@ -184,6 +193,14 @@ static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
> {
> return -ENODEV;
> }
> +
> +static inline int asus_hid_register_listener(struct asus_hid_listener *bdev)
> +{
> + return -ENODEV;
> +}
> +static inline void asus_hid_unregister_listener(struct asus_hid_listener *bdev)
> +{
> +}
> #endif
>
> /* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v9 07/11] platform/x86: asus-wmi: Add support for multiple kbd led handlers
2025-11-20 13:46 ` Denis Benato
@ 2025-11-20 14:40 ` Antheas Kapenekakis
2025-11-20 16:38 ` Ilpo Järvinen
1 sibling, 0 replies; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 14:40 UTC (permalink / raw)
To: Denis Benato
Cc: platform-driver-x86, linux-input, linux-kernel, Jiri Kosina,
Benjamin Tissoires, Corentin Chary, Luke D . Jones, Hans de Goede,
Ilpo Järvinen
On Thu, 20 Nov 2025 at 14:46, Denis Benato <benato.denis96@gmail.com> wrote:
>
>
> On 11/20/25 10:46, Antheas Kapenekakis wrote:
> > Some devices, such as the Z13 have multiple Aura devices connected
> > to them by USB. In addition, they might have a WMI interface for
> > RGB. In Windows, Armoury Crate exposes a unified brightness slider
> > for all of them, with 3 brightness levels.
> >
> > Therefore, to be synergistic in Linux, and support existing tooling
> > such as UPower, allow adding listeners to the RGB device of the WMI
> > interface. If WMI does not exist, lazy initialize the interface.
> >
> > Since hid-asus and asus-wmi can both interact with the led objects
> > including from an atomic context, protect the brightness access with a
> > spinlock and update the values from a workqueue. Use this workqueue to
> > also process WMI keyboard events, so they are handled asynchronously.
> >
> > Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > ---
> > drivers/platform/x86/asus-wmi.c | 174 ++++++++++++++++++---
> > include/linux/platform_data/x86/asus-wmi.h | 17 ++
> > 2 files changed, 167 insertions(+), 24 deletions(-)
> >
> > diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
> > index e72a2b5d158e..5f23aedbf34f 100644
> > --- a/drivers/platform/x86/asus-wmi.c
> > +++ b/drivers/platform/x86/asus-wmi.c
> > @@ -36,6 +36,7 @@
> > #include <linux/rfkill.h>
> > #include <linux/seq_file.h>
> > #include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > #include <linux/types.h>
> > #include <linux/units.h>
> >
> > @@ -258,6 +259,9 @@ struct asus_wmi {
> > int tpd_led_wk;
> > struct led_classdev kbd_led;
> > int kbd_led_wk;
> > + bool kbd_led_notify;
> > + bool kbd_led_avail;
> > + bool kbd_led_registered;
> > struct led_classdev lightbar_led;
> > int lightbar_led_wk;
> > struct led_classdev micmute_led;
> > @@ -266,6 +270,7 @@ struct asus_wmi {
> > struct work_struct tpd_led_work;
> > struct work_struct wlan_led_work;
> > struct work_struct lightbar_led_work;
> > + struct work_struct kbd_led_work;
> >
> > struct asus_rfkill wlan;
> > struct asus_rfkill bluetooth;
> > @@ -1530,6 +1535,99 @@ static void asus_wmi_battery_exit(struct asus_wmi *asus)
> >
> > /* LEDs ***********************************************************************/
> >
> > +struct asus_hid_ref {
> > + struct list_head listeners;
> > + struct asus_wmi *asus;
> > + /* Protects concurrent access from hid-asus and asus-wmi to leds */
> > + spinlock_t lock;
> > +};
> > +
> > +static struct asus_hid_ref asus_ref = {
> > + .listeners = LIST_HEAD_INIT(asus_ref.listeners),
> > + .asus = NULL,
> > + /*
> > + * Protects .asus, .asus.kbd_led_{wk,notify}, and .listener refs. Other
> > + * asus variables are read-only after .asus is set. Except the led cdev
> > + * device if not kbd_led_avail. That becomes read-only after the
> > + * first hid-asus listener registers and triggers the work queue. It is
> > + * then not referenced again until unregistering, which happens after
> > + * .asus ref is dropped. Since .asus needs to be accessed by hid-asus
> > + * IRQs to check if forwarding events is possible, a spinlock is used.
> > + */
> What are "That" and "It" referring to in this context?
>
> Are you absolutely sure you want to begin a sentence with "Except"?
I think it is pretty clear that both of them refer to the led cdev
device. Phrasing could be a bit better but it is not ambiguous.
> On "ref is dropped" I would continue with ": since .asus .....".
Add spaces in-between your replies, these are easy to miss.
Since... is a separate sentence and its meaning is correct. It should
not be chained with the first sentence.
Essentially, this paragraph says three things: 1) .asus,
.asus.kbd_led_{wk,notify}, and .listener refs are protected by the
spinlock. 2) the led cdev is not, because it is either initialized
during probe before asus.ref is registered so hid-asus cannot touch it
or in the workqueue (if not kbd_led_avail) which is single threaded
and guaranteed to continue until asus-wmi touches that variable again
because it will have destroyed the workqueue first. 3) A spinlock is
used because these variables are accessed in an IRQ context.
> > + .lock = __SPIN_LOCK_UNLOCKED(asus_ref.lock),
> > +};
> > +
> > +/*
> > + * Allows registering hid-asus listeners that want to be notified of
> > + * keyboard backlight changes.
> > + */
> > +int asus_hid_register_listener(struct asus_hid_listener *bdev)
> > +{
> > + struct asus_wmi *asus;
> > +
> > + guard(spinlock_irqsave)(&asus_ref.lock);
> > + list_add_tail(&bdev->list, &asus_ref.listeners);
> > + asus = asus_ref.asus;
> > + if (asus)
> > + queue_work(asus->led_workqueue, &asus->kbd_led_work);
> Are you sure this has to be protected by the guard too?
Yes. After you get a reference to asus_ref.asus you need to hold a
spinlock until it is dropped so asus-wmi is not able to exit or mutate
itself.
For the same reason, the workqueue can be triggered by both asus-wmi
and multiple hid-asus devices, so it needs to be bound as well.
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(asus_hid_register_listener);
> > +
> > +/*
> > + * Allows unregistering hid-asus listeners that were added with
> > + * asus_hid_register_listener().
> > + */
> > +void asus_hid_unregister_listener(struct asus_hid_listener *bdev)
> > +{
> > + guard(spinlock_irqsave)(&asus_ref.lock);
> > + list_del(&bdev->list);
> > +}
> > +EXPORT_SYMBOL_GPL(asus_hid_unregister_listener);
> > +
> > +static void do_kbd_led_set(struct led_classdev *led_cdev, int value);
> > +
> > +static void kbd_led_update_all(struct work_struct *work)
> > +{
> > + struct asus_wmi *asus;
> > + bool registered, notify;
> > + int ret, value;
> > +
> > + asus = container_of(work, struct asus_wmi, kbd_led_work);
> > +
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock) {
> > + registered = asus->kbd_led_registered;
> > + value = asus->kbd_led_wk;
> > + notify = asus->kbd_led_notify;
> > + }
> > +
> > + if (!registered) {
> > + /*
> > + * This workqueue runs under asus-wmi, which means probe has
> > + * completed and asus-wmi will keep running until it finishes.
> > + * Therefore, we can safely register the LED without holding
> > + * a spinlock.
> > + */
> > + ret = devm_led_classdev_register(&asus->platform_device->dev,
> > + &asus->kbd_led);
> > + if (!ret) {
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> > + asus->kbd_led_registered = true;
> > + } else {
> > + pr_warn("Failed to register keyboard backlight LED: %d\n", ret);
> > + return;
> > + }
> > + }
> > +
> > + if (value >= 0)
> > + do_kbd_led_set(&asus->kbd_led, value);
> > + if (notify) {
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> > + asus->kbd_led_notify = false;
> > + led_classdev_notify_brightness_hw_changed(&asus->kbd_led, value);
> > + }
> > +}
> > +
> > /*
> > * These functions actually update the LED's, and are called from a
> > * workqueue. By doing this as separate work rather than when the LED
> > @@ -1576,7 +1674,8 @@ static void kbd_led_update(struct asus_wmi *asus)
> > {
> > int ctrl_param = 0;
> >
> > - ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> > + ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
> > asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);
> > }
> >
> > @@ -1609,14 +1708,23 @@ static int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
> >
> > static void do_kbd_led_set(struct led_classdev *led_cdev, int value)
> > {
> > + struct asus_hid_listener *listener;
> > struct asus_wmi *asus;
> > int max_level;
> >
> > asus = container_of(led_cdev, struct asus_wmi, kbd_led);
> > max_level = asus->kbd_led.max_brightness;
> >
> > - asus->kbd_led_wk = clamp_val(value, 0, max_level);
> > - kbd_led_update(asus);
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> > + asus->kbd_led_wk = clamp_val(value, 0, max_level);
> > +
> > + if (asus->kbd_led_avail)
> > + kbd_led_update(asus);
> > +
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock) {
> > + list_for_each_entry(listener, &asus_ref.listeners, list)
> > + listener->brightness_set(listener, asus->kbd_led_wk);
> > + }
> > }
> >
> > static void kbd_led_set(struct led_classdev *led_cdev,
> > @@ -1631,10 +1739,11 @@ static void kbd_led_set(struct led_classdev *led_cdev,
> >
> > static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value)
> > {
> > - struct led_classdev *led_cdev = &asus->kbd_led;
> > -
> > - do_kbd_led_set(led_cdev, value);
> > - led_classdev_notify_brightness_hw_changed(led_cdev, asus->kbd_led_wk);
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock) {
> > + asus->kbd_led_wk = value;
> > + asus->kbd_led_notify = true;
> > + }
> > + queue_work(asus->led_workqueue, &asus->kbd_led_work);
> > }
> >
> > static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
> > @@ -1644,10 +1753,18 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
> >
> > asus = container_of(led_cdev, struct asus_wmi, kbd_led);
> >
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock) {
> > + if (!asus->kbd_led_avail)
> > + return asus->kbd_led_wk;
> > + }
> > +
> > retval = kbd_led_read(asus, &value, NULL);
> > if (retval < 0)
> > return retval;
> >
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> > + asus->kbd_led_wk = value;
> > +
> > return value;
> > }
> >
> > @@ -1759,7 +1876,9 @@ static int camera_led_set(struct led_classdev *led_cdev,
> >
> > static void asus_wmi_led_exit(struct asus_wmi *asus)
> > {
> > - led_classdev_unregister(&asus->kbd_led);
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> > + asus_ref.asus = NULL;
> > +
> > led_classdev_unregister(&asus->tpd_led);
> > led_classdev_unregister(&asus->wlan_led);
> > led_classdev_unregister(&asus->lightbar_led);
> > @@ -1797,22 +1916,25 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
> > goto error;
> > }
> >
> > - if (!kbd_led_read(asus, &led_val, NULL) && !dmi_check_system(asus_use_hid_led_dmi_ids)) {
> > - pr_info("using asus-wmi for asus::kbd_backlight\n");
> > - asus->kbd_led_wk = led_val;
> > - asus->kbd_led.name = "asus::kbd_backlight";
> > - asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
> > - asus->kbd_led.brightness_set = kbd_led_set;
> > - asus->kbd_led.brightness_get = kbd_led_get;
> > - asus->kbd_led.max_brightness = 3;
> > + asus->kbd_led.name = "asus::kbd_backlight";
> > + asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
> > + asus->kbd_led.brightness_set = kbd_led_set;
> > + asus->kbd_led.brightness_get = kbd_led_get;
> > + asus->kbd_led.max_brightness = 3;
> > + asus->kbd_led_avail = !kbd_led_read(asus, &led_val, NULL);
> > + INIT_WORK(&asus->kbd_led_work, kbd_led_update_all);
> >
> > + if (asus->kbd_led_avail) {
> > + asus->kbd_led_wk = led_val;
> > if (num_rgb_groups != 0)
> > asus->kbd_led.groups = kbd_rgb_mode_groups;
> > + } else
> > + asus->kbd_led_wk = -1;
> >
> > - rv = led_classdev_register(&asus->platform_device->dev,
> > - &asus->kbd_led);
> > - if (rv)
> > - goto error;
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock) {
> > + asus_ref.asus = asus;
> > + if (asus->kbd_led_avail || !list_empty(&asus_ref.listeners))
> > + queue_work(asus->led_workqueue, &asus->kbd_led_work);
> > }
> >
> > if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_WIRELESS_LED)
> > @@ -4272,6 +4394,7 @@ static int asus_wmi_get_event_code(union acpi_object *obj)
> >
> > static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
> > {
> > + enum led_brightness led_value;
> > unsigned int key_value = 1;
> > bool autorelease = 1;
> >
> > @@ -4288,19 +4411,22 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
> > return;
> > }
> >
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> > + led_value = asus->kbd_led_wk;
> > +
> > if (code == NOTIFY_KBD_BRTUP) {
> > - kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
> > + kbd_led_set_by_kbd(asus, led_value + 1);
> > return;
> > }
> > if (code == NOTIFY_KBD_BRTDWN) {
> > - kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1);
> > + kbd_led_set_by_kbd(asus, led_value - 1);
> > return;
> > }
> > if (code == NOTIFY_KBD_BRTTOGGLE) {
> > - if (asus->kbd_led_wk == asus->kbd_led.max_brightness)
> > + if (led_value == asus->kbd_led.max_brightness)
> > kbd_led_set_by_kbd(asus, 0);
> > else
> > - kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
> > + kbd_led_set_by_kbd(asus, led_value + 1);
> > return;
> > }
> >
> > diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
> > index 8a515179113d..1165039013b1 100644
> > --- a/include/linux/platform_data/x86/asus-wmi.h
> > +++ b/include/linux/platform_data/x86/asus-wmi.h
> > @@ -163,11 +163,20 @@ enum asus_ally_mcu_hack {
> > ASUS_WMI_ALLY_MCU_HACK_DISABLED,
> > };
> >
> > +/* Used to notify hid-asus when asus-wmi changes keyboard backlight */
> > +struct asus_hid_listener {
> > + struct list_head list;
> > + void (*brightness_set)(struct asus_hid_listener *listener, int brightness);
> > +};
> > +
> > #if IS_REACHABLE(CONFIG_ASUS_WMI)
> > void set_ally_mcu_hack(enum asus_ally_mcu_hack status);
> > void set_ally_mcu_powersave(bool enabled);
> > int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval);
> > int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval);
> > +
> > +int asus_hid_register_listener(struct asus_hid_listener *cdev);
> > +void asus_hid_unregister_listener(struct asus_hid_listener *cdev);
> > #else
> > static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status)
> > {
> > @@ -184,6 +193,14 @@ static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
> > {
> > return -ENODEV;
> > }
> > +
> > +static inline int asus_hid_register_listener(struct asus_hid_listener *bdev)
> > +{
> > + return -ENODEV;
> > +}
> > +static inline void asus_hid_unregister_listener(struct asus_hid_listener *bdev)
> > +{
> > +}
> > #endif
> >
> > /* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */
>
^ permalink raw reply [flat|nested] 26+ messages in thread* Re: [PATCH v9 07/11] platform/x86: asus-wmi: Add support for multiple kbd led handlers
2025-11-20 13:46 ` Denis Benato
2025-11-20 14:40 ` Antheas Kapenekakis
@ 2025-11-20 16:38 ` Ilpo Järvinen
1 sibling, 0 replies; 26+ messages in thread
From: Ilpo Järvinen @ 2025-11-20 16:38 UTC (permalink / raw)
To: Denis Benato
Cc: Antheas Kapenekakis, platform-driver-x86, linux-input, LKML,
Jiri Kosina, Benjamin Tissoires, Corentin Chary, Luke D . Jones,
Hans de Goede, Ilpo Järvinen
On Thu, 20 Nov 2025, Denis Benato wrote:
>
> On 11/20/25 10:46, Antheas Kapenekakis wrote:
> > Some devices, such as the Z13 have multiple Aura devices connected
> > to them by USB. In addition, they might have a WMI interface for
> > RGB. In Windows, Armoury Crate exposes a unified brightness slider
> > for all of them, with 3 brightness levels.
> >
> > Therefore, to be synergistic in Linux, and support existing tooling
> > such as UPower, allow adding listeners to the RGB device of the WMI
> > interface. If WMI does not exist, lazy initialize the interface.
> >
> > Since hid-asus and asus-wmi can both interact with the led objects
> > including from an atomic context, protect the brightness access with a
> > spinlock and update the values from a workqueue. Use this workqueue to
> > also process WMI keyboard events, so they are handled asynchronously.
> >
> > Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > ---
> > drivers/platform/x86/asus-wmi.c | 174 ++++++++++++++++++---
> > include/linux/platform_data/x86/asus-wmi.h | 17 ++
> > 2 files changed, 167 insertions(+), 24 deletions(-)
> >
> > diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
> > index e72a2b5d158e..5f23aedbf34f 100644
> > --- a/drivers/platform/x86/asus-wmi.c
> > +++ b/drivers/platform/x86/asus-wmi.c
> > @@ -36,6 +36,7 @@
> > #include <linux/rfkill.h>
> > #include <linux/seq_file.h>
> > #include <linux/slab.h>
> > +#include <linux/spinlock.h>
> > #include <linux/types.h>
> > #include <linux/units.h>
> >
> > @@ -258,6 +259,9 @@ struct asus_wmi {
> > int tpd_led_wk;
> > struct led_classdev kbd_led;
> > int kbd_led_wk;
> > + bool kbd_led_notify;
> > + bool kbd_led_avail;
> > + bool kbd_led_registered;
> > struct led_classdev lightbar_led;
> > int lightbar_led_wk;
> > struct led_classdev micmute_led;
> > @@ -266,6 +270,7 @@ struct asus_wmi {
> > struct work_struct tpd_led_work;
> > struct work_struct wlan_led_work;
> > struct work_struct lightbar_led_work;
> > + struct work_struct kbd_led_work;
> >
> > struct asus_rfkill wlan;
> > struct asus_rfkill bluetooth;
> > @@ -1530,6 +1535,99 @@ static void asus_wmi_battery_exit(struct asus_wmi *asus)
> >
> > /* LEDs ***********************************************************************/
> >
> > +struct asus_hid_ref {
> > + struct list_head listeners;
> > + struct asus_wmi *asus;
> > + /* Protects concurrent access from hid-asus and asus-wmi to leds */
> > + spinlock_t lock;
> > +};
> > +
> > +static struct asus_hid_ref asus_ref = {
> > + .listeners = LIST_HEAD_INIT(asus_ref.listeners),
> > + .asus = NULL,
> > + /*
> > + * Protects .asus, .asus.kbd_led_{wk,notify}, and .listener refs. Other
> > + * asus variables are read-only after .asus is set. Except the led cdev
> > + * device if not kbd_led_avail. That becomes read-only after the
> > + * first hid-asus listener registers and triggers the work queue. It is
> > + * then not referenced again until unregistering, which happens after
> > + * .asus ref is dropped. Since .asus needs to be accessed by hid-asus
> > + * IRQs to check if forwarding events is possible, a spinlock is used.
> > + */
> What are "That" and "It" referring to in this context?
>
> Are you absolutely sure you want to begin a sentence with "Except"?
Also some making it more than one paragraphs would help reading that bit
of text. You're stating like 4 important things here so I'd put each into
own paragraph.
I'm not entiry sure what the Except even refers to, to other variables or
read-onlyness, the next sentence explains it a bit more but it's too late
to understand the previous sentence.
--
i.
> On "ref is dropped" I would continue with ": since .asus .....".
> > + .lock = __SPIN_LOCK_UNLOCKED(asus_ref.lock),
> > +};
> > +
> > +/*
> > + * Allows registering hid-asus listeners that want to be notified of
> > + * keyboard backlight changes.
> > + */
> > +int asus_hid_register_listener(struct asus_hid_listener *bdev)
> > +{
> > + struct asus_wmi *asus;
> > +
> > + guard(spinlock_irqsave)(&asus_ref.lock);
> > + list_add_tail(&bdev->list, &asus_ref.listeners);
> > + asus = asus_ref.asus;
> > + if (asus)
> > + queue_work(asus->led_workqueue, &asus->kbd_led_work);
> Are you sure this has to be protected by the guard too?
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(asus_hid_register_listener);
> > +
> > +/*
> > + * Allows unregistering hid-asus listeners that were added with
> > + * asus_hid_register_listener().
> > + */
> > +void asus_hid_unregister_listener(struct asus_hid_listener *bdev)
> > +{
> > + guard(spinlock_irqsave)(&asus_ref.lock);
> > + list_del(&bdev->list);
> > +}
> > +EXPORT_SYMBOL_GPL(asus_hid_unregister_listener);
> > +
> > +static void do_kbd_led_set(struct led_classdev *led_cdev, int value);
> > +
> > +static void kbd_led_update_all(struct work_struct *work)
> > +{
> > + struct asus_wmi *asus;
> > + bool registered, notify;
> > + int ret, value;
> > +
> > + asus = container_of(work, struct asus_wmi, kbd_led_work);
> > +
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock) {
> > + registered = asus->kbd_led_registered;
> > + value = asus->kbd_led_wk;
> > + notify = asus->kbd_led_notify;
> > + }
> > +
> > + if (!registered) {
> > + /*
> > + * This workqueue runs under asus-wmi, which means probe has
> > + * completed and asus-wmi will keep running until it finishes.
> > + * Therefore, we can safely register the LED without holding
> > + * a spinlock.
> > + */
> > + ret = devm_led_classdev_register(&asus->platform_device->dev,
> > + &asus->kbd_led);
> > + if (!ret) {
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> > + asus->kbd_led_registered = true;
> > + } else {
> > + pr_warn("Failed to register keyboard backlight LED: %d\n", ret);
> > + return;
> > + }
> > + }
> > +
> > + if (value >= 0)
> > + do_kbd_led_set(&asus->kbd_led, value);
> > + if (notify) {
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> > + asus->kbd_led_notify = false;
> > + led_classdev_notify_brightness_hw_changed(&asus->kbd_led, value);
> > + }
> > +}
> > +
> > /*
> > * These functions actually update the LED's, and are called from a
> > * workqueue. By doing this as separate work rather than when the LED
> > @@ -1576,7 +1674,8 @@ static void kbd_led_update(struct asus_wmi *asus)
> > {
> > int ctrl_param = 0;
> >
> > - ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> > + ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
> > asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);
> > }
> >
> > @@ -1609,14 +1708,23 @@ static int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
> >
> > static void do_kbd_led_set(struct led_classdev *led_cdev, int value)
> > {
> > + struct asus_hid_listener *listener;
> > struct asus_wmi *asus;
> > int max_level;
> >
> > asus = container_of(led_cdev, struct asus_wmi, kbd_led);
> > max_level = asus->kbd_led.max_brightness;
> >
> > - asus->kbd_led_wk = clamp_val(value, 0, max_level);
> > - kbd_led_update(asus);
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> > + asus->kbd_led_wk = clamp_val(value, 0, max_level);
> > +
> > + if (asus->kbd_led_avail)
> > + kbd_led_update(asus);
> > +
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock) {
> > + list_for_each_entry(listener, &asus_ref.listeners, list)
> > + listener->brightness_set(listener, asus->kbd_led_wk);
> > + }
> > }
> >
> > static void kbd_led_set(struct led_classdev *led_cdev,
> > @@ -1631,10 +1739,11 @@ static void kbd_led_set(struct led_classdev *led_cdev,
> >
> > static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value)
> > {
> > - struct led_classdev *led_cdev = &asus->kbd_led;
> > -
> > - do_kbd_led_set(led_cdev, value);
> > - led_classdev_notify_brightness_hw_changed(led_cdev, asus->kbd_led_wk);
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock) {
> > + asus->kbd_led_wk = value;
> > + asus->kbd_led_notify = true;
> > + }
> > + queue_work(asus->led_workqueue, &asus->kbd_led_work);
> > }
> >
> > static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
> > @@ -1644,10 +1753,18 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
> >
> > asus = container_of(led_cdev, struct asus_wmi, kbd_led);
> >
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock) {
> > + if (!asus->kbd_led_avail)
> > + return asus->kbd_led_wk;
> > + }
> > +
> > retval = kbd_led_read(asus, &value, NULL);
> > if (retval < 0)
> > return retval;
> >
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> > + asus->kbd_led_wk = value;
> > +
> > return value;
> > }
> >
> > @@ -1759,7 +1876,9 @@ static int camera_led_set(struct led_classdev *led_cdev,
> >
> > static void asus_wmi_led_exit(struct asus_wmi *asus)
> > {
> > - led_classdev_unregister(&asus->kbd_led);
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> > + asus_ref.asus = NULL;
> > +
> > led_classdev_unregister(&asus->tpd_led);
> > led_classdev_unregister(&asus->wlan_led);
> > led_classdev_unregister(&asus->lightbar_led);
> > @@ -1797,22 +1916,25 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
> > goto error;
> > }
> >
> > - if (!kbd_led_read(asus, &led_val, NULL) && !dmi_check_system(asus_use_hid_led_dmi_ids)) {
> > - pr_info("using asus-wmi for asus::kbd_backlight\n");
> > - asus->kbd_led_wk = led_val;
> > - asus->kbd_led.name = "asus::kbd_backlight";
> > - asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
> > - asus->kbd_led.brightness_set = kbd_led_set;
> > - asus->kbd_led.brightness_get = kbd_led_get;
> > - asus->kbd_led.max_brightness = 3;
> > + asus->kbd_led.name = "asus::kbd_backlight";
> > + asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
> > + asus->kbd_led.brightness_set = kbd_led_set;
> > + asus->kbd_led.brightness_get = kbd_led_get;
> > + asus->kbd_led.max_brightness = 3;
> > + asus->kbd_led_avail = !kbd_led_read(asus, &led_val, NULL);
> > + INIT_WORK(&asus->kbd_led_work, kbd_led_update_all);
> >
> > + if (asus->kbd_led_avail) {
> > + asus->kbd_led_wk = led_val;
> > if (num_rgb_groups != 0)
> > asus->kbd_led.groups = kbd_rgb_mode_groups;
> > + } else
> > + asus->kbd_led_wk = -1;
> >
> > - rv = led_classdev_register(&asus->platform_device->dev,
> > - &asus->kbd_led);
> > - if (rv)
> > - goto error;
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock) {
> > + asus_ref.asus = asus;
> > + if (asus->kbd_led_avail || !list_empty(&asus_ref.listeners))
> > + queue_work(asus->led_workqueue, &asus->kbd_led_work);
> > }
> >
> > if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_WIRELESS_LED)
> > @@ -4272,6 +4394,7 @@ static int asus_wmi_get_event_code(union acpi_object *obj)
> >
> > static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
> > {
> > + enum led_brightness led_value;
> > unsigned int key_value = 1;
> > bool autorelease = 1;
> >
> > @@ -4288,19 +4411,22 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
> > return;
> > }
> >
> > + scoped_guard(spinlock_irqsave, &asus_ref.lock)
> > + led_value = asus->kbd_led_wk;
> > +
> > if (code == NOTIFY_KBD_BRTUP) {
> > - kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
> > + kbd_led_set_by_kbd(asus, led_value + 1);
> > return;
> > }
> > if (code == NOTIFY_KBD_BRTDWN) {
> > - kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1);
> > + kbd_led_set_by_kbd(asus, led_value - 1);
> > return;
> > }
> > if (code == NOTIFY_KBD_BRTTOGGLE) {
> > - if (asus->kbd_led_wk == asus->kbd_led.max_brightness)
> > + if (led_value == asus->kbd_led.max_brightness)
> > kbd_led_set_by_kbd(asus, 0);
> > else
> > - kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
> > + kbd_led_set_by_kbd(asus, led_value + 1);
> > return;
> > }
> >
> > diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
> > index 8a515179113d..1165039013b1 100644
> > --- a/include/linux/platform_data/x86/asus-wmi.h
> > +++ b/include/linux/platform_data/x86/asus-wmi.h
> > @@ -163,11 +163,20 @@ enum asus_ally_mcu_hack {
> > ASUS_WMI_ALLY_MCU_HACK_DISABLED,
> > };
> >
> > +/* Used to notify hid-asus when asus-wmi changes keyboard backlight */
> > +struct asus_hid_listener {
> > + struct list_head list;
> > + void (*brightness_set)(struct asus_hid_listener *listener, int brightness);
> > +};
> > +
> > #if IS_REACHABLE(CONFIG_ASUS_WMI)
> > void set_ally_mcu_hack(enum asus_ally_mcu_hack status);
> > void set_ally_mcu_powersave(bool enabled);
> > int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval);
> > int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval);
> > +
> > +int asus_hid_register_listener(struct asus_hid_listener *cdev);
> > +void asus_hid_unregister_listener(struct asus_hid_listener *cdev);
> > #else
> > static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status)
> > {
> > @@ -184,6 +193,14 @@ static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
> > {
> > return -ENODEV;
> > }
> > +
> > +static inline int asus_hid_register_listener(struct asus_hid_listener *bdev)
> > +{
> > + return -ENODEV;
> > +}
> > +static inline void asus_hid_unregister_listener(struct asus_hid_listener *bdev)
> > +{
> > +}
> > #endif
> >
> > /* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */
>
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v9 08/11] HID: asus: listen to the asus-wmi brightness device instead of creating one
2025-11-20 9:46 [PATCH v9 00/11] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling Antheas Kapenekakis
` (6 preceding siblings ...)
2025-11-20 9:46 ` [PATCH v9 07/11] platform/x86: asus-wmi: Add support for multiple kbd led handlers Antheas Kapenekakis
@ 2025-11-20 9:46 ` Antheas Kapenekakis
2025-11-20 9:46 ` [PATCH v9 09/11] platform/x86: asus-wmi: remove unused keyboard backlight quirk Antheas Kapenekakis
` (2 subsequent siblings)
10 siblings, 0 replies; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 9:46 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis
Some ROG laptops expose multiple interfaces for controlling the
keyboard/RGB brightness. This creates a name conflict under
asus::kbd_brightness, where the second device ends up being
named asus::kbd_brightness_1 and they are both broken.
Therefore, register a listener to the asus-wmi brightness device
instead of creating a new one.
Reviewed-by: Luke D. Jones <luke@ljones.dev>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 64 +++++++-----------------------------------
1 file changed, 10 insertions(+), 54 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 6193c9483bec..6a355c174f29 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -102,7 +102,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
struct asus_kbd_leds {
- struct led_classdev cdev;
+ struct asus_hid_listener listener;
struct hid_device *hdev;
struct work_struct work;
unsigned int brightness;
@@ -494,11 +494,11 @@ static void asus_schedule_work(struct asus_kbd_leds *led)
spin_unlock_irqrestore(&led->lock, flags);
}
-static void asus_kbd_backlight_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
+static void asus_kbd_backlight_set(struct asus_hid_listener *listener,
+ int brightness)
{
- struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds,
- cdev);
+ struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds,
+ listener);
unsigned long flags;
spin_lock_irqsave(&led->lock, flags);
@@ -508,20 +508,6 @@ static void asus_kbd_backlight_set(struct led_classdev *led_cdev,
asus_schedule_work(led);
}
-static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev)
-{
- struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds,
- cdev);
- enum led_brightness brightness;
- unsigned long flags;
-
- spin_lock_irqsave(&led->lock, flags);
- brightness = led->brightness;
- spin_unlock_irqrestore(&led->lock, flags);
-
- return brightness;
-}
-
static void asus_kbd_backlight_work(struct work_struct *work)
{
struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);
@@ -538,34 +524,6 @@ static void asus_kbd_backlight_work(struct work_struct *work)
hid_err(led->hdev, "Asus failed to set keyboard backlight: %d\n", ret);
}
-/* WMI-based keyboard backlight LED control (via asus-wmi driver) takes
- * precedence. We only activate HID-based backlight control when the
- * WMI control is not available.
- */
-static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
-{
- struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
- u32 value;
- int ret;
-
- if (!IS_ENABLED(CONFIG_ASUS_WMI))
- return false;
-
- if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD &&
- dmi_check_system(asus_use_hid_led_dmi_ids)) {
- hid_info(hdev, "using HID for asus::kbd_backlight\n");
- return false;
- }
-
- ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS,
- ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value);
- hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value);
- if (ret)
- return false;
-
- return !!(value & ASUS_WMI_DSTS_PRESENCE_BIT);
-}
-
/*
* We don't care about any other part of the string except the version section.
* Example strings: FGA80100.RC72LA.312_T01, FGA80100.RC71LS.318_T01
@@ -710,14 +668,11 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
drvdata->kbd_backlight->removed = false;
drvdata->kbd_backlight->brightness = 0;
drvdata->kbd_backlight->hdev = hdev;
- drvdata->kbd_backlight->cdev.name = "asus::kbd_backlight";
- drvdata->kbd_backlight->cdev.max_brightness = 3;
- drvdata->kbd_backlight->cdev.brightness_set = asus_kbd_backlight_set;
- drvdata->kbd_backlight->cdev.brightness_get = asus_kbd_backlight_get;
+ drvdata->kbd_backlight->listener.brightness_set = asus_kbd_backlight_set;
INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_backlight_work);
spin_lock_init(&drvdata->kbd_backlight->lock);
- ret = devm_led_classdev_register(&hdev->dev, &drvdata->kbd_backlight->cdev);
+ ret = asus_hid_register_listener(&drvdata->kbd_backlight->listener);
if (ret < 0) {
/* No need to have this still around */
devm_kfree(&hdev->dev, drvdata->kbd_backlight);
@@ -1106,7 +1061,7 @@ static int __maybe_unused asus_resume(struct hid_device *hdev) {
if (drvdata->kbd_backlight) {
const u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4,
- drvdata->kbd_backlight->cdev.brightness };
+ drvdata->kbd_backlight->brightness };
ret = asus_kbd_set_report(hdev, buf, sizeof(buf));
if (ret < 0) {
hid_err(hdev, "Asus failed to set keyboard backlight: %d\n", ret);
@@ -1232,7 +1187,6 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
if (is_vendor && (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) &&
- !asus_kbd_wmi_led_control_present(hdev) &&
asus_kbd_register_leds(hdev))
hid_warn(hdev, "Failed to initialize backlight.\n");
@@ -1279,6 +1233,8 @@ static void asus_remove(struct hid_device *hdev)
unsigned long flags;
if (drvdata->kbd_backlight) {
+ asus_hid_unregister_listener(&drvdata->kbd_backlight->listener);
+
spin_lock_irqsave(&drvdata->kbd_backlight->lock, flags);
drvdata->kbd_backlight->removed = true;
spin_unlock_irqrestore(&drvdata->kbd_backlight->lock, flags);
--
2.52.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v9 09/11] platform/x86: asus-wmi: remove unused keyboard backlight quirk
2025-11-20 9:46 [PATCH v9 00/11] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling Antheas Kapenekakis
` (7 preceding siblings ...)
2025-11-20 9:46 ` [PATCH v9 08/11] HID: asus: listen to the asus-wmi brightness device instead of creating one Antheas Kapenekakis
@ 2025-11-20 9:46 ` Antheas Kapenekakis
2025-11-20 9:46 ` [PATCH v9 10/11] platform/x86: asus-wmi: add keyboard brightness event handler Antheas Kapenekakis
2025-11-20 9:46 ` [PATCH v9 11/11] HID: asus: add support for the asus-wmi brightness handler Antheas Kapenekakis
10 siblings, 0 replies; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 9:46 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis
The quirk for selecting whether keyboard backlight should be controlled
by HID or WMI is not needed anymore, so remove it.
Reviewed-by: Luke D. Jones <luke@ljones.dev>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
include/linux/platform_data/x86/asus-wmi.h | 40 ----------------------
1 file changed, 40 deletions(-)
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index 1165039013b1..d8c5269854b0 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -203,44 +203,4 @@ static inline void asus_hid_unregister_listener(struct asus_hid_listener *bdev)
}
#endif
-/* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */
-static const struct dmi_system_id asus_use_hid_led_dmi_ids[] = {
- {
- .matches = {
- DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Zephyrus"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Strix"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Flow"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_PRODUCT_FAMILY, "ProArt P16"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "GA403U"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "GU605M"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_NAME, "RC71L"),
- },
- },
- { },
-};
-
#endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v9 10/11] platform/x86: asus-wmi: add keyboard brightness event handler
2025-11-20 9:46 [PATCH v9 00/11] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling Antheas Kapenekakis
` (8 preceding siblings ...)
2025-11-20 9:46 ` [PATCH v9 09/11] platform/x86: asus-wmi: remove unused keyboard backlight quirk Antheas Kapenekakis
@ 2025-11-20 9:46 ` Antheas Kapenekakis
2025-11-20 9:46 ` [PATCH v9 11/11] HID: asus: add support for the asus-wmi brightness handler Antheas Kapenekakis
10 siblings, 0 replies; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 9:46 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis
The keyboard brightness control of Asus WMI keyboards is handled in
kernel, which leads to the shortcut going from brightness 0, to 1,
to 2, and 3.
However, for HID keyboards it is exposed as a key and handled by the
user's desktop environment. For the toggle button, this means that
brightness control becomes on/off. In addition, in the absence of a
DE, the keyboard brightness does not work.
Therefore, expose an event handler for the keyboard brightness control
which can then be used by hid-asus. Since this handler is called from
an interrupt context, defer the actual work to a workqueue.
In the process, introduce ASUS_EV_MAX_BRIGHTNESS to hold the constant
for maximum brightness since it is shared between hid-asus/asus-wmi.
Reviewed-by: Luke D. Jones <luke@ljones.dev>
Tested-by: Luke D. Jones <luke@ljones.dev>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/platform/x86/asus-wmi.c | 46 +++++++++++++++++++---
include/linux/platform_data/x86/asus-wmi.h | 13 ++++++
2 files changed, 54 insertions(+), 5 deletions(-)
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 5f23aedbf34f..a7482c97cc5b 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -1628,6 +1628,44 @@ static void kbd_led_update_all(struct work_struct *work)
}
}
+/*
+ * This function is called from hid-asus to inform asus-wmi of brightness
+ * changes initiated by the keyboard backlight keys.
+ */
+int asus_hid_event(enum asus_hid_event event)
+{
+ struct asus_wmi *asus;
+ int brightness;
+
+ guard(spinlock_irqsave)(&asus_ref.lock);
+ asus = asus_ref.asus;
+ if (!asus || !asus->kbd_led_registered)
+ return -EBUSY;
+
+ brightness = asus->kbd_led_wk;
+
+ switch (event) {
+ case ASUS_EV_BRTUP:
+ brightness += 1;
+ break;
+ case ASUS_EV_BRTDOWN:
+ brightness -= 1;
+ break;
+ case ASUS_EV_BRTTOGGLE:
+ if (brightness >= ASUS_EV_MAX_BRIGHTNESS)
+ brightness = 0;
+ else
+ brightness += 1;
+ break;
+ }
+
+ asus->kbd_led_wk = clamp_val(brightness, 0, ASUS_EV_MAX_BRIGHTNESS);
+ asus->kbd_led_notify = true;
+ queue_work(asus->led_workqueue, &asus->kbd_led_work);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asus_hid_event);
+
/*
* These functions actually update the LED's, and are called from a
* workqueue. By doing this as separate work rather than when the LED
@@ -1710,13 +1748,11 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value)
{
struct asus_hid_listener *listener;
struct asus_wmi *asus;
- int max_level;
asus = container_of(led_cdev, struct asus_wmi, kbd_led);
- max_level = asus->kbd_led.max_brightness;
scoped_guard(spinlock_irqsave, &asus_ref.lock)
- asus->kbd_led_wk = clamp_val(value, 0, max_level);
+ asus->kbd_led_wk = clamp_val(value, 0, ASUS_EV_MAX_BRIGHTNESS);
if (asus->kbd_led_avail)
kbd_led_update(asus);
@@ -1920,7 +1956,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
asus->kbd_led.brightness_set = kbd_led_set;
asus->kbd_led.brightness_get = kbd_led_get;
- asus->kbd_led.max_brightness = 3;
+ asus->kbd_led.max_brightness = ASUS_EV_MAX_BRIGHTNESS;
asus->kbd_led_avail = !kbd_led_read(asus, &led_val, NULL);
INIT_WORK(&asus->kbd_led_work, kbd_led_update_all);
@@ -4423,7 +4459,7 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
return;
}
if (code == NOTIFY_KBD_BRTTOGGLE) {
- if (led_value == asus->kbd_led.max_brightness)
+ if (led_value == ASUS_EV_MAX_BRIGHTNESS)
kbd_led_set_by_kbd(asus, 0);
else
kbd_led_set_by_kbd(asus, led_value + 1);
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index d8c5269854b0..3f679598b629 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -169,6 +169,14 @@ struct asus_hid_listener {
void (*brightness_set)(struct asus_hid_listener *listener, int brightness);
};
+enum asus_hid_event {
+ ASUS_EV_BRTUP,
+ ASUS_EV_BRTDOWN,
+ ASUS_EV_BRTTOGGLE,
+};
+
+#define ASUS_EV_MAX_BRIGHTNESS 3
+
#if IS_REACHABLE(CONFIG_ASUS_WMI)
void set_ally_mcu_hack(enum asus_ally_mcu_hack status);
void set_ally_mcu_powersave(bool enabled);
@@ -177,6 +185,7 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval);
int asus_hid_register_listener(struct asus_hid_listener *cdev);
void asus_hid_unregister_listener(struct asus_hid_listener *cdev);
+int asus_hid_event(enum asus_hid_event event);
#else
static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status)
{
@@ -201,6 +210,10 @@ static inline int asus_hid_register_listener(struct asus_hid_listener *bdev)
static inline void asus_hid_unregister_listener(struct asus_hid_listener *bdev)
{
}
+static inline int asus_hid_event(enum asus_hid_event event)
+{
+ return -ENODEV;
+}
#endif
#endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* [PATCH v9 11/11] HID: asus: add support for the asus-wmi brightness handler
2025-11-20 9:46 [PATCH v9 00/11] HID: asus: Fix ASUS ROG Laptop's Keyboard backlight handling Antheas Kapenekakis
` (9 preceding siblings ...)
2025-11-20 9:46 ` [PATCH v9 10/11] platform/x86: asus-wmi: add keyboard brightness event handler Antheas Kapenekakis
@ 2025-11-20 9:46 ` Antheas Kapenekakis
2025-11-20 14:22 ` Denis Benato
10 siblings, 1 reply; 26+ messages in thread
From: Antheas Kapenekakis @ 2025-11-20 9:46 UTC (permalink / raw)
To: platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen, Denis Benato,
Antheas Kapenekakis
If the asus-wmi brightness handler is available, send the
keyboard brightness events to it instead of passing them
to userspace. If it is not, fall back to sending them to it.
Reviewed-by: Luke D. Jones <luke@ljones.dev>
Tested-by: Luke D. Jones <luke@ljones.dev>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
drivers/hid/hid-asus.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 6a355c174f29..ff5aaebc38e3 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -324,6 +324,17 @@ static int asus_event(struct hid_device *hdev, struct hid_field *field,
usage->hid & HID_USAGE);
}
+ if (usage->type == EV_KEY && value) {
+ switch (usage->code) {
+ case KEY_KBDILLUMUP:
+ return !asus_hid_event(ASUS_EV_BRTUP);
+ case KEY_KBDILLUMDOWN:
+ return !asus_hid_event(ASUS_EV_BRTDOWN);
+ case KEY_KBDILLUMTOGGLE:
+ return !asus_hid_event(ASUS_EV_BRTTOGGLE);
+ }
+ }
+
return 0;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 26+ messages in thread* Re: [PATCH v9 11/11] HID: asus: add support for the asus-wmi brightness handler
2025-11-20 9:46 ` [PATCH v9 11/11] HID: asus: add support for the asus-wmi brightness handler Antheas Kapenekakis
@ 2025-11-20 14:22 ` Denis Benato
0 siblings, 0 replies; 26+ messages in thread
From: Denis Benato @ 2025-11-20 14:22 UTC (permalink / raw)
To: Antheas Kapenekakis, platform-driver-x86, linux-input
Cc: linux-kernel, Jiri Kosina, Benjamin Tissoires, Corentin Chary,
Luke D . Jones, Hans de Goede, Ilpo Järvinen
On 11/20/25 10:46, Antheas Kapenekakis wrote:
> If the asus-wmi brightness handler is available, send the
> keyboard brightness events to it instead of passing them
> to userspace. If it is not, fall back to sending them to it.
>
> Reviewed-by: Luke D. Jones <luke@ljones.dev>
> Tested-by: Luke D. Jones <luke@ljones.dev>
> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
> drivers/hid/hid-asus.c | 11 +++++++++++
> 1 file changed, 11 insertions(+)
>
> diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> index 6a355c174f29..ff5aaebc38e3 100644
> --- a/drivers/hid/hid-asus.c
> +++ b/drivers/hid/hid-asus.c
> @@ -324,6 +324,17 @@ static int asus_event(struct hid_device *hdev, struct hid_field *field,
> usage->hid & HID_USAGE);
> }
>
> + if (usage->type == EV_KEY && value) {
> + switch (usage->code) {
> + case KEY_KBDILLUMUP:
> + return !asus_hid_event(ASUS_EV_BRTUP);
> + case KEY_KBDILLUMDOWN:
> + return !asus_hid_event(ASUS_EV_BRTDOWN);
> + case KEY_KBDILLUMTOGGLE:
> + return !asus_hid_event(ASUS_EV_BRTTOGGLE);
> + }
> + }
> +
> return 0;
> }
>
Reviewed-by: Denis Benato <benato.denis96@gmail.com>
^ permalink raw reply [flat|nested] 26+ messages in thread