summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.com>2026-02-09 17:32:14 +0100
committerJiri Kosina <jkosina@suse.com>2026-02-09 17:32:14 +0100
commit984d6f361d19486fcd8fc13d29112fe73123f482 (patch)
tree88a00c32976e70b59de14414bb7ceb084973b7e7
parente4aa247d94a04574297a8bc9fabbede0dcba1ab6 (diff)
parentf631011e36b87b173b71c7592b558ad05d791228 (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.c110
-rw-r--r--include/linux/platform_data/x86/asus-wmi.h1
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