git: d5bf6a7245cc - main - acpi_asus_wmi(4): Add support for WMI event queue

From: Vladimir Kondratyev <wulf_at_FreeBSD.org>
Date: Mon, 17 Mar 2025 15:46:59 UTC
The branch main has been updated by wulf:

URL: https://cgit.FreeBSD.org/src/commit/?id=d5bf6a7245cc7825d17ebb00d1e7f07ae6dc32e6

commit d5bf6a7245cc7825d17ebb00d1e7f07ae6dc32e6
Author:     Vladimir Kondratyev <wulf@FreeBSD.org>
AuthorDate: 2025-03-17 15:45:14 +0000
Commit:     Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2025-03-17 15:45:14 +0000

    acpi_asus_wmi(4): Add support for WMI event queue
    
    Event codes are expected to be retrieved from a queue on at least some
    models. Specifically, very likely the ACPI WMI devices with _UID ATK are
    queued whereas those with ASUSWMI are not.
    
    Sponsored by:   Future Crew LLC
    MFC after:      1 month
    Reviewed by:    mav
    Differential Revision:  https://reviews.freebsd.org/D48984
---
 sys/dev/acpi_support/acpi_asus_wmi.c | 89 ++++++++++++++++++++++++++++++------
 1 file changed, 76 insertions(+), 13 deletions(-)

diff --git a/sys/dev/acpi_support/acpi_asus_wmi.c b/sys/dev/acpi_support/acpi_asus_wmi.c
index 968de5fe5e87..e3d16b9345ee 100644
--- a/sys/dev/acpi_support/acpi_asus_wmi.c
+++ b/sys/dev/acpi_support/acpi_asus_wmi.c
@@ -112,6 +112,12 @@ ACPI_MODULE_NAME("ASUS-WMI")
 #define ASUS_WMI_DSTS_BRIGHTNESS_MASK   0x000000FF
 #define ASUS_WMI_DSTS_MAX_BRIGTH_MASK   0x0000FF00
 
+/* Events */
+#define ASUS_WMI_EVENT_QUEUE_SIZE	0x10
+#define ASUS_WMI_EVENT_QUEUE_END	0x1
+#define ASUS_WMI_EVENT_MASK		0xFFFF
+#define ASUS_WMI_EVENT_VALUE_ATK	0xFF
+
 struct acpi_asus_wmi_softc {
 	device_t	dev;
 	device_t	wmi_dev;
@@ -120,6 +126,7 @@ struct acpi_asus_wmi_softc {
 	struct sysctl_oid	*sysctl_tree;
 	int		dsts_id;
 	int		handle_keys;
+	bool		event_queue;
 	struct cdev	*kbd_bkl;
 	uint32_t	kbd_bkl_level;
 #ifdef EVDEV_SUPPORT
@@ -363,6 +370,8 @@ static int	acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc,
 		    UINT32 dev_id, UINT32 *retval);
 static int	acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc,
 		    UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval);
+static int	acpi_asus_wmi_get_event_code(device_t wmi_dev, UINT32 notify,
+		    int *code);
 static void	acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context);
 static int	acpi_asus_wmi_backlight_update_status(device_t dev,
 		    struct backlight_props *props);
@@ -463,7 +472,7 @@ acpi_asus_wmi_attach(device_t dev)
 {
 	struct acpi_asus_wmi_softc	*sc;
 	UINT32			val;
-	int			dev_id, i;
+	int			dev_id, i, code;
 	bool			have_kbd_bkl = false;
 
 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
@@ -577,6 +586,23 @@ next:
 	}
 	ACPI_SERIAL_END(asus_wmi);
 
+	/* Detect and flush event queue */
+	if (sc->dsts_id == ASUS_WMI_METHODID_DSTS2) {
+		for (i = 0; i <= ASUS_WMI_EVENT_QUEUE_SIZE; i++) {
+			if (acpi_asus_wmi_get_event_code(sc->wmi_dev,
+			    ASUS_WMI_EVENT_VALUE_ATK, &code) != 0) {
+				device_printf(dev,
+				    "Can not flush event queue\n");
+				break;
+			}
+			if (code == ASUS_WMI_EVENT_QUEUE_END ||
+			    code == ASUS_WMI_EVENT_MASK) {
+				sc->event_queue = true;
+				break;
+			}
+		}
+	}
+
 #ifdef EVDEV_SUPPORT
 	if (sc->notify_guid != NULL) {
 		sc->evdev = evdev_alloc();
@@ -746,6 +772,24 @@ acpi_asus_wmi_free_buffer(ACPI_BUFFER* buf) {
 	}
 }
 
+static int
+acpi_asus_wmi_get_event_code(device_t wmi_dev, UINT32 notify, int *code)
+{
+	ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL };
+	ACPI_OBJECT *obj;
+	int error = 0;
+
+	if (ACPI_FAILURE(ACPI_WMI_GET_EVENT_DATA(wmi_dev, notify, &response)))
+		return (EIO);
+	obj = (ACPI_OBJECT*) response.Pointer;
+	if (obj && obj->Type == ACPI_TYPE_INTEGER)
+		*code = obj->Integer.Value & ASUS_WMI_EVENT_MASK;
+	else
+		error = EINVAL;
+	acpi_asus_wmi_free_buffer(&response);
+	return (error);
+}
+
 #ifdef EVDEV_SUPPORT
 static void
 acpi_asus_wmi_push_evdev_event(struct evdev_dev *evdev, UINT32 notify)
@@ -768,20 +812,11 @@ acpi_asus_wmi_push_evdev_event(struct evdev_dev *evdev, UINT32 notify)
 #endif
 
 static void
-acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context)
+acpi_asus_wmi_handle_event(struct acpi_asus_wmi_softc *sc, int code)
 {
-	device_t dev = context;
-	ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
 	UINT32 val;
-	int code = 0;
 
-	struct acpi_asus_wmi_softc *sc = device_get_softc(dev);
-	ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL };
-	ACPI_OBJECT *obj;
-	ACPI_WMI_GET_EVENT_DATA(sc->wmi_dev, notify, &response);
-	obj = (ACPI_OBJECT*) response.Pointer;
-	if (obj && obj->Type == ACPI_TYPE_INTEGER) {
-		code = obj->Integer.Value;
+	if (code != 0) {
 		acpi_UserNotify("ASUS", ACPI_ROOT_OBJECT,
 		    code);
 #ifdef EVDEV_SUPPORT
@@ -814,7 +849,35 @@ acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context)
 			    ASUS_WMI_DEVID_TOUCHPAD, val, NULL);
 		}
 	}
-	acpi_asus_wmi_free_buffer(&response);
+}
+
+static void
+acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context)
+{
+	device_t dev = context;
+	struct acpi_asus_wmi_softc *sc = device_get_softc(dev);
+	int code = 0, i = 1;
+
+	ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
+
+	if (sc->event_queue)
+		i += ASUS_WMI_EVENT_QUEUE_SIZE;
+	do {
+		if (acpi_asus_wmi_get_event_code(sc->wmi_dev, notify, &code)
+		    != 0) {
+			device_printf(dev, "Failed to get event code\n");
+			return;
+	        }
+		if (code == ASUS_WMI_EVENT_QUEUE_END ||
+		    code == ASUS_WMI_EVENT_MASK)
+			return;
+		acpi_asus_wmi_handle_event(sc, code);
+		if (notify != ASUS_WMI_EVENT_VALUE_ATK)
+			return;
+	} while (--i != 0);
+	if (sc->event_queue)
+		device_printf(dev, "Can not read event queue, "
+		    "last code: 0x%x\n", code);
 }
 
 static int