Download raw body.
acpi wmi asus driver
> From: "Ted Unangst" <tedu@tedunangst.com>
> Date: Sat, 10 May 2025 21:57:07 -0400
>
> My newish ASUS laptop needs WMI to handle hotkeys like backlight toggle.
>
> More importantly, for me, it's needed to handle the Fn-F hotkey to switch
> fan/performance profiles. The system is far more pleasant to use in whisper
> mode. I also notice a substantial improvement in battery life, without much
> performance difference. It affects the power limits, but more long term I
> think.
>
> Tested with both an AMD zenbook and Intel vivobook.
>
> Anyway, there's tons of other devices that use WMI as well. They will need
> their own WMI handlers, but I think this is a start. It's a little chatty,
> and maybe not fully knf, but perhaps it can be beaten into submission?
Probably worth cleaning this up.
> I include just acpiwmi.c for reading.
>
>
> /* $OpenBSD$ */
> /*
> * Copyright (c) 2025 Ted Unangst <tedu@openbsd.org>
> *
> * Permission to use, copy, modify, and distribute this software for any
> * purpose with or without fee is hereby granted, provided that the above
> * copyright notice and this permission notice appear in all copies.
> *
> * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> */
>
> #include <sys/param.h>
> #include <sys/systm.h>
> #include <sys/device.h>
>
> #include <machine/intr.h>
> #include <machine/bus.h>
>
> #include <dev/acpi/acpireg.h>
> #include <dev/acpi/acpivar.h>
> #include <dev/acpi/acpidev.h>
> #include <dev/acpi/amltypes.h>
> #include <dev/acpi/dsdt.h>
>
> struct acpiwmi_softc {
> struct device sc_dev;
> bus_space_tag_t sc_iot;
> bus_space_handle_t sc_ioh;
>
> struct acpi_softc *sc_acpi;
> struct aml_node *sc_node;
>
> SLIST_HEAD(, wmihandler) sc_handlers;
> };
>
> struct wmihandler {
> SLIST_ENTRY(wmihandler) w_next;
> struct acpiwmi_softc *w_sc;
> int (*w_event)(struct wmihandler *, int);
> char w_method[5];
> };
>
> int acpiwmi_match(struct device *, void *, void *);
> void acpiwmi_attach(struct device *, struct device *, void *);
> int acpiwmi_activate(struct device *, int);
> int acpiwmi_notify(struct aml_node *, int, void *);
>
> const struct cfattach acpiwmi_ca = {
> sizeof (struct acpiwmi_softc), acpiwmi_match, acpiwmi_attach,
> NULL, acpiwmi_activate
> };
>
> struct cfdriver acpiwmi_cd = {
> NULL, "acpiwmi", DV_DULL
> };
>
> const char *acpiwmi_hids[] = {
> "PNP0C14",
> NULL
> };
>
> int
> acpiwmi_match(struct device *parent, void *match, void *aux)
> {
> struct acpi_attach_args *aaa = aux;
> struct cfdata *cf = match;
>
> return acpi_matchhids(aaa, acpiwmi_hids, cf->cf_driver->cd_name);
> }
>
> struct guidinfo {
> uint8_t guid[16];
> uint8_t oid[2];
> uint8_t maxinstance;
> uint8_t flags;
> };
>
> #define WMI_EXPENSIVE 1
> #define WMI_METHOD 2
> #define WMI_STRING 4
> #define WMI_EVENT 8
>
> static char *
> guid_string(const uint8_t *guid, char *buf)
> {
> size_t space = 64; /* xxx */
> char *p = buf;
> int i = 0;
>
> /* 3210-54-76-89-012345 */
> for (i = 0; i < 4; i++) { // 3210
> snprintf(p, space, "%02X", guid[0 + 3 - i]);
> p += 2;
> }
> *p++ = '-';
> for (i = 0; i < 2; i++) { // 54
> snprintf(p, space, "%02X", guid[4 + 1 - i]);
> p += 2;
> }
> *p++ = '-';
> for (i = 0; i < 2; i++) { // 76
> snprintf(p, space, "%02X", guid[6 + 1 - i]);
> p += 2;
> }
> *p++ = '-';
> for (i = 0; i < 2; i++) { // 89
> snprintf(p, space, "%02X", guid[8 + i]);
> p += 2;
> }
> *p++ = '-';
> for (i = 0; i < 6; i++) { // 012345
> snprintf(p, space, "%02X", guid[10 + i]);
> p += 2;
> }
> return buf;
> }
>
> static int wmi_asus_init(struct acpiwmi_softc *, struct guidinfo *);
> static int wmi_asus_event(struct wmihandler *, int);
>
> struct wmi_target {
> uint8_t guid[16];
> int (*init)(struct acpiwmi_softc *, struct guidinfo *);
> };
>
> static struct wmi_target targets[] = {
> {
> /* "97845ED0-4E6D-11DE-8A39-0800200C9A66" */
> { 0xd0, 0x5e, 0x84, 0x97, 0x6d, 0x4e, 0xde, 0x11,
> 0x8a, 0x39, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 },
> wmi_asus_init,
> },
> };
>
> void
> acpiwmi_attach(struct device *parent, struct device *self, void *aux)
> {
> struct acpiwmi_softc *sc = (struct acpiwmi_softc *)self;
> struct acpi_attach_args *aaa = aux;
> struct aml_value res;
>
> sc->sc_acpi = (struct acpi_softc *)parent;
> sc->sc_node = aaa->aaa_node;
> SLIST_INIT(&sc->sc_handlers);
>
> printf(": %s", aaa->aaa_node->name);
>
> if (aml_evalname(sc->sc_acpi, sc->sc_node, "_WDG", 0, NULL, &res) == -1) {
> printf(": error\n");
> return;
> }
> printf("\n");
>
> int num = res.length / sizeof(struct guidinfo);
> struct guidinfo *guids = (struct guidinfo *)res.v_buffer;
> char buf[64];
> for (int i = 0; i < num; i++) {
> printf("method: %s\n", guid_string(guids[i].guid, buf));
> for (int j = 0; j < 1; j++) {
> if (memcmp(guids[i].guid, targets[j].guid, 16) == 0) {
> printf("found it! flags %d [%d, %d]\n", guids[i].flags,
> guids[i].oid[0], guids[i].oid[1]);
> wmi_asus_init(sc, &guids[i]);
> }
> }
> }
> aml_freevalue(&res);
>
> aml_register_notify(sc->sc_node, aaa->aaa_dev,
> acpiwmi_notify, sc, ACPIDEV_NOPOLL);
> }
>
> int
> acpiwmi_notify(struct aml_node *node, int note, void *arg)
> {
> struct aml_value input, res;
> struct acpiwmi_softc *sc = arg;
> struct wmihandler *wh;
>
> memset(&input, 0, sizeof(input));
> input.type = AML_OBJTYPE_INTEGER;
> input.v_integer = note; /* ??? */
> if (aml_evalname(sc->sc_acpi, sc->sc_node, "_WED", 1, &input, &res) == -1) {
> printf("trouble eval wed\n");
> return 0;
> }
> int code = res.v_integer;
> code &= 0xffff;
> aml_freevalue(&res);
> SLIST_FOREACH(wh, &sc->sc_handlers, w_next)
> wh->w_event(wh, code);
> return 0;
> }
>
> int
> acpiwmi_activate(struct device *self, int act)
> {
>
> return 0;
> }
>
> static int
> wmi_eval_method(struct wmihandler *wh, int32_t instance, uint32_t methodid,
> struct aml_value *input, struct aml_value *res)
> {
> struct aml_value params[3];
> struct acpiwmi_softc *sc = wh->w_sc;
>
> memset(¶ms, 0, sizeof(params));
> params[0].type = AML_OBJTYPE_INTEGER;
> params[0].v_integer = instance;
> params[1].type = AML_OBJTYPE_INTEGER;
> params[1].v_integer = methodid;
> params[2] = *input;
> if (aml_evalname(sc->sc_acpi, sc->sc_node, wh->w_method, 3, params, res) == -1) {
> printf("can't eval method\n");
> return -1;
> }
> return 0;
> }
>
> struct wmiasus {
> struct wmihandler w_handler;
> int w_backlight;
> int w_fnlock;
> int w_perf;
> int w_perfid;
> };
>
> #define ASUS_METHOD_INIT 0x54494E49
> #define ASUS_METHOD_DSTS 0x53545344
> #define ASUS_METHOD_DEVS 0x53564544
>
> #define ASUS_DEV_RSOC 0x00120057
> #define ASUS_DEV_ALS 0x00050001
> #define ASUS_DEV_PERF 0x00120075
> #define ASUS_DEV_PERF_2 0x00110019
> #define ASUS_DEV_BACKLIGHT 0x00050021
> #define ASUS_DEV_FNLOCK 0x00100023
>
> #define ASUS_EVENT_AC_OFF 0x57
> #define ASUS_EVENT_AC_ON 0x58
> #define ASUS_EVENT_BACKLIGHT 0xc7
> #define ASUS_EVENT_FNLOCK 0x4e
> #define ASUS_EVENT_PERF 0x9d
>
> static int
> asus_eval_method(struct wmiasus *wh, uint32_t methodid,
> uint32_t arg0, uint32_t arg1, uint32_t *retval)
> {
> int args[6] = { arg0, arg1, 0 };
> struct aml_value input, res;
>
> memset(&input, 0, sizeof(input));
> input.type = AML_OBJTYPE_BUFFER;
> input.length = sizeof(args);
> input.v_buffer = (void *)args;
> if (wmi_eval_method(&wh->w_handler, 0, methodid, &input, &res) == -1)
> return -1;
> if (retval)
> *retval = res.v_integer;
> aml_freevalue(&res);
> return 0;
> }
> static int
> asus_dev_get(struct wmiasus *wh, uint32_t devid, uint32_t *retval)
> {
> return asus_eval_method(wh, ASUS_METHOD_DSTS, devid, 0, retval);
> }
>
> static int
> asus_dev_set(struct wmiasus *wh, uint32_t devid, uint32_t val)
> {
> uint32_t retval;
> if (asus_eval_method(wh, ASUS_METHOD_DEVS, devid, val, &retval) == -1)
> return -1;
> return retval;
> }
> static void
> asus_toggle(struct wmiasus *wh, int devid, int *val)
> {
> int maxval = 1, mask = 0;
>
> switch (devid) {
> case ASUS_DEV_BACKLIGHT:
> mask = 0x80;
> break;
> case ASUS_DEV_PERF:
> case ASUS_DEV_PERF_2:
> maxval = 2;
> break;
> }
> *val += 1;
> if (*val > maxval)
> *val = 0;
> asus_dev_set(wh, devid, *val | mask);
> }
>
> static int
> wmi_asus_init(struct acpiwmi_softc *sc, struct guidinfo *ginfo)
> {
> uint32_t retval;
> struct wmiasus *wh;
>
> wh = malloc(sizeof(*wh), M_DEVBUF, M_NOWAIT | M_ZERO);
> if (!wh)
> return -1;
> wh->w_handler.w_sc = sc;
> snprintf(wh->w_handler.w_method, sizeof(wh->w_handler.w_method), "WM%c%c",
> ginfo->oid[0], ginfo->oid[1]);
>
> if (asus_eval_method(wh, ASUS_METHOD_INIT, 0, 0, &retval) == -1 ||
> retval != 1) {
> printf("cant init\n");
> return -1;
> }
> wh->w_handler.w_event = wmi_asus_event;
> SLIST_INSERT_HEAD(&sc->sc_handlers, &wh->w_handler, w_next);
>
> struct aml_value on;
> memset(&on, 0, sizeof(on));
> on.type = AML_OBJTYPE_INTEGER;
> on.v_integer = 1;
> char method[5];
> snprintf(method, sizeof(method), "WC%c%c", ginfo->oid[0], ginfo->oid[1]);
> aml_evalname(sc->sc_acpi, sc->sc_node, method, 1, &on, NULL);
> uint32_t res = 0;
> asus_dev_get(wh, ASUS_DEV_BACKLIGHT, &res);
> if (res == -2)
> printf("no led\n");
> else
> printf("led: %d\n", res & 0xffff);
> asus_dev_get(wh, ASUS_DEV_PERF, &res);
> if (res == -2)
> printf("no perf pol\n");
> else {
> printf("perf pol: %d\n", res & 0xffff);
> wh->w_perfid = ASUS_DEV_PERF;
> }
> asus_dev_get(wh, ASUS_DEV_PERF_2, &res);
> if (res == -2)
> printf("no perf pol\n");
> else {
> printf("perf pol: %d\n", res & 0xffff);
> wh->w_perfid = ASUS_DEV_PERF_2;
> }
> wh->w_backlight = 1; // defaults on
> asus_toggle(wh, ASUS_DEV_FNLOCK, &wh->w_fnlock); // turn on
> return 0;
> }
>
> int
> wmi_asus_event(struct wmihandler *wmi, int code)
> {
> struct wmiasus *wh = (struct wmiasus *)wmi;
>
> switch (code) {
> case ASUS_EVENT_AC_OFF:
> case ASUS_EVENT_AC_ON:
> break;
> case ASUS_EVENT_BACKLIGHT:
> asus_toggle(wh, ASUS_DEV_BACKLIGHT, &wh->w_backlight);
> printf("toggle backlight %d\n", wh->w_backlight);
> break;
> case ASUS_EVENT_FNLOCK:
> asus_toggle(wh, ASUS_DEV_FNLOCK, &wh->w_fnlock);
> printf("toggle fnlock %d\n", wh->w_fnlock);
> break;
> case ASUS_EVENT_PERF:
> asus_toggle(wh, wh->w_perfid, &wh->w_perf);
> printf("toggle perf %d\n", wh->w_perf);
> break;
> default:
> printf("asus button %d\n", code);
> break;
> }
> return 0;
> }
>
>
acpi wmi asus driver