diff options
| author | Jiri Kosina <jkosina@suse.com> | 2026-02-09 17:32:14 +0100 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.com> | 2026-02-09 17:32:14 +0100 |
| commit | 984d6f361d19486fcd8fc13d29112fe73123f482 (patch) | |
| tree | 88a00c32976e70b59de14414bb7ceb084973b7e7 | |
| parent | e4aa247d94a04574297a8bc9fabbede0dcba1ab6 (diff) | |
| parent | f631011e36b87b173b71c7592b558ad05d791228 (diff) | |
Merge branch 'for-6.20/asus' into for-linus
- Support for WMI (Fn+F5) fan control for Asus ROG laptops (Ionut Nechita)
- fn-lock support for Asus ProArt P16 (Connor Belli)
| -rw-r--r-- | drivers/hid/hid-asus.c | 110 | ||||
| -rw-r--r-- | include/linux/platform_data/x86/asus-wmi.h | 1 |
2 files changed, 109 insertions, 2 deletions
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index a444d41e53b6..6180217139d8 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -23,6 +23,7 @@ /* */ +#include <linux/acpi.h> #include <linux/dmi.h> #include <linux/hid.h> #include <linux/module.h> @@ -56,6 +57,16 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); #define ROG_ALLY_X_MIN_MCU 313 #define ROG_ALLY_MIN_MCU 319 +/* Spurious HID codes sent by QUIRK_ROG_NKEY_KEYBOARD devices */ +#define ASUS_SPURIOUS_CODE_0XEA 0xea +#define ASUS_SPURIOUS_CODE_0XEC 0xec +#define ASUS_SPURIOUS_CODE_0X02 0x02 +#define ASUS_SPURIOUS_CODE_0X8A 0x8a +#define ASUS_SPURIOUS_CODE_0X9E 0x9e + +/* Special key codes */ +#define ASUS_FAN_CTRL_KEY_CODE 0xae + #define SUPPORT_KBD_BACKLIGHT BIT(0) #define MAX_TOUCH_MAJOR 8 @@ -89,6 +100,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); #define QUIRK_ROG_NKEY_KEYBOARD BIT(11) #define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12) #define QUIRK_ROG_ALLY_XPAD BIT(13) +#define QUIRK_HID_FN_LOCK BIT(14) #define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \ @@ -132,6 +144,8 @@ struct asus_drvdata { int battery_stat; bool battery_in_query; unsigned long battery_next_query; + struct work_struct fn_lock_sync_work; + bool fn_lock; }; static int asus_report_battery(struct asus_drvdata *, u8 *, int); @@ -313,16 +327,47 @@ static int asus_e1239t_event(struct asus_drvdata *drvdat, u8 *data, int size) return 0; } +/* + * Send events to asus-wmi driver for handling special keys + */ +static int asus_wmi_send_event(struct asus_drvdata *drvdata, u8 code) +{ + int err; + u32 retval; + + err = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, + ASUS_WMI_METHODID_NOTIF, code, &retval); + if (err) { + pr_warn("Failed to notify asus-wmi: %d\n", err); + return err; + } + + if (retval != 0) { + pr_warn("Failed to notify asus-wmi (retval): 0x%x\n", retval); + return -EIO; + } + + return 0; +} + static int asus_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { - if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 && + struct asus_drvdata *drvdata = hid_get_drvdata(hdev); + + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ASUSVENDOR && (usage->hid & HID_USAGE) != 0x00 && (usage->hid & HID_USAGE) != 0xff && !usage->type) { hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n", usage->hid & HID_USAGE); } + if (drvdata->quirks & QUIRK_HID_FN_LOCK && + usage->type == EV_KEY && usage->code == KEY_FN_ESC && value == 1) { + drvdata->fn_lock = !drvdata->fn_lock; + schedule_work(&drvdata->fn_lock_sync_work); + } + return 0; } @@ -347,6 +392,43 @@ static int asus_raw_event(struct hid_device *hdev, if (report->id == FEATURE_KBD_LED_REPORT_ID1 || report->id == FEATURE_KBD_LED_REPORT_ID2) return -1; if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) { + if (report->id == FEATURE_KBD_REPORT_ID) { + /* + * Fn+F5 fan control key - try to send WMI event to toggle fan mode. + * If successful, block the event from reaching userspace. + * If asus-wmi is unavailable or the call fails, let the event + * pass to userspace so it can implement its own fan control. + */ + if (data[1] == ASUS_FAN_CTRL_KEY_CODE) { + int ret = asus_wmi_send_event(drvdata, ASUS_FAN_CTRL_KEY_CODE); + + if (ret == 0) { + /* Successfully handled by asus-wmi, block event */ + return -1; + } + + /* + * Warn if asus-wmi failed (but not if it's unavailable). + * Let the event reach userspace in all failure cases. + */ + if (ret != -ENODEV) + hid_warn(hdev, "Failed to notify asus-wmi: %d\n", ret); + } + + /* + * ASUS ROG laptops send these codes during normal operation + * with no discernable reason. Filter them out to avoid + * unmapped warning messages. + */ + if (data[1] == ASUS_SPURIOUS_CODE_0XEA || + data[1] == ASUS_SPURIOUS_CODE_0XEC || + data[1] == ASUS_SPURIOUS_CODE_0X02 || + data[1] == ASUS_SPURIOUS_CODE_0X8A || + data[1] == ASUS_SPURIOUS_CODE_0X9E) { + return -1; + } + } + /* * G713 and G733 send these codes on some keypresses, depending on * the key pressed it can trigger a shutdown event if not caught. @@ -457,6 +539,21 @@ static int asus_kbd_disable_oobe(struct hid_device *hdev) return 0; } +static int asus_kbd_set_fn_lock(struct hid_device *hdev, bool enabled) +{ + u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xd0, 0x4e, !!enabled }; + + return asus_kbd_set_report(hdev, buf, sizeof(buf)); +} + +static void asus_sync_fn_lock(struct work_struct *work) +{ + struct asus_drvdata *drvdata = + container_of(work, struct asus_drvdata, fn_lock_sync_work); + + asus_kbd_set_fn_lock(drvdata->hdev, drvdata->fn_lock); +} + static void asus_schedule_work(struct asus_kbd_leds *led) { unsigned long flags; @@ -928,6 +1025,12 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi) asus_kbd_register_leds(hdev)) hid_warn(hdev, "Failed to initialize backlight.\n"); + if (drvdata->quirks & QUIRK_HID_FN_LOCK) { + drvdata->fn_lock = true; + INIT_WORK(&drvdata->fn_lock_sync_work, asus_sync_fn_lock); + asus_kbd_set_fn_lock(hdev, true); + } + return 0; } @@ -1259,6 +1362,9 @@ static void asus_remove(struct hid_device *hdev) cancel_work_sync(&drvdata->kbd_backlight->work); } + if (drvdata->quirks & QUIRK_HID_FN_LOCK) + cancel_work_sync(&drvdata->fn_lock_sync_work); + hid_hw_stop(hdev); } @@ -1386,7 +1492,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_NKEY_KEYBOARD2), - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_HID_FN_LOCK }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR), QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 8a515179113d..e23b481ab118 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -27,6 +27,7 @@ #define ASUS_WMI_METHODID_KBFT 0x5446424B /* KeyBoard FilTer */ #define ASUS_WMI_METHODID_INIT 0x54494E49 /* INITialize */ #define ASUS_WMI_METHODID_HKEY 0x59454B48 /* Hot KEY ?? */ +#define ASUS_WMI_METHODID_NOTIF 0x00100021 /* Notify method */ #define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE |
