xref: /netbsd-src/sys/dev/acpi/wmi/wmi_acpi.c (revision 1899b7d2515cd69190f3eaeb8f8b8f66831fa0bc)
1*1899b7d2Sriastradh /*	$NetBSD: wmi_acpi.c,v 1.23 2023/08/11 08:36:59 riastradh Exp $	*/
2a913ee75Sjruoho 
3a913ee75Sjruoho /*-
4a913ee75Sjruoho  * Copyright (c) 2009, 2010 Jukka Ruohonen <jruohonen@iki.fi>
5a913ee75Sjruoho  * All rights reserved.
6a913ee75Sjruoho  *
7a913ee75Sjruoho  * Redistribution and use in source and binary forms, with or without
8a913ee75Sjruoho  * modification, are permitted provided that the following conditions
9a913ee75Sjruoho  * are met:
10a913ee75Sjruoho  *
11a913ee75Sjruoho  * 1. Redistributions of source code must retain the above copyright
12a913ee75Sjruoho  *    notice, this list of conditions and the following disclaimer.
13a913ee75Sjruoho  * 2. Redistributions in binary form must reproduce the above copyright
14a913ee75Sjruoho  *    notice, this list of conditions and the following disclaimer in the
15a913ee75Sjruoho  *    documentation and/or other materials provided with the distribution.
16a913ee75Sjruoho  *
17a913ee75Sjruoho  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18a913ee75Sjruoho  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19a913ee75Sjruoho  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20a913ee75Sjruoho  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21a913ee75Sjruoho  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22a913ee75Sjruoho  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23a913ee75Sjruoho  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24a913ee75Sjruoho  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25a913ee75Sjruoho  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26a913ee75Sjruoho  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27a913ee75Sjruoho  * SUCH DAMAGE.
28a913ee75Sjruoho  */
29a913ee75Sjruoho #include <sys/cdefs.h>
30*1899b7d2Sriastradh __KERNEL_RCSID(0, "$NetBSD: wmi_acpi.c,v 1.23 2023/08/11 08:36:59 riastradh Exp $");
31a913ee75Sjruoho 
32a913ee75Sjruoho #include <sys/param.h>
33a913ee75Sjruoho #include <sys/device.h>
34a913ee75Sjruoho #include <sys/endian.h>
35a913ee75Sjruoho #include <sys/kmem.h>
36a913ee75Sjruoho #include <sys/systm.h>
379750ca2fSjmcneill #include <sys/module.h>
38a913ee75Sjruoho 
39a913ee75Sjruoho #include <dev/acpi/acpireg.h>
40a913ee75Sjruoho #include <dev/acpi/acpivar.h>
41639d1b97Sjruoho #include <dev/acpi/acpi_ecvar.h>
42a913ee75Sjruoho #include <dev/acpi/wmi/wmi_acpivar.h>
43a913ee75Sjruoho 
44a913ee75Sjruoho #define _COMPONENT          ACPI_RESOURCE_COMPONENT
45a913ee75Sjruoho ACPI_MODULE_NAME            ("wmi_acpi")
46a913ee75Sjruoho 
47a913ee75Sjruoho /*
48a913ee75Sjruoho  * This implements something called "Microsoft Windows Management
491cb7819fSandvar  * Instrumentation" (WMI). This subset of ACPI is described in:
50a913ee75Sjruoho  *
51a913ee75Sjruoho  * http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx
52a913ee75Sjruoho  *
53a913ee75Sjruoho  * (Obtained on Thu Feb 12 18:21:44 EET 2009.)
54a913ee75Sjruoho  */
55a913ee75Sjruoho 
56a913ee75Sjruoho static int		acpi_wmi_match(device_t, cfdata_t, void *);
57a913ee75Sjruoho static void		acpi_wmi_attach(device_t, device_t, void *);
58a913ee75Sjruoho static int		acpi_wmi_detach(device_t, int);
595991cdfeSjmcneill static int		acpi_wmi_rescan(device_t, const char *, const int *);
605991cdfeSjmcneill static void		acpi_wmi_childdet(device_t, device_t);
61a913ee75Sjruoho static int		acpi_wmi_print(void *, const char *);
62a913ee75Sjruoho static bool		acpi_wmi_init(struct acpi_wmi_softc *);
63639d1b97Sjruoho static void		acpi_wmi_init_ec(struct acpi_wmi_softc *);
64fd34ea77Schs static void		acpi_wmi_add(struct acpi_wmi_softc *, ACPI_OBJECT *);
65a913ee75Sjruoho static void		acpi_wmi_del(struct acpi_wmi_softc *);
66460de0d6Sjruoho static void		acpi_wmi_dump(struct acpi_wmi_softc *);
67a913ee75Sjruoho static ACPI_STATUS	acpi_wmi_guid_get(struct acpi_wmi_softc *,
68a913ee75Sjruoho 				const char *, struct wmi_t **);
6955052ecaSjruoho static void		acpi_wmi_event_add(struct acpi_wmi_softc *);
7055052ecaSjruoho static void		acpi_wmi_event_del(struct acpi_wmi_softc *);
71a913ee75Sjruoho static void		acpi_wmi_event_handler(ACPI_HANDLE, uint32_t, void *);
72639d1b97Sjruoho static ACPI_STATUS	acpi_wmi_ec_handler(uint32_t, ACPI_PHYSICAL_ADDRESS,
73639d1b97Sjruoho 				uint32_t, ACPI_INTEGER *, void *, void *);
74a913ee75Sjruoho static bool		acpi_wmi_suspend(device_t, const pmf_qual_t *);
75a913ee75Sjruoho static bool		acpi_wmi_resume(device_t, const pmf_qual_t *);
76ff198a0bSjakllsch static ACPI_STATUS	acpi_wmi_enable_event(ACPI_HANDLE, uint8_t, bool);
77ff198a0bSjakllsch static ACPI_STATUS	acpi_wmi_enable_collection(ACPI_HANDLE, const char *, bool);
78a913ee75Sjruoho static bool		acpi_wmi_input(struct wmi_t *, uint8_t, uint8_t);
79a913ee75Sjruoho 
8037725553Sthorpej static const struct device_compatible_entry compat_data[] = {
8137725553Sthorpej 	{ .compat = "PNP0C14" },
8237725553Sthorpej 	{ .compat = "pnp0c14" },
8337725553Sthorpej 	DEVICE_COMPAT_EOL
84a913ee75Sjruoho };
85a913ee75Sjruoho 
865991cdfeSjmcneill CFATTACH_DECL2_NEW(acpiwmi, sizeof(struct acpi_wmi_softc),
875991cdfeSjmcneill     acpi_wmi_match, acpi_wmi_attach, acpi_wmi_detach, NULL,
885991cdfeSjmcneill     acpi_wmi_rescan, acpi_wmi_childdet);
89a913ee75Sjruoho 
90a913ee75Sjruoho static int
acpi_wmi_match(device_t parent,cfdata_t match,void * aux)91a913ee75Sjruoho acpi_wmi_match(device_t parent, cfdata_t match, void *aux)
92a913ee75Sjruoho {
93a913ee75Sjruoho 	struct acpi_attach_args *aa = aux;
94a913ee75Sjruoho 
9537725553Sthorpej 	return acpi_compatible_match(aa, compat_data);
96a913ee75Sjruoho }
97a913ee75Sjruoho 
98a913ee75Sjruoho static void
acpi_wmi_attach(device_t parent,device_t self,void * aux)99a913ee75Sjruoho acpi_wmi_attach(device_t parent, device_t self, void *aux)
100a913ee75Sjruoho {
101a913ee75Sjruoho 	struct acpi_wmi_softc *sc = device_private(self);
102a913ee75Sjruoho 	struct acpi_attach_args *aa = aux;
103a913ee75Sjruoho 
104a913ee75Sjruoho 	sc->sc_dev = self;
105a913ee75Sjruoho 	sc->sc_node = aa->aa_node;
106a913ee75Sjruoho 
107a913ee75Sjruoho 	sc->sc_child = NULL;
108639d1b97Sjruoho 	sc->sc_ecdev = NULL;
109a913ee75Sjruoho 	sc->sc_handler = NULL;
110a913ee75Sjruoho 
111a913ee75Sjruoho 	aprint_naive("\n");
112a913ee75Sjruoho 	aprint_normal(": ACPI WMI Interface\n");
113a913ee75Sjruoho 
114a913ee75Sjruoho 	if (acpi_wmi_init(sc) != true)
115a913ee75Sjruoho 		return;
116a913ee75Sjruoho 
117460de0d6Sjruoho 	acpi_wmi_dump(sc);
118639d1b97Sjruoho 	acpi_wmi_init_ec(sc);
11955052ecaSjruoho 	acpi_wmi_event_add(sc);
1205991cdfeSjmcneill 	acpi_wmi_rescan(self, NULL, NULL);
12155052ecaSjruoho 
12255052ecaSjruoho 	(void)pmf_device_register(self, acpi_wmi_suspend, acpi_wmi_resume);
123a913ee75Sjruoho }
124a913ee75Sjruoho 
125a913ee75Sjruoho static int
acpi_wmi_detach(device_t self,int flags)126a913ee75Sjruoho acpi_wmi_detach(device_t self, int flags)
127a913ee75Sjruoho {
128a913ee75Sjruoho 	struct acpi_wmi_softc *sc = device_private(self);
1297baff382Sriastradh 	int error;
1307baff382Sriastradh 
1317baff382Sriastradh 	error = config_detach_children(self, flags);
1327baff382Sriastradh 	if (error)
1337baff382Sriastradh 		return error;
134a913ee75Sjruoho 
13555052ecaSjruoho 	acpi_wmi_event_del(sc);
136a913ee75Sjruoho 
137639d1b97Sjruoho 	if (sc->sc_ecdev != NULL) {
138639d1b97Sjruoho 		(void)AcpiRemoveAddressSpaceHandler(sc->sc_node->ad_handle,
139639d1b97Sjruoho 		    ACPI_ADR_SPACE_EC, acpi_wmi_ec_handler);
140639d1b97Sjruoho 	}
141639d1b97Sjruoho 
142a913ee75Sjruoho 	acpi_wmi_del(sc);
143a913ee75Sjruoho 	pmf_device_deregister(self);
144a913ee75Sjruoho 
145a913ee75Sjruoho 	return 0;
146a913ee75Sjruoho }
147a913ee75Sjruoho 
148a913ee75Sjruoho static int
acpi_wmi_rescan(device_t self,const char * ifattr,const int * locators)1495991cdfeSjmcneill acpi_wmi_rescan(device_t self, const char *ifattr, const int *locators)
1505991cdfeSjmcneill {
1515991cdfeSjmcneill 	struct acpi_wmi_softc *sc = device_private(self);
1525991cdfeSjmcneill 
1532685996bSthorpej 	if (sc->sc_child == NULL) {
1542685996bSthorpej 		sc->sc_child =
155c7fb772bSthorpej 		    config_found(self, NULL, acpi_wmi_print, CFARGS_NONE);
1562685996bSthorpej 	}
1575991cdfeSjmcneill 
1585991cdfeSjmcneill 	return 0;
1595991cdfeSjmcneill }
1605991cdfeSjmcneill 
1615991cdfeSjmcneill static void
acpi_wmi_childdet(device_t self,device_t child)1625991cdfeSjmcneill acpi_wmi_childdet(device_t self, device_t child)
1635991cdfeSjmcneill {
1645991cdfeSjmcneill 	struct acpi_wmi_softc *sc = device_private(self);
1655991cdfeSjmcneill 
1665991cdfeSjmcneill 	if (sc->sc_child == child)
1675991cdfeSjmcneill 		sc->sc_child = NULL;
1685991cdfeSjmcneill }
1695991cdfeSjmcneill 
1705991cdfeSjmcneill static int
acpi_wmi_print(void * aux,const char * pnp)171a913ee75Sjruoho acpi_wmi_print(void *aux, const char *pnp)
172a913ee75Sjruoho {
173a913ee75Sjruoho 
174a913ee75Sjruoho 	if (pnp != NULL)
175a913ee75Sjruoho 		aprint_normal("acpiwmibus at %s", pnp);
176a913ee75Sjruoho 
177a913ee75Sjruoho 	return UNCONF;
178a913ee75Sjruoho }
179a913ee75Sjruoho 
180a913ee75Sjruoho static bool
acpi_wmi_init(struct acpi_wmi_softc * sc)181a913ee75Sjruoho acpi_wmi_init(struct acpi_wmi_softc *sc)
182a913ee75Sjruoho {
183a913ee75Sjruoho 	ACPI_OBJECT *obj;
184a913ee75Sjruoho 	ACPI_BUFFER buf;
185a913ee75Sjruoho 	ACPI_STATUS rv;
186a913ee75Sjruoho 	uint32_t len;
187a913ee75Sjruoho 
188a913ee75Sjruoho 	rv = acpi_eval_struct(sc->sc_node->ad_handle, "_WDG", &buf);
189a913ee75Sjruoho 
190a913ee75Sjruoho 	if (ACPI_FAILURE(rv))
191a913ee75Sjruoho 		goto fail;
192a913ee75Sjruoho 
193a913ee75Sjruoho 	obj = buf.Pointer;
194a913ee75Sjruoho 
195a913ee75Sjruoho 	if (obj->Type != ACPI_TYPE_BUFFER) {
196a913ee75Sjruoho 		rv = AE_TYPE;
197a913ee75Sjruoho 		goto fail;
198a913ee75Sjruoho 	}
199a913ee75Sjruoho 
200a913ee75Sjruoho 	len = obj->Buffer.Length;
201a913ee75Sjruoho 
202a913ee75Sjruoho 	if (len != obj->Package.Count) {
203a913ee75Sjruoho 		rv = AE_BAD_VALUE;
204a913ee75Sjruoho 		goto fail;
205a913ee75Sjruoho 	}
206a913ee75Sjruoho 
207a913ee75Sjruoho 	CTASSERT(sizeof(struct guid_t) == 20);
208a913ee75Sjruoho 
209a913ee75Sjruoho 	if (len < sizeof(struct guid_t) ||
210a913ee75Sjruoho 	    len % sizeof(struct guid_t) != 0) {
211a913ee75Sjruoho 		rv = AE_BAD_DATA;
212a913ee75Sjruoho 		goto fail;
213a913ee75Sjruoho 	}
214a913ee75Sjruoho 
215fd34ea77Schs 	acpi_wmi_add(sc, obj);
216fd34ea77Schs 	return true;
217a913ee75Sjruoho 
218a913ee75Sjruoho fail:
219a913ee75Sjruoho 	aprint_error_dev(sc->sc_dev, "failed to evaluate _WDG: %s\n",
220a913ee75Sjruoho 	    AcpiFormatException(rv));
221a913ee75Sjruoho 
222a913ee75Sjruoho 	if (buf.Pointer != NULL)
223a913ee75Sjruoho 		ACPI_FREE(buf.Pointer);
224a913ee75Sjruoho 
225a913ee75Sjruoho 	return false;
226a913ee75Sjruoho }
227a913ee75Sjruoho 
228fd34ea77Schs static void
acpi_wmi_add(struct acpi_wmi_softc * sc,ACPI_OBJECT * obj)229a913ee75Sjruoho acpi_wmi_add(struct acpi_wmi_softc *sc, ACPI_OBJECT *obj)
230a913ee75Sjruoho {
231a913ee75Sjruoho 	struct wmi_t *wmi;
232a913ee75Sjruoho 	size_t i, n, offset, siz;
233a913ee75Sjruoho 
234a913ee75Sjruoho 	siz = sizeof(struct guid_t);
235a913ee75Sjruoho 	n = obj->Buffer.Length / siz;
236a913ee75Sjruoho 
237a913ee75Sjruoho 	SIMPLEQ_INIT(&sc->wmi_head);
238a913ee75Sjruoho 
239a913ee75Sjruoho 	for (i = offset = 0; i < n; ++i) {
240a913ee75Sjruoho 
241fd34ea77Schs 		wmi = kmem_zalloc(sizeof(*wmi), KM_SLEEP);
242a913ee75Sjruoho 		(void)memcpy(&wmi->guid, obj->Buffer.Pointer + offset, siz);
243a913ee75Sjruoho 
244a913ee75Sjruoho 		wmi->eevent = false;
245a913ee75Sjruoho 		offset = offset + siz;
246a913ee75Sjruoho 
247a913ee75Sjruoho 		SIMPLEQ_INSERT_TAIL(&sc->wmi_head, wmi, wmi_link);
248a913ee75Sjruoho 	}
249a913ee75Sjruoho 
250a913ee75Sjruoho 	ACPI_FREE(obj);
251a913ee75Sjruoho }
252a913ee75Sjruoho 
253a913ee75Sjruoho static void
acpi_wmi_del(struct acpi_wmi_softc * sc)254a913ee75Sjruoho acpi_wmi_del(struct acpi_wmi_softc *sc)
255a913ee75Sjruoho {
256a913ee75Sjruoho 	struct wmi_t *wmi;
257a913ee75Sjruoho 
258a913ee75Sjruoho 	while (SIMPLEQ_FIRST(&sc->wmi_head) != NULL) {
259a913ee75Sjruoho 		wmi = SIMPLEQ_FIRST(&sc->wmi_head);
260a913ee75Sjruoho 		SIMPLEQ_REMOVE_HEAD(&sc->wmi_head, wmi_link);
261a913ee75Sjruoho 		kmem_free(wmi, sizeof(*wmi));
262a913ee75Sjruoho 	}
263a913ee75Sjruoho }
264a913ee75Sjruoho 
265460de0d6Sjruoho static void
acpi_wmi_dump(struct acpi_wmi_softc * sc)266460de0d6Sjruoho acpi_wmi_dump(struct acpi_wmi_softc *sc)
267460de0d6Sjruoho {
268460de0d6Sjruoho 	struct wmi_t *wmi;
269460de0d6Sjruoho 
270460de0d6Sjruoho 	KASSERT(SIMPLEQ_EMPTY(&sc->wmi_head) == 0);
271460de0d6Sjruoho 
272460de0d6Sjruoho 	SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) {
273460de0d6Sjruoho 
274460de0d6Sjruoho 		aprint_debug_dev(sc->sc_dev, "{%08X-%04X-%04X-",
275460de0d6Sjruoho 		    wmi->guid.data1, wmi->guid.data2, wmi->guid.data3);
276460de0d6Sjruoho 
277460de0d6Sjruoho 		aprint_debug("%02X%02X-%02X%02X%02X%02X%02X%02X} ",
278460de0d6Sjruoho 		    wmi->guid.data4[0], wmi->guid.data4[1],
279460de0d6Sjruoho 		    wmi->guid.data4[2], wmi->guid.data4[3],
280460de0d6Sjruoho 		    wmi->guid.data4[4], wmi->guid.data4[5],
281460de0d6Sjruoho 		    wmi->guid.data4[6], wmi->guid.data4[7]);
282460de0d6Sjruoho 
283460de0d6Sjruoho 		aprint_debug("oid %04X count %02X flags %02X\n",
284460de0d6Sjruoho 		    UGET16(wmi->guid.oid), wmi->guid.count, wmi->guid.flags);
285460de0d6Sjruoho 	}
286460de0d6Sjruoho }
287460de0d6Sjruoho 
288639d1b97Sjruoho static void
acpi_wmi_init_ec(struct acpi_wmi_softc * sc)289639d1b97Sjruoho acpi_wmi_init_ec(struct acpi_wmi_softc *sc)
290639d1b97Sjruoho {
291639d1b97Sjruoho 	ACPI_STATUS rv;
292639d1b97Sjruoho 	deviter_t i;
293639d1b97Sjruoho 	device_t d;
294639d1b97Sjruoho 
295639d1b97Sjruoho 	d = deviter_first(&i, DEVITER_F_ROOT_FIRST);
296639d1b97Sjruoho 
297639d1b97Sjruoho 	for (; d != NULL; d = deviter_next(&i)) {
298639d1b97Sjruoho 
299639d1b97Sjruoho 		if (device_is_a(d, "acpiec") != false ||
300639d1b97Sjruoho 		    device_is_a(d, "acpiecdt") != false) {
301639d1b97Sjruoho 			sc->sc_ecdev = d;
302639d1b97Sjruoho 			break;
303639d1b97Sjruoho 		}
304639d1b97Sjruoho 	}
305639d1b97Sjruoho 
306639d1b97Sjruoho 	deviter_release(&i);
307639d1b97Sjruoho 
308639d1b97Sjruoho 	if (sc->sc_ecdev == NULL)
309639d1b97Sjruoho 		return;
310639d1b97Sjruoho 
311639d1b97Sjruoho 	rv = AcpiInstallAddressSpaceHandler(sc->sc_node->ad_handle,
312639d1b97Sjruoho 	    ACPI_ADR_SPACE_EC, acpi_wmi_ec_handler, NULL, sc);
313639d1b97Sjruoho 
314639d1b97Sjruoho 	if (ACPI_FAILURE(rv))
315639d1b97Sjruoho 		sc->sc_ecdev = NULL;
316639d1b97Sjruoho }
317639d1b97Sjruoho 
318a913ee75Sjruoho static ACPI_STATUS
acpi_wmi_guid_get(struct acpi_wmi_softc * sc,const char * src,struct wmi_t ** out)319a913ee75Sjruoho acpi_wmi_guid_get(struct acpi_wmi_softc *sc,
320a913ee75Sjruoho     const char *src, struct wmi_t **out)
321a913ee75Sjruoho {
322a913ee75Sjruoho 	struct wmi_t *wmi;
323*1899b7d2Sriastradh 	struct guid_t guid;
324*1899b7d2Sriastradh 	char bin[16];
32587dcc5d7Sbouyer 	char hex[3];
326a913ee75Sjruoho 	const char *ptr;
327a913ee75Sjruoho 	uint8_t i;
328a913ee75Sjruoho 
329a913ee75Sjruoho 	if (sc == NULL || src == NULL || strlen(src) != 36)
330a913ee75Sjruoho 		return AE_BAD_PARAMETER;
331a913ee75Sjruoho 
332a913ee75Sjruoho 	for (ptr = src, i = 0; i < 16; i++) {
333a913ee75Sjruoho 
334a913ee75Sjruoho 		if (*ptr == '-')
335a913ee75Sjruoho 			ptr++;
336a913ee75Sjruoho 
337a913ee75Sjruoho 		(void)memcpy(hex, ptr, 2);
33887dcc5d7Sbouyer 		hex[2] = '\0';
339a913ee75Sjruoho 
3407a8e9522Sjruoho 		if (HEXCHAR(hex[0]) == 0 || HEXCHAR(hex[1]) == 0)
341a913ee75Sjruoho 			return AE_BAD_HEX_CONSTANT;
342a913ee75Sjruoho 
343a913ee75Sjruoho 		bin[i] = strtoul(hex, NULL, 16) & 0xFF;
344a913ee75Sjruoho 
345a913ee75Sjruoho 		ptr++;
346a913ee75Sjruoho 		ptr++;
347a913ee75Sjruoho 	}
348a913ee75Sjruoho 
349*1899b7d2Sriastradh 	guid.data1 = be32dec(&bin[0]);
350*1899b7d2Sriastradh 	guid.data2 = be16dec(&bin[4]);
351*1899b7d2Sriastradh 	guid.data3 = be16dec(&bin[6]);
352*1899b7d2Sriastradh 	memcpy(guid.data4, &bin[8], 8);
353a913ee75Sjruoho 
354a913ee75Sjruoho 	SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) {
355a913ee75Sjruoho 
356*1899b7d2Sriastradh 		if (GUIDCMP(&guid, &wmi->guid) != 0) {
357a913ee75Sjruoho 
358a913ee75Sjruoho 			if (out != NULL)
359a913ee75Sjruoho 				*out = wmi;
360a913ee75Sjruoho 
361a913ee75Sjruoho 			return AE_OK;
362a913ee75Sjruoho 		}
363a913ee75Sjruoho 	}
364a913ee75Sjruoho 
365a913ee75Sjruoho 	return AE_NOT_FOUND;
366a913ee75Sjruoho }
367a913ee75Sjruoho 
368a913ee75Sjruoho /*
369a913ee75Sjruoho  * Checks if a GUID is present. Child devices
370a913ee75Sjruoho  * can use this in their autoconf(9) routines.
371a913ee75Sjruoho  */
372a913ee75Sjruoho int
acpi_wmi_guid_match(device_t self,const char * guid)373a913ee75Sjruoho acpi_wmi_guid_match(device_t self, const char *guid)
374a913ee75Sjruoho {
375a913ee75Sjruoho 	struct acpi_wmi_softc *sc = device_private(self);
376a913ee75Sjruoho 	ACPI_STATUS rv;
377a913ee75Sjruoho 
378a913ee75Sjruoho 	rv = acpi_wmi_guid_get(sc, guid, NULL);
379a913ee75Sjruoho 
380a913ee75Sjruoho 	if (ACPI_SUCCESS(rv))
381a913ee75Sjruoho 		return 1;
382a913ee75Sjruoho 
383a913ee75Sjruoho 	return 0;
384a913ee75Sjruoho }
385a913ee75Sjruoho 
386a913ee75Sjruoho /*
387a913ee75Sjruoho  * Adds internal event handler.
388a913ee75Sjruoho  */
38955052ecaSjruoho static void
acpi_wmi_event_add(struct acpi_wmi_softc * sc)390a913ee75Sjruoho acpi_wmi_event_add(struct acpi_wmi_softc *sc)
391a913ee75Sjruoho {
392a913ee75Sjruoho 	struct wmi_t *wmi;
393a913ee75Sjruoho 	ACPI_STATUS rv;
394a913ee75Sjruoho 
39555052ecaSjruoho 	if (acpi_register_notify(sc->sc_node, acpi_wmi_event_handler) != true)
39655052ecaSjruoho 		return;
397a913ee75Sjruoho 
398a240caeaSjruoho 	/*
399ff198a0bSjakllsch 	 * Enable possible events, expensive or otherwise.
400a240caeaSjruoho 	 */
401a913ee75Sjruoho 	SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) {
402a913ee75Sjruoho 
403ff198a0bSjakllsch 		if ((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) != 0) {
404a913ee75Sjruoho 
405ff198a0bSjakllsch 			rv = acpi_wmi_enable_event(sc->sc_node->ad_handle,
406ff198a0bSjakllsch 			    wmi->guid.nid, true);
407a913ee75Sjruoho 
408a913ee75Sjruoho 			if (ACPI_SUCCESS(rv)) {
409a913ee75Sjruoho 				wmi->eevent = true;
410a913ee75Sjruoho 				continue;
411a913ee75Sjruoho 			}
412a913ee75Sjruoho 
413a240caeaSjruoho 			aprint_debug_dev(sc->sc_dev, "failed to enable "
414a913ee75Sjruoho 			    "expensive WExx: %s\n", AcpiFormatException(rv));
415a913ee75Sjruoho 		}
416a913ee75Sjruoho 	}
417a913ee75Sjruoho }
418a913ee75Sjruoho 
419a913ee75Sjruoho /*
420a913ee75Sjruoho  * Removes the internal event handler.
421a913ee75Sjruoho  */
42255052ecaSjruoho static void
acpi_wmi_event_del(struct acpi_wmi_softc * sc)423a913ee75Sjruoho acpi_wmi_event_del(struct acpi_wmi_softc *sc)
424a913ee75Sjruoho {
425a913ee75Sjruoho 	struct wmi_t *wmi;
426a913ee75Sjruoho 	ACPI_STATUS rv;
427a913ee75Sjruoho 
42855052ecaSjruoho 	acpi_deregister_notify(sc->sc_node);
429a913ee75Sjruoho 
430a913ee75Sjruoho 	SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) {
431a913ee75Sjruoho 
432a913ee75Sjruoho 		if (wmi->eevent != true)
433a913ee75Sjruoho 			continue;
434a913ee75Sjruoho 
435a913ee75Sjruoho 		KASSERT((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) != 0);
436a913ee75Sjruoho 
437ff198a0bSjakllsch 		rv = acpi_wmi_enable_event(sc->sc_node->ad_handle,
438ff198a0bSjakllsch 		    wmi->guid.nid, false);
439a913ee75Sjruoho 
440a913ee75Sjruoho 		if (ACPI_SUCCESS(rv)) {
441a913ee75Sjruoho 			wmi->eevent = false;
442a913ee75Sjruoho 			continue;
443a913ee75Sjruoho 		}
444a913ee75Sjruoho 
445a240caeaSjruoho 		aprint_debug_dev(sc->sc_dev, "failed to disable "
446a913ee75Sjruoho 		    "expensive WExx: %s\n", AcpiFormatException(rv));
447a913ee75Sjruoho 	}
448a913ee75Sjruoho }
449a913ee75Sjruoho 
450a913ee75Sjruoho /*
451a913ee75Sjruoho  * Returns extra information possibly associated with an event.
452a913ee75Sjruoho  */
453a913ee75Sjruoho ACPI_STATUS
acpi_wmi_event_get(device_t self,uint32_t event,ACPI_BUFFER * obuf)454a913ee75Sjruoho acpi_wmi_event_get(device_t self, uint32_t event, ACPI_BUFFER *obuf)
455a913ee75Sjruoho {
456a913ee75Sjruoho 	struct acpi_wmi_softc *sc = device_private(self);
457a913ee75Sjruoho 	struct wmi_t *wmi;
458a913ee75Sjruoho 	ACPI_OBJECT_LIST arg;
459a913ee75Sjruoho 	ACPI_OBJECT obj;
4607a8e9522Sjruoho 	ACPI_HANDLE hdl;
4617a8e9522Sjruoho 
462a913ee75Sjruoho 	if (sc == NULL || obuf == NULL)
463a913ee75Sjruoho 		return AE_BAD_PARAMETER;
464a913ee75Sjruoho 
465a913ee75Sjruoho 	if (sc->sc_handler == NULL)
466a913ee75Sjruoho 		return AE_ABORT_METHOD;
467a913ee75Sjruoho 
468a57241d4Sjruoho 	hdl = sc->sc_node->ad_handle;
469a57241d4Sjruoho 
470a913ee75Sjruoho 	obj.Type = ACPI_TYPE_INTEGER;
471a913ee75Sjruoho 	obj.Integer.Value = event;
472a913ee75Sjruoho 
473a913ee75Sjruoho 	arg.Count = 0x01;
474a913ee75Sjruoho 	arg.Pointer = &obj;
475a913ee75Sjruoho 
476a913ee75Sjruoho 	obuf->Pointer = NULL;
477a913ee75Sjruoho 	obuf->Length = ACPI_ALLOCATE_LOCAL_BUFFER;
478a913ee75Sjruoho 
479a913ee75Sjruoho 	SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) {
480a913ee75Sjruoho 
481a913ee75Sjruoho 		if ((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) == 0)
482a913ee75Sjruoho 			continue;
483a913ee75Sjruoho 
484a913ee75Sjruoho 		if (wmi->guid.nid != event)
485a913ee75Sjruoho 			continue;
486a913ee75Sjruoho 
4877a8e9522Sjruoho 		return AcpiEvaluateObject(hdl, "_WED", &arg, obuf);
488a913ee75Sjruoho 	}
489a913ee75Sjruoho 
490a913ee75Sjruoho 	return AE_NOT_FOUND;
491a913ee75Sjruoho }
492a913ee75Sjruoho 
493a913ee75Sjruoho /*
494a913ee75Sjruoho  * Forwards events to the external handler through the internal one.
495a913ee75Sjruoho  */
496a913ee75Sjruoho static void
acpi_wmi_event_handler(ACPI_HANDLE hdl,uint32_t evt,void * aux)497a913ee75Sjruoho acpi_wmi_event_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux)
498a913ee75Sjruoho {
49955052ecaSjruoho 	struct acpi_wmi_softc *sc;
50055052ecaSjruoho 	device_t self = aux;
50155052ecaSjruoho 
50255052ecaSjruoho 	sc = device_private(self);
503a913ee75Sjruoho 
504a913ee75Sjruoho 	if (sc->sc_child == NULL)
505a913ee75Sjruoho 		return;
506a913ee75Sjruoho 
507a913ee75Sjruoho 	if (sc->sc_handler == NULL)
508a913ee75Sjruoho 		return;
509a913ee75Sjruoho 
510a913ee75Sjruoho 	(*sc->sc_handler)(NULL, evt, sc->sc_child);
511a913ee75Sjruoho }
512a913ee75Sjruoho 
513a913ee75Sjruoho ACPI_STATUS
acpi_wmi_event_register(device_t self,ACPI_NOTIFY_HANDLER handler)514a913ee75Sjruoho acpi_wmi_event_register(device_t self, ACPI_NOTIFY_HANDLER handler)
515a913ee75Sjruoho {
516a913ee75Sjruoho 	struct acpi_wmi_softc *sc = device_private(self);
517a913ee75Sjruoho 
518a913ee75Sjruoho 	if (sc == NULL)
519a913ee75Sjruoho 		return AE_BAD_PARAMETER;
520a913ee75Sjruoho 
521a913ee75Sjruoho 	if (handler != NULL && sc->sc_handler != NULL)
522a913ee75Sjruoho 		return AE_ALREADY_EXISTS;
523a913ee75Sjruoho 
524a913ee75Sjruoho 	sc->sc_handler = handler;
525a913ee75Sjruoho 
526a913ee75Sjruoho 	return AE_OK;
527a913ee75Sjruoho }
528a913ee75Sjruoho 
529a913ee75Sjruoho ACPI_STATUS
acpi_wmi_event_deregister(device_t self)530a913ee75Sjruoho acpi_wmi_event_deregister(device_t self)
531a913ee75Sjruoho {
532a913ee75Sjruoho 	return acpi_wmi_event_register(self, NULL);
533a913ee75Sjruoho }
534a913ee75Sjruoho 
535a913ee75Sjruoho /*
536639d1b97Sjruoho  * Handler for EC regions, which may be embedded in WMI.
537639d1b97Sjruoho  */
538639d1b97Sjruoho static ACPI_STATUS
acpi_wmi_ec_handler(uint32_t func,ACPI_PHYSICAL_ADDRESS addr,uint32_t width,ACPI_INTEGER * val,void * setup,void * aux)539639d1b97Sjruoho acpi_wmi_ec_handler(uint32_t func, ACPI_PHYSICAL_ADDRESS addr,
540639d1b97Sjruoho     uint32_t width, ACPI_INTEGER *val, void *setup, void *aux)
541639d1b97Sjruoho {
542639d1b97Sjruoho 	struct acpi_wmi_softc *sc = aux;
543639d1b97Sjruoho 
544639d1b97Sjruoho 	if (aux == NULL || val == NULL)
545639d1b97Sjruoho 		return AE_BAD_PARAMETER;
546639d1b97Sjruoho 
547639d1b97Sjruoho 	if (addr > 0xFF || width % 8 != 0)
548639d1b97Sjruoho 		return AE_BAD_ADDRESS;
549639d1b97Sjruoho 
550639d1b97Sjruoho 	switch (func) {
551639d1b97Sjruoho 
552639d1b97Sjruoho 	case ACPI_READ:
553639d1b97Sjruoho 		(void)acpiec_bus_read(sc->sc_ecdev, addr, val, width);
554639d1b97Sjruoho 		break;
555639d1b97Sjruoho 
556639d1b97Sjruoho 	case ACPI_WRITE:
557639d1b97Sjruoho 		(void)acpiec_bus_write(sc->sc_ecdev, addr, *val, width);
558639d1b97Sjruoho 		break;
559639d1b97Sjruoho 
560639d1b97Sjruoho 	default:
561639d1b97Sjruoho 		return AE_BAD_PARAMETER;
562639d1b97Sjruoho 	}
563639d1b97Sjruoho 
564639d1b97Sjruoho 	return AE_OK;
565639d1b97Sjruoho }
566639d1b97Sjruoho 
567639d1b97Sjruoho /*
568a913ee75Sjruoho  * As there is no prior knowledge about the expensive
569a913ee75Sjruoho  * events that cause "significant overhead", try to
570a913ee75Sjruoho  * disable (enable) these before suspending (resuming).
571a913ee75Sjruoho  */
572a913ee75Sjruoho static bool
acpi_wmi_suspend(device_t self,const pmf_qual_t * qual)573a913ee75Sjruoho acpi_wmi_suspend(device_t self, const pmf_qual_t *qual)
574a913ee75Sjruoho {
575a913ee75Sjruoho 	struct acpi_wmi_softc *sc = device_private(self);
576a913ee75Sjruoho 
577a913ee75Sjruoho 	acpi_wmi_event_del(sc);
578a913ee75Sjruoho 
579a913ee75Sjruoho 	return true;
580a913ee75Sjruoho }
581a913ee75Sjruoho 
582a913ee75Sjruoho static bool
acpi_wmi_resume(device_t self,const pmf_qual_t * qual)583a913ee75Sjruoho acpi_wmi_resume(device_t self, const pmf_qual_t *qual)
584a913ee75Sjruoho {
585a913ee75Sjruoho 	struct acpi_wmi_softc *sc = device_private(self);
586a913ee75Sjruoho 
587a913ee75Sjruoho 	acpi_wmi_event_add(sc);
588a913ee75Sjruoho 
589a913ee75Sjruoho 	return true;
590a913ee75Sjruoho }
591a913ee75Sjruoho 
592a913ee75Sjruoho static ACPI_STATUS
acpi_wmi_enable_event(ACPI_HANDLE hdl,uint8_t nid,bool flag)593ff198a0bSjakllsch acpi_wmi_enable_event(ACPI_HANDLE hdl, uint8_t nid, bool flag)
594a913ee75Sjruoho {
595a913ee75Sjruoho 	char path[5];
596a913ee75Sjruoho 
597ff198a0bSjakllsch 	snprintf(path, sizeof(path), "WE%02X", nid);
598a913ee75Sjruoho 
599ff198a0bSjakllsch 	return acpi_eval_set_integer(hdl, path, (flag != false) ? 0x01 : 0x00);
600ff198a0bSjakllsch }
601ff198a0bSjakllsch 
602ff198a0bSjakllsch static ACPI_STATUS
acpi_wmi_enable_collection(ACPI_HANDLE hdl,const char * oid,bool flag)603ff198a0bSjakllsch acpi_wmi_enable_collection(ACPI_HANDLE hdl, const char *oid, bool flag)
604ff198a0bSjakllsch {
605ff198a0bSjakllsch 	char path[5];
606ff198a0bSjakllsch 
607ff198a0bSjakllsch 	strlcpy(path, "WC", sizeof(path));
608ff198a0bSjakllsch 	strlcat(path, oid, sizeof(path));
609a913ee75Sjruoho 
610a913ee75Sjruoho 	return acpi_eval_set_integer(hdl, path, (flag != false) ? 0x01 : 0x00);
611a913ee75Sjruoho }
612a913ee75Sjruoho 
613a913ee75Sjruoho static bool
acpi_wmi_input(struct wmi_t * wmi,uint8_t flag,uint8_t idx)614a913ee75Sjruoho acpi_wmi_input(struct wmi_t *wmi, uint8_t flag, uint8_t idx)
615a913ee75Sjruoho {
61687dcc5d7Sbouyer 	/* A data block may have no flags at all */
61787dcc5d7Sbouyer 	if ((wmi->guid.flags & flag) == 0 &&
61887dcc5d7Sbouyer 	    (flag == ACPI_WMI_FLAG_DATA  &&
61987dcc5d7Sbouyer 	     (wmi->guid.flags & ~ACPI_WMI_FLAG_EXPENSIVE) != 0))
620a913ee75Sjruoho 		return false;
621a913ee75Sjruoho 
622a913ee75Sjruoho 	if (wmi->guid.count == 0x00)
623a913ee75Sjruoho 		return false;
624a913ee75Sjruoho 
625a913ee75Sjruoho 	if (wmi->guid.count < idx)
626a913ee75Sjruoho 		return false;
627a913ee75Sjruoho 
628a913ee75Sjruoho 	return true;
629a913ee75Sjruoho }
630a913ee75Sjruoho 
631a913ee75Sjruoho /*
632a913ee75Sjruoho  * Makes a WMI data block query (WQxx). The corresponding control
633a913ee75Sjruoho  * method for data collection will be invoked if it is available.
634a913ee75Sjruoho  */
635a913ee75Sjruoho ACPI_STATUS
acpi_wmi_data_query(device_t self,const char * guid,uint8_t idx,ACPI_BUFFER * obuf)636a913ee75Sjruoho acpi_wmi_data_query(device_t self, const char *guid,
637a913ee75Sjruoho     uint8_t idx, ACPI_BUFFER *obuf)
638a913ee75Sjruoho {
639a913ee75Sjruoho 	struct acpi_wmi_softc *sc = device_private(self);
640a913ee75Sjruoho 	struct wmi_t *wmi;
641a913ee75Sjruoho 	char path[5] = "WQ";
642a913ee75Sjruoho 	ACPI_OBJECT_LIST arg;
643a913ee75Sjruoho 	ACPI_STATUS rv, rvxx;
644a913ee75Sjruoho 	ACPI_OBJECT obj;
645a913ee75Sjruoho 
646a913ee75Sjruoho 	rvxx = AE_SUPPORT;
647a913ee75Sjruoho 
648a913ee75Sjruoho 	if (obuf == NULL)
649a913ee75Sjruoho 		return AE_BAD_PARAMETER;
650a913ee75Sjruoho 
651a913ee75Sjruoho 	rv = acpi_wmi_guid_get(sc, guid, &wmi);
652a913ee75Sjruoho 
653a913ee75Sjruoho 	if (ACPI_FAILURE(rv))
654a913ee75Sjruoho 		return rv;
655a913ee75Sjruoho 
656a913ee75Sjruoho 	if (acpi_wmi_input(wmi, ACPI_WMI_FLAG_DATA, idx) != true)
657a913ee75Sjruoho 		return AE_BAD_DATA;
658a913ee75Sjruoho 
659a913ee75Sjruoho 	(void)strlcat(path, wmi->guid.oid, sizeof(path));
660a913ee75Sjruoho 
661a913ee75Sjruoho 	obj.Type = ACPI_TYPE_INTEGER;
662a913ee75Sjruoho 	obj.Integer.Value = idx;
663a913ee75Sjruoho 
664a913ee75Sjruoho 	arg.Count = 0x01;
665a913ee75Sjruoho 	arg.Pointer = &obj;
666a913ee75Sjruoho 
667a913ee75Sjruoho 	obuf->Pointer = NULL;
668a913ee75Sjruoho 	obuf->Length = ACPI_ALLOCATE_LOCAL_BUFFER;
669a913ee75Sjruoho 
670a913ee75Sjruoho 	/*
671a913ee75Sjruoho 	 * If the expensive flag is set, we should enable
672a913ee75Sjruoho 	 * data collection before evaluating the WQxx buffer.
673a913ee75Sjruoho 	 */
674a913ee75Sjruoho 	if ((wmi->guid.flags & ACPI_WMI_FLAG_EXPENSIVE) != 0) {
675a913ee75Sjruoho 
676ff198a0bSjakllsch 		rvxx = acpi_wmi_enable_collection(sc->sc_node->ad_handle,
677ff198a0bSjakllsch 		    wmi->guid.oid, true);
678a913ee75Sjruoho 	}
679a913ee75Sjruoho 
680a913ee75Sjruoho 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, obuf);
681a913ee75Sjruoho 
682a913ee75Sjruoho 	/* No longer needed. */
683a913ee75Sjruoho 	if (ACPI_SUCCESS(rvxx)) {
684a913ee75Sjruoho 
685ff198a0bSjakllsch 		(void)acpi_wmi_enable_collection(sc->sc_node->ad_handle,
686ff198a0bSjakllsch 		    wmi->guid.oid, false);
687a913ee75Sjruoho 	}
688a913ee75Sjruoho 
689a913ee75Sjruoho #ifdef DIAGNOSTIC
690a913ee75Sjruoho 	/*
691a913ee75Sjruoho 	 * XXX: It appears that quite a few laptops have WQxx
692a913ee75Sjruoho 	 * methods that are declared as expensive, but lack the
693a913ee75Sjruoho 	 * corresponding WCxx control method.
694a913ee75Sjruoho 	 *
695a913ee75Sjruoho 	 * -- Acer Aspire One is one example <jruohonen@iki.fi>.
696a913ee75Sjruoho 	 */
697a913ee75Sjruoho 	if (ACPI_FAILURE(rvxx) && rvxx != AE_SUPPORT)
698a913ee75Sjruoho 		aprint_error_dev(sc->sc_dev, "failed to evaluate WCxx "
699a913ee75Sjruoho 		    "for %s: %s\n", path, AcpiFormatException(rvxx));
700a913ee75Sjruoho #endif
701a913ee75Sjruoho 	return rv;
702a913ee75Sjruoho }
703a913ee75Sjruoho 
704a913ee75Sjruoho /*
705a913ee75Sjruoho  * Writes to a data block (WSxx).
706a913ee75Sjruoho  */
707a913ee75Sjruoho ACPI_STATUS
acpi_wmi_data_write(device_t self,const char * guid,uint8_t idx,ACPI_BUFFER * ibuf)708a913ee75Sjruoho acpi_wmi_data_write(device_t self, const char *guid,
709a913ee75Sjruoho     uint8_t idx, ACPI_BUFFER *ibuf)
710a913ee75Sjruoho {
711a913ee75Sjruoho 	struct acpi_wmi_softc *sc = device_private(self);
712a913ee75Sjruoho 	struct wmi_t *wmi;
713a913ee75Sjruoho 	ACPI_OBJECT_LIST arg;
714a913ee75Sjruoho 	ACPI_OBJECT obj[2];
715a913ee75Sjruoho 	char path[5] = "WS";
716a913ee75Sjruoho 	ACPI_STATUS rv;
717a913ee75Sjruoho 
718a913ee75Sjruoho 	if (ibuf == NULL)
719a913ee75Sjruoho 		return AE_BAD_PARAMETER;
720a913ee75Sjruoho 
721a913ee75Sjruoho 	rv = acpi_wmi_guid_get(sc, guid, &wmi);
722a913ee75Sjruoho 
723a913ee75Sjruoho 	if (ACPI_FAILURE(rv))
724a913ee75Sjruoho 		return rv;
725a913ee75Sjruoho 
726a913ee75Sjruoho 	if (acpi_wmi_input(wmi, ACPI_WMI_FLAG_DATA, idx) != true)
727a913ee75Sjruoho 		return AE_BAD_DATA;
728a913ee75Sjruoho 
729a913ee75Sjruoho 	(void)strlcat(path, wmi->guid.oid, sizeof(path));
730a913ee75Sjruoho 
731a913ee75Sjruoho 	obj[0].Integer.Value = idx;
732a913ee75Sjruoho 	obj[0].Type = ACPI_TYPE_INTEGER;
733a913ee75Sjruoho 
734a913ee75Sjruoho 	obj[1].Buffer.Length = ibuf->Length;
735a913ee75Sjruoho 	obj[1].Buffer.Pointer = ibuf->Pointer;
736a913ee75Sjruoho 
737a913ee75Sjruoho 	obj[1].Type = ((wmi->guid.flags & ACPI_WMI_FLAG_STRING) != 0) ?
738a913ee75Sjruoho 	    ACPI_TYPE_STRING : ACPI_TYPE_BUFFER;
739a913ee75Sjruoho 
740a913ee75Sjruoho 	arg.Count = 0x02;
741a913ee75Sjruoho 	arg.Pointer = obj;
742a913ee75Sjruoho 
743a913ee75Sjruoho 	return AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, NULL);
744a913ee75Sjruoho }
745a913ee75Sjruoho 
746a913ee75Sjruoho /*
747a913ee75Sjruoho  * Executes a method (WMxx).
748a913ee75Sjruoho  */
749a913ee75Sjruoho ACPI_STATUS
acpi_wmi_method(device_t self,const char * guid,uint8_t idx,uint32_t mid,ACPI_BUFFER * ibuf,ACPI_BUFFER * obuf)750a913ee75Sjruoho acpi_wmi_method(device_t self, const char *guid, uint8_t idx,
751a913ee75Sjruoho     uint32_t mid, ACPI_BUFFER *ibuf, ACPI_BUFFER *obuf)
752a913ee75Sjruoho {
753a913ee75Sjruoho 	struct acpi_wmi_softc *sc = device_private(self);
754a913ee75Sjruoho 	struct wmi_t *wmi;
755a913ee75Sjruoho 	ACPI_OBJECT_LIST arg;
756a913ee75Sjruoho 	ACPI_OBJECT obj[3];
757a913ee75Sjruoho 	char path[5] = "WM";
758a913ee75Sjruoho 	ACPI_STATUS rv;
759a913ee75Sjruoho 
760a913ee75Sjruoho 	if (ibuf == NULL || obuf == NULL)
761a913ee75Sjruoho 		return AE_BAD_PARAMETER;
762a913ee75Sjruoho 
763a913ee75Sjruoho 	rv = acpi_wmi_guid_get(sc, guid, &wmi);
764a913ee75Sjruoho 
765a913ee75Sjruoho 	if (ACPI_FAILURE(rv))
766a913ee75Sjruoho 		return rv;
767a913ee75Sjruoho 
768a913ee75Sjruoho 	if (acpi_wmi_input(wmi, ACPI_WMI_FLAG_METHOD, idx) != true)
769a913ee75Sjruoho 		return AE_BAD_DATA;
770a913ee75Sjruoho 
771a913ee75Sjruoho 	(void)strlcat(path, wmi->guid.oid, sizeof(path));
772a913ee75Sjruoho 
773a913ee75Sjruoho 	obj[0].Integer.Value = idx;
774a913ee75Sjruoho 	obj[1].Integer.Value = mid;
775a913ee75Sjruoho 	obj[0].Type = obj[1].Type = ACPI_TYPE_INTEGER;
776a913ee75Sjruoho 
777a913ee75Sjruoho 	obj[2].Buffer.Length = ibuf->Length;
778a913ee75Sjruoho 	obj[2].Buffer.Pointer = ibuf->Pointer;
779a913ee75Sjruoho 
780a913ee75Sjruoho 	obj[2].Type = ((wmi->guid.flags & ACPI_WMI_FLAG_STRING) != 0) ?
781a913ee75Sjruoho 	    ACPI_TYPE_STRING : ACPI_TYPE_BUFFER;
782a913ee75Sjruoho 
783a913ee75Sjruoho 	arg.Count = 0x03;
784a913ee75Sjruoho 	arg.Pointer = obj;
785a913ee75Sjruoho 
786a913ee75Sjruoho 	obuf->Pointer = NULL;
787a913ee75Sjruoho 	obuf->Length = ACPI_ALLOCATE_LOCAL_BUFFER;
788a913ee75Sjruoho 
789a913ee75Sjruoho 	return AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, obuf);
790a913ee75Sjruoho }
7919750ca2fSjmcneill 
7929750ca2fSjmcneill MODULE(MODULE_CLASS_DRIVER, acpiwmi, NULL);
7939750ca2fSjmcneill 
794b9f301d5Sjruoho #ifdef _MODULE
795b9f301d5Sjruoho #include "ioconf.c"
796b9f301d5Sjruoho #endif
7979750ca2fSjmcneill 
7989750ca2fSjmcneill static int
acpiwmi_modcmd(modcmd_t cmd,void * aux)799b9f301d5Sjruoho acpiwmi_modcmd(modcmd_t cmd, void *aux)
8009750ca2fSjmcneill {
801b9f301d5Sjruoho 	int rv = 0;
8029750ca2fSjmcneill 
8039750ca2fSjmcneill 	switch (cmd) {
8049750ca2fSjmcneill 
8059750ca2fSjmcneill 	case MODULE_CMD_INIT:
8069750ca2fSjmcneill 
807b9f301d5Sjruoho #ifdef _MODULE
808b9f301d5Sjruoho 		rv = config_init_component(cfdriver_ioconf_acpiwmi,
809b9f301d5Sjruoho 		    cfattach_ioconf_acpiwmi, cfdata_ioconf_acpiwmi);
810b9f301d5Sjruoho #endif
811b9f301d5Sjruoho 		break;
8129750ca2fSjmcneill 
8139750ca2fSjmcneill 	case MODULE_CMD_FINI:
8149750ca2fSjmcneill 
815b9f301d5Sjruoho #ifdef _MODULE
816b9f301d5Sjruoho 		rv = config_fini_component(cfdriver_ioconf_acpiwmi,
817b9f301d5Sjruoho 		    cfattach_ioconf_acpiwmi, cfdata_ioconf_acpiwmi);
818b9f301d5Sjruoho #endif
819b9f301d5Sjruoho 		break;
8209750ca2fSjmcneill 
8219750ca2fSjmcneill 	default:
822b9f301d5Sjruoho 		rv = ENOTTY;
8239750ca2fSjmcneill 	}
8249750ca2fSjmcneill 
825b9f301d5Sjruoho 	return rv;
826b9f301d5Sjruoho }
827