tech@,

Here is an initial implementation of a generic acpi wmi framework and a
single consumer for the framework that lets the volume adjustment keys
on an asus ux31e work.

The generic framework could be used to support hotkeys found in
different acer, dell, hp, msi, other laptops.

This is by no means complete as wmi can do all sorts of other stupid
things like blink leds, toggle radios, control backlights, etc. The code
has some style(9) issues.

Looking for feedback before I go any further.


Index: arch/amd64/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/amd64/conf/GENERIC,v
retrieving revision 1.352
diff -u -p -r1.352 GENERIC
--- arch/amd64/conf/GENERIC     4 Nov 2013 14:07:15 -0000       1.352
+++ arch/amd64/conf/GENERIC     17 Nov 2013 05:53:40 -0000
@@ -58,6 +58,8 @@ acpitoshiba*  at acpi?
 acpivideo*     at acpi?
 acpivout*      at acpivideo?
 acpipwrres*    at acpi?
+acpiwmi*       at acpi?
+acpiwmi_asus*  at acpiwmi?
 aibs*          at acpi?
 
 mpbios0                at bios0
Index: dev/acpi/acpi.c
===================================================================
RCS file: /cvs/src/sys/dev/acpi/acpi.c,v
retrieving revision 1.247
diff -u -p -r1.247 acpi.c
--- dev/acpi/acpi.c     6 Nov 2013 10:40:36 -0000       1.247
+++ dev/acpi/acpi.c     17 Nov 2013 05:53:42 -0000
@@ -2429,7 +2429,8 @@ acpi_foundhid(struct aml_node *node, voi
            !strcmp(dev, ACPI_DEV_TOSHIBA_SPA40)) {
                aaa.aaa_name = "acpitoshiba";
                acpi_toshiba_enabled = 1;
-       }
+       } else if (!strcmp(dev, ACPI_DEV_WMI))
+               aaa.aaa_name = "acpiwmi";
 
 
        if (aaa.aaa_name)
Index: dev/acpi/acpidev.h
===================================================================
RCS file: /cvs/src/sys/dev/acpi/acpidev.h,v
retrieving revision 1.33
diff -u -p -r1.33 acpidev.h
--- dev/acpi/acpidev.h  13 Jul 2012 10:37:40 -0000      1.33
+++ dev/acpi/acpidev.h  17 Nov 2013 05:53:42 -0000
@@ -22,6 +22,7 @@
 #include <sys/sensors.h>
 #include <sys/rwlock.h>
 #include <dev/acpi/acpireg.h>
+#include <dev/acpi/amltypes.h>
 
 #define DEVNAME(s)  ((s)->sc_dev.dv_xname)
 
@@ -337,4 +338,48 @@ struct acpiec_softc {
 
 void           acpibtn_disable_psw(void);
 void           acpibtn_enable_psw(void);
+
+struct acpiwmi_guid {
+       char guid[16];
+       union {
+               char object_id[2];
+               struct {
+                       unsigned char notify_id;
+                       unsigned char reserved;
+               };
+       };
+       uint8_t instance_count;
+       uint8_t flags;
+#define ACPI_WMI_EXPENSIVE   0x1
+#define ACPI_WMI_METHOD      0x2       /* GUID is a method */
+#define ACPI_WMI_STRING      0x4       /* GUID takes & returns a string */
+#define ACPI_WMI_EVENT       0x8       /* GUID is an event */
+};
+
+typedef void (*wmi_notify_handler) (uint32_t, void *);
+
+struct acpiwmi_block {
+       struct acpiwmi_guid guid_block;
+       char guid_string[37];
+       /* acpi_handle handle; */
+       wmi_notify_handler handler;
+       void *handler_data;
+       struct device dev;
+
+       SIMPLEQ_ENTRY(acpiwmi_block) wmi_link;
+};
+
+struct acpiwmi_softc {
+       struct device           sc_dev;
+
+       struct acpi_softc       *sc_acpi;
+       struct aml_node         *sc_devnode;
+
+       SIMPLEQ_HEAD(, acpiwmi_block) wmi_head;
+};
+
+int acpiwmi_guid_match(struct device *, const char *);
+int acpiwmi_install_notify_handler(struct acpiwmi_softc *, const char *, 
wmi_notify_handler, void *);
+int acpiwmi_evaluate_method(struct acpiwmi_softc *, const char *, uint8_t, 
uint32_t, const struct aml_value *, struct aml_value *);
+int acpiwmi_get_event_data(struct acpiwmi_softc *, uint32_t, struct aml_value 
*);
 #endif /* __DEV_ACPI_ACPIDEV_H__ */
Index: dev/acpi/acpireg.h
===================================================================
RCS file: /cvs/src/sys/dev/acpi/acpireg.h,v
retrieving revision 1.29
diff -u -p -r1.29 acpireg.h
--- dev/acpi/acpireg.h  6 Nov 2013 10:40:36 -0000       1.29
+++ dev/acpi/acpireg.h  17 Nov 2013 05:53:42 -0000
@@ -728,6 +728,7 @@ struct acpi_ivrs {
 #define ACPI_DEV_LD    "PNP0C0D"       /* Lid Device */
 #define ACPI_DEV_SBD   "PNP0C0E"       /* Sleep Button Device */
 #define ACPI_DEV_PILD  "PNP0C0F"       /* PCI Interrupt Link Device */
+#define ACPI_DEV_WMI   "pnp0c14"       /* Windows Management Instrumentation */
 #define ACPI_DEV_MEMD  "PNP0C80"       /* Memory Device */
 #define ACPI_DEV_SHC   "ACPI0001"      /* SMBus 1.0 Host Controller */
 #define ACPI_DEV_SMS1  "ACPI0002"      /* Smart Battery Subsystem */
Index: dev/acpi/acpiwmi.c
===================================================================
RCS file: dev/acpi/acpiwmi.c
diff -N dev/acpi/acpiwmi.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/acpi/acpiwmi.c  17 Nov 2013 05:53:42 -0000
@@ -0,0 +1,389 @@
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/workq.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>
+
+int    acpiwmi_match(struct device *, void *, void *);
+void   acpiwmi_attach(struct device *, struct device *, void *);
+int    acpiwmi_activate(struct device *, int);
+
+int    acpiwmi_foundwdg(struct aml_node *, void *);
+int    acpiwmi_gtoa(const char *, char *);
+void   acpiwmi_dump_wdg(const struct acpiwmi_guid *);
+int    acpiwmi_ec_notify(struct aml_node *, int, void *);
+int    acpiwmi_notify(struct aml_node *, int, void *);
+int    acpiwmi_attach_others(char *, void *);
+int    acpiwmi_print(void *, const char *);
+
+
+int    acpiwmi_method_enable(struct acpiwmi_softc *, struct acpiwmi_block *, 
int);
+bool   acpiwmi_find_guid(struct acpiwmi_softc *, const char *, struct 
acpiwmi_block **);
+
+struct cfattach acpiwmi_ca = {
+       sizeof(struct acpiwmi_softc),
+       acpiwmi_match,
+       acpiwmi_attach,
+       acpiwmi_activate
+};
+
+struct cfdriver acpiwmi_cd = {
+       NULL, "acpiwmi", DV_DULL
+};
+
+const char *acpiwmi_hids[] = { ACPI_DEV_WMI, 0 };
+
+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));
+}
+
+int
+acpiwmi_gtoa(const char *in, char *out)
+{
+       int i;
+
+       for (i = 3; i >= 0; i--)
+               out += snprintf(out, 3, "%02X", in[i] & 0xFF);
+
+       out += snprintf(out, 2, "-");
+       out += snprintf(out, 3, "%02X", in[5] & 0xFF);
+       out += snprintf(out, 3, "%02X", in[4] & 0xFF);
+       out += snprintf(out, 2, "-");
+       out += snprintf(out, 3, "%02X", in[7] & 0xFF);
+       out += snprintf(out, 3, "%02X", in[6] & 0xFF);
+       out += snprintf(out, 2, "-");
+       out += snprintf(out, 3, "%02X", in[8] & 0xFF);
+       out += snprintf(out, 3, "%02X", in[9] & 0xFF);
+       out += snprintf(out, 2, "-");
+
+       for (i = 10; i <= 15; i++)
+               out += snprintf(out, 3, "%02X", in[i] & 0xFF);
+
+       *out = '\0';
+       return 0;
+}
+
+void
+acpiwmi_dump_wdg(const struct acpiwmi_guid *g)
+{
+       char guid_string[37];
+
+       acpiwmi_gtoa(g->guid, guid_string);
+
+       printf("%s:\n", guid_string);
+       printf("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]);
+       printf("\tnotify_id: %02X\n", g->notify_id);
+       printf("\treserved: %02X\n", g->reserved);
+       printf("\tinstance_count: %d\n", g->instance_count);
+       printf("\tflags: %#x", g->flags);
+       if (g->flags) {
+               if (g->flags & ACPI_WMI_EXPENSIVE)
+                       printf(" ACPI_WMI_EXPENSIVE");
+               if (g->flags & ACPI_WMI_METHOD)
+                       printf(" ACPI_WMI_METHOD");
+               if (g->flags & ACPI_WMI_STRING)
+                       printf(" ACPI_WMI_STRING");
+               if (g->flags & ACPI_WMI_EVENT)
+                       printf(" ACPI_WMI_EVENT");
+       }
+       printf("\n");
+}
+
+int
+acpiwmi_notify(struct aml_node *node, int event, void *arg)
+{
+       struct acpiwmi_softc *sc = arg;
+       struct acpiwmi_block *wblock;
+       struct acpiwmi_guid *block;
+
+       SIMPLEQ_FOREACH(wblock, &sc->wmi_head, wmi_link) {
+               block = &wblock->guid_block;
+               if ((block->flags & ACPI_WMI_EVENT) &&
+                               (block->notify_id == event)) {
+                       if (wblock->handler)
+                               wblock->handler(event, wblock->handler_data);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+int
+acpiwmi_print(void *aux, const char *pnp)
+{
+       struct acpi_attach_args *aa = aux;
+
+       if (pnp) {
+               if (aa->aaa_name)
+                       printf("%s at %s", aa->aaa_name, pnp);
+               else
+                       return (QUIET);
+       }
+
+       return (UNCONF);
+}
+
+int
+acpiwmi_attach_others(char *name, void *arg)
+{
+       struct acpiwmi_softc    *sc = (struct acpiwmi_softc *)arg;
+       struct device           *self = (struct device *)arg;
+       struct acpi_attach_args aaa;
+
+       memset(&aaa, 0, sizeof(aaa));
+       aaa.aaa_iot = sc->sc_acpi->sc_iot;
+       aaa.aaa_memt = sc->sc_acpi->sc_memt;
+       aaa.aaa_node = sc->sc_devnode;
+       aaa.aaa_name = name;
+
+       config_found(self, &aaa, acpiwmi_print);
+
+       return (0);
+}
+
+int
+acpiwmi_foundwdg(struct aml_node *node, void *arg)
+{
+       struct acpiwmi_softc    *sc = (struct acpiwmi_softc *)arg;
+       struct aml_value         res;
+       struct acpiwmi_block    *wblock;
+       const struct acpiwmi_guid *gblock;
+       uint32_t                 i, total_blocks;
+       size_t                   guid_size;
+
+       if (aml_evalnode(sc->sc_acpi, node, 0, NULL, &res) != 0)
+               return 1;
+
+       if (res.type != AML_OBJTYPE_BUFFER)
+               return 1;
+
+       guid_size = sizeof(struct acpiwmi_guid);
+       total_blocks = res.length / guid_size;
+       printf(": _WDG ");
+
+       gblock = (const struct acpiwmi_guid *)res.v_buffer;
+
+       SIMPLEQ_INIT(&sc->wmi_head);
+
+       for (i = 0; i < total_blocks; i++) {
+
+               /* acpiwmi_dump_wdg(&gblock[i]); */
+
+               wblock = malloc(sizeof(struct acpiwmi_block), M_DEVBUF, 
+                               M_ZERO|M_WAITOK);
+
+               memcpy(&wblock->guid_block, &gblock[i], guid_size);
+               acpiwmi_gtoa(gblock[i].guid, wblock->guid_string);
+
+               /* XXX sometimes there might be duplicate guids */
+               SIMPLEQ_INSERT_TAIL(&sc->wmi_head, wblock, wmi_link);
+       }
+
+       aml_freevalue(&res);
+
+       return 0;
+}
+
+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;
+
+       sc->sc_acpi = (struct acpi_softc *)parent;
+       sc->sc_devnode = aaa->aaa_node;
+
+       aml_find_node(sc->sc_devnode, "_WDG", acpiwmi_foundwdg, sc);
+
+       aml_register_notify(sc->sc_devnode, aaa->aaa_dev,
+               acpiwmi_notify, sc, ACPIDEV_NOPOLL);
+
+       printf("\n");
+
+       acpiwmi_attach_others("acpiwmi_asus", sc);
+}
+
+int
+acpiwmi_activate(struct device *self, int act)
+{
+       return 0;
+}
+
+/*
+ * @@@: External API
+ */
+int
+acpiwmi_guid_match(struct device *self, const char *guid)
+{
+       struct acpiwmi_softc *sc = (struct acpiwmi_softc *)self;
+       struct acpiwmi_block *wmi;
+
+       SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link)
+               if (strncmp(guid, wmi->guid_string, 36) == 0)
+                       return 1;
+
+       return 0;
+}
+
+int
+acpiwmi_method_enable(struct acpiwmi_softc *sc, struct acpiwmi_block *wblock,
+               int enable)
+{
+       struct acpiwmi_guid *block = NULL;
+       char method[5];
+       struct aml_value val;
+
+       block = &wblock->guid_block;
+
+       if (!block)
+               return -1;
+
+       val.type = AML_OBJTYPE_INTEGER;
+       val.v_integer = enable;
+
+       snprintf(method, 5, "WE%02X", block->notify_id);
+
+       aml_evalname(sc->sc_acpi, sc->sc_devnode, method, 1, &val, NULL);
+
+       return 0;
+}
+
+int
+acpiwmi_install_notify_handler(struct acpiwmi_softc *sc, const char *guid,
+       wmi_notify_handler handler, void *data)
+{
+       struct acpiwmi_block *block;
+
+       if (!guid || !handler)
+               return -1;
+
+       SIMPLEQ_FOREACH(block, &sc->wmi_head, wmi_link) {
+               if (strncmp(block->guid_string, guid, 36) == 0) {
+                       if (block->handler)
+                               return -1;
+
+                       block->handler = handler;
+                       block->handler_data = data;
+
+                       acpiwmi_method_enable(sc, block, 1);
+               }
+       }
+
+       return 0;
+}
+
+bool
+acpiwmi_find_guid(struct acpiwmi_softc *sc, const char *guid, struct 
acpiwmi_block **out)
+{
+       struct acpiwmi_block *wblock;
+
+       SIMPLEQ_FOREACH(wblock, &sc->wmi_head, wmi_link) {
+               if (strncmp(guid, wblock->guid_string, 36) == 0)
+                       if (out)
+                               *out = wblock;
+                       return 1;
+       }
+
+       return 0;
+}
+
+int
+acpiwmi_evaluate_method(struct acpiwmi_softc *sc, const char *guid_string, 
uint8_t instance,
+    uint32_t method_id, const struct aml_value *in, struct aml_value *out)
+{
+       struct acpiwmi_guid *block = NULL;
+       struct acpiwmi_block *wblock = NULL;
+       char method[5];
+       int argc;
+       struct aml_value argv[3];
+
+       if (!acpiwmi_find_guid(sc, guid_string, &wblock))
+               return -1;
+
+       block = &wblock->guid_block;
+
+       if (!(block->flags & ACPI_WMI_METHOD))
+               return -1;
+
+       if (block->instance_count < instance)
+               return -1;
+
+       argc = 2;
+       argv[0].type = AML_OBJTYPE_INTEGER;
+       argv[0].v_integer = instance;
+       argv[1].type = AML_OBJTYPE_INTEGER;
+       argv[1].v_integer = method_id;
+
+       /* this needs to be initialized even if argc == 2 */
+       argv[2].type = AML_OBJTYPE_BUFFER;
+       argv[2].length = 0;
+       argv[2].v_buffer = NULL;
+
+#if 0
+       if (in) {
+               /* input.count = 3; */
+               argc = 3;
+
+               if (block->flags & ACPI_WMI_STRING) {
+                       /* params[2].type = ACPI_TYPE_STRING; */
+                       argv[2].type = AML_OBJTYPE_STRING;
+                       argv[2].v_string = in->v_string;
+               } else {
+                       /* params[2].type = ACPI_TYPE_BUFFER; */
+                       argv[2].type = AML_OBJTYPE_BUFFER;
+                       argv[2].v_buffer = in->v_buffer;
+               }
+               /*
+               params[2].buffer.length = in->length;
+               params[2].buffer.pointer = in->pointer;
+               */
+               argv[2].length = in->length;
+       }
+#endif
+
+       /* strncat(method, block->object_id, 2); */
+       snprintf(method, 5, "WM%s", block->object_id);
+
+       /* status = acpi_evaluate_object(handle, method, &input, out); */
+       aml_evalname(sc->sc_acpi, sc->sc_devnode, method, argc, argv, out);
+
+       /* return status; */
+       return 0;
+}
+
+int
+acpiwmi_get_event_data(struct acpiwmi_softc *sc, uint32_t event, 
+               struct aml_value *out)
+{
+       struct aml_value in;
+       struct acpiwmi_guid *gblock;
+       struct acpiwmi_block *wblock;
+
+       in.type = AML_OBJTYPE_INTEGER;
+       in.v_integer = event;
+
+       SIMPLEQ_FOREACH(wblock, &sc->wmi_head, wmi_link) {
+               gblock = &wblock->guid_block;
+
+               if ((gblock->flags & ACPI_WMI_EVENT) &&
+                       (gblock->notify_id == event)) {
+                       aml_evalname(sc->sc_acpi, sc->sc_devnode, "_WED",
+                                       1, &in, out);
+                       return 0;
+               }
+       }
+
+       return -1;
+}
Index: dev/acpi/acpiwmi_asus.c
===================================================================
RCS file: dev/acpi/acpiwmi_asus.c
diff -N dev/acpi/acpiwmi_asus.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/acpi/acpiwmi_asus.c     17 Nov 2013 05:53:42 -0000
@@ -0,0 +1,252 @@
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/systm.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>
+
+#include "audio.h"
+#include "wskbd.h"
+
+#define ACPI_ASUS_WMI_MGMT_GUID        "97845ED0-4E6D-11DE-8A39-0800200C9A66"
+#define ACPI_ASUS_WMI_EVENT_GUID       "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C"
+
+/* WMI Methods */
+#define ASUS_WMI_METHODID_SPEC          0x43455053
+#define ASUS_WMI_METHODID_SFUN          0x4E554653
+#define ASUS_WMI_METHODID_DSTS          0x53544344
+#define ASUS_WMI_METHODID_DSTS2         0x53545344
+#define ASUS_WMI_METHODID_DEVS          0x53564544
+#define ASUS_WMI_METHODID_INIT          0x54494E49
+#define ASUS_WMI_METHODID_HKEY          0x59454B48
+
+#define ASUS_WMI_UNSUPPORTED_METHOD     0xFFFFFFFE
+
+/* Wireless */
+#define ASUS_WMI_DEVID_HW_SWITCH        0x00010001
+#define ASUS_WMI_DEVID_WIRELESS_LED     0x00010002
+#define ASUS_WMI_DEVID_CWAP             0x00010003
+#define ASUS_WMI_DEVID_WLAN             0x00010011
+#define ASUS_WMI_DEVID_BLUETOOTH        0x00010013
+#define ASUS_WMI_DEVID_GPS              0x00010015
+#define ASUS_WMI_DEVID_WIMAX            0x00010017
+#define ASUS_WMI_DEVID_WWAN3G           0x00010019
+#define ASUS_WMI_DEVID_UWB              0x00010021
+
+/* LEDs */
+#define ASUS_WMI_DEVID_LED1             0x00020011
+#define ASUS_WMI_DEVID_LED2             0x00020012
+#define ASUS_WMI_DEVID_LED3             0x00020013
+#define ASUS_WMI_DEVID_LED4             0x00020014
+#define ASUS_WMI_DEVID_LED5             0x00020015
+#define ASUS_WMI_DEVID_LED6             0x00020016
+
+/* Backlight and Brightness */
+#define ASUS_WMI_DEVID_BACKLIGHT        0x00050011
+#define ASUS_WMI_DEVID_BRIGHTNESS       0x00050012
+#define ASUS_WMI_DEVID_KBD_BACKLIGHT    0x00050021
+#define ASUS_WMI_DEVID_LIGHT_SENSOR     0x00050022
+
+/* Misc */
+#define ASUS_WMI_DEVID_CAMERA           0x00060013
+#define ASUS_WMI_DEVID_CARDREADER       0x00080013
+#define ASUS_WMI_DEVID_TOUCHPAD         0x00100011
+#define ASUS_WMI_DEVID_TOUCHPAD_LED     0x00100012
+#define ASUS_WMI_DEVID_THERMAL_CTRL     0x00110011
+#define ASUS_WMI_DEVID_FAN_CTRL         0x00110012
+#define ASUS_WMI_DEVID_PROCESSOR_STATE  0x00120012
+
+/* DSTS masks */
+#define ASUS_WMI_DSTS_STATUS_BIT        0x00000001
+#define ASUS_WMI_DSTS_UNKNOWN_BIT       0x00000002
+#define ASUS_WMI_DSTS_PRESENCE_BIT      0x00010000
+#define ASUS_WMI_DSTS_USER_BIT          0x00020000
+#define ASUS_WMI_DSTS_BIOS_BIT          0x00040000
+#define ASUS_WMI_DSTS_BRIGHTNESS_MASK   0x000000FF
+#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK   0x0000FF00
+
+/* Keymap */
+#define ASUS_WMI_KEY_VOLUMEUP          0x30
+#define ASUS_WMI_KEY_VOLUMEDOWN                0x31
+#define ASUS_WMI_KEY_MUTE              0x32
+
+struct acpiwmi_asus_softc {
+       struct device           sc_dev;
+
+       struct acpi_softc       *sc_acpi;
+       struct aml_node         *sc_devnode;
+       struct acpiwmi_softc    *sc_parent;
+};
+
+int    acpiwmi_asus_match(struct device *, void *, void *);
+void   acpiwmi_asus_attach(struct device *, struct device *, void *);
+int    acpiwmi_asus_activate(struct device *, int);
+/*
+int    acpiwmi_asus_detach(device_t, int);
+void   acpiwmi_asus_notify_handler(ACPI_HANDLE, uint32_t, void *);
+bool   acpiwmi_asus_suspend(device_t, const pmf_qual_t *);
+bool   acpiwmi_asus_resume(device_t, const pmf_qual_t *);
+*/
+int    asus_wmi_evaluate_method(struct acpiwmi_asus_softc *, uint32_t, 
uint32_t, uint32_t, uint32_t *);
+void   asus_wmi_notify(uint32_t, void *);
+
+#if NAUDIO > 0 && NWSKBD > 0
+extern int wskbd_set_mixervolume(long, long);
+#endif
+
+struct cfattach acpiwmi_asus_ca = {
+       sizeof(struct acpiwmi_asus_softc),
+       acpiwmi_asus_match,
+       acpiwmi_asus_attach,
+       acpiwmi_asus_activate
+};
+
+struct cfdriver acpiwmi_asus_cd = {
+       NULL, "acpiwmi_asus", DV_DULL
+};
+
+int
+acpiwmi_asus_match(struct device *parent, void *match, void *aux)
+{
+       return acpiwmi_guid_match(parent, ACPI_ASUS_WMI_MGMT_GUID);
+}
+
+void
+acpiwmi_asus_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct acpiwmi_softc    *parent_sc = (struct acpiwmi_softc *)parent;
+       struct acpiwmi_asus_softc *sc = (struct acpiwmi_asus_softc *)self;
+       struct acpi_attach_args *aaa = aux;
+       int rv;
+
+       sc->sc_acpi = parent_sc->sc_acpi;
+       sc->sc_parent = parent_sc;
+       sc->sc_devnode = aaa->aaa_node;
+
+       if (!asus_wmi_evaluate_method(sc, ASUS_WMI_METHODID_INIT, 0, 0, &rv))
+               printf(": init: %#x", rv);
+
+       /*
+       if (!asus_wmi_evaluate_method(sc, ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv))
+               printf(" BIOS WMI version: %d.%d", rv >> 16, rv & 0xFF);
+       */
+
+       acpiwmi_install_notify_handler(parent_sc, ACPI_ASUS_WMI_EVENT_GUID,
+                                           asus_wmi_notify, sc);
+
+       printf("\n");
+}
+
+struct bios_args {
+       uint32_t arg0;
+       uint32_t arg1;
+} __packed;
+
+int
+asus_wmi_evaluate_method(struct acpiwmi_asus_softc *sc,
+    uint32_t method_id, uint32_t arg0, uint32_t arg1, uint32_t *retval)
+{
+       struct bios_args args = {
+               .arg0 = arg0,
+               .arg1 = arg1,
+       };
+       /*
+       struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
+       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+       acpi_status status;
+       union acpi_object *obj;
+       u32 tmp;
+       */
+       struct aml_value input;
+       struct aml_value output;
+       uint32_t tmp;
+
+       input.type = AML_OBJTYPE_BUFFER;
+       input.length = sizeof(args);
+       /* input.v_buffer = &args; */
+
+       /*
+       status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, method_id,
+                                    &input, &output);
+       */
+       acpiwmi_evaluate_method(sc->sc_parent, ACPI_ASUS_WMI_MGMT_GUID, 1,
+                       method_id, NULL, &output);
+
+       /*
+       if (ACPI_FAILURE(status))
+               goto exit;
+
+       obj = (union acpi_object *)output.pointer;
+       if (obj && obj->type == ACPI_TYPE_INTEGER)
+               tmp = (u32) obj->integer.value;
+       else
+               tmp = 0;
+       */
+
+       if (output.type == AML_OBJTYPE_INTEGER)
+               tmp = (uint32_t) output.v_integer;
+       else
+               tmp = 0;
+
+       if (retval)
+               *retval = tmp;
+
+       /*
+       kfree(obj);
+
+exit:
+       if (ACPI_FAILURE(status))
+               return -EIO;
+       */
+
+       aml_freevalue(&output);
+
+       if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
+               return -ENODEV;
+
+       return 0;
+}
+
+void
+asus_wmi_notify(uint32_t value, void *context)
+{
+       struct acpiwmi_asus_softc *sc = context;
+       struct aml_value response;
+
+       acpiwmi_get_event_data(sc->sc_parent, value, &response);
+
+       if (response.type != AML_OBJTYPE_INTEGER)
+               goto exit;
+
+       switch (response.v_integer) {
+#if NAUDIO > 0 && NWSKBD > 0
+       case ASUS_WMI_KEY_MUTE:
+               wskbd_set_mixervolume(0, 1);
+               break;
+       case ASUS_WMI_KEY_VOLUMEDOWN:
+               wskbd_set_mixervolume(-1, 1);
+               break;
+       case ASUS_WMI_KEY_VOLUMEUP:
+               wskbd_set_mixervolume(1, 1);
+               break;
+#else
+       case ASUS_WMI_KEY_MUTE:
+       case ASUS_WMI_KEY_VOLUMEDOWN:
+       case ASUS_WMI_KEY_VOLUMEUP:
+               break;
+#endif
+       }
+
+exit:
+       aml_freevalue(&response);
+}
+
+int
+acpiwmi_asus_activate(struct device *self, int act)
+{
+       return 0;
+}
+
Index: dev/acpi/files.acpi
===================================================================
RCS file: /cvs/src/sys/dev/acpi/files.acpi,v
retrieving revision 1.27
diff -u -p -r1.27 files.acpi
--- dev/acpi/files.acpi 6 Nov 2013 10:40:36 -0000       1.27
+++ dev/acpi/files.acpi 17 Nov 2013 05:53:42 -0000
@@ -107,6 +107,17 @@ device     acpipwrres
 attach acpipwrres at acpi
 file   dev/acpi/acpipwrres.c           acpipwrres needs-flag
 
+# Generic WMI
+define acpiwmi {}
+device acpiwmi
+attach acpiwmi at acpi
+file   dev/acpi/acpiwmi.c              acpiwmi
+
+# Asus WMI
+device acpiwmi_asus
+attach acpiwmi_asus at acpiwmi
+file   dev/acpi/acpiwmi_asus.c         acpiwmi_asus
+
 # ASUSTeK AI Booster ATK0110
 device aibs
 attach aibs at acpi

Reply via email to