On Sat, 22 Mar 2025 at 05:31, Luke D. Jones <luke@xxxxxxxxxx> wrote: > > On 21/03/25 11:09, Antheas Kapenekakis wrote: > > 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 <lkml@xxxxxxxxxxx> > > --- > > drivers/platform/x86/asus-wmi.c | 38 ++++++++++++++++++++++ > > include/linux/platform_data/x86/asus-wmi.h | 11 +++++++ > > 2 files changed, 49 insertions(+) > > > > diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c > > index 21e034be71b2f..45999dda9e7ed 100644 > > --- a/drivers/platform/x86/asus-wmi.c > > +++ b/drivers/platform/x86/asus-wmi.c > > @@ -1529,6 +1529,44 @@ void asus_brt_unregister_listener(struct asus_brt_listener *bdev) > > } > > EXPORT_SYMBOL_GPL(asus_brt_unregister_listener); > > > > +static void do_kbd_led_set(struct led_classdev *led_cdev, int value); > > + > > +int asus_brt_event(enum asus_brt_event event) > > +{ > > + int brightness; > > + > > + mutex_lock(&asus_brt_lock); > > + if (!asus_brt_ref || !asus_brt_ref->kbd_led_registered) { > > + mutex_unlock(&asus_brt_lock); > > + return -EBUSY; > > + } > > + brightness = asus_brt_ref->kbd_led_wk; > > + > > + switch (event) { > > + case ASUS_BRT_UP: > > + brightness += 1; > > + break; > > + case ASUS_BRT_DOWN: > > + brightness -= 1; > > + break; > > + case ASUS_BRT_TOGGLE: > > + if (brightness >= 3) > > + brightness = 0; > > + else > > + brightness += 1; > > + break; > > + } > > + > > + do_kbd_led_set(&asus_brt_ref->kbd_led, brightness); > > + led_classdev_notify_brightness_hw_changed(&asus_brt_ref->kbd_led, > > + asus_brt_ref->kbd_led_wk); > > + > > + mutex_unlock(&asus_brt_lock); > > + > > + return 0; > > +} > > +EXPORT_SYMBOL_GPL(asus_brt_event); > > + > > I quick test on 6.14-rc7 gives me this: > > [ 288.039255] BUG: sleeping function called from invalid context at > kernel/locking/mutex.c:258 > [ 288.039262] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 0, > name: swapper/17 > [ 288.039263] preempt_count: 101, expected: 0 > [ 288.039264] RCU nest depth: 0, expected: 0 > [ 288.039266] CPU: 17 UID: 0 PID: 0 Comm: swapper/17 Tainted: G > W 6.14.0-rc7+ #164 > [ 288.039268] Tainted: [W]=WARN > [ 288.039269] Hardware name: ASUSTeK COMPUTER INC. ROG Zephyrus M16 > GU604VY_GU604VY_00130747B/GU604VY, BIOS GU604VY.313 08/13/2024 > [ 288.039270] Call Trace: > [ 288.039272] <IRQ> > [ 288.039273] dump_stack_lvl+0x5d/0x80 > [ 288.039277] __might_resched.cold+0xba/0xc9 > [ 288.039282] mutex_lock+0x19/0x40 > [ 288.039285] asus_brt_event+0x13/0xb0 [asus_wmi] > [ 288.039292] asus_event+0x91/0xa0 [hid_asus] > [ 288.039295] hid_process_event+0xae/0x120 > [ 288.039298] hid_input_array_field+0x132/0x180 > [ 288.039300] hid_report_raw_event+0x360/0x4e0 > [ 288.039302] __hid_input_report.constprop.0+0xf1/0x180 > [ 288.039304] hid_irq_in+0x17f/0x1b0 > [ 288.039306] __usb_hcd_giveback_urb+0x98/0x110 > [ 288.039308] usb_giveback_urb_bh+0xbd/0x150 > [ 288.039310] process_one_work+0x171/0x290 > [ 288.039312] bh_worker+0x1ac/0x210 > [ 288.039314] tasklet_hi_action+0xe/0x30 > [ 288.039315] handle_softirqs+0xdb/0x1f0 > [ 288.039317] __irq_exit_rcu+0xc2/0xe0 > [ 288.039318] common_interrupt+0x85/0xa0 > [ 288.039320] </IRQ> > [ 288.039320] <TASK> > [ 288.039321] asm_common_interrupt+0x26/0x40 > [ 288.039323] RIP: 0010:cpuidle_enter_state+0xb9/0x2c0 > [ 288.039325] Code: 40 0f 84 1b 01 00 00 e8 35 e8 13 ff e8 40 f2 ff ff > 31 ff 49 89 c5 e8 c6 f0 12 ff 45 84 f6 0f 85 f2 00 00 00 fb 0f 1f 44 00 > 00 <45> 85 ff 0f 88 cf 00 00 00 49 63 f7 48 8d 04 76 48 8d 04 86 49 8d > [ 288.039326] RSP: 0018:ffffc90000253e90 EFLAGS: 00000246 > [ 288.039328] RAX: ffff888890680000 RBX: 0000000000000003 RCX: > 0000000000000000 > [ 288.039329] RDX: 00000043107862af RSI: fffffffd616e8210 RDI: > 0000000000000000 > [ 288.039329] RBP: ffff8888906ba370 R08: 0000000000000000 R09: > 0000000000000007 > [ 288.039330] R10: ffff88888ffa6098 R11: 0000000000000008 R12: > ffffffff82fd4140 > [ 288.039331] R13: 00000043107862af R14: 0000000000000000 R15: > 0000000000000003 > [ 288.039332] cpuidle_enter+0x28/0x40 > [ 288.039334] do_idle+0x1a8/0x200 > [ 288.039336] cpu_startup_entry+0x24/0x30 > [ 288.039337] start_secondary+0x11e/0x140 > [ 288.039340] common_startup_64+0x13e/0x141 > [ 288.039342] </TASK> > > I think you need to swap the mutex to a spin_lock_irqsave and associated > spin_unlock_irqrestore plus DEFINE_SPINLOCK(asus_brt_lock). > > I'm out of time tonight but I'll apply the above to your patches and > report back tomorrow if you don't get to it before I do. > > It might be worth checking any other mutex uses in the LED path too. Thank you for catching that, I will replace the mutex with a spinlock. Might have to do with the WMI method being active as I got no such issue in my device. I guess I will try to do a v3 today as that will hold back our kernel too. > Cheers, > Luke. > > > /* > > * These functions actually update the LED's, and are called from a > > * workqueue. By doing this as separate work rather than when the LED > > diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h > > index add04524031d8..2ac7912d8acd3 100644 > > --- a/include/linux/platform_data/x86/asus-wmi.h > > +++ b/include/linux/platform_data/x86/asus-wmi.h > > @@ -162,11 +162,18 @@ struct asus_brt_listener { > > void (*notify)(struct asus_brt_listener *listener, int brightness); > > }; > > > > +enum asus_brt_event { > > + ASUS_BRT_UP, > > + ASUS_BRT_DOWN, > > + ASUS_BRT_TOGGLE, > > +}; > > + > > #if IS_REACHABLE(CONFIG_ASUS_WMI) > > int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval); > > > > int asus_brt_register_listener(struct asus_brt_listener *cdev); > > void asus_brt_unregister_listener(struct asus_brt_listener *cdev); > > +int asus_brt_event(enum asus_brt_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_brt_register_listener(struct asus_brt_listener *bdev) > > static inline void asus_brt_unregister_listener(struct asus_brt_listener *bdev) > > { > > } > > +static inline int asus_brt_event(enum asus_brt_event event) > > +{ > > + return -ENODEV; > > +} > > #endif > > > > #endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */ >