Module Name:    src
Committed By:   jmcneill
Date:           Fri Oct  2 16:47:52 UTC 2009

Modified Files:
        src/sys/arch/amd64/conf: GENERIC
        src/sys/arch/i386/conf: ALL GENERIC
        src/sys/dev/acpi: files.acpi
Added Files:
        src/sys/dev/acpi: wmi_acpi.c wmi_acpivar.h

Log Message:
PR# kern/42139: ACPI WMI: new driver

Import acpiwmi(4) from Jukka Ruohonen. From the PR:

Attached is a driver that implements ACPI WMI API:

http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx

The WMI is used to provide a generic interface for OEMs to use certain
platform/laptop-specific additions to the standard ACPI in a somewhat
portable way. These can be hotkeys for additional buttons, different event
handlers (wireless kill switch, lid switch, etc.), and so on. At least HP
and Acer use it by default nowadays.

The benefit of this interface would be portability. For an example, instead
of hpqlb(4) that works only with certain HP models, we could have a generic
HP WMI-driver that should work in theory across all HP laptops. On many new
laptops WMI may also be the only way to access laptop/manufacturer-specific
features.


To generate a diff of this commit:
cvs rdiff -u -r1.256 -r1.257 src/sys/arch/amd64/conf/GENERIC
cvs rdiff -u -r1.216 -r1.217 src/sys/arch/i386/conf/ALL
cvs rdiff -u -r1.947 -r1.948 src/sys/arch/i386/conf/GENERIC
cvs rdiff -u -r1.59 -r1.60 src/sys/dev/acpi/files.acpi
cvs rdiff -u -r0 -r1.1 src/sys/dev/acpi/wmi_acpi.c \
    src/sys/dev/acpi/wmi_acpivar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/amd64/conf/GENERIC
diff -u src/sys/arch/amd64/conf/GENERIC:1.256 src/sys/arch/amd64/conf/GENERIC:1.257
--- src/sys/arch/amd64/conf/GENERIC:1.256	Wed Sep 30 22:19:27 2009
+++ src/sys/arch/amd64/conf/GENERIC	Fri Oct  2 16:47:52 2009
@@ -1,4 +1,4 @@
-# $NetBSD: GENERIC,v 1.256 2009/09/30 22:19:27 jmcneill Exp $
+# $NetBSD: GENERIC,v 1.257 2009/10/02 16:47:52 jmcneill Exp $
 #
 # GENERIC machine description file
 #
@@ -22,7 +22,7 @@
 
 options 	INCLUDE_CONFIG_FILE	# embed config file in kernel binary
 
-#ident 		"GENERIC-$Revision: 1.256 $"
+#ident 		"GENERIC-$Revision: 1.257 $"
 
 maxusers	64		# estimated number of users
 
@@ -279,6 +279,7 @@
 acpiecdt* 	at acpi?		# ACPI Embedded Controller (early binding)
 acpilid* 	at acpi?		# ACPI Lid Switch
 acpitz* 	at acpi?		# ACPI Thermal Zone
+acpiwmi*	at acpi?		# ACPI WMI Mapper
 
 # Mainboard devices
 aiboost* 	at acpi?		# ASUS AI Booster Hardware monitor

Index: src/sys/arch/i386/conf/ALL
diff -u src/sys/arch/i386/conf/ALL:1.216 src/sys/arch/i386/conf/ALL:1.217
--- src/sys/arch/i386/conf/ALL:1.216	Wed Sep 30 22:19:00 2009
+++ src/sys/arch/i386/conf/ALL	Fri Oct  2 16:47:52 2009
@@ -1,4 +1,4 @@
-# $NetBSD: ALL,v 1.216 2009/09/30 22:19:00 jmcneill Exp $
+# $NetBSD: ALL,v 1.217 2009/10/02 16:47:52 jmcneill Exp $
 # From NetBSD: GENERIC,v 1.787 2006/10/01 18:37:54 bouyer Exp
 #
 # ALL machine description file
@@ -17,7 +17,7 @@
 
 options 	INCLUDE_CONFIG_FILE	# embed config file in kernel binary
 
-#ident 		"ALL-$Revision: 1.216 $"
+#ident 		"ALL-$Revision: 1.217 $"
 
 maxusers	32		# estimated number of users
 
@@ -351,6 +351,7 @@
 acpiecdt* 	at acpi?		# ACPI Embedded Controller (early binding)
 acpilid* 	at acpi?		# ACPI Lid Switch
 acpitz* 	at acpi?		# ACPI Thermal Zone
+acpiwmi*	at acpi?		# ACPI WMI Mapper
 
 # Mainboard devices
 aiboost* 	at acpi?		# ASUS AI Booster Hardware monitor

Index: src/sys/arch/i386/conf/GENERIC
diff -u src/sys/arch/i386/conf/GENERIC:1.947 src/sys/arch/i386/conf/GENERIC:1.948
--- src/sys/arch/i386/conf/GENERIC:1.947	Wed Sep 30 22:19:00 2009
+++ src/sys/arch/i386/conf/GENERIC	Fri Oct  2 16:47:52 2009
@@ -1,4 +1,4 @@
-# $NetBSD: GENERIC,v 1.947 2009/09/30 22:19:00 jmcneill Exp $
+# $NetBSD: GENERIC,v 1.948 2009/10/02 16:47:52 jmcneill Exp $
 #
 # GENERIC machine description file
 #
@@ -22,7 +22,7 @@
 
 options 	INCLUDE_CONFIG_FILE	# embed config file in kernel binary
 
-#ident 		"GENERIC-$Revision: 1.947 $"
+#ident 		"GENERIC-$Revision: 1.948 $"
 
 maxusers	64		# estimated number of users
 
@@ -361,6 +361,7 @@
 acpiecdt* 	at acpi?		# ACPI Embedded Controller (early binding)
 acpilid* 	at acpi?		# ACPI Lid Switch
 acpitz* 	at acpi?		# ACPI Thermal Zone
+acpiwmi*	at acpi?		# ACPI WMI Mapper
 
 # Mainboard devices
 aiboost*	at acpi?		# ASUS AI Booster Hardware Monitor

Index: src/sys/dev/acpi/files.acpi
diff -u src/sys/dev/acpi/files.acpi:1.59 src/sys/dev/acpi/files.acpi:1.60
--- src/sys/dev/acpi/files.acpi:1.59	Wed Sep 30 20:44:50 2009
+++ src/sys/dev/acpi/files.acpi	Fri Oct  2 16:47:52 2009
@@ -1,4 +1,4 @@
-#	$NetBSD: files.acpi,v 1.59 2009/09/30 20:44:50 jmcneill Exp $
+#	$NetBSD: files.acpi,v 1.60 2009/10/02 16:47:52 jmcneill Exp $
 
 include "dev/acpi/acpica/files.acpica"
 
@@ -145,3 +145,8 @@
 # Winbond Integrated Media Reader
 attach	wb at acpinodebus with wb_acpi
 file	dev/acpi/wb_acpi.c		wb_acpi
+
+# ACPI-WMI Mapper
+device	acpiwmi
+attach	acpiwmi at acpinodebus
+file	dev/acpi/wmi_acpi.c		acpiwmi

Added files:

Index: src/sys/dev/acpi/wmi_acpi.c
diff -u /dev/null src/sys/dev/acpi/wmi_acpi.c:1.1
--- /dev/null	Fri Oct  2 16:47:52 2009
+++ src/sys/dev/acpi/wmi_acpi.c	Fri Oct  2 16:47:52 2009
@@ -0,0 +1,695 @@
+/*	$NetBSD: wmi_acpi.c,v 1.1 2009/10/02 16:47:52 jmcneill Exp $	*/
+
+/*-
+ * Copyright (c) 2009 Jukka Ruohonen <jruoho...@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: wmi_acpi.c,v 1.1 2009/10/02 16:47:52 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/endian.h>
+#include <sys/kmem.h>
+
+#include <dev/acpi/acpivar.h>
+#include <dev/acpi/wmi_acpivar.h>
+/*
+ * This implements something called "Microsoft Windows Management
+ * Instrumentation" (WMI). This subset of ACPI is desribed in:
+ *
+ * http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx
+ *
+ * (Obtained on Thu Feb 12 18:21:44 EET 2009.)
+ */
+struct guid_t {
+	/*
+	 * The GUID itself. The used format is the usual 32-16-16-64-bit
+	 * representation. All except the fourth field are in native byte
+	 * order. A 32-16-16-16-48-bit hexadecimal notation with hyphens
+	 * is used for human-readable GUIDs.
+	 */
+	struct {
+		uint32_t data1;
+		uint16_t data2;
+		uint16_t data3;
+		uint8_t  data4[8];
+	} __packed;
+
+	union {
+		char oid[2];            /* ACPI object ID. */
+
+		struct {
+			uint8_t nid;    /* Notification value. */
+			uint8_t res;    /* Reserved. */
+		} __packed;
+	} __packed;
+
+	uint8_t count;	                /* Number of instances. */
+	uint8_t flags;		        /* Additional flags. */
+
+} __packed;
+
+struct wmi_t {
+	struct guid_t       guid;
+	ACPI_HANDLE         handle;
+	bool                eevent;
+
+	SIMPLEQ_ENTRY(wmi_t) wmi_link;
+};
+
+struct acpi_wmi_softc {
+	device_t	      sc_dev;
+	struct acpi_devnode  *sc_node;
+	struct wmi_t         *sc_wmi;
+	bool                  sc_event;
+};
+
+static SIMPLEQ_HEAD(, wmi_t) wmi_head =
+	SIMPLEQ_HEAD_INITIALIZER(wmi_head);
+
+#define ACPI_WMI_FLAG_EXPENSIVE 0x01
+#define ACPI_WMI_FLAG_METHOD    0x02
+#define ACPI_WMI_FLAG_STRING    0x04
+#define ACPI_WMI_FLAG_EVENT     0x08
+#define ACPI_WMI_FLAG_DATA      (ACPI_WMI_FLAG_EXPENSIVE |		\
+	                         ACPI_WMI_FLAG_STRING)
+
+#define UGET16(x)   (*(u_int16_t *)(x))
+#define UGET64(x)   (*(u_int64_t *)(x))
+
+#define HEXCHAR(x)  (((x) >= '0' && (x) <= '9') ||			\
+	             ((x) >= 'a' && (x) <= 'f') ||			\
+		     ((x) >= 'A' && (x) <= 'F'))
+
+#define GUIDCMP(a, b)							\
+	((a)->data1 == (b)->data1 &&					\
+	 (a)->data2 == (b)->data2 &&					\
+	 (a)->data3 == (b)->data3 &&					\
+	 UGET64((a)->data4) == UGET64((b)->data4))
+
+static int         acpi_wmi_match(device_t, cfdata_t, void *);
+static void        acpi_wmi_attach(device_t, device_t, void *);
+static bool        acpi_wmi_init(ACPI_HANDLE, const char * const);
+static bool        acpi_wmi_add(ACPI_OBJECT *, ACPI_HANDLE);
+static void        acpi_wmi_del(void);
+
+#ifdef ACPIVERBOSE
+static void        acpi_wmi_dump(const char * const);
+#endif
+
+static ACPI_STATUS acpi_wmi_guid_get(const u_char * const, struct wmi_t **);
+static bool        acpi_wmi_event_add(struct acpi_wmi_softc *);
+static bool        acpi_wmi_event_del(struct acpi_wmi_softc *);
+static void        acpi_wmi_event_handler(ACPI_HANDLE, uint32_t, void *);
+static bool        acpi_wmi_suspend(device_t PMF_FN_PROTO);
+static bool        acpi_wmi_resume(device_t PMF_FN_PROTO);
+static ACPI_STATUS acpi_wmi_enable(const ACPI_HANDLE, const char * const,
+                                   const bool, const bool);
+
+static ACPI_NOTIFY_HANDLER wmi_handler;
+
+const char * const acpi_wmi_ids[] = {
+	"PNP0C14",
+	NULL
+};
+
+CFATTACH_DECL_NEW(acpiwmi, sizeof(struct acpi_wmi_softc),
+    acpi_wmi_match, acpi_wmi_attach, NULL, NULL);
+
+static int
+acpi_wmi_match(device_t parent, cfdata_t match, void *aux)
+{
+	struct acpi_attach_args *aa = aux;
+
+	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
+		return 0;
+
+	return acpi_match_hid(aa->aa_node->ad_devinfo, acpi_wmi_ids);
+}
+
+static void
+acpi_wmi_attach(device_t parent, device_t self, void *aux)
+{
+	struct acpi_wmi_softc *sc = device_private(self);
+	struct acpi_attach_args *aa = aux;
+	const char *xname;
+
+	sc->sc_dev = self;
+	sc->sc_node = aa->aa_node;
+
+	aprint_naive("\n");
+	aprint_normal(": ACPI WMI Interface\n");
+
+	wmi_handler = NULL;
+	xname = device_xname(sc->sc_dev);
+
+	if (acpi_wmi_init(sc->sc_node->ad_handle, xname) != true)
+		return;
+
+#ifdef ACPIVERBOSE
+	acpi_wmi_dump(xname);
+#endif
+	/*
+	 * Add internal event handler. If the user-driver
+	 * registers an external event handler with us, we
+	 * will forward events through this.
+	 */
+	sc->sc_event = acpi_wmi_event_add(sc);
+
+	if (pmf_device_register(sc->sc_dev,
+		acpi_wmi_suspend, acpi_wmi_resume) != true)
+		aprint_error_dev(self, "failed to register power handler\n");
+}
+
+static bool
+acpi_wmi_init(ACPI_HANDLE hdl, const char * const xname)
+{
+	ACPI_OBJECT *obj;
+	ACPI_BUFFER buf;
+	ACPI_STATUS rv;
+	uint32_t len;
+
+	rv = acpi_eval_struct(hdl, "_WDG", &buf);
+
+	if (ACPI_FAILURE(rv))
+		goto fail;
+
+	obj = buf.Pointer;
+	len = obj->Buffer.Length;
+
+	KASSERT(obj->Type == ACPI_TYPE_BUFFER);
+
+	if (len != obj->Package.Count) {
+		rv = AE_BAD_VALUE;
+		goto fail;
+	}
+
+	KASSERT(sizeof(struct guid_t) == 20);
+
+	if (len < sizeof(struct guid_t) ||
+	    len % sizeof(struct guid_t) != 0) {
+		rv = AE_BAD_DATA;
+		goto fail;
+	}
+
+	return acpi_wmi_add(obj, hdl);
+
+fail:
+	aprint_error("%s: failed to evaluate _WDG: %s\n",
+	    xname, AcpiFormatException(rv));
+
+	if (buf.Pointer != NULL)
+		AcpiOsFree(buf.Pointer);
+
+	return false;
+}
+
+static bool
+acpi_wmi_add(ACPI_OBJECT *obj, ACPI_HANDLE hdl)
+{
+	struct wmi_t *wmi;
+	size_t i, n, offset, siz;
+
+	siz = sizeof(struct guid_t);
+	n = obj->Buffer.Length / siz;
+
+	for (i = offset = 0; i < n; ++i) {
+
+		if ((wmi = kmem_zalloc(sizeof(*wmi), KM_NOSLEEP)) == NULL)
+			goto fail;
+
+		ACPI_MEMCPY(&wmi->guid, obj->Buffer.Pointer + offset, siz);
+
+		wmi->handle = hdl;
+		wmi->eevent = false;
+
+		offset = offset + siz;
+
+		SIMPLEQ_INSERT_TAIL(&wmi_head, wmi, wmi_link);
+	}
+
+	AcpiOsFree(obj);
+
+	return true;
+
+fail:
+	AcpiOsFree(obj);
+	acpi_wmi_del();
+
+	return false;
+}
+
+static void
+acpi_wmi_del(void)
+{
+	struct wmi_t *wmi;
+
+	if (SIMPLEQ_EMPTY(&wmi_head))
+		return;
+
+	while (SIMPLEQ_FIRST(&wmi_head) != NULL) {
+
+		wmi = SIMPLEQ_FIRST(&wmi_head);
+		SIMPLEQ_REMOVE_HEAD(&wmi_head, wmi_link);
+
+		KASSERT(wmi != NULL);
+
+		kmem_free(wmi, sizeof(*wmi));
+	}
+}
+
+#ifdef ACPIVERBOSE
+static void
+acpi_wmi_dump(const char * const xname)
+{
+	struct wmi_t *wmi;
+
+	KASSERT(!(SIMPLEQ_EMPTY(&wmi_head)));
+
+	SIMPLEQ_FOREACH(wmi, &wmi_head, wmi_link) {
+
+		aprint_normal("%s: {%08X-%04X-%04X-", xname,
+		    wmi->guid.data1, wmi->guid.data2, wmi->guid.data3);
+
+		aprint_normal("%02X%02X-%02X%02X%02X%02X%02X%02X} ",
+		    wmi->guid.data4[0], wmi->guid.data4[1],
+		    wmi->guid.data4[2], wmi->guid.data4[3],
+		    wmi->guid.data4[4], wmi->guid.data4[5],
+		    wmi->guid.data4[6], wmi->guid.data4[7]);
+
+		aprint_normal("oid %04X count %02X flags %02X\n",
+		    UGET16(wmi->guid.oid), wmi->guid.count, wmi->guid.flags);
+	}
+}
+#endif
+
+static ACPI_STATUS
+acpi_wmi_guid_get(const u_char * const src, struct wmi_t **out)
+{
+	struct wmi_t *wmi;
+	struct guid_t *guid;
+	u_char bin[16];
+	u_char hex[2];
+	const char *ptr;
+	uint8_t i;
+
+	KASSERT(src != NULL);
+
+	if (ACPI_STRLEN(src) != 36)
+		return AE_BAD_PARAMETER;
+
+	for (ptr = src, i = 0; i < 16; i++) {
+
+		if (*ptr == '-')
+			ptr++;
+
+		ACPI_MEMCPY(hex, ptr, 2);
+
+		if (!HEXCHAR(hex[0]) || !HEXCHAR(hex[1]))
+			return AE_BAD_HEX_CONSTANT;
+
+		bin[i] = ACPI_STRTOUL(hex, NULL, 16) & 0xFF;
+
+		ptr++;
+		ptr++;
+	}
+
+	guid = (struct guid_t *)bin;
+	guid->data1 = be32toh(guid->data1);
+	guid->data2 = be16toh(guid->data2);
+	guid->data3 = be16toh(guid->data3);
+
+	SIMPLEQ_FOREACH(wmi, &wmi_head, wmi_link) {
+
+		if (GUIDCMP(guid, &wmi->guid)) {
+
+			if (out != NULL)
+				*out = wmi;
+
+			return AE_OK;
+		}
+	}
+
+	return AE_NOT_FOUND;
+}
+/*
+ * Checks if a GUID is present. Drivers can
+ * use this in their autoconf(9) routines.
+ */
+int
+acpi_wmi_guid_match(const u_char const * guid)
+{
+	ACPI_STATUS rv;
+
+	if (guid == NULL)
+		return 0;
+
+	rv = acpi_wmi_guid_get(guid, NULL);
+
+	if (ACPI_SUCCESS(rv))
+		return 1;
+
+	return 0;
+}
+/*
+ * Adds internal event handler.
+ */
+static bool
+acpi_wmi_event_add(struct acpi_wmi_softc *sc)
+{
+	struct wmi_t *wmi;
+	ACPI_STATUS rv;
+
+	rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle,
+	    ACPI_DEVICE_NOTIFY, acpi_wmi_event_handler, sc);
+
+	if (ACPI_FAILURE(rv)) {
+		aprint_error_dev(sc->sc_dev, "failed to install notify "
+		    "handler: %s\n", AcpiFormatException(rv));
+		return false;
+	}
+
+	/* Enable possible expensive events. */
+	SIMPLEQ_FOREACH(wmi, &wmi_head, wmi_link) {
+
+		if ((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) &&
+		    (wmi->guid.flags & ACPI_WMI_FLAG_EXPENSIVE)) {
+
+			rv = acpi_wmi_enable(wmi->handle,
+			    wmi->guid.oid, false, true);
+
+			if (ACPI_SUCCESS(rv)) {
+				wmi->eevent = true;
+				continue;
+			}
+
+			aprint_error_dev(sc->sc_dev, "failed to enable "
+			    "expensive WExx: %s\n", AcpiFormatException(rv));
+		}
+	}
+
+	return true;
+}
+/*
+ * Removes the internal event handler.
+ */
+static bool
+acpi_wmi_event_del(struct acpi_wmi_softc *sc)
+{
+	struct wmi_t *wmi;
+	ACPI_STATUS rv;
+
+	KASSERT(sc->sc_event != false);
+
+	rv = AcpiRemoveNotifyHandler(sc->sc_node->ad_handle,
+	    ACPI_DEVICE_NOTIFY, acpi_wmi_event_handler);
+
+	if (ACPI_FAILURE(rv)) {
+		aprint_error_dev(sc->sc_dev, "failed to remove notify "
+		    "handler: %s\n", AcpiFormatException(rv));
+		return false;
+	}
+
+	SIMPLEQ_FOREACH(wmi, &wmi_head, wmi_link) {
+
+		if (wmi->eevent != true)
+			continue;
+
+		KASSERT(wmi->guid.flags & ACPI_WMI_FLAG_EVENT);
+		KASSERT(wmi->guid.flags & ACPI_WMI_FLAG_EXPENSIVE);
+
+		rv = acpi_wmi_enable(wmi->handle, wmi->guid.oid, false, false);
+
+		if (ACPI_SUCCESS(rv)) {
+			wmi->eevent = false;
+			continue;
+		}
+
+		aprint_error_dev(sc->sc_dev, "failed to disable "
+		    "expensive WExx: %s\n", AcpiFormatException(rv));
+	}
+
+	return true;
+}
+
+static void
+acpi_wmi_event_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux)
+{
+	device_t self = aux;
+	struct acpi_wmi_softc *sc = device_private(self);
+	/*
+	 * No external handler, nothing to do.
+	 */
+	if (wmi_handler == NULL)
+		return;
+	/*
+	 * The third parameter is "reserved"; it could be
+	 * used e.g. to pass the _WED-buffer on each event.
+	 */
+	(*wmi_handler)(sc->sc_node->ad_handle, evt, NULL);
+}
+/*
+ * Adds or removes (NULL) the external event handler.
+ */
+ACPI_STATUS
+acpi_wmi_event_register(ACPI_NOTIFY_HANDLER handler)
+{
+
+	if (handler != NULL && wmi_handler != NULL)
+		return AE_ALREADY_EXISTS;
+
+	wmi_handler = handler;
+
+	return AE_OK;
+}
+/*
+ * As there is no prior knowledge about the expensive
+ * events that cause "significant overhead", try to
+ * disable (enable) these before suspending (resuming).
+ */
+static bool
+acpi_wmi_suspend(device_t self PMF_FN_ARGS)
+{
+	struct acpi_wmi_softc *sc = device_private(self);
+
+	if (sc->sc_event != true)
+		return true;
+
+	if (acpi_wmi_event_del(sc) != false)
+		sc->sc_event = false;
+
+	return true;
+}
+
+static bool
+acpi_wmi_resume(device_t self PMF_FN_ARGS)
+{
+	struct acpi_wmi_softc *sc = device_private(self);
+
+	if (sc->sc_event != false) {
+		aprint_debug_dev(sc->sc_dev, "event handler enabled?\n");
+		return true;
+	}
+
+	if (acpi_wmi_event_add(sc) != false)
+		sc->sc_event = true;
+
+	return true;
+}
+/*
+ * Enables or disables data collection (WCxx) or an event (WExx).
+ */
+static ACPI_STATUS
+acpi_wmi_enable(const ACPI_HANDLE hdl, const char * const oid,
+    const bool data, const bool flag)
+{
+	ACPI_OBJECT_LIST arg;
+	ACPI_OBJECT obj;
+	char path[5];
+	const char *str;
+
+	str = (data != false) ? "WC" : "WE";
+
+	(void)strlcpy(path, str, sizeof(path));
+	(void)strlcat(path, oid, sizeof(path));
+
+	obj.Type = ACPI_TYPE_INTEGER;
+	obj.Integer.Value = (flag != false) ? 0x01 : 0x00;
+
+	arg.Count = 0x01;
+	arg.Pointer = &obj;
+
+	return AcpiEvaluateObject(hdl, path, &arg, NULL);
+}
+/*
+ * Makes a WMI data block query (WQxx). The corresponding control
+ * method for data collection will be invoked if it is available.
+ */
+ACPI_STATUS
+acpi_wmi_data_query(const u_char * const guid, ACPI_BUFFER *obuf)
+{
+	struct wmi_t *wmi;
+	char path[5] = "WQ";
+	ACPI_STATUS rva, rvb;
+
+	if (guid == NULL || obuf == NULL)
+		return AE_BAD_PARAMETER;
+
+	rva = acpi_wmi_guid_get(guid, &wmi);
+
+	if (ACPI_FAILURE(rva))
+		return rva;
+
+	rvb = AE_SUPPORT;
+
+	if (!(wmi->guid.flags & ACPI_WMI_FLAG_DATA))
+		return AE_BAD_PARAMETER;
+
+	if (wmi->guid.count == 0x00 || wmi->guid.flags == 0x00)
+		return AE_BAD_VALUE;
+
+	(void)strlcat(path, wmi->guid.oid, sizeof(path));
+	/*
+	 * If the expensive flag is set, we should enable
+	 * data collection before evaluating the WQxx buffer.
+	 */
+	if (wmi->guid.flags & ACPI_WMI_FLAG_EXPENSIVE)
+		rvb = acpi_wmi_enable(wmi->handle, wmi->guid.oid, true, true);
+
+	rva = acpi_eval_struct(wmi->handle, path, obuf);
+
+	/* No longer needed. */
+	if (ACPI_SUCCESS(rvb))
+		(void)acpi_wmi_enable(wmi->handle, wmi->guid.oid, true, false);
+
+#ifdef DIAGNOSTIC
+	/*
+	 * XXX: It appears that quite a few laptops have WQxx
+	 * methods that are declared as expensive, but lack the
+	 * corresponding WCxx control method.
+	 *
+	 * -- Acer Aspire One is one example <jruoho...@iki.fi>.
+	 */
+	if (ACPI_FAILURE(rvb) && rvb != AE_SUPPORT)
+		aprint_error("acpiwmi0: failed to evaluate WCxx "
+		    "for %s: %s\n", path, AcpiFormatException(rvb));
+#endif
+	return rva;
+}
+/*
+ * Writes to a data block (WSxx).
+ */
+ACPI_STATUS
+acpi_wmi_data_write(const u_char * const guid,
+    const uint8_t idx, ACPI_BUFFER *ibuf)
+{
+	struct wmi_t *wmi;
+	ACPI_OBJECT_LIST arg;
+	ACPI_OBJECT obj[2];
+	char path[5] = "WS";
+	ACPI_STATUS rv;
+
+	rv = acpi_wmi_guid_get(guid, &wmi);
+
+	if (ACPI_FAILURE(rv))
+		return rv;
+
+	if (!(wmi->guid.flags & ACPI_WMI_FLAG_DATA))
+		return AE_BAD_PARAMETER;
+
+	if (wmi->guid.count == 0x00 || wmi->guid.flags == 0x00)
+		return AE_BAD_VALUE;
+
+	if (wmi->guid.count < idx)
+		return AE_BAD_PARAMETER;
+
+	(void)strlcat(path, wmi->guid.oid, sizeof(path));
+
+	obj[0].Integer.Value = idx;
+	obj[0].Type = ACPI_TYPE_INTEGER;
+
+	KASSERT(ibuf != NULL);
+
+	obj[1].Buffer.Length = ibuf->Length;
+	obj[1].Buffer.Pointer = ibuf->Pointer;
+
+	obj[1].Type = (wmi->guid.flags & ACPI_WMI_FLAG_STRING) ?
+	    ACPI_TYPE_STRING : ACPI_TYPE_INTEGER;
+
+	arg.Count = 0x02;
+	arg.Pointer = obj;
+
+	return AcpiEvaluateObject(wmi->handle, path, &arg, NULL);
+}
+/*
+ * Executes a method (WMxx).
+ */
+ACPI_STATUS
+acpi_wmi_method(const u_char * const guid, const uint8_t idx,
+    const uint32_t mid, ACPI_BUFFER *ibuf, ACPI_BUFFER *obuf)
+{
+	struct wmi_t *wmi;
+	ACPI_OBJECT_LIST arg;
+	ACPI_OBJECT obj[3];
+	char path[5] = "WM";
+	ACPI_STATUS rv;
+
+	rv = acpi_wmi_guid_get(guid, &wmi);
+
+	if (ACPI_FAILURE(rv))
+		return rv;
+
+	if (!(wmi->guid.flags & ACPI_WMI_FLAG_METHOD))
+		return AE_BAD_PARAMETER;
+
+	if (wmi->guid.count == 0x00 || wmi->guid.flags == 0x00)
+		return AE_BAD_VALUE;
+
+	if (wmi->guid.count < idx)
+		return AE_BAD_PARAMETER;
+
+	(void)strlcat(path, wmi->guid.oid, sizeof(path));
+
+	obj[0].Integer.Value = idx;
+	obj[1].Integer.Value = mid;
+	obj[0].Type = obj[1].Type = ACPI_TYPE_INTEGER;
+
+	KASSERT(ibuf != NULL);
+
+	obj[2].Buffer.Length = ibuf->Length;
+	obj[2].Buffer.Pointer = ibuf->Pointer;
+
+	obj[2].Type = (wmi->guid.flags & ACPI_WMI_FLAG_STRING) ?
+	    ACPI_TYPE_STRING : ACPI_TYPE_INTEGER;
+
+	arg.Count = 0x03;
+	arg.Pointer = obj;
+
+	obuf->Pointer = NULL;
+	obuf->Length = ACPI_ALLOCATE_BUFFER;
+
+	return AcpiEvaluateObject(wmi->handle, path, &arg, obuf);
+}
Index: src/sys/dev/acpi/wmi_acpivar.h
diff -u /dev/null src/sys/dev/acpi/wmi_acpivar.h:1.1
--- /dev/null	Fri Oct  2 16:47:52 2009
+++ src/sys/dev/acpi/wmi_acpivar.h	Fri Oct  2 16:47:52 2009
@@ -0,0 +1,43 @@
+/*	$NetBSD: wmi_acpivar.h,v 1.1 2009/10/02 16:47:52 jmcneill Exp $	*/
+
+/*-
+ * Copyright (c) 2009 Jukka Ruohonen <jruoho...@iki.fi>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef WMI_ACPIVAR_H
+#define WMI_ACPIVAR_H
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: wmi_acpivar.h,v 1.1 2009/10/02 16:47:52 jmcneill Exp $");
+
+ACPI_STATUS acpi_wmi_event_register(ACPI_NOTIFY_HANDLER);
+int         acpi_wmi_guid_match(const u_char const *);
+ACPI_STATUS acpi_wmi_data_query(const u_char * const, ACPI_BUFFER *);
+ACPI_STATUS acpi_wmi_data_write(const u_char * const, const uint8_t,
+                                ACPI_BUFFER *);
+ACPI_STATUS acpi_wmi_method(const u_char * const, const uint8_t,
+                            const uint32_t, ACPI_BUFFER *, ACPI_BUFFER *);
+
+#endif	/* WMI_ACPIVAR_H */

Reply via email to