HID: asus: Add RGB Support to Asus Z13, Ally, unify backlight asus-wmi, and Z13 QOL
v3 cover HID: asus: Add RGB Support to Asus Z13, Ally, unify backlight asus-wmi, and Z13 QOL
This is a three part series which does the following:
- Clean init sequence, fix the keyboard of the Z13 (touchpad,fan button)
- Unifies backlight handling to happen under asus-wmi so that all Aura devices have synced brightness controls and the backlight button works properly when it is on a USB laptop keyboard instead of one w/ WMI.
- Adds RGB support to hid-asus, solid colors only, and for the ROG Ally units and the Asus Z13 2025 first.
In V3, RGB controls are pretty stable, but as per Luke there needs to be a discussion about how it should be merged, so the last two patches can be considered as separate. The Z13 Folio has a unique pid, and so do the ally units, so RGB can be safely enabled on those. For the rest, there are cases where the same pid is used in laptops that have a white only keyboard.
For more context, see cover letter of V1.
---
V2: https://lore.kernel.org/all/[email protected]/
V1: https://lore.kernel.org/all/[email protected]/
Changes since V2:
- Check lazy init succeds in asus-wmi before setting register variable
- make explicit check in asus_hid_register_listener for listener existing to avoid re-init
- rename asus_brt to asus_hid in most places and harmonize everything
- switch to a spinlock instead of a mutex to avoid kernel ooops
- fixup hid device quirks to avoid multiple RGB devices while still exposing all input vendor devices. This includes moving rgb init to probe instead of the input_configured callbacks.
- Remove fan key (during retest it appears to be 0xae that is already supported by hid-asus)
- Never unregister asus::kbd_backlight while asus-wmi is active, as that
- removes fds from userspace and breaks backlight functionality. All
- current mainline drivers do not support backlight hotplugging, so most userspace software (e.g., KDE, UPower) is built with that assumption. For the Ally, since it disconnects its controller during sleep, this caused the backlight slider to not work in KDE.
Changes since V1:
- Add basic RGB support on hid-asus, (Z13/Ally) tested in KDE/Z13
- Fix ifdef else having an invalid signature (reported by kernel robot)
- Restore input arguments to init and keyboard function so they can be re-used for RGB controls.
- Remove Z13 delay (it did not work to fix the touchpad) and replace it with a HID_GROUP_GENERIC quirk to allow hid-multitouch to load. Squash keyboard rename into it.
- Unregister brightness listener before removing work queue to avoid a race condition causing corruption
- Remove spurious mutex unlock in asus_brt_event
- Place mutex lock in kbd_led_set after LED_UNREGISTERING check to avoid relocking the mutex and causing a deadlock when unregistering leds
- Add extra check during unregistering to avoid calling unregister when no led device is registered.
- Temporarily HID_QUIRK_INPUT_PER_APP from the ROG endpoint as it causes the driver to create 4 RGB handlers per device. I also suspect some extra events sneak through (KDE had the @@@@@@).
Antheas Kapenekakis (10):
HID: asus: refactor init sequence per spec
HID: asus: prevent binding to all HID devices on ROG
HID: Asus: add Z13 folio to generic group for multitouch to work platform/x86: asus-wmi: Add support for multiple kbd RGB handlers
HID: asus: listen to the asus-wmi brightness device instead of
creating one
platform/x86: asus-wmi: remove unused keyboard backlight quirk platform/x86: asus-wmi: add keyboard brightness event handler
HID: asus: add support for the asus-wmi brightness handler
HID: asus: add basic RGB support
HID: asus: add RGB support to the ROG Ally units
drivers/hid/hid-asus.c | 354 +++++++++++++++------ drivers/hid/hid-ids.h | 2 +- drivers/platform/x86/asus-wmi.c | 152 ++++++++- include/linux/platform_data/x86/asus-wmi.h | 67 ++-- 4 files changed, 423 insertions(+), 152 deletions(-)
base-commit: 4701f33a10702d5fc577c32434eb62adde0a1ae1
Signature · 1 line
2.48.1
PATCH v3 1/10 HID: asus: refactor init sequence per spec
Currently, asus_kbd_init() uses a reverse engineered init sequence from Windows, which contains the handshakes from multiple programs. Keep the main one, which is 0x5a (meant for brightness drivers).
In addition, perform a get_response and check if the response is the same. To avoid regressions, print an error if the response does not match instead of rejecting device.
Then, refactor asus_kbd_get_functions() to use the same ID it is called with, instead of hardcoding it to 0x5a so that it may be used for 0x0d in the future.
--- drivers/hid/hid-asus.c | 82 +++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 36 deletions(-)
drivers/hid/hid-asus.c +46 -36
@@ -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
@@ -388,14 +388,41 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bustatic int asus_kbd_init (struct hid_device *hdev,u8 report_id) { -const u8 buf[] = { report_id,0x41 ,0x53 ,0x55 ,0x53 ,0x20 ,0x54 , -0x65 ,0x63 ,0x68 ,0x2e ,0x49 ,0x6e ,0x63 ,0x2e ,0x00 }; +/* +* Asus handshake identifying us as a driver (0x5A) +* 0x5A then ASCII for "ASUS Tech.Inc." +* 0x5D is for userspace Windows applications. +* +* The handshake is first sent as a set_report, then retrieved +* from a get_report to verify the response. +*/ +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_err (hdev,"Asus handshake returned invalid response: %*ph\n" , +FEATURE_KBD_REPORT_SIZE , readbuf); +// Do not return error if handshake is wrong to avoid regressions + } + +kfree (readbuf);return ret; }
@@ -417,7 +444,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 ) {
@@ -540,42 +567,25 @@ 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; - -/* The LED endpoint is initialised in two HID */ - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID1 ); -if (ret <0 ) -return ret; - - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID2 ); -if (ret <0 ) -return ret; + ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID ); +if (ret <0 ) +return ret; -if (dmi_match (DMI_PRODUCT_FAMILY ,"ProArt P16" )) { - ret =asus_kbd_disable_oobe (hdev); -if (ret <0 ) -return ret; - } - }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 (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 ; } +/* Check for backlight support */ +if (!(kbd_func &SUPPORT_KBD_BACKLIGHT )) +return -ENODEV ; + drvdata->kbd_backlight =devm_kzalloc (&hdev->dev,sizeof (struct asus_kbd_leds ),GFP_KERNEL );
Signature · 1 line
2.48.1
Luke D. Jones
maintainerRe: [PATCH v3 01/10] HID: asus: refactor init sequence per spec
Quoted 34 lines · Antheas Kapenekakis
Currently,
asus_kbd_init()uses a reverse engineered init sequence from Windows, which contains the handshakes from multiple programs. Keep the main one, which is0x5a(meant for brightness drivers).In addition, perform a
get_responseand check if the response is the same. To avoid regressions, print an error if the response does not match instead of rejecting device.Then, refactor
asus_kbd_get_functions()to use the same ID it is called with, instead of hardcoding it to0x5aso that it may be used for0x0din the future.Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 82 +++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 36 deletions(-)drivers/hid/hid-asus.c +1
-1@@ -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@@ -388,14 +388,41 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bustatic int asus_kbd_init (struct hid_device *hdev,u8 report_id) {
drivers/hid/hid-asus.c static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu · line 398
Quoted 10 lines
drivers/hid/hid-asus.c +8
-2-const u8 buf[] = { report_id,0x41 ,0x53 ,0x55 ,0x53 ,0x20 ,0x54 , -0x65 ,0x63 ,0x68 ,0x2e ,0x49 ,0x6e ,0x63 ,0x2e ,0x00 }; +/* +* Asus handshake identifying us as a driver (0x5A) +* 0x5A then ASCII for "ASUS Tech.Inc." +* 0x5D is for userspace Windows applications. +* +* The handshake is first sent as a set_report, then retrieved +* from a get_report to verify the response. +*/
This entire comment is not required, especially not the last paragraph. From what I've seen in .dll reversing attempts there's no real distinction from driver/app and it's simply an init/enable sequence common to almost every ITE MCU that ASUS have used (slash, anime, Ally).
Please remove.
Quoted 53 lines
drivers/hid/hid-asus.c +24
-11+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_err (hdev,"Asus handshake returned invalid response: %*ph\n" , +FEATURE_KBD_REPORT_SIZE , readbuf); +// Do not return error if handshake is wrong to avoid regressions + } + +kfree (readbuf);return ret; }@@ -417,7 +444,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 ) {@@ -540,42 +567,25 @@ 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; - -/* The LED endpoint is initialised in two HID */ - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID1 );
drivers/hid/hid-asus.c static int asus_kbd_register_leds(struct hid_device *hdev) · line 573
Quoted 10 lines
drivers/hid/hid-asus.c +3
-6-if (ret <0 ) -return ret; - - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID2 ); -if (ret <0 ) -return ret; + ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID ); +if (ret <0 ) +return ret;
I don't have any non-ROG equipment to test. There have been some cases of Vivobook using the same MCU, but these otherwise used something else.
And the oldest hardware I have uses a 0x1866, which uses the same init sequence but works with both 0x5A and 0x5D report ID for init (on same endpoint). There are instances of other laptops that accept both 0x5A and 0x5D, and some that have only 0x5D.
I think you will need to change this to try both 0x5A and 0x5D sorry. The older models using 0x1854, 0x1869, 0x1866 PID may regress and although I'm reasonably confident there won't be issues due to age of those, it's not a risk I'm willing to take, I've spent all morning trawling through archives of info and I can't actually verify this other than from my memory.
I mentioned 0x5E being used for some of the oddball devices like slash and anime, don't worry about that one, it's a bridge that can be crossed at a later time. But it does illustrate that other report ID have been used for init.
Otherwise the cleanup is good.
No other comments required and I'll sign off with the above corrections.
Cheers,
Luke
Quoted 34 lines
drivers/hid/hid-asus.c +10
-16-if (dmi_match (DMI_PRODUCT_FAMILY ,"ProArt P16" )) { - ret =asus_kbd_disable_oobe (hdev); -if (ret <0 ) -return ret; - } - }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 (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 ; } +/* Check for backlight support */ +if (!(kbd_func &SUPPORT_KBD_BACKLIGHT )) +return -ENODEV ; + drvdata->kbd_backlight =devm_kzalloc (&hdev->dev,sizeof (struct asus_kbd_leds ),GFP_KERNEL );
Re: [PATCH v3 01/10] HID: asus: refactor init sequence per spec
Quoted 52 lines · Luke D. Jones
Quoted 34 lines · Antheas Kapenekakis
Currently,
asus_kbd_init()uses a reverse engineered init sequence from Windows, which contains the handshakes from multiple programs. Keep the main one, which is0x5a(meant for brightness drivers).In addition, perform a
get_responseand check if the response is the same. To avoid regressions, print an error if the response does not match instead of rejecting device.Then, refactor
asus_kbd_get_functions()to use the same ID it is called with, instead of hardcoding it to0x5aso that it may be used for0x0din the future.Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 82 +++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 36 deletions(-)drivers/hid/hid-asus.c +1
-1@@ -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@@ -388,14 +388,41 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bustatic int asus_kbd_init (struct hid_device *hdev,u8 report_id) {drivers/hid/hid-asus.c static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu · line 398
Quoted 10 lines
drivers/hid/hid-asus.c +8
-2-const u8 buf[] = { report_id,0x41 ,0x53 ,0x55 ,0x53 ,0x20 ,0x54 , -0x65 ,0x63 ,0x68 ,0x2e ,0x49 ,0x6e ,0x63 ,0x2e ,0x00 }; +/* +* Asus handshake identifying us as a driver (0x5A) +* 0x5A then ASCII for "ASUS Tech.Inc." +* 0x5D is for userspace Windows applications. +* +* The handshake is first sent as a set_report, then retrieved +* from a get_report to verify the response. +*/ This entire comment is not required, especially not the last paragraph.
From what I've seen in .dll reversing attempts there's no real
distinction from driver/app and it's simply an init/enable sequence common to almost every ITE MCU that ASUS have used (slash, anime, Ally).
Quoted 1 line
Please remove.
It is a context comment but can be removed.
Quoted 62 lines
Quoted 62 lines
drivers/hid/hid-asus.c +27
-17+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_err (hdev,"Asus handshake returned invalid response: %*ph\n" , +FEATURE_KBD_REPORT_SIZE , readbuf); +// Do not return error if handshake is wrong to avoid regressions + } + +kfree (readbuf);return ret; }@@ -417,7 +444,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 ) {@@ -540,42 +567,25 @@ 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; - -/* The LED endpoint is initialised in two HID */ - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID1 ); -if (ret <0 ) -return ret; - - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID2 ); -if (ret <0 ) -return ret; + ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID ); +if (ret <0 ) +return ret;
Quoted 8 lines
Quoted 1 line
I don't have any non-ROG equipment to test. There have been some cases of Vivobook using the same MCU, but these otherwise used something else. And the oldest hardware I have uses a
0x1866, which uses the same init sequence but works with both0x5Aand0x5Dreport ID for init (on same endpoint). There are instances of other laptops that accept both0x5Aand0x5D, and some that have only0x5D.
The driver sets the brightness using 0x5a and accepts keyboard shortcuts only from 0x5a. Therefore it needs to init only with 0x5a.
How would those laptops regress and in what way?
Quoted 6 lines
I think you will need to change this to try both
0x5Aand0x5Dsorry. The older models using0x1854,0x1869,0x1866PID may regress and although I'm reasonably confident there won't be issues due to age of those, it's not a risk I'm willing to take, I've spent all morning trawling through archives of info and I can't actually verify this other than from my memory.
For devices that support RGB, only when RGB is set, 0x5D is initialized too.
Quoted 47 lines
I mentioned
0x5Ebeing used for some of the oddball devices like slash and anime, don't worry about that one, it's a bridge that can be crossed at a later time. But it does illustrate that other report ID have been used for init.Otherwise the cleanup is good.
No other comments required and I'll sign off with the above corrections.
Cheers,
LukeQuoted 34 lines
drivers/hid/hid-asus.c +10
-16-if (dmi_match (DMI_PRODUCT_FAMILY ,"ProArt P16" )) { - ret =asus_kbd_disable_oobe (hdev); -if (ret <0 ) -return ret; - } - }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 (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 ; } +/* Check for backlight support */ +if (!(kbd_func &SUPPORT_KBD_BACKLIGHT )) +return -ENODEV ; + drvdata->kbd_backlight =devm_kzalloc (&hdev->dev,sizeof (struct asus_kbd_leds ),GFP_KERNEL );
Luke D. Jones
maintainerRe: [PATCH v3 01/10] HID: asus: refactor init sequence per spec
Quoted 128 lines · Antheas Kapenekakis
Quoted 52 lines · Luke D. Jones
Quoted 34 lines · Antheas Kapenekakis
Currently,
asus_kbd_init()uses a reverse engineered init sequence from Windows, which contains the handshakes from multiple programs. Keep the main one, which is0x5a(meant for brightness drivers).In addition, perform a
get_responseand check if the response is the same. To avoid regressions, print an error if the response does not match instead of rejecting device.Then, refactor
asus_kbd_get_functions()to use the same ID it is called with, instead of hardcoding it to0x5aso that it may be used for0x0din the future.Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 82 +++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 36 deletions(-)drivers/hid/hid-asus.c +1
-1@@ -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@@ -388,14 +388,41 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bustatic int asus_kbd_init (struct hid_device *hdev,u8 report_id) {drivers/hid/hid-asus.c static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu · line 398
Quoted 10 lines
drivers/hid/hid-asus.c +8
-2-const u8 buf[] = { report_id,0x41 ,0x53 ,0x55 ,0x53 ,0x20 ,0x54 , -0x65 ,0x63 ,0x68 ,0x2e ,0x49 ,0x6e ,0x63 ,0x2e ,0x00 }; +/* +* Asus handshake identifying us as a driver (0x5A) +* 0x5A then ASCII for "ASUS Tech.Inc." +* 0x5D is for userspace Windows applications. +* +* The handshake is first sent as a set_report, then retrieved +* from a get_report to verify the response. +*/ This entire comment is not required, especially not the last paragraph.
From what I've seen in .dll reversing attempts there's no real
distinction from driver/app and it's simply an init/enable sequence common to almost every ITE MCU that ASUS have used (slash, anime, Ally).Quoted 1 line
Please remove.
It is a context comment but can be removed.
Quoted 62 lines
Quoted 62 lines
drivers/hid/hid-asus.c +27
-17+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_err (hdev,"Asus handshake returned invalid response: %*ph\n" , +FEATURE_KBD_REPORT_SIZE , readbuf); +// Do not return error if handshake is wrong to avoid regressions + } + +kfree (readbuf);return ret; }@@ -417,7 +444,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 ) {@@ -540,42 +567,25 @@ 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; - -/* The LED endpoint is initialised in two HID */ - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID1 ); -if (ret <0 ) -return ret; - - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID2 ); -if (ret <0 ) -return ret; + ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID ); +if (ret <0 ) +return ret;Quoted 8 lines
Quoted 1 line
I don't have any non-ROG equipment to test. There have been some cases of Vivobook using the same MCU, but these otherwise used something else. And the oldest hardware I have uses a
0x1866, which uses the same init sequence but works with both0x5Aand0x5Dreport ID for init (on same endpoint). There are instances of other laptops that accept both0x5Aand0x5D, and some that have only0x5D.
Quoted 5 lines
The driver sets the brightness using
0x5aand accepts keyboard shortcuts only from0x5a. Therefore it needs to init only with0x5a.How would those laptops regress and in what way?
The media keys fail to work (vol, mic, rog). Can you please accept that I do know some laptops used only 0x5D, and these are older models, around 5+ years. The only thing I have to go on is my memory unfortunately, and I've been trying to find the concrete examples.
Quoted 8 lines
Quoted 6 lines
I think you will need to change this to try both
0x5Aand0x5Dsorry. The older models using0x1854,0x1869,0x1866PID may regress and although I'm reasonably confident there won't be issues due to age of those, it's not a risk I'm willing to take, I've spent all morning trawling through archives of info and I can't actually verify this other than from my memory.For devices that support RGB, only when RGB is set,
0x5Dis initialized too.
Sure. But as I've said above.. Please add both to init. It's only done once, and it doesn't hurt anything plus doesn't risk regressing older hardware.
If I can get the proper evidence that only 0x5A is required I'm happy to use only that, but until then I don't want that risk. And it's only a small thing here.
Cheers,
Luke.
Quoted 48 lines
Quoted 48 lines
I mentioned
0x5Ebeing used for some of the oddball devices like slash and anime, don't worry about that one, it's a bridge that can be crossed at a later time. But it does illustrate that other report ID have been used for init.Otherwise the cleanup is good.
No other comments required and I'll sign off with the above corrections.
Cheers,
LukeQuoted 34 lines
drivers/hid/hid-asus.c +10
-16-if (dmi_match (DMI_PRODUCT_FAMILY ,"ProArt P16" )) { - ret =asus_kbd_disable_oobe (hdev); -if (ret <0 ) -return ret; - } - }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 (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 ; } +/* Check for backlight support */ +if (!(kbd_func &SUPPORT_KBD_BACKLIGHT )) +return -ENODEV ; + drvdata->kbd_backlight =devm_kzalloc (&hdev->dev,sizeof (struct asus_kbd_leds ),GFP_KERNEL );
Re: [PATCH v3 01/10] HID: asus: refactor init sequence per spec
Quoted 133 lines · Luke D. Jones
Quoted 131 lines · Antheas Kapenekakis
Quoted 52 lines · Luke D. Jones
Quoted 34 lines · Antheas Kapenekakis
Currently,
asus_kbd_init()uses a reverse engineered init sequence from Windows, which contains the handshakes from multiple programs. Keep the main one, which is0x5a(meant for brightness drivers).In addition, perform a
get_responseand check if the response is the same. To avoid regressions, print an error if the response does not match instead of rejecting device.Then, refactor
asus_kbd_get_functions()to use the same ID it is called with, instead of hardcoding it to0x5aso that it may be used for0x0din the future.Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 82 +++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 36 deletions(-)drivers/hid/hid-asus.c +1
-1@@ -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@@ -388,14 +388,41 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bustatic int asus_kbd_init (struct hid_device *hdev,u8 report_id) {drivers/hid/hid-asus.c static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu · line 398
Quoted 10 lines
drivers/hid/hid-asus.c +8
-2-const u8 buf[] = { report_id,0x41 ,0x53 ,0x55 ,0x53 ,0x20 ,0x54 , -0x65 ,0x63 ,0x68 ,0x2e ,0x49 ,0x6e ,0x63 ,0x2e ,0x00 }; +/* +* Asus handshake identifying us as a driver (0x5A) +* 0x5A then ASCII for "ASUS Tech.Inc." +* 0x5D is for userspace Windows applications. +* +* The handshake is first sent as a set_report, then retrieved +* from a get_report to verify the response. +*/ This entire comment is not required, especially not the last paragraph.
From what I've seen in .dll reversing attempts there's no real
distinction from driver/app and it's simply an init/enable sequence common to almost every ITE MCU that ASUS have used (slash, anime, Ally).Quoted 1 line
Please remove.
It is a context comment but can be removed.
Quoted 62 lines
Quoted 62 lines
drivers/hid/hid-asus.c +27
-17+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_err (hdev,"Asus handshake returned invalid response: %*ph\n" , +FEATURE_KBD_REPORT_SIZE , readbuf); +// Do not return error if handshake is wrong to avoid regressions + } + +kfree (readbuf);return ret; }@@ -417,7 +444,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 ) {@@ -540,42 +567,25 @@ 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; - -/* The LED endpoint is initialised in two HID */ - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID1 ); -if (ret <0 ) -return ret; - - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID2 ); -if (ret <0 ) -return ret; + ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID ); +if (ret <0 ) +return ret;Quoted 8 lines
Quoted 1 line
I don't have any non-ROG equipment to test. There have been some cases of Vivobook using the same MCU, but these otherwise used something else. And the oldest hardware I have uses a
0x1866, which uses the same init sequence but works with both0x5Aand0x5Dreport ID for init (on same endpoint). There are instances of other laptops that accept both0x5Aand0x5D, and some that have only0x5D.The driver sets the brightness using
0x5aand accepts keyboard shortcuts only from0x5a. Therefore it needs to init only with0x5a.
Quoted 7 lines
Quoted 2 lines
How would those laptops regress and in what way?
The media keys fail to work (vol, mic, rog). Can you please accept that I do know some laptops used only
0x5D, and these are older models, around 5+ years. The only thing I have to go on is my memory unfortunately, and I've been trying to find the concrete examples.
I just looked at the history. Yeah it seems you added ID1 in 2020 with some other commands. But on the same commit you blocked 0x5d and 0x5e, so it means those keyboards use 0x5a to send keyboard events.
Nevertheless, it is not worth looking up or risking regressions for old hardware. I will readd 0x5d, 0x5e for USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD and USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2, since those are the ones you added the inits with.
But I will still keep them off for the Z13 and Ally.
By the way,
Antheas
Quoted 69 lines
Quoted 8 lines
Quoted 6 lines
I think you will need to change this to try both
0x5Aand0x5Dsorry. The older models using0x1854,0x1869,0x1866PID may regress and although I'm reasonably confident there won't be issues due to age of those, it's not a risk I'm willing to take, I've spent all morning trawling through archives of info and I can't actually verify this other than from my memory.For devices that support RGB, only when RGB is set,
0x5Dis initialized too.Sure. But as I've said above.. Please add both to init. It's only done once, and it doesn't hurt anything plus doesn't risk regressing older hardware.
If I can get the proper evidence that only
0x5Ais required I'm happy to use only that, but until then I don't want that risk. And it's only a small thing here.Cheers,
Luke.Quoted 48 lines
Quoted 48 lines
I mentioned
0x5Ebeing used for some of the oddball devices like slash and anime, don't worry about that one, it's a bridge that can be crossed at a later time. But it does illustrate that other report ID have been used for init.Otherwise the cleanup is good.
No other comments required and I'll sign off with the above corrections.
Cheers,
LukeQuoted 34 lines
drivers/hid/hid-asus.c +10
-16-if (dmi_match (DMI_PRODUCT_FAMILY ,"ProArt P16" )) { - ret =asus_kbd_disable_oobe (hdev); -if (ret <0 ) -return ret; - } - }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 (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 ; } +/* Check for backlight support */ +if (!(kbd_func &SUPPORT_KBD_BACKLIGHT )) +return -ENODEV ; + drvdata->kbd_backlight =devm_kzalloc (&hdev->dev,sizeof (struct asus_kbd_leds ),GFP_KERNEL );
Re: [PATCH v3 01/10] HID: asus: refactor init sequence per spec
Quoted 153 lines · Antheas Kapenekakis
Quoted 133 lines · Luke D. Jones
Quoted 131 lines · Antheas Kapenekakis
Quoted 52 lines · Luke D. Jones
Quoted 34 lines · Antheas Kapenekakis
Currently,
asus_kbd_init()uses a reverse engineered init sequence from Windows, which contains the handshakes from multiple programs. Keep the main one, which is0x5a(meant for brightness drivers).In addition, perform a
get_responseand check if the response is the same. To avoid regressions, print an error if the response does not match instead of rejecting device.Then, refactor
asus_kbd_get_functions()to use the same ID it is called with, instead of hardcoding it to0x5aso that it may be used for0x0din the future.Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 82 +++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 36 deletions(-)drivers/hid/hid-asus.c +1
-1@@ -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@@ -388,14 +388,41 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bustatic int asus_kbd_init (struct hid_device *hdev,u8 report_id) {drivers/hid/hid-asus.c static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu · line 398
Quoted 10 lines
drivers/hid/hid-asus.c +8
-2-const u8 buf[] = { report_id,0x41 ,0x53 ,0x55 ,0x53 ,0x20 ,0x54 , -0x65 ,0x63 ,0x68 ,0x2e ,0x49 ,0x6e ,0x63 ,0x2e ,0x00 }; +/* +* Asus handshake identifying us as a driver (0x5A) +* 0x5A then ASCII for "ASUS Tech.Inc." +* 0x5D is for userspace Windows applications. +* +* The handshake is first sent as a set_report, then retrieved +* from a get_report to verify the response. +*/ This entire comment is not required, especially not the last paragraph.
From what I've seen in .dll reversing attempts there's no real
distinction from driver/app and it's simply an init/enable sequence common to almost every ITE MCU that ASUS have used (slash, anime, Ally).Quoted 1 line
Please remove.
It is a context comment but can be removed.
Quoted 62 lines
Quoted 62 lines
drivers/hid/hid-asus.c +27
-17+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_err (hdev,"Asus handshake returned invalid response: %*ph\n" , +FEATURE_KBD_REPORT_SIZE , readbuf); +// Do not return error if handshake is wrong to avoid regressions + } + +kfree (readbuf);return ret; }@@ -417,7 +444,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 ) {@@ -540,42 +567,25 @@ 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; - -/* The LED endpoint is initialised in two HID */ - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID1 ); -if (ret <0 ) -return ret; - - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID2 ); -if (ret <0 ) -return ret; + ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID ); +if (ret <0 ) +return ret;Quoted 8 lines
Quoted 1 line
I don't have any non-ROG equipment to test. There have been some cases of Vivobook using the same MCU, but these otherwise used something else. And the oldest hardware I have uses a
0x1866, which uses the same init sequence but works with both0x5Aand0x5Dreport ID for init (on same endpoint). There are instances of other laptops that accept both0x5Aand0x5D, and some that have only0x5D.The driver sets the brightness using
0x5aand accepts keyboard shortcuts only from0x5a. Therefore it needs to init only with0x5a.Quoted 7 lines
Quoted 2 lines
How would those laptops regress and in what way?
The media keys fail to work (vol, mic, rog). Can you please accept that I do know some laptops used only
0x5D, and these are older models, around 5+ years. The only thing I have to go on is my memory unfortunately, and I've been trying to find the concrete examples.I just looked at the history. Yeah it seems you added ID1 in 2020 with some other commands. But on the same commit you blocked
0x5dand0x5e, so it means those keyboards use0x5ato send keyboard events.Nevertheless, it is not worth looking up or risking regressions for old hardware. I will readd
0x5d,0x5eforUSB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARDandUSB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2, since those are the ones you added the inits with.
Quoted 3 lines
But I will still keep them off for the Z13 and Ally.
By the way,
I guess I did not finish this. I was going to express some concern about the Claymore keyboard. But it seems it does not have a backlight quirk so it is ok
Quoted 71 lines
Antheas
Quoted 69 lines
Quoted 8 lines
Quoted 6 lines
I think you will need to change this to try both
0x5Aand0x5Dsorry. The older models using0x1854,0x1869,0x1866PID may regress and although I'm reasonably confident there won't be issues due to age of those, it's not a risk I'm willing to take, I've spent all morning trawling through archives of info and I can't actually verify this other than from my memory.For devices that support RGB, only when RGB is set,
0x5Dis initialized too.Sure. But as I've said above.. Please add both to init. It's only done once, and it doesn't hurt anything plus doesn't risk regressing older hardware.
If I can get the proper evidence that only
0x5Ais required I'm happy to use only that, but until then I don't want that risk. And it's only a small thing here.Cheers,
Luke.Quoted 48 lines
Quoted 48 lines
I mentioned
0x5Ebeing used for some of the oddball devices like slash and anime, don't worry about that one, it's a bridge that can be crossed at a later time. But it does illustrate that other report ID have been used for init.Otherwise the cleanup is good.
No other comments required and I'll sign off with the above corrections.
Cheers,
LukeQuoted 34 lines
drivers/hid/hid-asus.c +10
-16-if (dmi_match (DMI_PRODUCT_FAMILY ,"ProArt P16" )) { - ret =asus_kbd_disable_oobe (hdev); -if (ret <0 ) -return ret; - } - }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 (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 ; } +/* Check for backlight support */ +if (!(kbd_func &SUPPORT_KBD_BACKLIGHT )) +return -ENODEV ; + drvdata->kbd_backlight =devm_kzalloc (&hdev->dev,sizeof (struct asus_kbd_leds ),GFP_KERNEL );
Luke D. Jones
maintainerRe: [PATCH v3 01/10] HID: asus: refactor init sequence per spec
Quoted 154 lines · Antheas Kapenekakis
Quoted 153 lines · Antheas Kapenekakis
Quoted 133 lines · Luke D. Jones
Quoted 131 lines · Antheas Kapenekakis
Quoted 52 lines · Luke D. Jones
Quoted 34 lines · Antheas Kapenekakis
Currently,
asus_kbd_init()uses a reverse engineered init sequence from Windows, which contains the handshakes from multiple programs. Keep the main one, which is0x5a(meant for brightness drivers).In addition, perform a
get_responseand check if the response is the same. To avoid regressions, print an error if the response does not match instead of rejecting device.Then, refactor
asus_kbd_get_functions()to use the same ID it is called with, instead of hardcoding it to0x5aso that it may be used for0x0din the future.Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 82 +++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 36 deletions(-)drivers/hid/hid-asus.c +1
-1@@ -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@@ -388,14 +388,41 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bustatic int asus_kbd_init (struct hid_device *hdev,u8 report_id) {drivers/hid/hid-asus.c static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu · line 398
Quoted 10 lines
drivers/hid/hid-asus.c +8
-2-const u8 buf[] = { report_id,0x41 ,0x53 ,0x55 ,0x53 ,0x20 ,0x54 , -0x65 ,0x63 ,0x68 ,0x2e ,0x49 ,0x6e ,0x63 ,0x2e ,0x00 }; +/* +* Asus handshake identifying us as a driver (0x5A) +* 0x5A then ASCII for "ASUS Tech.Inc." +* 0x5D is for userspace Windows applications. +* +* The handshake is first sent as a set_report, then retrieved +* from a get_report to verify the response. +*/ This entire comment is not required, especially not the last paragraph.
From what I've seen in .dll reversing attempts there's no real
distinction from driver/app and it's simply an init/enable sequence common to almost every ITE MCU that ASUS have used (slash, anime, Ally).Quoted 1 line
Please remove.
It is a context comment but can be removed.
Quoted 62 lines
Quoted 62 lines
drivers/hid/hid-asus.c +27
-17+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_err (hdev,"Asus handshake returned invalid response: %*ph\n" , +FEATURE_KBD_REPORT_SIZE , readbuf); +// Do not return error if handshake is wrong to avoid regressions + } + +kfree (readbuf);return ret; }@@ -417,7 +444,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 ) {@@ -540,42 +567,25 @@ 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; - -/* The LED endpoint is initialised in two HID */ - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID1 ); -if (ret <0 ) -return ret; - - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID2 ); -if (ret <0 ) -return ret; + ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID ); +if (ret <0 ) +return ret;Quoted 8 lines
Quoted 1 line
I don't have any non-ROG equipment to test. There have been some cases of Vivobook using the same MCU, but these otherwise used something else. And the oldest hardware I have uses a
0x1866, which uses the same init sequence but works with both0x5Aand0x5Dreport ID for init (on same endpoint). There are instances of other laptops that accept both0x5Aand0x5D, and some that have only0x5D.The driver sets the brightness using
0x5aand accepts keyboard shortcuts only from0x5a. Therefore it needs to init only with0x5a.Quoted 7 lines
Quoted 2 lines
How would those laptops regress and in what way?
The media keys fail to work (vol, mic, rog). Can you please accept that I do know some laptops used only
0x5D, and these are older models, around 5+ years. The only thing I have to go on is my memory unfortunately, and I've been trying to find the concrete examples.I just looked at the history. Yeah it seems you added ID1 in 2020 with some other commands. But on the same commit you blocked
0x5dand0x5e, so it means those keyboards use0x5ato send keyboard events.Nevertheless, it is not worth looking up or risking regressions for old hardware. I will readd
0x5d,0x5eforUSB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARDandUSB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2, since those are the ones you added the inits with.
Quoted 8 lines
Quoted 3 lines
But I will still keep them off for the Z13 and Ally.
By the way,
I guess I did not finish this. I was going to express some concern about the Claymore keyboard. But it seems it does not have a backlight quirk so it is ok
The external keyboards are a bit funky yeah. I don't think the Claymore is in very wide use at all either. In any case you're correct.
Quoted 71 lines
Quoted 71 lines
Antheas
Quoted 69 lines
Quoted 8 lines
Quoted 6 lines
I think you will need to change this to try both
0x5Aand0x5Dsorry. The older models using0x1854,0x1869,0x1866PID may regress and although I'm reasonably confident there won't be issues due to age of those, it's not a risk I'm willing to take, I've spent all morning trawling through archives of info and I can't actually verify this other than from my memory.For devices that support RGB, only when RGB is set,
0x5Dis initialized too.Sure. But as I've said above.. Please add both to init. It's only done once, and it doesn't hurt anything plus doesn't risk regressing older hardware.
If I can get the proper evidence that only
0x5Ais required I'm happy to use only that, but until then I don't want that risk. And it's only a small thing here.Cheers,
Luke.Quoted 48 lines
Quoted 48 lines
I mentioned
0x5Ebeing used for some of the oddball devices like slash and anime, don't worry about that one, it's a bridge that can be crossed at a later time. But it does illustrate that other report ID have been used for init.Otherwise the cleanup is good.
No other comments required and I'll sign off with the above corrections.
Cheers,
LukeQuoted 34 lines
drivers/hid/hid-asus.c +10
-16-if (dmi_match (DMI_PRODUCT_FAMILY ,"ProArt P16" )) { - ret =asus_kbd_disable_oobe (hdev); -if (ret <0 ) -return ret; - } - }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 (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 ; } +/* Check for backlight support */ +if (!(kbd_func &SUPPORT_KBD_BACKLIGHT )) +return -ENODEV ; + drvdata->kbd_backlight =devm_kzalloc (&hdev->dev,sizeof (struct asus_kbd_leds ),GFP_KERNEL );
Luke D. Jones
maintainerRe: [PATCH v3 01/10] HID: asus: refactor init sequence per spec
In reply to Antheas KapenekakisQuoted 146 lines · Antheas Kapenekakis
Quoted 133 lines · Luke D. Jones
Quoted 131 lines · Antheas Kapenekakis
Quoted 52 lines · Luke D. Jones
Quoted 34 lines · Antheas Kapenekakis
Currently,
asus_kbd_init()uses a reverse engineered init sequence from Windows, which contains the handshakes from multiple programs. Keep the main one, which is0x5a(meant for brightness drivers).In addition, perform a
get_responseand check if the response is the same. To avoid regressions, print an error if the response does not match instead of rejecting device.Then, refactor
asus_kbd_get_functions()to use the same ID it is called with, instead of hardcoding it to0x5aso that it may be used for0x0din the future.Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 82 +++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 36 deletions(-)drivers/hid/hid-asus.c +1
-1@@ -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@@ -388,14 +388,41 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bustatic int asus_kbd_init (struct hid_device *hdev,u8 report_id) {drivers/hid/hid-asus.c static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu · line 398
Quoted 10 lines
drivers/hid/hid-asus.c +8
-2-const u8 buf[] = { report_id,0x41 ,0x53 ,0x55 ,0x53 ,0x20 ,0x54 , -0x65 ,0x63 ,0x68 ,0x2e ,0x49 ,0x6e ,0x63 ,0x2e ,0x00 }; +/* +* Asus handshake identifying us as a driver (0x5A) +* 0x5A then ASCII for "ASUS Tech.Inc." +* 0x5D is for userspace Windows applications. +* +* The handshake is first sent as a set_report, then retrieved +* from a get_report to verify the response. +*/ This entire comment is not required, especially not the last paragraph.
From what I've seen in .dll reversing attempts there's no real
distinction from driver/app and it's simply an init/enable sequence common to almost every ITE MCU that ASUS have used (slash, anime, Ally).Quoted 1 line
Please remove.
It is a context comment but can be removed.
Quoted 62 lines
Quoted 62 lines
drivers/hid/hid-asus.c +27
-17+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_err (hdev,"Asus handshake returned invalid response: %*ph\n" , +FEATURE_KBD_REPORT_SIZE , readbuf); +// Do not return error if handshake is wrong to avoid regressions + } + +kfree (readbuf);return ret; }@@ -417,7 +444,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 ) {@@ -540,42 +567,25 @@ 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; - -/* The LED endpoint is initialised in two HID */ - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID1 ); -if (ret <0 ) -return ret; - - ret =asus_kbd_init (hdev,FEATURE_KBD_LED_REPORT_ID2 ); -if (ret <0 ) -return ret; + ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID ); +if (ret <0 ) +return ret;Quoted 8 lines
Quoted 1 line
I don't have any non-ROG equipment to test. There have been some cases of Vivobook using the same MCU, but these otherwise used something else. And the oldest hardware I have uses a
0x1866, which uses the same init sequence but works with both0x5Aand0x5Dreport ID for init (on same endpoint). There are instances of other laptops that accept both0x5Aand0x5D, and some that have only0x5D.The driver sets the brightness using
0x5aand accepts keyboard shortcuts only from0x5a. Therefore it needs to init only with0x5a.Quoted 7 lines
Quoted 2 lines
How would those laptops regress and in what way?
The media keys fail to work (vol, mic, rog). Can you please accept that I do know some laptops used only
0x5D, and these are older models, around 5+ years. The only thing I have to go on is my memory unfortunately, and I've been trying to find the concrete examples.I just looked at the history. Yeah it seems you added ID1 in 2020 with some other commands. But on the same commit you blocked
0x5dand0x5e, so it means those keyboards use0x5ato send keyboard events.
Quoted 6 lines
Nevertheless, it is not worth looking up or risking regressions for old hardware. I will readd
0x5d,0x5eforUSB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARDandUSB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2, since those are the ones you added the inits with.
Thank you. Please know that I'm not trying to be a pain, I only don't want to take that risk when I know things were working for all before now, and that I know at least some laptops required 0x5D historically. I do know you can drop 0x5E here.
I saw we have:
#define INPUT_REPORT_ID 0x5d
#define FEATURE_KBD_LED_REPORT_ID1 0x5d
Maybe unifying to just one of these can be added as a cleanup?
Cheers,
Luke.
Quoted 75 lines
But I will still keep them off for the Z13 and Ally.
By the way,
Antheas
Quoted 69 lines
Quoted 8 lines
Quoted 6 lines
I think you will need to change this to try both
0x5Aand0x5Dsorry. The older models using0x1854,0x1869,0x1866PID may regress and although I'm reasonably confident there won't be issues due to age of those, it's not a risk I'm willing to take, I've spent all morning trawling through archives of info and I can't actually verify this other than from my memory.For devices that support RGB, only when RGB is set,
0x5Dis initialized too.Sure. But as I've said above.. Please add both to init. It's only done once, and it doesn't hurt anything plus doesn't risk regressing older hardware.
If I can get the proper evidence that only
0x5Ais required I'm happy to use only that, but until then I don't want that risk. And it's only a small thing here.Cheers,
Luke.Quoted 48 lines
Quoted 48 lines
I mentioned
0x5Ebeing used for some of the oddball devices like slash and anime, don't worry about that one, it's a bridge that can be crossed at a later time. But it does illustrate that other report ID have been used for init.Otherwise the cleanup is good.
No other comments required and I'll sign off with the above corrections.
Cheers,
LukeQuoted 34 lines
drivers/hid/hid-asus.c +10
-16-if (dmi_match (DMI_PRODUCT_FAMILY ,"ProArt P16" )) { - ret =asus_kbd_disable_oobe (hdev); -if (ret <0 ) -return ret; - } - }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 (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 ; } +/* Check for backlight support */ +if (!(kbd_func &SUPPORT_KBD_BACKLIGHT )) +return -ENODEV ; + drvdata->kbd_backlight =devm_kzalloc (&hdev->dev,sizeof (struct asus_kbd_leds ),GFP_KERNEL );
PATCH v3 2/10 HID: asus: prevent binding to all HID devices on ROG
In reply to Antheas KapenekakisROG keyboards are HID compliant and only care about the endpoint that produces vendor events (e.g., fan mode) and has the keyboard backlight.
Therefore, handle all of the endpoints of ROG keyboards as compliant, by adding HID_QUIRK_INPUT_PER_APP and, for devices other than the vendor one, by adding QUIRK_HANDLE_GENERIC to stop mutating them.
Due to HID_QUIRK_INPUT_PER_APP, rgb register is moved into probe, as the input_* functions are called multiple times (4 for the Z13).
--- drivers/hid/hid-asus.c | 69 ++++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 16 deletions(-)
drivers/hid/hid-asus.c +53 -16
@@ -84,6 +84,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");#define QUIRK_MEDION_E1239T BIT(10)#define QUIRK_ROG_NKEY_KEYBOARD BIT(11)#define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12) +#define QUIRK_HANDLE_GENERIC BIT(13)#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \
@@ -120,7 +121,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;
@@ -326,6 +326,10 @@ static int asus_raw_event(struct hid_device *hdev, {struct asus_drvdata *drvdata =hid_get_drvdata (hdev); +if (drvdata->quirks &QUIRK_HANDLE_GENERIC ) +/* NOOP on generic HID devices to avoid side effects. */ +return 0 ; +if (drvdata->battery && data[0 ] ==BATTERY_REPORT_ID )return asus_report_battery (drvdata, data, size);
@@ -774,6 +778,10 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)struct input_dev *input = hi->input;struct asus_drvdata *drvdata =hid_get_drvdata (hdev); +if (drvdata->quirks &QUIRK_HANDLE_GENERIC ) +/* NOOP on generic HID devices to avoid side effects. */ +return 0 ; +/* T100CHI uses MULTI_INPUT, bind the touchpad to the mouse hid_input */ if (drvdata->quirks &QUIRK_T100CHI && hi->report->id !=T100CHI_MOUSE_REPORT_ID )
@@ -827,11 +835,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 ; }
@@ -851,6 +854,10 @@ static int asus_input_mapping(struct hid_device *hdev,return -1 ; } +if (drvdata->quirks &QUIRK_HANDLE_GENERIC ) +/* NOOP on generic HID devices to avoid side effects. */ +return 0 ; +/* * Ignore a bunch of bogus collections in the T100CHI descriptor. * This avoids a bunch of non-functional hid_input devices getting
@@ -901,15 +908,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 ; }
@@ -1026,8 +1024,10 @@ 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; +int ret, is_vendor =0 ; drvdata =devm_kzalloc (&hdev->dev,sizeof (*drvdata),GFP_KERNEL );if (drvdata ==NULL ) {
@@ -1111,12 +1111,45 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)return ret; } +/* +* Check for the vendor interface (0xff31) to init the RGB. +* 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 ) ==0xff310000 ) + is_vendor = true; + } + +/* +* For ROG keyboards, make them hid compliant by +* creating one input per application. For interfaces other than +* the vendor one, disable hid-asus handlers. +*/ +if (drvdata->quirks &QUIRK_ROG_NKEY_KEYBOARD ) { +if (!is_vendor) + drvdata->quirks |=QUIRK_HANDLE_GENERIC ; + hdev->quirks |=HID_QUIRK_INPUT_PER_APP ; + } + 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" ); + +/* +* 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 ; +if (!drvdata->input) {hid_err (hdev,"Asus input not registered\n" ); ret = -ENOMEM ;
@@ -1167,6 +1200,10 @@ static const __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, {struct asus_drvdata *drvdata =hid_get_drvdata (hdev); +if (drvdata->quirks &QUIRK_HANDLE_GENERIC ) +/* NOOP on generic HID devices to avoid side effects. */ +return rdesc; +if (drvdata->quirks &QUIRK_FIX_NOTEBOOK_REPORT && *rsize >=56 && rdesc[54 ] ==0x25 && rdesc[55 ] ==0x65 ) {hid_info (hdev,"Fixing up Asus notebook report descriptor\n" );
Signature · 1 line
2.48.1
Re: [PATCH v3 02/10] HID: asus: prevent binding to all HID devices on ROG
Quoted 155 lines · Antheas Kapenekakis
ROG keyboards are HID compliant and only care about the endpoint that produces vendor events (e.g., fan mode) and has the keyboard backlight.
Therefore, handle all of the endpoints of ROG keyboards as compliant, by adding
HID_QUIRK_INPUT_PER_APPand, for devices other than the vendor one, by addingQUIRK_HANDLE_GENERICto stop mutating them.Due to
HID_QUIRK_INPUT_PER_APP, rgb register is moved into probe, as the input_* functions are called multiple times (4 for the Z13).Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 69 ++++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 16 deletions(-)drivers/hid/hid-asus.c +49
-16@@ -84,6 +84,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");#define QUIRK_MEDION_E1239T BIT(10)#define QUIRK_ROG_NKEY_KEYBOARD BIT(11)#define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12) +#define QUIRK_HANDLE_GENERIC BIT(13)#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \@@ -120,7 +121,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;@@ -326,6 +326,10 @@ static int asus_raw_event(struct hid_device *hdev, {struct asus_drvdata *drvdata =hid_get_drvdata (hdev); +if (drvdata->quirks &QUIRK_HANDLE_GENERIC ) +/* NOOP on generic HID devices to avoid side effects. */ +return 0 ; +if (drvdata->battery && data[0 ] ==BATTERY_REPORT_ID )return asus_report_battery (drvdata, data, size);@@ -774,6 +778,10 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)struct input_dev *input = hi->input;struct asus_drvdata *drvdata =hid_get_drvdata (hdev); +if (drvdata->quirks &QUIRK_HANDLE_GENERIC ) +/* NOOP on generic HID devices to avoid side effects. */ +return 0 ; +/* T100CHI uses MULTI_INPUT, bind the touchpad to the mouse hid_input */ if (drvdata->quirks &QUIRK_T100CHI && hi->report->id !=T100CHI_MOUSE_REPORT_ID )@@ -827,11 +835,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 ; }@@ -851,6 +854,10 @@ static int asus_input_mapping(struct hid_device *hdev,return -1 ; } +if (drvdata->quirks &QUIRK_HANDLE_GENERIC ) +/* NOOP on generic HID devices to avoid side effects. */ +return 0 ; +/* * Ignore a bunch of bogus collections in the T100CHI descriptor. * This avoids a bunch of non-functional hid_input devices getting @@ -901,15 +908,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 ; }@@ -1026,8 +1024,10 @@ 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; +int ret, is_vendor =0 ; drvdata =devm_kzalloc (&hdev->dev,sizeof (*drvdata),GFP_KERNEL );if (drvdata ==NULL ) {@@ -1111,12 +1111,45 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)return ret; } +/* +* Check for the vendor interface (0xff31) to init the RGB. +* 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 ) ==0xff310000 ) + is_vendor = true; + } + +/* +* For ROG keyboards, make them hid compliant by +* creating one input per application. For interfaces other than +* the vendor one, disable hid-asus handlers. +*/ +if (drvdata->quirks &QUIRK_ROG_NKEY_KEYBOARD ) { +if (!is_vendor) + drvdata->quirks |=QUIRK_HANDLE_GENERIC ; + hdev->quirks |=HID_QUIRK_INPUT_PER_APP ; + } + 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" ); + +/* +* 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 ; +if (!drvdata->input) {hid_err (hdev,"Asus input not registered\n" ); ret = -ENOMEM ;
drivers/hid/hid-asus.c static const __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, · line 1209
Quoted 10 lines
drivers/hid/hid-asus.c +4
-0@@ -1167,6 +1200,10 @@ static const __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, {struct asus_drvdata *drvdata =hid_get_drvdata (hdev); +if (drvdata->quirks &QUIRK_HANDLE_GENERIC ) +/* NOOP on generic HID devices to avoid side effects. */ +return rdesc; +if (drvdata->quirks &QUIRK_FIX_NOTEBOOK_REPORT && *rsize >=56 && rdesc[54 ] ==0x25 && rdesc[55 ] ==0x65 ) {hid_info (hdev,"Fixing up Asus notebook report descriptor\n" );
Ilpo might have some feedback. I'm otherwise happy with this.
PATCH v3 3/10 HID: Asus: add Z13 folio to generic group for multitouch to work
In reply to Antheas KapenekakisThe Asus Z13 folio has a multitouch touchpad that needs to bind to the hid-multitouch driver in order to work properly. So bind it to the HID_GROUP_GENERIC group to release the touchpad and move it to the bottom so that the comment applies to it.
While at it, change the generic KEYBOARD3 name to Z13_FOLIO.
--- drivers/hid/hid-asus.c | 6 +++--- drivers/hid/hid-ids.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-)
drivers/hid/hid-asus.c +3 -3
@@ -1319,9 +1319,6 @@ static const struct hid_device_id asus_devices[] = { {HID_USB_DEVICE (USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2 ), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, - { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, -USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD3 ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR ), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
@@ -1351,6 +1348,9 @@ static const struct hid_device_id asus_devices[] = {* Note bind to the HID_GROUP_GENERIC group, so that we only bind to the keyboard * part, while letting hid-multitouch.c handle the touchpad. */ + {HID_DEVICE (BUS_USB ,HID_GROUP_GENERIC , +USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO ), + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD) }, { }
drivers/hid/hid-ids.h +1 -1
@@ -209,7 +209,7 @@#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD 0x1866#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2 0x19b6 -#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD3 0x1a30 +#define USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO 0x1a30#define USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR 0x18c6#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY 0x1abe#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X 0x1b4c
Signature · 1 line
2.48.1
Luke D. Jones
maintainerRe: [PATCH v3 03/10] HID: Asus: add Z13 folio to generic group for multitouch to work
Quoted 10 lines · Antheas Kapenekakis
The Asus Z13 folio has a multitouch touchpad that needs to bind to the hid-multitouch driver in order to work properly. So bind it to the
HID_GROUP_GENERICgroup to release the touchpad and move it to the bottom so that the comment applies to it.While at it, change the generic KEYBOARD3 name to
Z13_FOLIO.Reviewed-by Luke D. Jones <[email protected]>Signed-off-by Antheas Kapenekakis <[email protected]>---
Never been clear on tag order but I've always put authors at top. Just something I noticed as it's different to what i do.
Quoted 41 lines
drivers/hid/hid-asus.c | 6 +++--- drivers/hid/hid-ids.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-)drivers/hid/hid-asus.c +3
-3@@ -1319,9 +1319,6 @@ static const struct hid_device_id asus_devices[] = { {HID_USB_DEVICE (USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2 ), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, - { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, -USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD3 ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR ), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },@@ -1351,6 +1348,9 @@ static const struct hid_device_id asus_devices[] = {* Note bind to the HID_GROUP_GENERIC group, so that we only bind to the keyboard * part, while letting hid-multitouch.c handle the touchpad. */ + {HID_DEVICE (BUS_USB ,HID_GROUP_GENERIC , +USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO ), + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD) }, { }drivers/hid/hid-ids.h +1
-1@@ -209,7 +209,7 @@#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD 0x1866#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2 0x19b6 -#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD3 0x1a30 +#define USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO 0x1a30#define USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR 0x18c6#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY 0x1abe#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X 0x1b4c
Re: [PATCH v3 03/10] HID: Asus: add Z13 folio to generic group for multitouch to work
Quoted 7 lines · Luke D. Jones
Quoted 5 lines · Antheas Kapenekakis
The Asus Z13 folio has a multitouch touchpad that needs to bind to the hid-multitouch driver in order to work properly. So bind it to the
HID_GROUP_GENERICgroup to release the touchpad and move it to the bottom so that the comment applies to it.
Quoted 8 lines
Quoted 5 lines
While at it, change the generic KEYBOARD3 name to
Z13_FOLIO.Reviewed-by Luke D. Jones <[email protected]>Signed-off-by Antheas Kapenekakis <[email protected]>---
Never been clear on tag order but I've always put authors at top. Just something I noticed as it's different to what i do.
Tag was added by b4
Antheas
Quoted 42 lines
Quoted 41 lines
drivers/hid/hid-asus.c | 6 +++--- drivers/hid/hid-ids.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-)drivers/hid/hid-asus.c +3
-3@@ -1319,9 +1319,6 @@ static const struct hid_device_id asus_devices[] = { {HID_USB_DEVICE (USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2 ), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, - { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, -USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD3 ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR ), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },@@ -1351,6 +1348,9 @@ static const struct hid_device_id asus_devices[] = {* Note bind to the HID_GROUP_GENERIC group, so that we only bind to the keyboard * part, while letting hid-multitouch.c handle the touchpad. */ + {HID_DEVICE (BUS_USB ,HID_GROUP_GENERIC , +USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO ), + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD) }, { }drivers/hid/hid-ids.h +1
-1@@ -209,7 +209,7 @@#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD 0x1866#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2 0x19b6 -#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD3 0x1a30 +#define USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO 0x1a30#define USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR 0x18c6#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY 0x1abe#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X 0x1b4c
PATCH v3 4/10 platform/x86: asus-wmi: Add support for multiple kbd RGB handlers
In reply to Antheas KapenekakisSome 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.
--- drivers/platform/x86/asus-wmi.c | 113 ++++++++++++++++++--- include/linux/platform_data/x86/asus-wmi.h | 16 +++ 2 files changed, 117 insertions(+), 12 deletions(-)
drivers/platform/x86/asus-wmi.c +101 -12
@@ -254,6 +254,8 @@ struct asus_wmi {int tpd_led_wk;struct led_classdev kbd_led;int kbd_led_wk; +bool kbd_led_avail; +bool kbd_led_registered;struct led_classdev lightbar_led;int lightbar_led_wk;struct led_classdev micmute_led;
@@ -1487,6 +1489,53 @@ static void asus_wmi_battery_exit(struct asus_wmi *asus)/* LEDs ***********************************************************************/ +struct asus_hid_ref { +struct list_head listeners; +struct asus_wmi *asus; +spinlock_t lock; +}; + +struct asus_hid_ref asus_ref = { + .listeners =LIST_HEAD_INIT (asus_ref.listeners), + .asus =NULL , + .lock =__SPIN_LOCK_UNLOCKED (asus_ref.lock), +}; + +int asus_hid_register_listener (struct asus_hid_listener *bdev) +{ +unsigned long flags; +int ret =0 ; + +spin_lock_irqsave (&asus_ref.lock, flags); +list_add_tail (&bdev->list, &asus_ref.listeners); +if (asus_ref.asus) { +if (asus_ref.asus->kbd_led_registered && asus_ref.asus->kbd_led_wk >=0 ) + bdev->brightness_set (bdev, asus_ref.asus->kbd_led_wk); + +if (!asus_ref.asus->kbd_led_registered) { + ret =led_classdev_register ( + &asus_ref.asus->platform_device->dev, + &asus_ref.asus->kbd_led); +if (!ret) + asus_ref.asus->kbd_led_registered = true; + } + } +spin_unlock_irqrestore (&asus_ref.lock, flags); + +return ret; +} +EXPORT_SYMBOL_GPL (asus_hid_register_listener); + +void asus_hid_unregister_listener (struct asus_hid_listener *bdev) +{ +unsigned long flags; + +spin_lock_irqsave (&asus_ref.lock, flags); +list_del (&bdev->list); +spin_unlock_irqrestore (&asus_ref.lock, flags); +} +EXPORT_SYMBOL_GPL (asus_hid_unregister_listener); +/* * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED
@@ -1566,6 +1615,7 @@ 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;
@@ -1573,25 +1623,39 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) max_level = asus->kbd_led.max_brightness; asus->kbd_led_wk =clamp_val (value,0 , max_level); -kbd_led_update (asus); + +if (asus->kbd_led_avail) +kbd_led_update (asus); + +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,enum led_brightness value) { +unsigned long flags; +/* Prevent disabling keyboard backlight on module unregister */ if (led_cdev->flags &LED_UNREGISTERING )return ; +spin_lock_irqsave (&asus_ref.lock, flags);do_kbd_led_set (led_cdev, value); +spin_unlock_irqrestore (&asus_ref.lock, flags); }static void kbd_led_set_by_kbd (struct asus_wmi *asus,enum led_brightness value) { -struct led_classdev *led_cdev = &asus->kbd_led; +struct led_classdev *led_cdev; +unsigned long flags; + +spin_lock_irqsave (&asus_ref.lock, flags); + led_cdev = &asus->kbd_led;do_kbd_led_set (led_cdev, value);led_classdev_notify_brightness_hw_changed (led_cdev, asus->kbd_led_wk); +spin_unlock_irqrestore (&asus_ref.lock, flags); }static enum led_brightness kbd_led_get (struct led_classdev *led_cdev)
@@ -1601,6 +1665,9 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) asus = container_of(led_cdev, struct asus_wmi, kbd_led); +if (!asus->kbd_led_avail) +return asus->kbd_led_wk; + retval =kbd_led_read (asus, &value,NULL );if (retval <0 )return retval;
@@ -1716,7 +1783,14 @@ 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); +unsigned long flags; + +spin_lock_irqsave (&asus_ref.lock, flags); + asus_ref.asus =NULL ; +if (asus->kbd_led_registered) +led_classdev_unregister (&asus->kbd_led); +spin_unlock_irqrestore (&asus_ref.lock, flags); +led_classdev_unregister (&asus->tpd_led);led_classdev_unregister (&asus->wlan_led);led_classdev_unregister (&asus->lightbar_led);
@@ -1730,6 +1804,8 @@ static void asus_wmi_led_exit(struct asus_wmi *asus)static int asus_wmi_led_init (struct asus_wmi *asus) {int rv =0 , num_rgb_groups =0 , led_val; +unsigned long flags; +bool has_listeners;if (asus->kbd_rgb_dev) kbd_rgb_mode_groups[num_rgb_groups++] = &kbd_rgb_mode_group;
@@ -1754,24 +1830,37 @@ 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.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 ); + +if (asus->kbd_led_avail) 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 ; +else + asus->kbd_led_wk =-1 ; + +if (asus->kbd_led_avail && num_rgb_groups !=0 ) + asus->kbd_led.groups = kbd_rgb_mode_groups; -if (num_rgb_groups !=0 ) - asus->kbd_led.groups = kbd_rgb_mode_groups; +spin_lock_irqsave (&asus_ref.lock, flags); + has_listeners = !list_empty (&asus_ref.listeners); +spin_unlock_irqrestore (&asus_ref.lock, flags); +if (asus->kbd_led_avail || has_listeners) { rv =led_classdev_register (&asus->platform_device->dev, &asus->kbd_led);if (rv) goto error; + asus->kbd_led_registered = true; } +spin_lock_irqsave (&asus_ref.lock, flags); + asus_ref.asus = asus; +spin_unlock_irqrestore (&asus_ref.lock, flags); +if (asus_wmi_dev_is_present (asus,ASUS_WMI_DEVID_WIRELESS_LED ) && (asus->driver->quirks->wapf >0 )) {INIT_WORK (&asus->wlan_led_work, wlan_led_update);
include/linux/platform_data/x86/asus-wmi.h +16 -0
@@ -157,14 +157,30 @@#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00#define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F +struct asus_hid_listener { +struct list_head list; +void (*brightness_set)(struct asus_hid_listener *listener,int brightness); +}; +#if IS_REACHABLE (CONFIG_ASUS_WMI )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 int asus_wmi_evaluate_method (u32 method_id,u32 arg0,u32 arg1,u32 *retval) {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 */
Signature · 1 line
2.48.1
Re: [PATCH v3 04/10] platform/x86: asus-wmi: Add support for multiple kbd RGB handlers
Quoted 148 lines · 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.
Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/platform/x86/asus-wmi.c | 113 ++++++++++++++++++--- include/linux/platform_data/x86/asus-wmi.h | 16 +++ 2 files changed, 117 insertions(+), 12 deletions(-)drivers/platform/x86/asus-wmi.c +70
-3@@ -254,6 +254,8 @@ struct asus_wmi {int tpd_led_wk;struct led_classdev kbd_led;int kbd_led_wk; +bool kbd_led_avail; +bool kbd_led_registered;struct led_classdev lightbar_led;int lightbar_led_wk;struct led_classdev micmute_led;@@ -1487,6 +1489,53 @@ static void asus_wmi_battery_exit(struct asus_wmi *asus)/* LEDs ***********************************************************************/ +struct asus_hid_ref { +struct list_head listeners; +struct asus_wmi *asus; +spinlock_t lock; +}; + +struct asus_hid_ref asus_ref = { + .listeners =LIST_HEAD_INIT (asus_ref.listeners), + .asus =NULL , + .lock =__SPIN_LOCK_UNLOCKED (asus_ref.lock), +}; + +int asus_hid_register_listener (struct asus_hid_listener *bdev) +{ +unsigned long flags; +int ret =0 ; + +spin_lock_irqsave (&asus_ref.lock, flags); +list_add_tail (&bdev->list, &asus_ref.listeners); +if (asus_ref.asus) { +if (asus_ref.asus->kbd_led_registered && asus_ref.asus->kbd_led_wk >=0 ) + bdev->brightness_set (bdev, asus_ref.asus->kbd_led_wk); + +if (!asus_ref.asus->kbd_led_registered) { + ret =led_classdev_register ( + &asus_ref.asus->platform_device->dev, + &asus_ref.asus->kbd_led); +if (!ret) + asus_ref.asus->kbd_led_registered = true; + } + } +spin_unlock_irqrestore (&asus_ref.lock, flags); + +return ret; +} +EXPORT_SYMBOL_GPL (asus_hid_register_listener); + +void asus_hid_unregister_listener (struct asus_hid_listener *bdev) +{ +unsigned long flags; + +spin_lock_irqsave (&asus_ref.lock, flags); +list_del (&bdev->list); +spin_unlock_irqrestore (&asus_ref.lock, flags); +} +EXPORT_SYMBOL_GPL (asus_hid_unregister_listener); +/* * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED @@ -1566,6 +1615,7 @@ 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;@@ -1573,25 +1623,39 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) max_level = asus->kbd_led.max_brightness; asus->kbd_led_wk =clamp_val (value,0 , max_level); -kbd_led_update (asus); + +if (asus->kbd_led_avail) +kbd_led_update (asus); + +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,enum led_brightness value) { +unsigned long flags; +/* Prevent disabling keyboard backlight on module unregister */ if (led_cdev->flags &LED_UNREGISTERING )return ; +spin_lock_irqsave (&asus_ref.lock, flags);do_kbd_led_set (led_cdev, value); +spin_unlock_irqrestore (&asus_ref.lock, flags); }static void kbd_led_set_by_kbd (struct asus_wmi *asus,enum led_brightness value) { -struct led_classdev *led_cdev = &asus->kbd_led; +struct led_classdev *led_cdev; +unsigned long flags; + +spin_lock_irqsave (&asus_ref.lock, flags); + led_cdev = &asus->kbd_led;do_kbd_led_set (led_cdev, value);led_classdev_notify_brightness_hw_changed (led_cdev, asus->kbd_led_wk); +spin_unlock_irqrestore (&asus_ref.lock, flags); }static enum led_brightness kbd_led_get (struct led_classdev *led_cdev)@@ -1601,6 +1665,9 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) asus = container_of(led_cdev, struct asus_wmi, kbd_led); +if (!asus->kbd_led_avail) +return asus->kbd_led_wk; + retval =kbd_led_read (asus, &value,NULL );if (retval <0 )return retval;@@ -1716,7 +1783,14 @@ 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); +unsigned long flags;
drivers/platform/x86/asus-wmi.c static int camera_led_set(struct led_classdev *led_cdev, · line 1796
Quoted 10 lines
drivers/platform/x86/asus-wmi.c +7
-0+ +spin_lock_irqsave (&asus_ref.lock, flags); + asus_ref.asus =NULL ; +if (asus->kbd_led_registered) +led_classdev_unregister (&asus->kbd_led); +spin_unlock_irqrestore (&asus_ref.lock, flags); +led_classdev_unregister (&asus->tpd_led);led_classdev_unregister (&asus->wlan_led);led_classdev_unregister (&asus->lightbar_led);
I'm getting the following on module unload:
[ 247.540357] BUG: sleeping function called from invalid context at kernel/locking/rwsem.c:1576 [ 247.540360] in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 37685, name: rmmod [ 247.540361] preempt_count: 1, expected: 0 [ 247.540361] RCU nest depth: 0, expected: 0 [ 247.540362] CPU: 6 UID: 0 PID: 37685 Comm: rmmod Not tainted 6.14.0-rc7+ #164 [ 247.540364] Hardware name: ASUSTeK COMPUTER INC. ROG Zephyrus M16 GU604VY_GU604VY_00130747B/GU604VY, BIOS GU604VY.313 08/13/2024 [ 247.540365] Call Trace: [ 247.540366] <TASK> [ 247.540366] dump_stack_lvl+0x5d/0x80 [ 247.540372] __might_resched.cold+0xba/0xc9 [ 247.540374] down_write+0x19/0x70 [ 247.540375] led_classdev_unregister+0x31/0x110 [ 247.540378] asus_wmi_led_exit+0x96/0xa0 [asus_wmi] [ 247.540382] asus_wmi_remove+0x71/0xf0 [asus_wmi] [ 247.540385] platform_remove+0x1b/0x30 [ 247.540386] device_release_driver_internal+0x197/0x200 [ 247.540388] bus_remove_device+0xc4/0x130 [ 247.540389] device_del+0x15f/0x3e0 [ 247.540391] platform_device_del+0x20/0x80 [ 247.540392] platform_device_unregister+0xd/0x30 [ 247.540393] asus_wmi_unregister_driver+0x20/0x40 [asus_wmi] [ 247.540395] __do_sys_delete_module+0x1d1/0x310 [ 247.540397] do_syscall_64+0x82/0x160 [ 247.540398] ? syscall_exit_to_user_mode+0x10/0x1e0 [ 247.540399] ? do_syscall_64+0x8e/0x160 [ 247.540399] ? syscall_exit_to_user_mode+0x10/0x1e0 [ 247.540400] ? do_syscall_64+0x8e/0x160 [ 247.540401] ? syscall_exit_to_user_mode+0x10/0x1e0 [ 247.540401] ? do_syscall_64+0x8e/0x160 [ 247.540402] ? exc_page_fault+0x6a/0xc0 [ 247.540402] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 247.540404] RIP: 0033:0x7fc1164d3bcb
The error message indicates the code is in an atomic context (in_atomic(): 1), with interrupts disabled (irqs_disabled(): 1).
I would move:
drivers/platform/x86/asus-wmi.c static int camera_led_set(struct led_classdev *led_cdev, · line 1791
Quoted 2 lines
drivers/platform/x86/asus-wmi.c +2
-0+if (asus->kbd_led_registered) +led_classdev_unregister (&asus->kbd_led);
to outside of the spinlock. I quickly tested this and it fixes that particular issue.
Quoted 81 lines
drivers/platform/x86/asus-wmi.c +24
-9@@ -1730,6 +1804,8 @@ static void asus_wmi_led_exit(struct asus_wmi *asus)static int asus_wmi_led_init (struct asus_wmi *asus) {int rv =0 , num_rgb_groups =0 , led_val; +unsigned long flags; +bool has_listeners;if (asus->kbd_rgb_dev) kbd_rgb_mode_groups[num_rgb_groups++] = &kbd_rgb_mode_group;@@ -1754,24 +1830,37 @@ 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.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 ); + +if (asus->kbd_led_avail) 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 ; +else + asus->kbd_led_wk =-1 ; + +if (asus->kbd_led_avail && num_rgb_groups !=0 ) + asus->kbd_led.groups = kbd_rgb_mode_groups; -if (num_rgb_groups !=0 ) - asus->kbd_led.groups = kbd_rgb_mode_groups; +spin_lock_irqsave (&asus_ref.lock, flags); + has_listeners = !list_empty (&asus_ref.listeners); +spin_unlock_irqrestore (&asus_ref.lock, flags); +if (asus->kbd_led_avail || has_listeners) { rv =led_classdev_register (&asus->platform_device->dev, &asus->kbd_led);if (rv) goto error; + asus->kbd_led_registered = true; } +spin_lock_irqsave (&asus_ref.lock, flags); + asus_ref.asus = asus; +spin_unlock_irqrestore (&asus_ref.lock, flags); +if (asus_wmi_dev_is_present (asus,ASUS_WMI_DEVID_WIRELESS_LED ) && (asus->driver->quirks->wapf >0 )) {INIT_WORK (&asus->wlan_led_work, wlan_led_update);include/linux/platform_data/x86/asus-wmi.h +9
-0@@ -157,14 +157,30 @@#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00#define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F +struct asus_hid_listener { +struct list_head list; +void (*brightness_set)(struct asus_hid_listener *listener,int brightness); +}; +#if IS_REACHABLE (CONFIG_ASUS_WMI )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 int asus_wmi_evaluate_method (u32 method_id,u32 arg0,u32 arg1,u32 *retval) {return -ENODEV ; } +
include/linux/platform_data/x86/asus-wmi.h line 186
Quoted 10 lines
include/linux/platform_data/x86/asus-wmi.h +7
-0+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 */
I did additional testing on the older 0x1866 which has WMI plus HID brightness ability and it appears to work fine. I might add debug logging later to gie us some insight in to this exact situation though.
Fixing the spinlock, I'll add my tags:
Re: [PATCH v3 04/10] platform/x86: asus-wmi: Add support for multiple kbd RGB handlers
In reply to Antheas KapenekakisHi Antheas,
Note not a full review, just taking a generic look at the new API between asus-wmi and asus-hid.
Quoted 188 lines · 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.
Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/platform/x86/asus-wmi.c | 113 ++++++++++++++++++--- include/linux/platform_data/x86/asus-wmi.h | 16 +++ 2 files changed, 117 insertions(+), 12 deletions(-)drivers/platform/x86/asus-wmi.c +88
-10@@ -254,6 +254,8 @@ struct asus_wmi {int tpd_led_wk;struct led_classdev kbd_led;int kbd_led_wk; +bool kbd_led_avail; +bool kbd_led_registered;struct led_classdev lightbar_led;int lightbar_led_wk;struct led_classdev micmute_led;@@ -1487,6 +1489,53 @@ static void asus_wmi_battery_exit(struct asus_wmi *asus)/* LEDs ***********************************************************************/ +struct asus_hid_ref { +struct list_head listeners; +struct asus_wmi *asus; +spinlock_t lock; +}; + +struct asus_hid_ref asus_ref = { + .listeners =LIST_HEAD_INIT (asus_ref.listeners), + .asus =NULL , + .lock =__SPIN_LOCK_UNLOCKED (asus_ref.lock), +}; + +int asus_hid_register_listener (struct asus_hid_listener *bdev) +{ +unsigned long flags; +int ret =0 ; + +spin_lock_irqsave (&asus_ref.lock, flags); +list_add_tail (&bdev->list, &asus_ref.listeners); +if (asus_ref.asus) { +if (asus_ref.asus->kbd_led_registered && asus_ref.asus->kbd_led_wk >=0 ) + bdev->brightness_set (bdev, asus_ref.asus->kbd_led_wk); + +if (!asus_ref.asus->kbd_led_registered) { + ret =led_classdev_register ( + &asus_ref.asus->platform_device->dev, + &asus_ref.asus->kbd_led); +if (!ret) + asus_ref.asus->kbd_led_registered = true; + } + } +spin_unlock_irqrestore (&asus_ref.lock, flags); + +return ret; +} +EXPORT_SYMBOL_GPL (asus_hid_register_listener); + +void asus_hid_unregister_listener (struct asus_hid_listener *bdev) +{ +unsigned long flags; + +spin_lock_irqsave (&asus_ref.lock, flags); +list_del (&bdev->list); +spin_unlock_irqrestore (&asus_ref.lock, flags); +} +EXPORT_SYMBOL_GPL (asus_hid_unregister_listener); +/* * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED @@ -1566,6 +1615,7 @@ 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;@@ -1573,25 +1623,39 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) max_level = asus->kbd_led.max_brightness; asus->kbd_led_wk =clamp_val (value,0 , max_level); -kbd_led_update (asus); + +if (asus->kbd_led_avail) +kbd_led_update (asus); + +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,enum led_brightness value) { +unsigned long flags; +/* Prevent disabling keyboard backlight on module unregister */ if (led_cdev->flags &LED_UNREGISTERING )return ; +spin_lock_irqsave (&asus_ref.lock, flags);do_kbd_led_set (led_cdev, value); +spin_unlock_irqrestore (&asus_ref.lock, flags); }static void kbd_led_set_by_kbd (struct asus_wmi *asus,enum led_brightness value) { -struct led_classdev *led_cdev = &asus->kbd_led; +struct led_classdev *led_cdev; +unsigned long flags; + +spin_lock_irqsave (&asus_ref.lock, flags); + led_cdev = &asus->kbd_led;do_kbd_led_set (led_cdev, value);led_classdev_notify_brightness_hw_changed (led_cdev, asus->kbd_led_wk); +spin_unlock_irqrestore (&asus_ref.lock, flags); }static enum led_brightness kbd_led_get (struct led_classdev *led_cdev)@@ -1601,6 +1665,9 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) asus = container_of(led_cdev, struct asus_wmi, kbd_led); +if (!asus->kbd_led_avail) +return asus->kbd_led_wk; + retval =kbd_led_read (asus, &value,NULL );if (retval <0 )return retval;@@ -1716,7 +1783,14 @@ 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); +unsigned long flags; + +spin_lock_irqsave (&asus_ref.lock, flags); + asus_ref.asus =NULL ; +if (asus->kbd_led_registered) +led_classdev_unregister (&asus->kbd_led); +spin_unlock_irqrestore (&asus_ref.lock, flags); +led_classdev_unregister (&asus->tpd_led);led_classdev_unregister (&asus->wlan_led);led_classdev_unregister (&asus->lightbar_led);@@ -1730,6 +1804,8 @@ static void asus_wmi_led_exit(struct asus_wmi *asus)static int asus_wmi_led_init (struct asus_wmi *asus) {int rv =0 , num_rgb_groups =0 , led_val; +unsigned long flags; +bool has_listeners;if (asus->kbd_rgb_dev) kbd_rgb_mode_groups[num_rgb_groups++] = &kbd_rgb_mode_group;@@ -1754,24 +1830,37 @@ 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.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 ); + +if (asus->kbd_led_avail) 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 ; +else
drivers/platform/x86/asus-wmi.c static int asus_wmi_led_init(struct asus_wmi *asus) · line 1850
Quoted 10 lines
drivers/platform/x86/asus-wmi.c +7
-2+ asus->kbd_led_wk =-1 ; + +if (asus->kbd_led_avail && num_rgb_groups !=0 ) + asus->kbd_led.groups = kbd_rgb_mode_groups; -if (num_rgb_groups !=0 ) - asus->kbd_led.groups = kbd_rgb_mode_groups; +spin_lock_irqsave (&asus_ref.lock, flags); + has_listeners = !list_empty (&asus_ref.listeners); +spin_unlock_irqrestore (&asus_ref.lock, flags);
It seems to me that you should also call brightness_set() on all the kbds already in the list so that their brightness gets synced with the wmi kbd-backlight brightness when the wmi driver loads later then the hid driver ?
drivers/platform/x86/asus-wmi.c static int asus_wmi_led_init(struct asus_wmi *asus) · line 1861
Quoted 11 lines
drivers/platform/x86/asus-wmi.c +4
-0+if (asus->kbd_led_avail || has_listeners) { rv =led_classdev_register (&asus->platform_device->dev, &asus->kbd_led);if (rv) goto error; + asus->kbd_led_registered = true; } +spin_lock_irqsave (&asus_ref.lock, flags); + asus_ref.asus = asus;
There is race here where a hid keyboard might show up between the 2 places you take the lock, in that case if there is no wmi kbd-backlight then you will not register the led_classdev when asus_hid_register_listener() gets called between the unlock and the lock... I'm not sure what the best way is to fix this.
Regards,
Hans
Quoted 40 lines
drivers/platform/x86/asus-wmi.c +2
-0+spin_unlock_irqrestore (&asus_ref.lock, flags); +if (asus_wmi_dev_is_present (asus,ASUS_WMI_DEVID_WIRELESS_LED ) && (asus->driver->quirks->wapf >0 )) {INIT_WORK (&asus->wlan_led_work, wlan_led_update);include/linux/platform_data/x86/asus-wmi.h +16
-0@@ -157,14 +157,30 @@#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00#define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F +struct asus_hid_listener { +struct list_head list; +void (*brightness_set)(struct asus_hid_listener *listener,int brightness); +}; +#if IS_REACHABLE (CONFIG_ASUS_WMI )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 int asus_wmi_evaluate_method (u32 method_id,u32 arg0,u32 arg1,u32 *retval) {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 */
Re: [PATCH v3 04/10] platform/x86: asus-wmi: Add support for multiple kbd RGB handlers
Quoted 200 lines · Hans de Goede
Hi Antheas,
Note not a full review, just taking a generic look at the new API between asus-wmi and asus-hid.
Quoted 193 lines · 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.
Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/platform/x86/asus-wmi.c | 113 ++++++++++++++++++--- include/linux/platform_data/x86/asus-wmi.h | 16 +++ 2 files changed, 117 insertions(+), 12 deletions(-)drivers/platform/x86/asus-wmi.c +92
-10@@ -254,6 +254,8 @@ struct asus_wmi {int tpd_led_wk;struct led_classdev kbd_led;int kbd_led_wk; +bool kbd_led_avail; +bool kbd_led_registered;struct led_classdev lightbar_led;int lightbar_led_wk;struct led_classdev micmute_led;@@ -1487,6 +1489,53 @@ static void asus_wmi_battery_exit(struct asus_wmi *asus)/* LEDs ***********************************************************************/ +struct asus_hid_ref { +struct list_head listeners; +struct asus_wmi *asus; +spinlock_t lock; +}; + +struct asus_hid_ref asus_ref = { + .listeners =LIST_HEAD_INIT (asus_ref.listeners), + .asus =NULL , + .lock =__SPIN_LOCK_UNLOCKED (asus_ref.lock), +}; + +int asus_hid_register_listener (struct asus_hid_listener *bdev) +{ +unsigned long flags; +int ret =0 ; + +spin_lock_irqsave (&asus_ref.lock, flags); +list_add_tail (&bdev->list, &asus_ref.listeners); +if (asus_ref.asus) { +if (asus_ref.asus->kbd_led_registered && asus_ref.asus->kbd_led_wk >=0 ) + bdev->brightness_set (bdev, asus_ref.asus->kbd_led_wk); + +if (!asus_ref.asus->kbd_led_registered) { + ret =led_classdev_register ( + &asus_ref.asus->platform_device->dev, + &asus_ref.asus->kbd_led); +if (!ret) + asus_ref.asus->kbd_led_registered = true; + } + } +spin_unlock_irqrestore (&asus_ref.lock, flags); + +return ret; +} +EXPORT_SYMBOL_GPL (asus_hid_register_listener); + +void asus_hid_unregister_listener (struct asus_hid_listener *bdev) +{ +unsigned long flags; + +spin_lock_irqsave (&asus_ref.lock, flags); +list_del (&bdev->list); +spin_unlock_irqrestore (&asus_ref.lock, flags); +} +EXPORT_SYMBOL_GPL (asus_hid_unregister_listener); +/* * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED @@ -1566,6 +1615,7 @@ 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;@@ -1573,25 +1623,39 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) max_level = asus->kbd_led.max_brightness; asus->kbd_led_wk =clamp_val (value,0 , max_level); -kbd_led_update (asus); + +if (asus->kbd_led_avail) +kbd_led_update (asus); + +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,enum led_brightness value) { +unsigned long flags; +/* Prevent disabling keyboard backlight on module unregister */ if (led_cdev->flags &LED_UNREGISTERING )return ; +spin_lock_irqsave (&asus_ref.lock, flags);do_kbd_led_set (led_cdev, value); +spin_unlock_irqrestore (&asus_ref.lock, flags); }static void kbd_led_set_by_kbd (struct asus_wmi *asus,enum led_brightness value) { -struct led_classdev *led_cdev = &asus->kbd_led; +struct led_classdev *led_cdev; +unsigned long flags; + +spin_lock_irqsave (&asus_ref.lock, flags); + led_cdev = &asus->kbd_led;do_kbd_led_set (led_cdev, value);led_classdev_notify_brightness_hw_changed (led_cdev, asus->kbd_led_wk); +spin_unlock_irqrestore (&asus_ref.lock, flags); }static enum led_brightness kbd_led_get (struct led_classdev *led_cdev)@@ -1601,6 +1665,9 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) asus = container_of(led_cdev, struct asus_wmi, kbd_led); +if (!asus->kbd_led_avail) +return asus->kbd_led_wk; + retval =kbd_led_read (asus, &value,NULL );if (retval <0 )return retval;@@ -1716,7 +1783,14 @@ 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); +unsigned long flags; + +spin_lock_irqsave (&asus_ref.lock, flags); + asus_ref.asus =NULL ; +if (asus->kbd_led_registered) +led_classdev_unregister (&asus->kbd_led); +spin_unlock_irqrestore (&asus_ref.lock, flags); +led_classdev_unregister (&asus->tpd_led);led_classdev_unregister (&asus->wlan_led);led_classdev_unregister (&asus->lightbar_led);@@ -1730,6 +1804,8 @@ static void asus_wmi_led_exit(struct asus_wmi *asus)static int asus_wmi_led_init (struct asus_wmi *asus) {int rv =0 , num_rgb_groups =0 , led_val; +unsigned long flags; +bool has_listeners;if (asus->kbd_rgb_dev) kbd_rgb_mode_groups[num_rgb_groups++] = &kbd_rgb_mode_group;@@ -1754,24 +1830,37 @@ 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.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 ); + +if (asus->kbd_led_avail) 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 ; +else + asus->kbd_led_wk =-1 ; + +if (asus->kbd_led_avail && num_rgb_groups !=0 ) + asus->kbd_led.groups = kbd_rgb_mode_groups;
Quoted 10 lines
drivers/platform/x86/asus-wmi.c static int asus_wmi_led_init(struct asus_wmi *asus) · line 1850
Quoted 5 lines
drivers/platform/x86/asus-wmi.c +3
-2-if (num_rgb_groups !=0 ) - asus->kbd_led.groups = kbd_rgb_mode_groups; +spin_lock_irqsave (&asus_ref.lock, flags); + has_listeners = !list_empty (&asus_ref.listeners); +spin_unlock_irqrestore (&asus_ref.lock, flags);It seems to me that you should also call
brightness_set()on all the kbds already in the list so that their brightness gets synced with the wmi kbd-backlight brightness when the wmi driver loads later then the hid driver ?
You raise a good point here. Let me think about it.
But yes, if we do a notify when kbd_led_wk != -1 if the hid device connects after, we should do it for the before case too.
Quoted 9 lines
Quoted 9 lines
drivers/platform/x86/asus-wmi.c +2
-0+if (asus->kbd_led_avail || has_listeners) { rv =led_classdev_register (&asus->platform_device->dev, &asus->kbd_led);if (rv) goto error; + asus->kbd_led_registered = true; }
Quoted 8 lines
drivers/platform/x86/asus-wmi.c static int asus_wmi_led_init(struct asus_wmi *asus) · line 1860
Quoted 2 lines
drivers/platform/x86/asus-wmi.c +2
-0+spin_lock_irqsave (&asus_ref.lock, flags); + asus_ref.asus = asus;There is race here where a hid keyboard might show up between the 2 places you take the lock, in that case if there is no wmi kbd-backlight then you will not register the
led_classdevwhenasus_hid_register_listener()gets called between the unlock and the lock... I'm not sure what the best way is to fix this.
Thanks for catching that, probably a recheck:
has_listeners = !list_empty(&asus_ref.listeners);
if (has_listeners) goto register;
Quoted 47 lines
Regards,
Hans
Quoted 40 lines
drivers/platform/x86/asus-wmi.c +2
-0+spin_unlock_irqrestore (&asus_ref.lock, flags); +if (asus_wmi_dev_is_present (asus,ASUS_WMI_DEVID_WIRELESS_LED ) && (asus->driver->quirks->wapf >0 )) {INIT_WORK (&asus->wlan_led_work, wlan_led_update);include/linux/platform_data/x86/asus-wmi.h +16
-0@@ -157,14 +157,30 @@#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00#define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F +struct asus_hid_listener { +struct list_head list; +void (*brightness_set)(struct asus_hid_listener *listener,int brightness); +}; +#if IS_REACHABLE (CONFIG_ASUS_WMI )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 int asus_wmi_evaluate_method (u32 method_id,u32 arg0,u32 arg1,u32 *retval) {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 */
Re: [PATCH v3 04/10] platform/x86: asus-wmi: Add support for multiple kbd RGB handlers
Hi,
Quoted 235 lines · Antheas Kapenekakis
Quoted 200 lines · Hans de Goede
Hi Antheas,
Note not a full review, just taking a generic look at the new API between asus-wmi and asus-hid.
Quoted 193 lines · 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.
Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/platform/x86/asus-wmi.c | 113 ++++++++++++++++++--- include/linux/platform_data/x86/asus-wmi.h | 16 +++ 2 files changed, 117 insertions(+), 12 deletions(-)drivers/platform/x86/asus-wmi.c +92
-10@@ -254,6 +254,8 @@ struct asus_wmi {int tpd_led_wk;struct led_classdev kbd_led;int kbd_led_wk; +bool kbd_led_avail; +bool kbd_led_registered;struct led_classdev lightbar_led;int lightbar_led_wk;struct led_classdev micmute_led;@@ -1487,6 +1489,53 @@ static void asus_wmi_battery_exit(struct asus_wmi *asus)/* LEDs ***********************************************************************/ +struct asus_hid_ref { +struct list_head listeners; +struct asus_wmi *asus; +spinlock_t lock; +}; + +struct asus_hid_ref asus_ref = { + .listeners =LIST_HEAD_INIT (asus_ref.listeners), + .asus =NULL , + .lock =__SPIN_LOCK_UNLOCKED (asus_ref.lock), +}; + +int asus_hid_register_listener (struct asus_hid_listener *bdev) +{ +unsigned long flags; +int ret =0 ; + +spin_lock_irqsave (&asus_ref.lock, flags); +list_add_tail (&bdev->list, &asus_ref.listeners); +if (asus_ref.asus) { +if (asus_ref.asus->kbd_led_registered && asus_ref.asus->kbd_led_wk >=0 ) + bdev->brightness_set (bdev, asus_ref.asus->kbd_led_wk); + +if (!asus_ref.asus->kbd_led_registered) { + ret =led_classdev_register ( + &asus_ref.asus->platform_device->dev, + &asus_ref.asus->kbd_led); +if (!ret) + asus_ref.asus->kbd_led_registered = true; + } + } +spin_unlock_irqrestore (&asus_ref.lock, flags); + +return ret; +} +EXPORT_SYMBOL_GPL (asus_hid_register_listener); + +void asus_hid_unregister_listener (struct asus_hid_listener *bdev) +{ +unsigned long flags; + +spin_lock_irqsave (&asus_ref.lock, flags); +list_del (&bdev->list); +spin_unlock_irqrestore (&asus_ref.lock, flags); +} +EXPORT_SYMBOL_GPL (asus_hid_unregister_listener); +/* * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED @@ -1566,6 +1615,7 @@ 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;@@ -1573,25 +1623,39 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) max_level = asus->kbd_led.max_brightness; asus->kbd_led_wk =clamp_val (value,0 , max_level); -kbd_led_update (asus); + +if (asus->kbd_led_avail) +kbd_led_update (asus); + +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,enum led_brightness value) { +unsigned long flags; +/* Prevent disabling keyboard backlight on module unregister */ if (led_cdev->flags &LED_UNREGISTERING )return ; +spin_lock_irqsave (&asus_ref.lock, flags);do_kbd_led_set (led_cdev, value); +spin_unlock_irqrestore (&asus_ref.lock, flags); }static void kbd_led_set_by_kbd (struct asus_wmi *asus,enum led_brightness value) { -struct led_classdev *led_cdev = &asus->kbd_led; +struct led_classdev *led_cdev; +unsigned long flags; + +spin_lock_irqsave (&asus_ref.lock, flags); + led_cdev = &asus->kbd_led;do_kbd_led_set (led_cdev, value);led_classdev_notify_brightness_hw_changed (led_cdev, asus->kbd_led_wk); +spin_unlock_irqrestore (&asus_ref.lock, flags); }static enum led_brightness kbd_led_get (struct led_classdev *led_cdev)@@ -1601,6 +1665,9 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) asus = container_of(led_cdev, struct asus_wmi, kbd_led); +if (!asus->kbd_led_avail) +return asus->kbd_led_wk; + retval =kbd_led_read (asus, &value,NULL );if (retval <0 )return retval;@@ -1716,7 +1783,14 @@ 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); +unsigned long flags; + +spin_lock_irqsave (&asus_ref.lock, flags); + asus_ref.asus =NULL ; +if (asus->kbd_led_registered) +led_classdev_unregister (&asus->kbd_led); +spin_unlock_irqrestore (&asus_ref.lock, flags); +led_classdev_unregister (&asus->tpd_led);led_classdev_unregister (&asus->wlan_led);led_classdev_unregister (&asus->lightbar_led);@@ -1730,6 +1804,8 @@ static void asus_wmi_led_exit(struct asus_wmi *asus)static int asus_wmi_led_init (struct asus_wmi *asus) {int rv =0 , num_rgb_groups =0 , led_val; +unsigned long flags; +bool has_listeners;if (asus->kbd_rgb_dev) kbd_rgb_mode_groups[num_rgb_groups++] = &kbd_rgb_mode_group;@@ -1754,24 +1830,37 @@ 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.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 ); + +if (asus->kbd_led_avail) 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 ; +else + asus->kbd_led_wk =-1 ; + +if (asus->kbd_led_avail && num_rgb_groups !=0 ) + asus->kbd_led.groups = kbd_rgb_mode_groups;Quoted 10 lines
drivers/platform/x86/asus-wmi.c static int asus_wmi_led_init(struct asus_wmi *asus) · line 1850
Quoted 5 lines
drivers/platform/x86/asus-wmi.c +3
-2-if (num_rgb_groups !=0 ) - asus->kbd_led.groups = kbd_rgb_mode_groups; +spin_lock_irqsave (&asus_ref.lock, flags); + has_listeners = !list_empty (&asus_ref.listeners); +spin_unlock_irqrestore (&asus_ref.lock, flags);It seems to me that you should also call
brightness_set()on all the kbds already in the list so that their brightness gets synced with the wmi kbd-backlight brightness when the wmi driver loads later then the hid driver ?You raise a good point here. Let me think about it.
But yes, if we do a notify when
kbd_led_wk!= -1 if the hid device connects after, we should do it for the before case too.Quoted 9 lines
Quoted 9 lines
drivers/platform/x86/asus-wmi.c +2
-0+if (asus->kbd_led_avail || has_listeners) { rv =led_classdev_register (&asus->platform_device->dev, &asus->kbd_led);if (rv) goto error; + asus->kbd_led_registered = true; }Quoted 8 lines
drivers/platform/x86/asus-wmi.c static int asus_wmi_led_init(struct asus_wmi *asus) · line 1860
Quoted 2 lines
drivers/platform/x86/asus-wmi.c +2
-0+spin_lock_irqsave (&asus_ref.lock, flags); + asus_ref.asus = asus;There is race here where a hid keyboard might show up between the 2 places you take the lock, in that case if there is no wmi kbd-backlight then you will not register the
led_classdevwhenasus_hid_register_listener()gets called between the unlock and the lock... I'm not sure what the best way is to fix this.
Quoted 4 lines
Thanks for catching that, probably a recheck:
has_listeners= !list_empty(&asus_ref.listeners);
if (has_listeners) goto register;
I just noticed that in asus_hid_register_listener() you keep the lock locked while calling led_classdev_register() so that apparently is ok to do. In which case you can just hold the lock over the led_classdev_register() call here too.
Regards,
Hans
Quoted 42 lines
Quoted 41 lines
Quoted 40 lines
drivers/platform/x86/asus-wmi.c +2
-0+spin_unlock_irqrestore (&asus_ref.lock, flags); +if (asus_wmi_dev_is_present (asus,ASUS_WMI_DEVID_WIRELESS_LED ) && (asus->driver->quirks->wapf >0 )) {INIT_WORK (&asus->wlan_led_work, wlan_led_update);include/linux/platform_data/x86/asus-wmi.h +16
-0@@ -157,14 +157,30 @@#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00#define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F +struct asus_hid_listener { +struct list_head list; +void (*brightness_set)(struct asus_hid_listener *listener,int brightness); +}; +#if IS_REACHABLE (CONFIG_ASUS_WMI )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 int asus_wmi_evaluate_method (u32 method_id,u32 arg0,u32 arg1,u32 *retval) {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 */
PATCH v3 5/10 HID: asus: listen to the asus-wmi brightness device instead of creating one
In reply to Antheas KapenekakisSome 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.
--- drivers/hid/hid-asus.c | 65 +++++++----------------------------------- 1 file changed, 11 insertions(+), 54 deletions(-)
drivers/hid/hid-asus.c +11 -54
@@ -96,7 +96,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;
@@ -493,11 +493,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);
@@ -507,20 +507,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);
@@ -537,34 +523,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 ); -} -static int asus_kbd_register_leds (struct hid_device *hdev) {struct asus_drvdata *drvdata =hid_get_drvdata (hdev);
@@ -599,14 +557,12 @@ 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);
@@ -1000,7 +956,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);
@@ -1139,7 +1095,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" );
@@ -1180,6 +1135,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);
Signature · 1 line
2.48.1
Re: [PATCH v3 05/10] HID: asus: listen to the asus-wmi brightness device instead of creating one
Quoted 134 lines · 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 namedasus::kbd_brightness_1and they are both broken.Therefore, register a listener to the asus-wmi brightness device instead of creating a new one.
Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 65 +++++++----------------------------------- 1 file changed, 11 insertions(+), 54 deletions(-)drivers/hid/hid-asus.c +9
-54@@ -96,7 +96,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;@@ -493,11 +493,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);@@ -507,20 +507,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);@@ -537,34 +523,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 ); -} -static int asus_kbd_register_leds (struct hid_device *hdev) {struct asus_drvdata *drvdata =hid_get_drvdata (hdev);@@ -599,14 +557,12 @@ 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);@@ -1000,7 +956,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);@@ -1139,7 +1095,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" );
drivers/hid/hid-asus.c static void asus_remove(struct hid_device *hdev) · line 1142
Quoted 8 lines
drivers/hid/hid-asus.c +2
-0@@ -1180,6 +1135,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);
PATCH v3 6/10 platform/x86: asus-wmi: remove unused keyboard backlight quirk
In reply to Antheas KapenekakisThe quirk for selecting whether keyboard backlight should be controlled by HID or WMI is not needed anymore, so remove it.
--- include/linux/platform_data/x86/asus-wmi.h | 40 ---------------------- 1 file changed, 40 deletions(-)
include/linux/platform_data/x86/asus-wmi.h +0 -40
@@ -183,44 +183,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 */
Signature · 1 line
2.48.1
Re: [PATCH v3 06/10] platform/x86: asus-wmi: remove unused keyboard backlight quirk
Quoted 47 lines · Antheas Kapenekakis
The quirk for selecting whether keyboard backlight should be controlled by HID or WMI is not needed anymore, so remove it.
Signed-off-by Antheas Kapenekakis <[email protected]>--- include/linux/platform_data/x86/asus-wmi.h | 40 ---------------------- 1 file changed, 40 deletions(-)include/linux/platform_data/x86/asus-wmi.h +0
-31@@ -183,44 +183,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" ), - },
include/linux/platform_data/x86/asus-wmi.h static inline void asus_hid_unregister_listener(struct asus_hid_listener *bdev) · line 186
Quoted 10 lines
include/linux/platform_data/x86/asus-wmi.h +0
-9- }, - { - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "RC71L"), - }, - }, - { }, -}; -#endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */
I might have mentioned it already but I think you should squash this commit into where asus_use_hid_led_dmi_ids use is removed. It's something I've been asked to do on a few occasions to prevent possible error/warning on patch tests (unused).
Otherwise if it stays separate:
PATCH v3 7/10 platform/x86: asus-wmi: add keyboard brightness event handler
In reply to Antheas KapenekakisCurrenlty, the keyboard brightness control of Asus WMI keyboards is handled in the 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.
--- drivers/platform/x86/asus-wmi.c | 39 ++++++++++++++++++++++ include/linux/platform_data/x86/asus-wmi.h | 11 ++++++ 2 files changed, 50 insertions(+)
drivers/platform/x86/asus-wmi.c +39 -0
@@ -1536,6 +1536,45 @@ void asus_hid_unregister_listener(struct asus_hid_listener *bdev) }EXPORT_SYMBOL_GPL (asus_hid_unregister_listener); +static void do_kbd_led_set (struct led_classdev *led_cdev,int value); + +int asus_hid_event (enum asus_hid_event event) +{ +unsigned long flags; +int brightness; + +spin_lock_irqsave (&asus_ref.lock, flags); +if (!asus_ref.asus || !asus_ref.asus->kbd_led_registered) { +spin_unlock_irqrestore (&asus_ref.lock, flags); +return -EBUSY ; + } + brightness = asus_ref.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 >=3 ) + brightness =0 ; +else + brightness +=1 ; +break ; + } + +do_kbd_led_set (&asus_ref.asus->kbd_led, brightness); +led_classdev_notify_brightness_hw_changed (&asus_ref.asus->kbd_led, + asus_ref.asus->kbd_led_wk); + +spin_unlock_irqrestore (&asus_ref.lock, flags); + +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
include/linux/platform_data/x86/asus-wmi.h +11 -0
@@ -162,11 +162,18 @@ 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 , +}; +#if IS_REACHABLE (CONFIG_ASUS_WMI )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 int asus_wmi_evaluate_method (u32 method_id,u32 arg0,u32 arg1,u32 *retval)
@@ -181,6 +188,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 */
Signature · 1 line
2.48.1
Re: [PATCH v3 07/10] platform/x86: asus-wmi: add keyboard brightness event handler
Quoted 92 lines · Antheas Kapenekakis
Currenlty, the keyboard brightness control of Asus WMI keyboards is handled in the 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.
Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/platform/x86/asus-wmi.c | 39 ++++++++++++++++++++++ include/linux/platform_data/x86/asus-wmi.h | 11 ++++++ 2 files changed, 50 insertions(+)drivers/platform/x86/asus-wmi.c +39
-0@@ -1536,6 +1536,45 @@ void asus_hid_unregister_listener(struct asus_hid_listener *bdev) }EXPORT_SYMBOL_GPL (asus_hid_unregister_listener); +static void do_kbd_led_set (struct led_classdev *led_cdev,int value); + +int asus_hid_event (enum asus_hid_event event) +{ +unsigned long flags; +int brightness; + +spin_lock_irqsave (&asus_ref.lock, flags); +if (!asus_ref.asus || !asus_ref.asus->kbd_led_registered) { +spin_unlock_irqrestore (&asus_ref.lock, flags); +return -EBUSY ; + } + brightness = asus_ref.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 >=3 ) + brightness =0 ; +else + brightness +=1 ; +break ; + } + +do_kbd_led_set (&asus_ref.asus->kbd_led, brightness); +led_classdev_notify_brightness_hw_changed (&asus_ref.asus->kbd_led, + asus_ref.asus->kbd_led_wk); + +spin_unlock_irqrestore (&asus_ref.lock, flags); + +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 include/linux/platform_data/x86/asus-wmi.h +7
-0@@ -162,11 +162,18 @@ 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 , +}; +#if IS_REACHABLE (CONFIG_ASUS_WMI )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 int asus_wmi_evaluate_method (u32 method_id,u32 arg0,u32 arg1,u32 *retval)
include/linux/platform_data/x86/asus-wmi.h static inline int asus_hid_register_listener(struct asus_hid_listener *bdev) · line 197
Quoted 10 lines
include/linux/platform_data/x86/asus-wmi.h +4
-0@@ -181,6 +188,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 */
PATCH v3 8/10 HID: asus: add support for the asus-wmi brightness handler
In reply to Antheas KapenekakisIf 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.
--- drivers/hid/hid-asus.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/hid/hid-asus.c +11 -0
@@ -318,6 +318,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 ; }
Signature · 1 line
2.48.1
Re: [PATCH v3 08/10] HID: asus: add support for the asus-wmi brightness handler
Quoted 21 lines · 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.
Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 11 +++++++++++ 1 file changed, 11 insertions(+)drivers/hid/hid-asus.c +4
-0@@ -318,6 +318,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 );
drivers/hid/hid-asus.c static int asus_event(struct hid_device *hdev, struct hid_field *field, · line 334
Quoted 10 lines
drivers/hid/hid-asus.c +7
-0+case KEY_KBDILLUMDOWN : +return !asus_hid_event (ASUS_EV_BRTDOWN ); +case KEY_KBDILLUMTOGGLE : +return !asus_hid_event (ASUS_EV_BRTTOGGLE ); + } + } +return 0 ; }
Possible commit squash candidate in to patch 7. I'll defer to Ilpo and Hans though.
PATCH v3 9/10 HID: asus: add basic RGB support
In reply to Antheas KapenekakisAdds basic RGB support to hid-asus through multi-index. The interface works quite well, but has not gone through much stability testing. Applied on demand, if userspace does not touch the RGB sysfs, not even initialization is done. Ensuring compatibility with existing userspace programs.
--- drivers/hid/hid-asus.c | 169 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 155 insertions(+), 14 deletions(-)
drivers/hid/hid-asus.c +155 -14
@@ -30,6 +30,7 @@#include <linux/input/mt.h> #include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */ #include <linux/power_supply.h> +#include <linux/led-class-multicolor.h> #include <linux/leds.h> #include "hid-ids.h"
@@ -85,6 +86,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_HANDLE_GENERIC BIT(13) +#define QUIRK_ROG_NKEY_RGB BIT(14)#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \
@@ -97,9 +99,15 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");struct asus_kbd_leds {struct asus_hid_listener listener; +struct led_classdev_mc mc_led; +struct mc_subled subled_info[3 ];struct hid_device *hdev;struct work_struct work;unsigned int brightness; +uint8_t rgb_colors[3 ]; +bool rgb_init; +bool rgb_set; +bool rgb_registered;spinlock_t lock;bool removed; };
@@ -504,23 +512,67 @@ static void asus_schedule_work(struct asus_kbd_leds *led)spin_unlock_irqrestore (&led->lock, flags); } -static void asus_kbd_backlight_set (struct asus_hid_listener *listener, +static void do_asus_kbd_backlight_set (struct asus_kbd_leds *led,int brightness) +{ +unsigned long flags; + +spin_lock_irqsave (&led->lock, flags); + led->brightness = brightness; +spin_unlock_irqrestore (&led->lock, flags); + +asus_schedule_work (led); +} + +static void asus_kbd_listener_set (struct asus_hid_listener *listener,int brightness) { struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds, listener); +do_asus_kbd_backlight_set (led, brightness); +if (led->rgb_registered) { + led->mc_led.led_cdev.brightness = brightness; +led_classdev_notify_brightness_hw_changed (&led->mc_led.led_cdev, + brightness); + } +} + +static void asus_kbd_brightness_set (struct led_classdev *led_cdev, +enum led_brightness brightness) +{ +struct led_classdev_mc *mc_cdev =lcdev_to_mccdev (led_cdev); + struct asus_kbd_leds *led = container_of(mc_cdev, struct asus_kbd_leds, + mc_led);unsigned long flags;spin_lock_irqsave (&led->lock, flags); - led->brightness = brightness; + led->rgb_colors[0 ] = mc_cdev->subled_info[0 ].intensity; + led->rgb_colors[1 ] = mc_cdev->subled_info[1 ].intensity; + led->rgb_colors[2 ] = mc_cdev->subled_info[2 ].intensity; + led->rgb_set = true;spin_unlock_irqrestore (&led->lock, flags); -asus_schedule_work (led); +do_asus_kbd_backlight_set (led, brightness); +} + +static enum led_brightness asus_kbd_brightness_get (struct led_classdev *led_cdev) +{ +struct led_classdev_mc *mc_led; +struct asus_kbd_leds *led; +enum led_brightness brightness; +unsigned long flags; + + mc_led =lcdev_to_mccdev (led_cdev); + led = container_of(mc_led, struct asus_kbd_leds, mc_led); + +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) +static void asus_kbd_backlight_work (struct asus_kbd_leds *led) { - struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);u8 buf[] = {FEATURE_KBD_REPORT_ID ,0xba ,0xc5 ,0xc4 ,0x00 };int ret;unsigned long flags;
@@ -534,10 +586,69 @@ static void asus_kbd_backlight_work(struct work_struct *work)hid_err (led->hdev,"Asus failed to set keyboard backlight: %d\n" , ret); } +static void asus_kbd_rgb_work (struct asus_kbd_leds *led) +{ +u8 rgb_buf[][7 ] = { + {FEATURE_KBD_LED_REPORT_ID1 ,0xB3 },/* set mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB5 },/* apply mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB4 },/* save to mem */ + }; +unsigned long flags; +uint8_t colors[3 ]; +bool rgb_init, rgb_set; +int ret; + +spin_lock_irqsave (&led->lock, flags); + rgb_init = led->rgb_init; + rgb_set = led->rgb_set; + led->rgb_set = false; + colors[0 ] = led->rgb_colors[0 ]; + colors[1 ] = led->rgb_colors[1 ]; + colors[2 ] = led->rgb_colors[2 ]; +spin_unlock_irqrestore (&led->lock, flags); + +if (!rgb_set) +return ; + +if (rgb_init) { + ret =asus_kbd_init (led->hdev,FEATURE_KBD_LED_REPORT_ID1 ); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to init RGB: %d\n" , ret); +return ; + } +spin_lock_irqsave (&led->lock, flags); + led->rgb_init = false; +spin_unlock_irqrestore (&led->lock, flags); + } + +/* Protocol is: 54b3 zone (0=all) mode (0=solid) RGB */ + rgb_buf[0 ][4 ] = colors[0 ]; + rgb_buf[0 ][5 ] = colors[1 ]; + rgb_buf[0 ][6 ] = colors[2 ]; + +for (size_t i =0 ; i <ARRAY_SIZE (rgb_buf); i++) { + ret =asus_kbd_set_report (led->hdev, rgb_buf[i],sizeof (rgb_buf[i])); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to set RGB: %d\n" , ret); +return ; + } + } +} + +static void asus_kbd_work (struct work_struct *work) +{ + struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, + work); +asus_kbd_backlight_work (led); +asus_kbd_rgb_work (led); +} +static int asus_kbd_register_leds (struct hid_device *hdev) {struct asus_drvdata *drvdata =hid_get_drvdata (hdev);unsigned char kbd_func; +struct asus_kbd_leds *leds; +bool no_led;int ret; ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID );
@@ -565,21 +676,51 @@ static int asus_kbd_register_leds(struct hid_device *hdev)if (!drvdata->kbd_backlight)return -ENOMEM ; - drvdata->kbd_backlight->removed = false; - drvdata->kbd_backlight->brightness =0 ; - drvdata->kbd_backlight->hdev = hdev; - drvdata->kbd_backlight->listener.brightness_set = asus_kbd_backlight_set; -INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_backlight_work); + leds = drvdata->kbd_backlight; + leds->removed = false; + leds->brightness =3 ; + leds->hdev = hdev; + leds->listener.brightness_set = asus_kbd_listener_set; + + leds->rgb_colors[0 ] =0 ; + leds->rgb_colors[1 ] =0 ; + leds->rgb_colors[2 ] =0 ; + leds->rgb_init = true; + leds->rgb_set = false; + leds->mc_led.led_cdev.name =devm_kasprintf (&hdev->dev,GFP_KERNEL , +"asus-%s-led" , +strlen (hdev->uniq) ? + hdev->uniq :dev_name (&hdev->dev)); + leds->mc_led.led_cdev.flags =LED_BRIGHT_HW_CHANGED ; + leds->mc_led.led_cdev.max_brightness =3 , + leds->mc_led.led_cdev.brightness_set = asus_kbd_brightness_set, + leds->mc_led.led_cdev.brightness_get = asus_kbd_brightness_get, + leds->mc_led.subled_info = leds->subled_info, + leds->mc_led.num_colors =ARRAY_SIZE (leds->subled_info), + leds->subled_info[0 ].color_index =LED_COLOR_ID_RED ; + leds->subled_info[1 ].color_index =LED_COLOR_ID_GREEN ; + leds->subled_info[2 ].color_index =LED_COLOR_ID_BLUE ; + +INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_work);spin_lock_init (&drvdata->kbd_backlight->lock); ret =asus_hid_register_listener (&drvdata->kbd_backlight->listener); + no_led = !!ret; + +if (drvdata->quirks &QUIRK_ROG_NKEY_RGB ) { + ret =devm_led_classdev_multicolor_register ( + &hdev->dev, &leds->mc_led); +if (!ret) + leds->rgb_registered = true; + no_led &= !!ret; + } -if (ret <0 ) { +if (no_led) {/* No need to have this still around */ devm_kfree (&hdev->dev, drvdata->kbd_backlight); } -return ret; +return no_led ? -ENODEV :0 ; }/*
@@ -1289,7 +1430,7 @@ static const struct hid_device_id asus_devices[] = { QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, {HID_USB_DEVICE (USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY ), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
@@ -1318,7 +1459,7 @@ static const struct hid_device_id asus_devices[] = {*/ {HID_DEVICE (BUS_USB ,HID_GROUP_GENERIC ,USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD) }, { }
Signature · 1 line
2.48.1
Luke D. Jones
maintainerRe: [PATCH v3 09/10] HID: asus: add basic RGB support
Quoted 259 lines · Antheas Kapenekakis
Adds basic RGB support to hid-asus through multi-index. The interface works quite well, but has not gone through much stability testing. Applied on demand, if userspace does not touch the RGB sysfs, not even initialization is done. Ensuring compatibility with existing userspace programs.
Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 169 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 155 insertions(+), 14 deletions(-)drivers/hid/hid-asus.c +154
-13@@ -30,6 +30,7 @@#include <linux/input/mt.h> #include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */ #include <linux/power_supply.h> +#include <linux/led-class-multicolor.h> #include <linux/leds.h> #include "hid-ids.h" @@ -85,6 +86,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_HANDLE_GENERIC BIT(13) +#define QUIRK_ROG_NKEY_RGB BIT(14)#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \@@ -97,9 +99,15 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");struct asus_kbd_leds {struct asus_hid_listener listener; +struct led_classdev_mc mc_led; +struct mc_subled subled_info[3 ];struct hid_device *hdev;struct work_struct work;unsigned int brightness; +uint8_t rgb_colors[3 ]; +bool rgb_init; +bool rgb_set; +bool rgb_registered;spinlock_t lock;bool removed; };@@ -504,23 +512,67 @@ static void asus_schedule_work(struct asus_kbd_leds *led)spin_unlock_irqrestore (&led->lock, flags); } -static void asus_kbd_backlight_set (struct asus_hid_listener *listener, +static void do_asus_kbd_backlight_set (struct asus_kbd_leds *led,int brightness) +{ +unsigned long flags; + +spin_lock_irqsave (&led->lock, flags); + led->brightness = brightness; +spin_unlock_irqrestore (&led->lock, flags); + +asus_schedule_work (led); +} + +static void asus_kbd_listener_set (struct asus_hid_listener *listener,int brightness) { struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds, listener); +do_asus_kbd_backlight_set (led, brightness); +if (led->rgb_registered) { + led->mc_led.led_cdev.brightness = brightness; +led_classdev_notify_brightness_hw_changed (&led->mc_led.led_cdev, + brightness); + } +} + +static void asus_kbd_brightness_set (struct led_classdev *led_cdev, +enum led_brightness brightness) +{ +struct led_classdev_mc *mc_cdev =lcdev_to_mccdev (led_cdev); + struct asus_kbd_leds *led = container_of(mc_cdev, struct asus_kbd_leds, + mc_led);unsigned long flags;spin_lock_irqsave (&led->lock, flags); - led->brightness = brightness; + led->rgb_colors[0 ] = mc_cdev->subled_info[0 ].intensity; + led->rgb_colors[1 ] = mc_cdev->subled_info[1 ].intensity; + led->rgb_colors[2 ] = mc_cdev->subled_info[2 ].intensity; + led->rgb_set = true;spin_unlock_irqrestore (&led->lock, flags); -asus_schedule_work (led); +do_asus_kbd_backlight_set (led, brightness); +} + +static enum led_brightness asus_kbd_brightness_get (struct led_classdev *led_cdev) +{ +struct led_classdev_mc *mc_led; +struct asus_kbd_leds *led; +enum led_brightness brightness; +unsigned long flags; + + mc_led =lcdev_to_mccdev (led_cdev); + led = container_of(mc_led, struct asus_kbd_leds, mc_led); + +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) +static void asus_kbd_backlight_work (struct asus_kbd_leds *led) { - struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);u8 buf[] = {FEATURE_KBD_REPORT_ID ,0xba ,0xc5 ,0xc4 ,0x00 };int ret;unsigned long flags;@@ -534,10 +586,69 @@ static void asus_kbd_backlight_work(struct work_struct *work)hid_err (led->hdev,"Asus failed to set keyboard backlight: %d\n" , ret); } +static void asus_kbd_rgb_work (struct asus_kbd_leds *led) +{ +u8 rgb_buf[][7 ] = { + {FEATURE_KBD_LED_REPORT_ID1 ,0xB3 },/* set mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB5 },/* apply mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB4 },/* save to mem */ + }; +unsigned long flags; +uint8_t colors[3 ]; +bool rgb_init, rgb_set; +int ret; + +spin_lock_irqsave (&led->lock, flags); + rgb_init = led->rgb_init; + rgb_set = led->rgb_set; + led->rgb_set = false; + colors[0 ] = led->rgb_colors[0 ]; + colors[1 ] = led->rgb_colors[1 ]; + colors[2 ] = led->rgb_colors[2 ]; +spin_unlock_irqrestore (&led->lock, flags); + +if (!rgb_set) +return ; + +if (rgb_init) { + ret =asus_kbd_init (led->hdev,FEATURE_KBD_LED_REPORT_ID1 ); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to init RGB: %d\n" , ret); +return ; + } +spin_lock_irqsave (&led->lock, flags); + led->rgb_init = false; +spin_unlock_irqrestore (&led->lock, flags); + } + +/* Protocol is: 54b3 zone (0=all) mode (0=solid) RGB */ + rgb_buf[0 ][4 ] = colors[0 ]; + rgb_buf[0 ][5 ] = colors[1 ]; + rgb_buf[0 ][6 ] = colors[2 ]; + +for (size_t i =0 ; i <ARRAY_SIZE (rgb_buf); i++) { + ret =asus_kbd_set_report (led->hdev, rgb_buf[i],sizeof (rgb_buf[i])); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to set RGB: %d\n" , ret); +return ; + } + } +} + +static void asus_kbd_work (struct work_struct *work) +{ + struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, + work); +asus_kbd_backlight_work (led); +asus_kbd_rgb_work (led); +} +static int asus_kbd_register_leds (struct hid_device *hdev) {struct asus_drvdata *drvdata =hid_get_drvdata (hdev);unsigned char kbd_func; +struct asus_kbd_leds *leds; +bool no_led;int ret; ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID );@@ -565,21 +676,51 @@ static int asus_kbd_register_leds(struct hid_device *hdev)if (!drvdata->kbd_backlight)return -ENOMEM ; - drvdata->kbd_backlight->removed = false; - drvdata->kbd_backlight->brightness =0 ; - drvdata->kbd_backlight->hdev = hdev; - drvdata->kbd_backlight->listener.brightness_set = asus_kbd_backlight_set; -INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_backlight_work); + leds = drvdata->kbd_backlight; + leds->removed = false; + leds->brightness =3 ; + leds->hdev = hdev; + leds->listener.brightness_set = asus_kbd_listener_set; + + leds->rgb_colors[0 ] =0 ; + leds->rgb_colors[1 ] =0 ; + leds->rgb_colors[2 ] =0 ; + leds->rgb_init = true; + leds->rgb_set = false; + leds->mc_led.led_cdev.name =devm_kasprintf (&hdev->dev,GFP_KERNEL , +"asus-%s-led" , +strlen (hdev->uniq) ? + hdev->uniq :dev_name (&hdev->dev)); + leds->mc_led.led_cdev.flags =LED_BRIGHT_HW_CHANGED ; + leds->mc_led.led_cdev.max_brightness =3 , + leds->mc_led.led_cdev.brightness_set = asus_kbd_brightness_set, + leds->mc_led.led_cdev.brightness_get = asus_kbd_brightness_get, + leds->mc_led.subled_info = leds->subled_info, + leds->mc_led.num_colors =ARRAY_SIZE (leds->subled_info), + leds->subled_info[0 ].color_index =LED_COLOR_ID_RED ; + leds->subled_info[1 ].color_index =LED_COLOR_ID_GREEN ; + leds->subled_info[2 ].color_index =LED_COLOR_ID_BLUE ; + +INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_work);spin_lock_init (&drvdata->kbd_backlight->lock); ret =asus_hid_register_listener (&drvdata->kbd_backlight->listener); + no_led = !!ret; + +if (drvdata->quirks &QUIRK_ROG_NKEY_RGB ) { + ret =devm_led_classdev_multicolor_register ( + &hdev->dev, &leds->mc_led); +if (!ret) + leds->rgb_registered = true; + no_led &= !!ret; + } -if (ret <0 ) { +if (no_led) {/* No need to have this still around */ devm_kfree (&hdev->dev, drvdata->kbd_backlight); } -return ret; +return no_led ? -ENODEV :0 ; }/* @@ -1289,7 +1430,7 @@ static const struct hid_device_id asus_devices[] = { QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, {HID_USB_DEVICE (USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY ), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
drivers/hid/hid-asus.c static const struct hid_device_id asus_devices[] = { · line 1465
Quoted 8 lines
drivers/hid/hid-asus.c +1
-1@@ -1318,7 +1459,7 @@ static const struct hid_device_id asus_devices[] = {*/ {HID_DEVICE (BUS_USB ,HID_GROUP_GENERIC ,USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD) }, { }
Tested as working on 0x1866 and 0x19B6 PID devices. No code review.
They have a path of /sys/class/leds/asus-0003:0B05:19B6.0001-led, was this intentional? I was under the impression that you would have added the mcled to the base asus::kbd_backlight.. Apologies if the answer is in code, I've only tested for now, not much time for full review but I do have some questions/opinons:
I *think* the majority of ROG are RGB, but now I regret not actually tracking this so I hope ASUS come back to me with some kind of query we can ask the MCU for this info.
Because I think the majority are RGB I'm coming around to saying yes to this patch but with caveats:
1. mcled added to the base led
2. asus:rgb:kbd_backlight name if RGB, asus:white:kbd_backlight if white only, asus::kbd_backlight if unknown
3. a quirk system to label an MCU as white only (since the bulk are RGB)
If you only do 1 + 2, or even just 1 I'm happy with that. I can take care of either 3, or 2 + 3. The last one just means I'll have to spend some time looking up specs or writing a crawler to find data.
I will try to review the code in the next days, but if you change it based on the above I'll hold off until then.
Cheers,
Luke.
P.S: on almost all white-only LED devices I've encountered the RED channel works for white intensity (green and blue do nothing). So even if we decide to blanket enable RGB for 0x1866 and 0x19B6 it won't cause issues beyond the naming and API facing.
Luke D. Jones
maintainerRe: [PATCH v3 09/10] HID: asus: add basic RGB support
In reply to Antheas KapenekakisQuoted 204 lines · Antheas Kapenekakis
Adds basic RGB support to hid-asus through multi-index. The interface works quite well, but has not gone through much stability testing. Applied on demand, if userspace does not touch the RGB sysfs, not even initialization is done. Ensuring compatibility with existing userspace programs.
Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 169 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 155 insertions(+), 14 deletions(-)drivers/hid/hid-asus.c +121
-10@@ -30,6 +30,7 @@#include <linux/input/mt.h> #include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */ #include <linux/power_supply.h> +#include <linux/led-class-multicolor.h> #include <linux/leds.h> #include "hid-ids.h" @@ -85,6 +86,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_HANDLE_GENERIC BIT(13) +#define QUIRK_ROG_NKEY_RGB BIT(14)#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \@@ -97,9 +99,15 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");struct asus_kbd_leds {struct asus_hid_listener listener; +struct led_classdev_mc mc_led; +struct mc_subled subled_info[3 ];struct hid_device *hdev;struct work_struct work;unsigned int brightness; +uint8_t rgb_colors[3 ]; +bool rgb_init; +bool rgb_set; +bool rgb_registered;spinlock_t lock;bool removed; };@@ -504,23 +512,67 @@ static void asus_schedule_work(struct asus_kbd_leds *led)spin_unlock_irqrestore (&led->lock, flags); } -static void asus_kbd_backlight_set (struct asus_hid_listener *listener, +static void do_asus_kbd_backlight_set (struct asus_kbd_leds *led,int brightness) +{ +unsigned long flags; + +spin_lock_irqsave (&led->lock, flags); + led->brightness = brightness; +spin_unlock_irqrestore (&led->lock, flags); + +asus_schedule_work (led); +} + +static void asus_kbd_listener_set (struct asus_hid_listener *listener,int brightness) { struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds, listener); +do_asus_kbd_backlight_set (led, brightness); +if (led->rgb_registered) { + led->mc_led.led_cdev.brightness = brightness; +led_classdev_notify_brightness_hw_changed (&led->mc_led.led_cdev, + brightness); + } +} + +static void asus_kbd_brightness_set (struct led_classdev *led_cdev, +enum led_brightness brightness) +{ +struct led_classdev_mc *mc_cdev =lcdev_to_mccdev (led_cdev); + struct asus_kbd_leds *led = container_of(mc_cdev, struct asus_kbd_leds, + mc_led);unsigned long flags;spin_lock_irqsave (&led->lock, flags); - led->brightness = brightness; + led->rgb_colors[0 ] = mc_cdev->subled_info[0 ].intensity; + led->rgb_colors[1 ] = mc_cdev->subled_info[1 ].intensity; + led->rgb_colors[2 ] = mc_cdev->subled_info[2 ].intensity; + led->rgb_set = true;spin_unlock_irqrestore (&led->lock, flags); -asus_schedule_work (led); +do_asus_kbd_backlight_set (led, brightness); +} + +static enum led_brightness asus_kbd_brightness_get (struct led_classdev *led_cdev) +{ +struct led_classdev_mc *mc_led; +struct asus_kbd_leds *led; +enum led_brightness brightness; +unsigned long flags; + + mc_led =lcdev_to_mccdev (led_cdev); + led = container_of(mc_led, struct asus_kbd_leds, mc_led); + +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) +static void asus_kbd_backlight_work (struct asus_kbd_leds *led) { - struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);u8 buf[] = {FEATURE_KBD_REPORT_ID ,0xba ,0xc5 ,0xc4 ,0x00 };int ret;unsigned long flags;@@ -534,10 +586,69 @@ static void asus_kbd_backlight_work(struct work_struct *work)hid_err (led->hdev,"Asus failed to set keyboard backlight: %d\n" , ret); } +static void asus_kbd_rgb_work (struct asus_kbd_leds *led) +{ +u8 rgb_buf[][7 ] = { + {FEATURE_KBD_LED_REPORT_ID1 ,0xB3 },/* set mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB5 },/* apply mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB4 },/* save to mem */ + }; +unsigned long flags; +uint8_t colors[3 ]; +bool rgb_init, rgb_set; +int ret; + +spin_lock_irqsave (&led->lock, flags); + rgb_init = led->rgb_init; + rgb_set = led->rgb_set; + led->rgb_set = false; + colors[0 ] = led->rgb_colors[0 ]; + colors[1 ] = led->rgb_colors[1 ]; + colors[2 ] = led->rgb_colors[2 ]; +spin_unlock_irqrestore (&led->lock, flags); + +if (!rgb_set) +return ; + +if (rgb_init) { + ret =asus_kbd_init (led->hdev,FEATURE_KBD_LED_REPORT_ID1 ); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to init RGB: %d\n" , ret); +return ; + } +spin_lock_irqsave (&led->lock, flags); + led->rgb_init = false; +spin_unlock_irqrestore (&led->lock, flags); + } + +/* Protocol is: 54b3 zone (0=all) mode (0=solid) RGB */ + rgb_buf[0 ][4 ] = colors[0 ]; + rgb_buf[0 ][5 ] = colors[1 ]; + rgb_buf[0 ][6 ] = colors[2 ]; + +for (size_t i =0 ; i <ARRAY_SIZE (rgb_buf); i++) { + ret =asus_kbd_set_report (led->hdev, rgb_buf[i],sizeof (rgb_buf[i])); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to set RGB: %d\n" , ret); +return ; + } + } +} + +static void asus_kbd_work (struct work_struct *work) +{ + struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, + work); +asus_kbd_backlight_work (led); +asus_kbd_rgb_work (led); +} +static int asus_kbd_register_leds (struct hid_device *hdev) {struct asus_drvdata *drvdata =hid_get_drvdata (hdev);unsigned char kbd_func; +struct asus_kbd_leds *leds; +bool no_led;int ret; ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID );@@ -565,21 +676,51 @@ static int asus_kbd_register_leds(struct hid_device *hdev)if (!drvdata->kbd_backlight)return -ENOMEM ; - drvdata->kbd_backlight->removed = false; - drvdata->kbd_backlight->brightness =0 ; - drvdata->kbd_backlight->hdev = hdev; - drvdata->kbd_backlight->listener.brightness_set = asus_kbd_backlight_set; -INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_backlight_work); + leds = drvdata->kbd_backlight; + leds->removed = false; + leds->brightness =3 ; + leds->hdev = hdev; + leds->listener.brightness_set = asus_kbd_listener_set;
drivers/hid/hid-asus.c static int asus_kbd_register_leds(struct hid_device *hdev) · line 693
Quoted 10 lines
drivers/hid/hid-asus.c +10
-0+ + leds->rgb_colors[0 ] =0 ; + leds->rgb_colors[1 ] =0 ; + leds->rgb_colors[2 ] =0 ; + leds->rgb_init = true; + leds->rgb_set = false; + leds->mc_led.led_cdev.name =devm_kasprintf (&hdev->dev,GFP_KERNEL , +"asus-%s-led" , +strlen (hdev->uniq) ? + hdev->uniq :dev_name (&hdev->dev));
A quick note. This breaks convention for LED names. The style guide is at Documentation/leds/leds-class.rst. Per my parallel email to this one I would like to see the mentioned naming scheme asus:rgb:kbd_backlight adopted. Expanding further on one of the points there you might need to move the led_classdev_mc in to asus-wmi to fulfil having the single sysfs endpoint. Since you're using the listner pattern it shouldn't be much work.
Quoted 53 lines
drivers/hid/hid-asus.c +24
-4+ leds->mc_led.led_cdev.flags =LED_BRIGHT_HW_CHANGED ; + leds->mc_led.led_cdev.max_brightness =3 , + leds->mc_led.led_cdev.brightness_set = asus_kbd_brightness_set, + leds->mc_led.led_cdev.brightness_get = asus_kbd_brightness_get, + leds->mc_led.subled_info = leds->subled_info, + leds->mc_led.num_colors =ARRAY_SIZE (leds->subled_info), + leds->subled_info[0 ].color_index =LED_COLOR_ID_RED ; + leds->subled_info[1 ].color_index =LED_COLOR_ID_GREEN ; + leds->subled_info[2 ].color_index =LED_COLOR_ID_BLUE ; + +INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_work);spin_lock_init (&drvdata->kbd_backlight->lock); ret =asus_hid_register_listener (&drvdata->kbd_backlight->listener); + no_led = !!ret; + +if (drvdata->quirks &QUIRK_ROG_NKEY_RGB ) { + ret =devm_led_classdev_multicolor_register ( + &hdev->dev, &leds->mc_led); +if (!ret) + leds->rgb_registered = true; + no_led &= !!ret; + } -if (ret <0 ) { +if (no_led) {/* No need to have this still around */ devm_kfree (&hdev->dev, drvdata->kbd_backlight); } -return ret; +return no_led ? -ENODEV :0 ; }/* @@ -1289,7 +1430,7 @@ static const struct hid_device_id asus_devices[] = { QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, {HID_USB_DEVICE (USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY ), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },@@ -1318,7 +1459,7 @@ static const struct hid_device_id asus_devices[] = {*/ {HID_DEVICE (BUS_USB ,HID_GROUP_GENERIC ,USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD) }, { }
Re: [PATCH v3 09/10] HID: asus: add basic RGB support
Quoted 213 lines · Luke D. Jones
Quoted 211 lines · Antheas Kapenekakis
Adds basic RGB support to hid-asus through multi-index. The interface works quite well, but has not gone through much stability testing. Applied on demand, if userspace does not touch the RGB sysfs, not even initialization is done. Ensuring compatibility with existing userspace programs.
Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 169 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 155 insertions(+), 14 deletions(-)drivers/hid/hid-asus.c +128
-10@@ -30,6 +30,7 @@#include <linux/input/mt.h> #include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */ #include <linux/power_supply.h> +#include <linux/led-class-multicolor.h> #include <linux/leds.h> #include "hid-ids.h" @@ -85,6 +86,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_HANDLE_GENERIC BIT(13) +#define QUIRK_ROG_NKEY_RGB BIT(14)#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \@@ -97,9 +99,15 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");struct asus_kbd_leds {struct asus_hid_listener listener; +struct led_classdev_mc mc_led; +struct mc_subled subled_info[3 ];struct hid_device *hdev;struct work_struct work;unsigned int brightness; +uint8_t rgb_colors[3 ]; +bool rgb_init; +bool rgb_set; +bool rgb_registered;spinlock_t lock;bool removed; };@@ -504,23 +512,67 @@ static void asus_schedule_work(struct asus_kbd_leds *led)spin_unlock_irqrestore (&led->lock, flags); } -static void asus_kbd_backlight_set (struct asus_hid_listener *listener, +static void do_asus_kbd_backlight_set (struct asus_kbd_leds *led,int brightness) +{ +unsigned long flags; + +spin_lock_irqsave (&led->lock, flags); + led->brightness = brightness; +spin_unlock_irqrestore (&led->lock, flags); + +asus_schedule_work (led); +} + +static void asus_kbd_listener_set (struct asus_hid_listener *listener,int brightness) { struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds, listener); +do_asus_kbd_backlight_set (led, brightness); +if (led->rgb_registered) { + led->mc_led.led_cdev.brightness = brightness; +led_classdev_notify_brightness_hw_changed (&led->mc_led.led_cdev, + brightness); + } +} + +static void asus_kbd_brightness_set (struct led_classdev *led_cdev, +enum led_brightness brightness) +{ +struct led_classdev_mc *mc_cdev =lcdev_to_mccdev (led_cdev); + struct asus_kbd_leds *led = container_of(mc_cdev, struct asus_kbd_leds, + mc_led);unsigned long flags;spin_lock_irqsave (&led->lock, flags); - led->brightness = brightness; + led->rgb_colors[0 ] = mc_cdev->subled_info[0 ].intensity; + led->rgb_colors[1 ] = mc_cdev->subled_info[1 ].intensity; + led->rgb_colors[2 ] = mc_cdev->subled_info[2 ].intensity; + led->rgb_set = true;spin_unlock_irqrestore (&led->lock, flags); -asus_schedule_work (led); +do_asus_kbd_backlight_set (led, brightness); +} + +static enum led_brightness asus_kbd_brightness_get (struct led_classdev *led_cdev) +{ +struct led_classdev_mc *mc_led; +struct asus_kbd_leds *led; +enum led_brightness brightness; +unsigned long flags; + + mc_led =lcdev_to_mccdev (led_cdev); + led = container_of(mc_led, struct asus_kbd_leds, mc_led); + +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) +static void asus_kbd_backlight_work (struct asus_kbd_leds *led) { - struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);u8 buf[] = {FEATURE_KBD_REPORT_ID ,0xba ,0xc5 ,0xc4 ,0x00 };int ret;unsigned long flags;@@ -534,10 +586,69 @@ static void asus_kbd_backlight_work(struct work_struct *work)hid_err (led->hdev,"Asus failed to set keyboard backlight: %d\n" , ret); } +static void asus_kbd_rgb_work (struct asus_kbd_leds *led) +{ +u8 rgb_buf[][7 ] = { + {FEATURE_KBD_LED_REPORT_ID1 ,0xB3 },/* set mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB5 },/* apply mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB4 },/* save to mem */ + }; +unsigned long flags; +uint8_t colors[3 ]; +bool rgb_init, rgb_set; +int ret; + +spin_lock_irqsave (&led->lock, flags); + rgb_init = led->rgb_init; + rgb_set = led->rgb_set; + led->rgb_set = false; + colors[0 ] = led->rgb_colors[0 ]; + colors[1 ] = led->rgb_colors[1 ]; + colors[2 ] = led->rgb_colors[2 ]; +spin_unlock_irqrestore (&led->lock, flags); + +if (!rgb_set) +return ; + +if (rgb_init) { + ret =asus_kbd_init (led->hdev,FEATURE_KBD_LED_REPORT_ID1 ); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to init RGB: %d\n" , ret); +return ; + } +spin_lock_irqsave (&led->lock, flags); + led->rgb_init = false; +spin_unlock_irqrestore (&led->lock, flags); + } + +/* Protocol is: 54b3 zone (0=all) mode (0=solid) RGB */ + rgb_buf[0 ][4 ] = colors[0 ]; + rgb_buf[0 ][5 ] = colors[1 ]; + rgb_buf[0 ][6 ] = colors[2 ]; + +for (size_t i =0 ; i <ARRAY_SIZE (rgb_buf); i++) { + ret =asus_kbd_set_report (led->hdev, rgb_buf[i],sizeof (rgb_buf[i])); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to set RGB: %d\n" , ret); +return ; + } + } +} + +static void asus_kbd_work (struct work_struct *work) +{ + struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, + work); +asus_kbd_backlight_work (led); +asus_kbd_rgb_work (led); +} +static int asus_kbd_register_leds (struct hid_device *hdev) {struct asus_drvdata *drvdata =hid_get_drvdata (hdev);unsigned char kbd_func; +struct asus_kbd_leds *leds; +bool no_led;int ret; ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID );@@ -565,21 +676,51 @@ static int asus_kbd_register_leds(struct hid_device *hdev)if (!drvdata->kbd_backlight)return -ENOMEM ; - drvdata->kbd_backlight->removed = false; - drvdata->kbd_backlight->brightness =0 ; - drvdata->kbd_backlight->hdev = hdev; - drvdata->kbd_backlight->listener.brightness_set = asus_kbd_backlight_set; -INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_backlight_work); + leds = drvdata->kbd_backlight; + leds->removed = false; + leds->brightness =3 ; + leds->hdev = hdev; + leds->listener.brightness_set = asus_kbd_listener_set; + + leds->rgb_colors[0 ] =0 ; + leds->rgb_colors[1 ] =0 ; + leds->rgb_colors[2 ] =0 ; + leds->rgb_init = true; + leds->rgb_set = false; + leds->mc_led.led_cdev.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
Quoted 8 lines
drivers/hid/hid-asus.c static int asus_kbd_register_leds(struct hid_device *hdev) · line 693
Quoted 3 lines
drivers/hid/hid-asus.c +3
-0+"asus-%s-led" , +strlen (hdev->uniq) ? + hdev->uniq : dev_name(&hdev->dev));A quick note. This breaks convention for LED names. The style guide is at
Documentation/leds/leds-class.rst. Per my parallel email to this one I would like to see the mentioned naming schemeasus:rgb:kbd_backlightadopted.
Perhaps. It would be the first kbd_backlight driver to have "rgb" in it. It is a bit out of scope for this series as I do not touch the functionality of it but I can add a patch for it and a fixes e305a71cea37a64c75 tag.
Quoted 4 lines
Expanding further on one of the points there you might need to move the
led_classdev_mcin to asus-wmi to fulfil having the single sysfs endpoint. Since you're using the listner pattern it shouldn't be much work.
I only want the brightness to sync, not the color. Only the brightness between Aura devices needs to be the same. In this case asus::kbd_backlight if it has a color controls the wmi color, and the asus- devices control the usb.
Also, groups are not dynamic so this is not possible. E.g., if you setup a WMI listener that does not have RGB, and then the USB keyboard connects you can no longer change the groups unless you reconnect the device.
Quoted 54 lines
Quoted 53 lines
drivers/hid/hid-asus.c +24
-4+ leds->mc_led.led_cdev.flags =LED_BRIGHT_HW_CHANGED ; + leds->mc_led.led_cdev.max_brightness =3 , + leds->mc_led.led_cdev.brightness_set = asus_kbd_brightness_set, + leds->mc_led.led_cdev.brightness_get = asus_kbd_brightness_get, + leds->mc_led.subled_info = leds->subled_info, + leds->mc_led.num_colors =ARRAY_SIZE (leds->subled_info), + leds->subled_info[0 ].color_index =LED_COLOR_ID_RED ; + leds->subled_info[1 ].color_index =LED_COLOR_ID_GREEN ; + leds->subled_info[2 ].color_index =LED_COLOR_ID_BLUE ; + +INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_work);spin_lock_init (&drvdata->kbd_backlight->lock); ret =asus_hid_register_listener (&drvdata->kbd_backlight->listener); + no_led = !!ret; + +if (drvdata->quirks &QUIRK_ROG_NKEY_RGB ) { + ret =devm_led_classdev_multicolor_register ( + &hdev->dev, &leds->mc_led); +if (!ret) + leds->rgb_registered = true; + no_led &= !!ret; + } -if (ret <0 ) { +if (no_led) {/* No need to have this still around */ devm_kfree (&hdev->dev, drvdata->kbd_backlight); } -return ret; +return no_led ? -ENODEV :0 ; }/* @@ -1289,7 +1430,7 @@ static const struct hid_device_id asus_devices[] = { QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, {HID_USB_DEVICE (USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY ), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },@@ -1318,7 +1459,7 @@ static const struct hid_device_id asus_devices[] = {*/ {HID_DEVICE (BUS_USB ,HID_GROUP_GENERIC ,USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD) }, { }
Re: [PATCH v3 09/10] HID: asus: add basic RGB support
Quoted 239 lines · Antheas Kapenekakis
Quoted 213 lines · Luke D. Jones
Quoted 211 lines · Antheas Kapenekakis
Adds basic RGB support to hid-asus through multi-index. The interface works quite well, but has not gone through much stability testing. Applied on demand, if userspace does not touch the RGB sysfs, not even initialization is done. Ensuring compatibility with existing userspace programs.
Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 169 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 155 insertions(+), 14 deletions(-)drivers/hid/hid-asus.c +128
-10@@ -30,6 +30,7 @@#include <linux/input/mt.h> #include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */ #include <linux/power_supply.h> +#include <linux/led-class-multicolor.h> #include <linux/leds.h> #include "hid-ids.h" @@ -85,6 +86,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_HANDLE_GENERIC BIT(13) +#define QUIRK_ROG_NKEY_RGB BIT(14)#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \@@ -97,9 +99,15 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");struct asus_kbd_leds {struct asus_hid_listener listener; +struct led_classdev_mc mc_led; +struct mc_subled subled_info[3 ];struct hid_device *hdev;struct work_struct work;unsigned int brightness; +uint8_t rgb_colors[3 ]; +bool rgb_init; +bool rgb_set; +bool rgb_registered;spinlock_t lock;bool removed; };@@ -504,23 +512,67 @@ static void asus_schedule_work(struct asus_kbd_leds *led)spin_unlock_irqrestore (&led->lock, flags); } -static void asus_kbd_backlight_set (struct asus_hid_listener *listener, +static void do_asus_kbd_backlight_set (struct asus_kbd_leds *led,int brightness) +{ +unsigned long flags; + +spin_lock_irqsave (&led->lock, flags); + led->brightness = brightness; +spin_unlock_irqrestore (&led->lock, flags); + +asus_schedule_work (led); +} + +static void asus_kbd_listener_set (struct asus_hid_listener *listener,int brightness) { struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds, listener); +do_asus_kbd_backlight_set (led, brightness); +if (led->rgb_registered) { + led->mc_led.led_cdev.brightness = brightness; +led_classdev_notify_brightness_hw_changed (&led->mc_led.led_cdev, + brightness); + } +} + +static void asus_kbd_brightness_set (struct led_classdev *led_cdev, +enum led_brightness brightness) +{ +struct led_classdev_mc *mc_cdev =lcdev_to_mccdev (led_cdev); + struct asus_kbd_leds *led = container_of(mc_cdev, struct asus_kbd_leds, + mc_led);unsigned long flags;spin_lock_irqsave (&led->lock, flags); - led->brightness = brightness; + led->rgb_colors[0 ] = mc_cdev->subled_info[0 ].intensity; + led->rgb_colors[1 ] = mc_cdev->subled_info[1 ].intensity; + led->rgb_colors[2 ] = mc_cdev->subled_info[2 ].intensity; + led->rgb_set = true;spin_unlock_irqrestore (&led->lock, flags); -asus_schedule_work (led); +do_asus_kbd_backlight_set (led, brightness); +} + +static enum led_brightness asus_kbd_brightness_get (struct led_classdev *led_cdev) +{ +struct led_classdev_mc *mc_led; +struct asus_kbd_leds *led; +enum led_brightness brightness; +unsigned long flags; + + mc_led =lcdev_to_mccdev (led_cdev); + led = container_of(mc_led, struct asus_kbd_leds, mc_led); + +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) +static void asus_kbd_backlight_work (struct asus_kbd_leds *led) { - struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);u8 buf[] = {FEATURE_KBD_REPORT_ID ,0xba ,0xc5 ,0xc4 ,0x00 };int ret;unsigned long flags;@@ -534,10 +586,69 @@ static void asus_kbd_backlight_work(struct work_struct *work)hid_err (led->hdev,"Asus failed to set keyboard backlight: %d\n" , ret); } +static void asus_kbd_rgb_work (struct asus_kbd_leds *led) +{ +u8 rgb_buf[][7 ] = { + {FEATURE_KBD_LED_REPORT_ID1 ,0xB3 },/* set mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB5 },/* apply mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB4 },/* save to mem */ + }; +unsigned long flags; +uint8_t colors[3 ]; +bool rgb_init, rgb_set; +int ret; + +spin_lock_irqsave (&led->lock, flags); + rgb_init = led->rgb_init; + rgb_set = led->rgb_set; + led->rgb_set = false; + colors[0 ] = led->rgb_colors[0 ]; + colors[1 ] = led->rgb_colors[1 ]; + colors[2 ] = led->rgb_colors[2 ]; +spin_unlock_irqrestore (&led->lock, flags); + +if (!rgb_set) +return ; + +if (rgb_init) { + ret =asus_kbd_init (led->hdev,FEATURE_KBD_LED_REPORT_ID1 ); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to init RGB: %d\n" , ret); +return ; + } +spin_lock_irqsave (&led->lock, flags); + led->rgb_init = false; +spin_unlock_irqrestore (&led->lock, flags); + } + +/* Protocol is: 54b3 zone (0=all) mode (0=solid) RGB */ + rgb_buf[0 ][4 ] = colors[0 ]; + rgb_buf[0 ][5 ] = colors[1 ]; + rgb_buf[0 ][6 ] = colors[2 ]; + +for (size_t i =0 ; i <ARRAY_SIZE (rgb_buf); i++) { + ret =asus_kbd_set_report (led->hdev, rgb_buf[i],sizeof (rgb_buf[i])); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to set RGB: %d\n" , ret); +return ; + } + } +} + +static void asus_kbd_work (struct work_struct *work) +{ + struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, + work); +asus_kbd_backlight_work (led); +asus_kbd_rgb_work (led); +} +static int asus_kbd_register_leds (struct hid_device *hdev) {struct asus_drvdata *drvdata =hid_get_drvdata (hdev);unsigned char kbd_func; +struct asus_kbd_leds *leds; +bool no_led;int ret; ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID );@@ -565,21 +676,51 @@ static int asus_kbd_register_leds(struct hid_device *hdev)if (!drvdata->kbd_backlight)return -ENOMEM ; - drvdata->kbd_backlight->removed = false; - drvdata->kbd_backlight->brightness =0 ; - drvdata->kbd_backlight->hdev = hdev; - drvdata->kbd_backlight->listener.brightness_set = asus_kbd_backlight_set; -INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_backlight_work); + leds = drvdata->kbd_backlight; + leds->removed = false; + leds->brightness =3 ; + leds->hdev = hdev; + leds->listener.brightness_set = asus_kbd_listener_set; + + leds->rgb_colors[0 ] =0 ; + leds->rgb_colors[1 ] =0 ; + leds->rgb_colors[2 ] =0 ; + leds->rgb_init = true; + leds->rgb_set = false; + leds->mc_led.led_cdev.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,Quoted 8 lines
drivers/hid/hid-asus.c static int asus_kbd_register_leds(struct hid_device *hdev) · line 693
Quoted 3 lines
drivers/hid/hid-asus.c +3
-0+"asus-%s-led" , +strlen (hdev->uniq) ? + hdev->uniq : dev_name(&hdev->dev));A quick note. This breaks convention for LED names. The style guide is at
Documentation/leds/leds-class.rst. Per my parallel email to this one I would like to see the mentioned naming schemeasus:rgb:kbd_backlightadopted.Perhaps. It would be the first
kbd_backlightdriver to have "rgb" in it. It is a bit out of scope for this series as I do not touch the functionality of it but I can add a patch for it and a fixese305a71cea37a64c75tag.Quoted 4 lines
Expanding further on one of the points there you might need to move the
led_classdev_mcin to asus-wmi to fulfil having the single sysfs endpoint. Since you're using the listner pattern it shouldn't be much work.I only want the brightness to sync, not the color. Only the brightness between Aura devices needs to be the same. In this case
asus::kbd_backlightif it has a color controls the wmi color, and the asus- devices control the usb.
Quoted 4 lines
Also, groups are not dynamic so this is not possible. E.g., if you setup a WMI listener that does not have RGB, and then the USB keyboard connects you can no longer change the groups unless you reconnect the device.
Sorry, I confused the patches. Yes you are right. I cannot do kbd_backlight because userspace might pick the wrong handler. And with this patch series on the Z13 there are 3, one for the common backlight, one for the keyboard, and one for the lightbar.
I can do asus-UNIQ:rgb:peripheral though. There is no appropriate function that is close enough currently. Also, since there can be multiple devices, UNIQ or something similar needs to be added, otherwise the led handler will suffix _X.
Antheas
Quoted 54 lines
Quoted 54 lines
Quoted 53 lines
drivers/hid/hid-asus.c +24
-4+ leds->mc_led.led_cdev.flags =LED_BRIGHT_HW_CHANGED ; + leds->mc_led.led_cdev.max_brightness =3 , + leds->mc_led.led_cdev.brightness_set = asus_kbd_brightness_set, + leds->mc_led.led_cdev.brightness_get = asus_kbd_brightness_get, + leds->mc_led.subled_info = leds->subled_info, + leds->mc_led.num_colors =ARRAY_SIZE (leds->subled_info), + leds->subled_info[0 ].color_index =LED_COLOR_ID_RED ; + leds->subled_info[1 ].color_index =LED_COLOR_ID_GREEN ; + leds->subled_info[2 ].color_index =LED_COLOR_ID_BLUE ; + +INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_work);spin_lock_init (&drvdata->kbd_backlight->lock); ret =asus_hid_register_listener (&drvdata->kbd_backlight->listener); + no_led = !!ret; + +if (drvdata->quirks &QUIRK_ROG_NKEY_RGB ) { + ret =devm_led_classdev_multicolor_register ( + &hdev->dev, &leds->mc_led); +if (!ret) + leds->rgb_registered = true; + no_led &= !!ret; + } -if (ret <0 ) { +if (no_led) {/* No need to have this still around */ devm_kfree (&hdev->dev, drvdata->kbd_backlight); } -return ret; +return no_led ? -ENODEV :0 ; }/* @@ -1289,7 +1430,7 @@ static const struct hid_device_id asus_devices[] = { QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, {HID_USB_DEVICE (USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY ), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },@@ -1318,7 +1459,7 @@ static const struct hid_device_id asus_devices[] = {*/ {HID_DEVICE (BUS_USB ,HID_GROUP_GENERIC ,USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD) }, { }
Luke D. Jones
maintainerRe: [PATCH v3 09/10] HID: asus: add basic RGB support
Quoted 250 lines · Antheas Kapenekakis
Quoted 239 lines · Antheas Kapenekakis
Quoted 213 lines · Luke D. Jones
Quoted 211 lines · Antheas Kapenekakis
Adds basic RGB support to hid-asus through multi-index. The interface works quite well, but has not gone through much stability testing. Applied on demand, if userspace does not touch the RGB sysfs, not even initialization is done. Ensuring compatibility with existing userspace programs.
Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 169 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 155 insertions(+), 14 deletions(-)drivers/hid/hid-asus.c +128
-10@@ -30,6 +30,7 @@#include <linux/input/mt.h> #include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */ #include <linux/power_supply.h> +#include <linux/led-class-multicolor.h> #include <linux/leds.h> #include "hid-ids.h" @@ -85,6 +86,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_HANDLE_GENERIC BIT(13) +#define QUIRK_ROG_NKEY_RGB BIT(14)#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \@@ -97,9 +99,15 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");struct asus_kbd_leds {struct asus_hid_listener listener; +struct led_classdev_mc mc_led; +struct mc_subled subled_info[3 ];struct hid_device *hdev;struct work_struct work;unsigned int brightness; +uint8_t rgb_colors[3 ]; +bool rgb_init; +bool rgb_set; +bool rgb_registered;spinlock_t lock;bool removed; };@@ -504,23 +512,67 @@ static void asus_schedule_work(struct asus_kbd_leds *led)spin_unlock_irqrestore (&led->lock, flags); } -static void asus_kbd_backlight_set (struct asus_hid_listener *listener, +static void do_asus_kbd_backlight_set (struct asus_kbd_leds *led,int brightness) +{ +unsigned long flags; + +spin_lock_irqsave (&led->lock, flags); + led->brightness = brightness; +spin_unlock_irqrestore (&led->lock, flags); + +asus_schedule_work (led); +} + +static void asus_kbd_listener_set (struct asus_hid_listener *listener,int brightness) { struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds, listener); +do_asus_kbd_backlight_set (led, brightness); +if (led->rgb_registered) { + led->mc_led.led_cdev.brightness = brightness; +led_classdev_notify_brightness_hw_changed (&led->mc_led.led_cdev, + brightness); + } +} + +static void asus_kbd_brightness_set (struct led_classdev *led_cdev, +enum led_brightness brightness) +{ +struct led_classdev_mc *mc_cdev =lcdev_to_mccdev (led_cdev); + struct asus_kbd_leds *led = container_of(mc_cdev, struct asus_kbd_leds, + mc_led);unsigned long flags;spin_lock_irqsave (&led->lock, flags); - led->brightness = brightness; + led->rgb_colors[0 ] = mc_cdev->subled_info[0 ].intensity; + led->rgb_colors[1 ] = mc_cdev->subled_info[1 ].intensity; + led->rgb_colors[2 ] = mc_cdev->subled_info[2 ].intensity; + led->rgb_set = true;spin_unlock_irqrestore (&led->lock, flags); -asus_schedule_work (led); +do_asus_kbd_backlight_set (led, brightness); +} + +static enum led_brightness asus_kbd_brightness_get (struct led_classdev *led_cdev) +{ +struct led_classdev_mc *mc_led; +struct asus_kbd_leds *led; +enum led_brightness brightness; +unsigned long flags; + + mc_led =lcdev_to_mccdev (led_cdev); + led = container_of(mc_led, struct asus_kbd_leds, mc_led); + +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) +static void asus_kbd_backlight_work (struct asus_kbd_leds *led) { - struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);u8 buf[] = {FEATURE_KBD_REPORT_ID ,0xba ,0xc5 ,0xc4 ,0x00 };int ret;unsigned long flags;@@ -534,10 +586,69 @@ static void asus_kbd_backlight_work(struct work_struct *work)hid_err (led->hdev,"Asus failed to set keyboard backlight: %d\n" , ret); } +static void asus_kbd_rgb_work (struct asus_kbd_leds *led) +{ +u8 rgb_buf[][7 ] = { + {FEATURE_KBD_LED_REPORT_ID1 ,0xB3 },/* set mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB5 },/* apply mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB4 },/* save to mem */ + }; +unsigned long flags; +uint8_t colors[3 ]; +bool rgb_init, rgb_set; +int ret; + +spin_lock_irqsave (&led->lock, flags); + rgb_init = led->rgb_init; + rgb_set = led->rgb_set; + led->rgb_set = false; + colors[0 ] = led->rgb_colors[0 ]; + colors[1 ] = led->rgb_colors[1 ]; + colors[2 ] = led->rgb_colors[2 ]; +spin_unlock_irqrestore (&led->lock, flags); + +if (!rgb_set) +return ; + +if (rgb_init) { + ret =asus_kbd_init (led->hdev,FEATURE_KBD_LED_REPORT_ID1 ); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to init RGB: %d\n" , ret); +return ; + } +spin_lock_irqsave (&led->lock, flags); + led->rgb_init = false; +spin_unlock_irqrestore (&led->lock, flags); + } + +/* Protocol is: 54b3 zone (0=all) mode (0=solid) RGB */ + rgb_buf[0 ][4 ] = colors[0 ]; + rgb_buf[0 ][5 ] = colors[1 ]; + rgb_buf[0 ][6 ] = colors[2 ]; + +for (size_t i =0 ; i <ARRAY_SIZE (rgb_buf); i++) { + ret =asus_kbd_set_report (led->hdev, rgb_buf[i],sizeof (rgb_buf[i])); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to set RGB: %d\n" , ret); +return ; + } + } +} + +static void asus_kbd_work (struct work_struct *work) +{ + struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, + work); +asus_kbd_backlight_work (led); +asus_kbd_rgb_work (led); +} +static int asus_kbd_register_leds (struct hid_device *hdev) {struct asus_drvdata *drvdata =hid_get_drvdata (hdev);unsigned char kbd_func; +struct asus_kbd_leds *leds; +bool no_led;int ret; ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID );@@ -565,21 +676,51 @@ static int asus_kbd_register_leds(struct hid_device *hdev)if (!drvdata->kbd_backlight)return -ENOMEM ; - drvdata->kbd_backlight->removed = false; - drvdata->kbd_backlight->brightness =0 ; - drvdata->kbd_backlight->hdev = hdev; - drvdata->kbd_backlight->listener.brightness_set = asus_kbd_backlight_set; -INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_backlight_work); + leds = drvdata->kbd_backlight; + leds->removed = false; + leds->brightness =3 ; + leds->hdev = hdev; + leds->listener.brightness_set = asus_kbd_listener_set; + + leds->rgb_colors[0 ] =0 ; + leds->rgb_colors[1 ] =0 ; + leds->rgb_colors[2 ] =0 ; + leds->rgb_init = true; + leds->rgb_set = false; + leds->mc_led.led_cdev.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,Quoted 8 lines
drivers/hid/hid-asus.c static int asus_kbd_register_leds(struct hid_device *hdev) · line 693
Quoted 3 lines
drivers/hid/hid-asus.c +3
-0+"asus-%s-led" , +strlen (hdev->uniq) ? + hdev->uniq : dev_name(&hdev->dev));A quick note. This breaks convention for LED names. The style guide is at
Documentation/leds/leds-class.rst. Per my parallel email to this one I would like to see the mentioned naming schemeasus:rgb:kbd_backlightadopted.Perhaps. It would be the first
kbd_backlightdriver to have "rgb" in it. It is a bit out of scope for this series as I do not touch the functionality of it but I can add a patch for it and a fixese305a71cea37a64c75tag.Quoted 4 lines
Expanding further on one of the points there you might need to move the
led_classdev_mcin to asus-wmi to fulfil having the single sysfs endpoint. Since you're using the listner pattern it shouldn't be much work.I only want the brightness to sync, not the color. Only the brightness between Aura devices needs to be the same. In this case
asus::kbd_backlightif it has a color controls the wmi color, and the asus- devices control the usb.Quoted 4 lines
Also, groups are not dynamic so this is not possible. E.g., if you setup a WMI listener that does not have RGB, and then the USB keyboard connects you can no longer change the groups unless you reconnect the device.
Sorry, I confused the patches. Yes you are right. I cannot do
kbd_backlightbecause userspace might pick the wrong handler. And with this patch series on the Z13 there are 3, one for the common backlight, one for the keyboard, and one for the lightbar.
Quoted 5 lines
I can do asus-UNIQ:rgb:peripheral though. There is no appropriate function that is close enough currently. Also, since there can be multiple devices, UNIQ or something similar needs to be added, otherwise the led handler will suffix _X.
That sounds appropriate yeah. Is there an issue with the suffix number being added? I've not encountered any myself.
Cheers.
Luke.
Quoted 56 lines
Antheas
Quoted 54 lines
Quoted 54 lines
Quoted 53 lines
drivers/hid/hid-asus.c +24
-4+ leds->mc_led.led_cdev.flags =LED_BRIGHT_HW_CHANGED ; + leds->mc_led.led_cdev.max_brightness =3 , + leds->mc_led.led_cdev.brightness_set = asus_kbd_brightness_set, + leds->mc_led.led_cdev.brightness_get = asus_kbd_brightness_get, + leds->mc_led.subled_info = leds->subled_info, + leds->mc_led.num_colors =ARRAY_SIZE (leds->subled_info), + leds->subled_info[0 ].color_index =LED_COLOR_ID_RED ; + leds->subled_info[1 ].color_index =LED_COLOR_ID_GREEN ; + leds->subled_info[2 ].color_index =LED_COLOR_ID_BLUE ; + +INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_work);spin_lock_init (&drvdata->kbd_backlight->lock); ret =asus_hid_register_listener (&drvdata->kbd_backlight->listener); + no_led = !!ret; + +if (drvdata->quirks &QUIRK_ROG_NKEY_RGB ) { + ret =devm_led_classdev_multicolor_register ( + &hdev->dev, &leds->mc_led); +if (!ret) + leds->rgb_registered = true; + no_led &= !!ret; + } -if (ret <0 ) { +if (no_led) {/* No need to have this still around */ devm_kfree (&hdev->dev, drvdata->kbd_backlight); } -return ret; +return no_led ? -ENODEV :0 ; }/* @@ -1289,7 +1430,7 @@ static const struct hid_device_id asus_devices[] = { QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, {HID_USB_DEVICE (USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY ), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },@@ -1318,7 +1459,7 @@ static const struct hid_device_id asus_devices[] = {*/ {HID_DEVICE (BUS_USB ,HID_GROUP_GENERIC ,USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD) }, { }
Luke D. Jones
maintainerRe: [PATCH v3 09/10] HID: asus: add basic RGB support
In reply to Antheas KapenekakisQuoted 223 lines · Antheas Kapenekakis
Quoted 213 lines · Luke D. Jones
Quoted 211 lines · Antheas Kapenekakis
Adds basic RGB support to hid-asus through multi-index. The interface works quite well, but has not gone through much stability testing. Applied on demand, if userspace does not touch the RGB sysfs, not even initialization is done. Ensuring compatibility with existing userspace programs.
Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 169 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 155 insertions(+), 14 deletions(-)drivers/hid/hid-asus.c +128
-10@@ -30,6 +30,7 @@#include <linux/input/mt.h> #include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */ #include <linux/power_supply.h> +#include <linux/led-class-multicolor.h> #include <linux/leds.h> #include "hid-ids.h" @@ -85,6 +86,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_HANDLE_GENERIC BIT(13) +#define QUIRK_ROG_NKEY_RGB BIT(14)#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \@@ -97,9 +99,15 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");struct asus_kbd_leds {struct asus_hid_listener listener; +struct led_classdev_mc mc_led; +struct mc_subled subled_info[3 ];struct hid_device *hdev;struct work_struct work;unsigned int brightness; +uint8_t rgb_colors[3 ]; +bool rgb_init; +bool rgb_set; +bool rgb_registered;spinlock_t lock;bool removed; };@@ -504,23 +512,67 @@ static void asus_schedule_work(struct asus_kbd_leds *led)spin_unlock_irqrestore (&led->lock, flags); } -static void asus_kbd_backlight_set (struct asus_hid_listener *listener, +static void do_asus_kbd_backlight_set (struct asus_kbd_leds *led,int brightness) +{ +unsigned long flags; + +spin_lock_irqsave (&led->lock, flags); + led->brightness = brightness; +spin_unlock_irqrestore (&led->lock, flags); + +asus_schedule_work (led); +} + +static void asus_kbd_listener_set (struct asus_hid_listener *listener,int brightness) { struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds, listener); +do_asus_kbd_backlight_set (led, brightness); +if (led->rgb_registered) { + led->mc_led.led_cdev.brightness = brightness; +led_classdev_notify_brightness_hw_changed (&led->mc_led.led_cdev, + brightness); + } +} + +static void asus_kbd_brightness_set (struct led_classdev *led_cdev, +enum led_brightness brightness) +{ +struct led_classdev_mc *mc_cdev =lcdev_to_mccdev (led_cdev); + struct asus_kbd_leds *led = container_of(mc_cdev, struct asus_kbd_leds, + mc_led);unsigned long flags;spin_lock_irqsave (&led->lock, flags); - led->brightness = brightness; + led->rgb_colors[0 ] = mc_cdev->subled_info[0 ].intensity; + led->rgb_colors[1 ] = mc_cdev->subled_info[1 ].intensity; + led->rgb_colors[2 ] = mc_cdev->subled_info[2 ].intensity; + led->rgb_set = true;spin_unlock_irqrestore (&led->lock, flags); -asus_schedule_work (led); +do_asus_kbd_backlight_set (led, brightness); +} + +static enum led_brightness asus_kbd_brightness_get (struct led_classdev *led_cdev) +{ +struct led_classdev_mc *mc_led; +struct asus_kbd_leds *led; +enum led_brightness brightness; +unsigned long flags; + + mc_led =lcdev_to_mccdev (led_cdev); + led = container_of(mc_led, struct asus_kbd_leds, mc_led); + +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) +static void asus_kbd_backlight_work (struct asus_kbd_leds *led) { - struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);u8 buf[] = {FEATURE_KBD_REPORT_ID ,0xba ,0xc5 ,0xc4 ,0x00 };int ret;unsigned long flags;@@ -534,10 +586,69 @@ static void asus_kbd_backlight_work(struct work_struct *work)hid_err (led->hdev,"Asus failed to set keyboard backlight: %d\n" , ret); } +static void asus_kbd_rgb_work (struct asus_kbd_leds *led) +{ +u8 rgb_buf[][7 ] = { + {FEATURE_KBD_LED_REPORT_ID1 ,0xB3 },/* set mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB5 },/* apply mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB4 },/* save to mem */ + }; +unsigned long flags; +uint8_t colors[3 ]; +bool rgb_init, rgb_set; +int ret; + +spin_lock_irqsave (&led->lock, flags); + rgb_init = led->rgb_init; + rgb_set = led->rgb_set; + led->rgb_set = false; + colors[0 ] = led->rgb_colors[0 ]; + colors[1 ] = led->rgb_colors[1 ]; + colors[2 ] = led->rgb_colors[2 ]; +spin_unlock_irqrestore (&led->lock, flags); + +if (!rgb_set) +return ; + +if (rgb_init) { + ret =asus_kbd_init (led->hdev,FEATURE_KBD_LED_REPORT_ID1 ); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to init RGB: %d\n" , ret); +return ; + } +spin_lock_irqsave (&led->lock, flags); + led->rgb_init = false; +spin_unlock_irqrestore (&led->lock, flags); + } + +/* Protocol is: 54b3 zone (0=all) mode (0=solid) RGB */ + rgb_buf[0 ][4 ] = colors[0 ]; + rgb_buf[0 ][5 ] = colors[1 ]; + rgb_buf[0 ][6 ] = colors[2 ]; + +for (size_t i =0 ; i <ARRAY_SIZE (rgb_buf); i++) { + ret =asus_kbd_set_report (led->hdev, rgb_buf[i],sizeof (rgb_buf[i])); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to set RGB: %d\n" , ret); +return ; + } + } +} + +static void asus_kbd_work (struct work_struct *work) +{ + struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, + work); +asus_kbd_backlight_work (led); +asus_kbd_rgb_work (led); +} +static int asus_kbd_register_leds (struct hid_device *hdev) {struct asus_drvdata *drvdata =hid_get_drvdata (hdev);unsigned char kbd_func; +struct asus_kbd_leds *leds; +bool no_led;int ret; ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID );@@ -565,21 +676,51 @@ static int asus_kbd_register_leds(struct hid_device *hdev)if (!drvdata->kbd_backlight)return -ENOMEM ; - drvdata->kbd_backlight->removed = false; - drvdata->kbd_backlight->brightness =0 ; - drvdata->kbd_backlight->hdev = hdev; - drvdata->kbd_backlight->listener.brightness_set = asus_kbd_backlight_set; -INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_backlight_work); + leds = drvdata->kbd_backlight; + leds->removed = false; + leds->brightness =3 ; + leds->hdev = hdev; + leds->listener.brightness_set = asus_kbd_listener_set; + + leds->rgb_colors[0 ] =0 ; + leds->rgb_colors[1 ] =0 ; + leds->rgb_colors[2 ] =0 ; + leds->rgb_init = true; + leds->rgb_set = false; + leds->mc_led.led_cdev.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,Quoted 8 lines
drivers/hid/hid-asus.c static int asus_kbd_register_leds(struct hid_device *hdev) · line 693
Quoted 3 lines
drivers/hid/hid-asus.c +3
-0+"asus-%s-led" , +strlen (hdev->uniq) ? + hdev->uniq : dev_name(&hdev->dev));A quick note. This breaks convention for LED names. The style guide is at
Documentation/leds/leds-class.rst. Per my parallel email to this one I would like to see the mentioned naming schemeasus:rgb:kbd_backlightadopted.
Quoted 5 lines
Perhaps. It would be the first
kbd_backlightdriver to have "rgb" in it. It is a bit out of scope for this series as I do not touch the functionality of it but I can add a patch for it and a fixese305a71cea37a64c75tag.
Your proposal for naming in other reply is good :)
If the intent is to keep 0-3 brightness and RGB controls separated the kbd_backlight doesn't need adjustment in naming, particularly if the RGB is *not* inside asus:rgb:kbd_backlight.
Quoted 10 lines
Quoted 4 lines
Expanding further on one of the points there you might need to move the
led_classdev_mcin to asus-wmi to fulfil having the single sysfs endpoint. Since you're using the listner pattern it shouldn't be much work.I only want the brightness to sync, not the color. Only the brightness between Aura devices needs to be the same. In this case
asus::kbd_backlightif it has a color controls the wmi color, and the asus- devices control the usb.
Hmm, what about multicolour brightness? Otherwise yeah, understood and I'm fine with that. Given you now ustilise the kbd_dill<up/down> directly I don't see any issues there either.
Quoted 4 lines
Also, groups are not dynamic so this is not possible. E.g., if you setup a WMI listener that does not have RGB, and then the USB keyboard connects you can no longer change the groups unless you reconnect the device.
That's a shame. Oh well.
I would have preferred RGB and brightness combined. At a glance I would have stored RGB in a global or similar, then if a listener has mcled available that value would be applied.
But userpsace should be using udev libs and similar to find devices like this, so naming is more of a hint alongside the attributes. Meaning name changes or including mcled inside standard led shouldn't break things. Neither should keeping them separated as you have.
Apologies for the bikeshedding on this. I had been reluctant to add RGB myself for a number of reasons:
1. Windows uses LampArray for the dynamic LED (new)
2. Otherwise you need to use MCU software mode, that's what that mode is for
3. I don't know of a capabilities request for MCU software mode
4. Or you're forced to set MCU mode static/solid
5. Single colour keybaords vs RGB, on same PID :(
I considered adding attributes to mcled in sysfs for mode/speed/direction. But then I had to add an enourmous table of modes-per-model.
At the end of the day I think your solution is fine and we don't have much other choice beyond trying to introduce a new API better suited for RGB keyboards with many features. I suspect it will also be fine for other laptops since the mode is set on write to RGB, so hidraw is still open to userspace.
With the other changes in place I'll experiment a little with the laptops I have and see how it works. I have two that i will need to check HID and WMI on even though it's not an issue for the Z13 and Ally, it might highlight any pain points and improve things further. Could be a few days, or coming weekend sorry, I've got a massive amount of work on, besides my own kernel patches.
Cheers,
Luke.
Quoted 54 lines
Quoted 54 lines
Quoted 53 lines
drivers/hid/hid-asus.c +24
-4+ leds->mc_led.led_cdev.flags =LED_BRIGHT_HW_CHANGED ; + leds->mc_led.led_cdev.max_brightness =3 , + leds->mc_led.led_cdev.brightness_set = asus_kbd_brightness_set, + leds->mc_led.led_cdev.brightness_get = asus_kbd_brightness_get, + leds->mc_led.subled_info = leds->subled_info, + leds->mc_led.num_colors =ARRAY_SIZE (leds->subled_info), + leds->subled_info[0 ].color_index =LED_COLOR_ID_RED ; + leds->subled_info[1 ].color_index =LED_COLOR_ID_GREEN ; + leds->subled_info[2 ].color_index =LED_COLOR_ID_BLUE ; + +INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_work);spin_lock_init (&drvdata->kbd_backlight->lock); ret =asus_hid_register_listener (&drvdata->kbd_backlight->listener); + no_led = !!ret; + +if (drvdata->quirks &QUIRK_ROG_NKEY_RGB ) { + ret =devm_led_classdev_multicolor_register ( + &hdev->dev, &leds->mc_led); +if (!ret) + leds->rgb_registered = true; + no_led &= !!ret; + } -if (ret <0 ) { +if (no_led) {/* No need to have this still around */ devm_kfree (&hdev->dev, drvdata->kbd_backlight); } -return ret; +return no_led ? -ENODEV :0 ; }/* @@ -1289,7 +1430,7 @@ static const struct hid_device_id asus_devices[] = { QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, {HID_USB_DEVICE (USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY ), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },@@ -1318,7 +1459,7 @@ static const struct hid_device_id asus_devices[] = {*/ {HID_DEVICE (BUS_USB ,HID_GROUP_GENERIC ,USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD) }, { }
Re: [PATCH v3 09/10] HID: asus: add basic RGB support
Quoted 256 lines · Luke D. Jones
Quoted 223 lines · Antheas Kapenekakis
Quoted 213 lines · Luke D. Jones
Quoted 211 lines · Antheas Kapenekakis
Adds basic RGB support to hid-asus through multi-index. The interface works quite well, but has not gone through much stability testing. Applied on demand, if userspace does not touch the RGB sysfs, not even initialization is done. Ensuring compatibility with existing userspace programs.
Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 169 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 155 insertions(+), 14 deletions(-)drivers/hid/hid-asus.c +128
-10@@ -30,6 +30,7 @@#include <linux/input/mt.h> #include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */ #include <linux/power_supply.h> +#include <linux/led-class-multicolor.h> #include <linux/leds.h> #include "hid-ids.h" @@ -85,6 +86,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_HANDLE_GENERIC BIT(13) +#define QUIRK_ROG_NKEY_RGB BIT(14)#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \@@ -97,9 +99,15 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");struct asus_kbd_leds {struct asus_hid_listener listener; +struct led_classdev_mc mc_led; +struct mc_subled subled_info[3 ];struct hid_device *hdev;struct work_struct work;unsigned int brightness; +uint8_t rgb_colors[3 ]; +bool rgb_init; +bool rgb_set; +bool rgb_registered;spinlock_t lock;bool removed; };@@ -504,23 +512,67 @@ static void asus_schedule_work(struct asus_kbd_leds *led)spin_unlock_irqrestore (&led->lock, flags); } -static void asus_kbd_backlight_set (struct asus_hid_listener *listener, +static void do_asus_kbd_backlight_set (struct asus_kbd_leds *led,int brightness) +{ +unsigned long flags; + +spin_lock_irqsave (&led->lock, flags); + led->brightness = brightness; +spin_unlock_irqrestore (&led->lock, flags); + +asus_schedule_work (led); +} + +static void asus_kbd_listener_set (struct asus_hid_listener *listener,int brightness) { struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds, listener); +do_asus_kbd_backlight_set (led, brightness); +if (led->rgb_registered) { + led->mc_led.led_cdev.brightness = brightness; +led_classdev_notify_brightness_hw_changed (&led->mc_led.led_cdev, + brightness); + } +} + +static void asus_kbd_brightness_set (struct led_classdev *led_cdev, +enum led_brightness brightness) +{ +struct led_classdev_mc *mc_cdev =lcdev_to_mccdev (led_cdev); + struct asus_kbd_leds *led = container_of(mc_cdev, struct asus_kbd_leds, + mc_led);unsigned long flags;spin_lock_irqsave (&led->lock, flags); - led->brightness = brightness; + led->rgb_colors[0 ] = mc_cdev->subled_info[0 ].intensity; + led->rgb_colors[1 ] = mc_cdev->subled_info[1 ].intensity; + led->rgb_colors[2 ] = mc_cdev->subled_info[2 ].intensity; + led->rgb_set = true;spin_unlock_irqrestore (&led->lock, flags); -asus_schedule_work (led); +do_asus_kbd_backlight_set (led, brightness); +} + +static enum led_brightness asus_kbd_brightness_get (struct led_classdev *led_cdev) +{ +struct led_classdev_mc *mc_led; +struct asus_kbd_leds *led; +enum led_brightness brightness; +unsigned long flags; + + mc_led =lcdev_to_mccdev (led_cdev); + led = container_of(mc_led, struct asus_kbd_leds, mc_led); + +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) +static void asus_kbd_backlight_work (struct asus_kbd_leds *led) { - struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);u8 buf[] = {FEATURE_KBD_REPORT_ID ,0xba ,0xc5 ,0xc4 ,0x00 };int ret;unsigned long flags;@@ -534,10 +586,69 @@ static void asus_kbd_backlight_work(struct work_struct *work)hid_err (led->hdev,"Asus failed to set keyboard backlight: %d\n" , ret); } +static void asus_kbd_rgb_work (struct asus_kbd_leds *led) +{ +u8 rgb_buf[][7 ] = { + {FEATURE_KBD_LED_REPORT_ID1 ,0xB3 },/* set mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB5 },/* apply mode */ + {FEATURE_KBD_LED_REPORT_ID1 ,0xB4 },/* save to mem */ + }; +unsigned long flags; +uint8_t colors[3 ]; +bool rgb_init, rgb_set; +int ret; + +spin_lock_irqsave (&led->lock, flags); + rgb_init = led->rgb_init; + rgb_set = led->rgb_set; + led->rgb_set = false; + colors[0 ] = led->rgb_colors[0 ]; + colors[1 ] = led->rgb_colors[1 ]; + colors[2 ] = led->rgb_colors[2 ]; +spin_unlock_irqrestore (&led->lock, flags); + +if (!rgb_set) +return ; + +if (rgb_init) { + ret =asus_kbd_init (led->hdev,FEATURE_KBD_LED_REPORT_ID1 ); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to init RGB: %d\n" , ret); +return ; + } +spin_lock_irqsave (&led->lock, flags); + led->rgb_init = false; +spin_unlock_irqrestore (&led->lock, flags); + } + +/* Protocol is: 54b3 zone (0=all) mode (0=solid) RGB */ + rgb_buf[0 ][4 ] = colors[0 ]; + rgb_buf[0 ][5 ] = colors[1 ]; + rgb_buf[0 ][6 ] = colors[2 ]; + +for (size_t i =0 ; i <ARRAY_SIZE (rgb_buf); i++) { + ret =asus_kbd_set_report (led->hdev, rgb_buf[i],sizeof (rgb_buf[i])); +if (ret <0 ) { +hid_err (led->hdev,"Asus failed to set RGB: %d\n" , ret); +return ; + } + } +} + +static void asus_kbd_work (struct work_struct *work) +{ + struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, + work); +asus_kbd_backlight_work (led); +asus_kbd_rgb_work (led); +} +static int asus_kbd_register_leds (struct hid_device *hdev) {struct asus_drvdata *drvdata =hid_get_drvdata (hdev);unsigned char kbd_func; +struct asus_kbd_leds *leds; +bool no_led;int ret; ret =asus_kbd_init (hdev,FEATURE_KBD_REPORT_ID );@@ -565,21 +676,51 @@ static int asus_kbd_register_leds(struct hid_device *hdev)if (!drvdata->kbd_backlight)return -ENOMEM ; - drvdata->kbd_backlight->removed = false; - drvdata->kbd_backlight->brightness =0 ; - drvdata->kbd_backlight->hdev = hdev; - drvdata->kbd_backlight->listener.brightness_set = asus_kbd_backlight_set; -INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_backlight_work); + leds = drvdata->kbd_backlight; + leds->removed = false; + leds->brightness =3 ; + leds->hdev = hdev; + leds->listener.brightness_set = asus_kbd_listener_set; + + leds->rgb_colors[0 ] =0 ; + leds->rgb_colors[1 ] =0 ; + leds->rgb_colors[2 ] =0 ; + leds->rgb_init = true; + leds->rgb_set = false; + leds->mc_led.led_cdev.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,Quoted 8 lines
drivers/hid/hid-asus.c static int asus_kbd_register_leds(struct hid_device *hdev) · line 693
Quoted 3 lines
drivers/hid/hid-asus.c +3
-0+"asus-%s-led" , +strlen (hdev->uniq) ? + hdev->uniq : dev_name(&hdev->dev));A quick note. This breaks convention for LED names. The style guide is at
Documentation/leds/leds-class.rst. Per my parallel email to this one I would like to see the mentioned naming schemeasus:rgb:kbd_backlightadopted.Quoted 5 lines
Perhaps. It would be the first
kbd_backlightdriver to have "rgb" in it. It is a bit out of scope for this series as I do not touch the functionality of it but I can add a patch for it and a fixese305a71cea37a64c75tag.Your proposal for naming in other reply is good :)
If the intent is to keep 0-3 brightness and RGB controls separated thekbd_backlightdoesn't need adjustment in naming, particularly if the RGB is *not* insideasus:rgb:kbd_backlight.Quoted 10 lines
Quoted 4 lines
Expanding further on one of the points there you might need to move the
led_classdev_mcin to asus-wmi to fulfil having the single sysfs endpoint. Since you're using the listner pattern it shouldn't be much work.I only want the brightness to sync, not the color. Only the brightness between Aura devices needs to be the same. In this case
asus::kbd_backlightif it has a color controls the wmi color, and the asus- devices control the usb.Hmm, what about multicolour brightness? Otherwise yeah, understood and I'm fine with that. Given you now ustilise the kbd_dill<up/down> directly I don't see any issues there either.
Quoted 4 lines
Also, groups are not dynamic so this is not possible. E.g., if you setup a WMI listener that does not have RGB, and then the USB keyboard connects you can no longer change the groups unless you reconnect the device.
Quoted 5 lines
That's a shame. Oh well.
I would have preferred RGB and brightness combined. At a glance I would have stored RGB in a global or similar, then if a listener has mcled available that value would be applied.
There is also a brightness var in the usb endpoint thats duplicated. So it can be controlled individually. But writing a brightness to asus::kbd_brightness or using the keyboard shortcut will override it. Only way I could think of to manage it.
Quoted 9 lines
But userpsace should be using udev libs and similar to find devices like this, so naming is more of a hint alongside the attributes. Meaning name changes or including mcled inside standard led shouldn't break things. Neither should keeping them separated as you have.
Apologies for the bikeshedding on this. I had been reluctant to add RGB myself for a number of reasons:
1. Windows uses LampArray for the dynamic LED (new)
Yeah i played a bit with dynamic lighting on windows on the Ally. It looks like a mess with Armoury crate and windows fighting over the leds.
Quoted 9 lines
2. Otherwise you need to use MCU software mode, that's what that mode is for
3. I don't know of a capabilities request for MCU software mode
4. Or you're forced to set MCU mode static/solid
5. Single colour keybaords vs RGB, on same PID :(I considered adding attributes to mcled in sysfs for mode/speed/direction. But then I had to add an enourmous table of modes-per-model.
Quoted 5 lines
At the end of the day I think your solution is fine and we don't have much other choice beyond trying to introduce a new API better suited for RGB keyboards with many features. I suspect it will also be fine for other laptops since the mode is set on write to RGB, so hidraw is still open to userspace.
I think solid is a great start. I found myself using it this week. Then getting the KDE guys to add a color picker.
Quoted 6 lines
With the other changes in place I'll experiment a little with the laptops I have and see how it works. I have two that i will need to check HID and WMI on even though it's not an issue for the Z13 and Ally, it might highlight any pain points and improve things further. Could be a few days, or coming weekend sorry, I've got a massive amount of work on, besides my own kernel patches.
That's alright with me. I plan to send a V4 middle of the week. I will try to include a patch that inits 0x5d that can be reverted later if it is not needed.
Also have some other stuff to deal with Bazzite. Mesa 25 was a big PITA
Antheas
Quoted 58 lines
Cheers,
Luke.Quoted 54 lines
Quoted 54 lines
Quoted 53 lines
drivers/hid/hid-asus.c +24
-4+ leds->mc_led.led_cdev.flags =LED_BRIGHT_HW_CHANGED ; + leds->mc_led.led_cdev.max_brightness =3 , + leds->mc_led.led_cdev.brightness_set = asus_kbd_brightness_set, + leds->mc_led.led_cdev.brightness_get = asus_kbd_brightness_get, + leds->mc_led.subled_info = leds->subled_info, + leds->mc_led.num_colors =ARRAY_SIZE (leds->subled_info), + leds->subled_info[0 ].color_index =LED_COLOR_ID_RED ; + leds->subled_info[1 ].color_index =LED_COLOR_ID_GREEN ; + leds->subled_info[2 ].color_index =LED_COLOR_ID_BLUE ; + +INIT_WORK (&drvdata->kbd_backlight->work, asus_kbd_work);spin_lock_init (&drvdata->kbd_backlight->lock); ret =asus_hid_register_listener (&drvdata->kbd_backlight->listener); + no_led = !!ret; + +if (drvdata->quirks &QUIRK_ROG_NKEY_RGB ) { + ret =devm_led_classdev_multicolor_register ( + &hdev->dev, &leds->mc_led); +if (!ret) + leds->rgb_registered = true; + no_led &= !!ret; + } -if (ret <0 ) { +if (no_led) {/* No need to have this still around */ devm_kfree (&hdev->dev, drvdata->kbd_backlight); } -return ret; +return no_led ? -ENODEV :0 ; }/* @@ -1289,7 +1430,7 @@ static const struct hid_device_id asus_devices[] = { QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, {HID_USB_DEVICE (USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY ), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },@@ -1318,7 +1459,7 @@ static const struct hid_device_id asus_devices[] = {*/ {HID_DEVICE (BUS_USB ,HID_GROUP_GENERIC ,USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD) }, { }
PATCH v3 10/10 HID: asus: add RGB support to the ROG Ally units
In reply to Antheas KapenekakisApply the RGB quirk to the QOG Ally units to enable basic RGB support.
--- drivers/hid/hid-asus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/hid/hid-asus.c +2 -2
@@ -1433,10 +1433,10 @@ static const struct hid_device_id asus_devices[] = { QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, {HID_USB_DEVICE (USB_VENDOR_ID_ASUSTEK ,USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD ), QUIRK_ROG_CLAYMORE_II_KEYBOARD },
Signature · 1 line
2.48.1
Re: [PATCH v3 10/10] HID: asus: add RGB support to the ROG Ally units
Quoted 14 lines · Antheas Kapenekakis
Apply the RGB quirk to the QOG Ally units to enable basic RGB support.
Signed-off-by Antheas Kapenekakis <[email protected]>--- drivers/hid/hid-asus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)drivers/hid/hid-asus.c +0
-0@@ -1433,10 +1433,10 @@ static const struct hid_device_id asus_devices[] = { QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
drivers/hid/hid-asus.c static const struct hid_device_id asus_devices[] = { · line 1442
Quoted 10 lines
drivers/hid/hid-asus.c +2
-2USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X ), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD ), QUIRK_ROG_CLAYMORE_II_KEYBOARD },
This is fine. We can deal with reverting later if required, but even with my hid-asus-ally work as is, it doesn't affect it.