1 /* $NetBSD: wmi_acpi.c,v 1.7 2010/01/04 10:06:53 jruoho Exp $ */
4 * Copyright (c) 2009, 2010 Jukka Ruohonen <jruohonen@iki.fi>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: wmi_acpi.c,v 1.7 2010/01/04 10:06:53 jruoho Exp $");
32 #include <sys/param.h>
33 #include <sys/device.h>
34 #include <sys/endian.h>
37 #include <dev/acpi/acpireg.h>
38 #include <dev/acpi/acpivar.h>
39 #include <dev/acpi/wmi_acpivar.h>
41 #define _COMPONENT ACPI_RESOURCE_COMPONENT
42 ACPI_MODULE_NAME ("wmi_acpi")
45 * This implements something called "Microsoft Windows Management
46 * Instrumentation" (WMI). This subset of ACPI is desribed in:
48 * http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx
50 * (Obtained on Thu Feb 12 18:21:44 EET 2009.)
55 * The GUID itself. The used format is the usual 32-16-16-64-bit
56 * representation. All except the fourth field are in native byte
57 * order. A 32-16-16-16-48-bit hexadecimal notation with hyphens
58 * is used for human-readable GUIDs.
68 char oid
[2]; /* ACPI object ID. */
71 uint8_t nid
; /* Notification value. */
72 uint8_t res
; /* Reserved. */
76 uint8_t count
; /* Number of instances. */
77 uint8_t flags
; /* Additional flags. */
85 SIMPLEQ_ENTRY(wmi_t
) wmi_link
;
88 struct acpi_wmi_softc
{
91 ACPI_NOTIFY_HANDLER sc_handler
;
92 struct acpi_devnode
*sc_node
;
94 SIMPLEQ_HEAD(, wmi_t
) wmi_head
;
97 #define ACPI_WMI_FLAG_EXPENSIVE 0x01
98 #define ACPI_WMI_FLAG_METHOD 0x02
99 #define ACPI_WMI_FLAG_STRING 0x04
100 #define ACPI_WMI_FLAG_EVENT 0x08
101 #define ACPI_WMI_FLAG_DATA (ACPI_WMI_FLAG_EXPENSIVE | \
102 ACPI_WMI_FLAG_STRING)
104 #define UGET16(x) (*(uint16_t *)(x))
105 #define UGET64(x) (*(uint64_t *)(x))
107 #define HEXCHAR(x) (((x) >= '0' && (x) <= '9') || \
108 ((x) >= 'a' && (x) <= 'f') || \
109 ((x) >= 'A' && (x) <= 'F'))
111 #define GUIDCMP(a, b) \
112 ((a)->data1 == (b)->data1 && \
113 (a)->data2 == (b)->data2 && \
114 (a)->data3 == (b)->data3 && \
115 UGET64((a)->data4) == UGET64((b)->data4))
117 static int acpi_wmi_match(device_t
, cfdata_t
, void *);
118 static void acpi_wmi_attach(device_t
, device_t
, void *);
119 static int acpi_wmi_print(void *, const char *);
120 static bool acpi_wmi_init(struct acpi_wmi_softc
*);
121 static bool acpi_wmi_add(struct acpi_wmi_softc
*, ACPI_OBJECT
*);
122 static void acpi_wmi_del(struct acpi_wmi_softc
*);
125 static void acpi_wmi_dump(struct acpi_wmi_softc
*);
128 static ACPI_STATUS
acpi_wmi_guid_get(struct acpi_wmi_softc
*,
129 const char *, struct wmi_t
**);
130 static void acpi_wmi_event_add(struct acpi_wmi_softc
*);
131 static void acpi_wmi_event_del(struct acpi_wmi_softc
*);
132 static void acpi_wmi_event_handler(ACPI_HANDLE
, uint32_t, void *);
133 static bool acpi_wmi_suspend(device_t
, pmf_qual_t
);
134 static bool acpi_wmi_resume(device_t
, pmf_qual_t
);
135 static ACPI_STATUS
acpi_wmi_enable(ACPI_HANDLE
, const char *, bool, bool);
136 static bool acpi_wmi_input(struct wmi_t
*, uint8_t, uint8_t);
138 const char * const acpi_wmi_ids
[] = {
143 CFATTACH_DECL_NEW(acpiwmi
, sizeof(struct acpi_wmi_softc
),
144 acpi_wmi_match
, acpi_wmi_attach
, NULL
, NULL
);
147 acpi_wmi_match(device_t parent
, cfdata_t match
, void *aux
)
149 struct acpi_attach_args
*aa
= aux
;
151 if (aa
->aa_node
->ad_type
!= ACPI_TYPE_DEVICE
)
154 return acpi_match_hid(aa
->aa_node
->ad_devinfo
, acpi_wmi_ids
);
158 acpi_wmi_attach(device_t parent
, device_t self
, void *aux
)
160 struct acpi_wmi_softc
*sc
= device_private(self
);
161 struct acpi_attach_args
*aa
= aux
;
164 sc
->sc_node
= aa
->aa_node
;
165 sc
->sc_handler
= NULL
;
168 aprint_normal(": ACPI WMI Interface\n");
170 if (acpi_wmi_init(sc
) != true)
177 acpi_wmi_event_add(sc
);
179 if (pmf_device_register(sc
->sc_dev
,
180 acpi_wmi_suspend
, acpi_wmi_resume
) != true)
181 aprint_error_dev(self
, "failed to register power handler\n");
183 /* Attach a child device to the pseudo-bus. */
184 sc
->sc_child
= config_found_ia(self
, "acpiwmibus",
185 NULL
, acpi_wmi_print
);
189 acpi_wmi_print(void *aux
, const char *pnp
)
193 aprint_normal("acpiwmibus at %s", pnp
);
199 acpi_wmi_init(struct acpi_wmi_softc
*sc
)
206 rv
= acpi_eval_struct(sc
->sc_node
->ad_handle
, "_WDG", &buf
);
208 if (ACPI_FAILURE(rv
))
212 len
= obj
->Buffer
.Length
;
214 KASSERT(obj
->Type
== ACPI_TYPE_BUFFER
);
216 if (len
!= obj
->Package
.Count
) {
221 KASSERT(sizeof(struct guid_t
) == 20);
223 if (len
< sizeof(struct guid_t
) ||
224 len
% sizeof(struct guid_t
) != 0) {
229 return acpi_wmi_add(sc
, obj
);
232 aprint_error_dev(sc
->sc_dev
, "failed to evaluate _WDG: %s\n",
233 AcpiFormatException(rv
));
235 if (buf
.Pointer
!= NULL
)
236 ACPI_FREE(buf
.Pointer
);
242 acpi_wmi_add(struct acpi_wmi_softc
*sc
, ACPI_OBJECT
*obj
)
245 size_t i
, n
, offset
, siz
;
247 siz
= sizeof(struct guid_t
);
248 n
= obj
->Buffer
.Length
/ siz
;
250 SIMPLEQ_INIT(&sc
->wmi_head
);
252 for (i
= offset
= 0; i
< n
; ++i
) {
254 if ((wmi
= kmem_zalloc(sizeof(*wmi
), KM_NOSLEEP
)) == NULL
)
257 ACPI_MEMCPY(&wmi
->guid
, obj
->Buffer
.Pointer
+ offset
, siz
);
260 offset
= offset
+ siz
;
262 SIMPLEQ_INSERT_TAIL(&sc
->wmi_head
, wmi
, wmi_link
);
277 acpi_wmi_del(struct acpi_wmi_softc
*sc
)
281 if (SIMPLEQ_EMPTY(&sc
->wmi_head
))
284 while (SIMPLEQ_FIRST(&sc
->wmi_head
) != NULL
) {
286 wmi
= SIMPLEQ_FIRST(&sc
->wmi_head
);
287 SIMPLEQ_REMOVE_HEAD(&sc
->wmi_head
, wmi_link
);
289 KASSERT(wmi
!= NULL
);
291 kmem_free(wmi
, sizeof(*wmi
));
297 acpi_wmi_dump(struct acpi_wmi_softc
*sc
)
301 KASSERT(!(SIMPLEQ_EMPTY(&sc
->wmi_head
)));
303 SIMPLEQ_FOREACH(wmi
, &sc
->wmi_head
, wmi_link
) {
305 aprint_normal_dev(sc
->sc_dev
, "{%08X-%04X-%04X-",
306 wmi
->guid
.data1
, wmi
->guid
.data2
, wmi
->guid
.data3
);
308 aprint_normal("%02X%02X-%02X%02X%02X%02X%02X%02X} ",
309 wmi
->guid
.data4
[0], wmi
->guid
.data4
[1],
310 wmi
->guid
.data4
[2], wmi
->guid
.data4
[3],
311 wmi
->guid
.data4
[4], wmi
->guid
.data4
[5],
312 wmi
->guid
.data4
[6], wmi
->guid
.data4
[7]);
314 aprint_normal("oid %04X count %02X flags %02X\n",
315 UGET16(wmi
->guid
.oid
), wmi
->guid
.count
, wmi
->guid
.flags
);
321 acpi_wmi_guid_get(struct acpi_wmi_softc
*sc
,
322 const char *src
, struct wmi_t
**out
)
331 if (sc
== NULL
|| src
== NULL
|| ACPI_STRLEN(src
) != 36)
332 return AE_BAD_PARAMETER
;
334 for (ptr
= src
, i
= 0; i
< 16; i
++) {
339 ACPI_MEMCPY(hex
, ptr
, 2);
341 if (!HEXCHAR(hex
[0]) || !HEXCHAR(hex
[1]))
342 return AE_BAD_HEX_CONSTANT
;
344 bin
[i
] = ACPI_STRTOUL(hex
, NULL
, 16) & 0xFF;
350 guid
= (struct guid_t
*)bin
;
351 guid
->data1
= be32toh(guid
->data1
);
352 guid
->data2
= be16toh(guid
->data2
);
353 guid
->data3
= be16toh(guid
->data3
);
355 SIMPLEQ_FOREACH(wmi
, &sc
->wmi_head
, wmi_link
) {
357 if (GUIDCMP(guid
, &wmi
->guid
)) {
370 * Checks if a GUID is present. Child devices
371 * can use this in their autoconf(9) routines.
374 acpi_wmi_guid_match(device_t self
, const char *guid
)
376 struct acpi_wmi_softc
*sc
= device_private(self
);
378 if (ACPI_SUCCESS(acpi_wmi_guid_get(sc
, guid
, NULL
)))
385 * Adds internal event handler.
388 acpi_wmi_event_add(struct acpi_wmi_softc
*sc
)
393 rv
= AcpiInstallNotifyHandler(sc
->sc_node
->ad_handle
,
394 ACPI_ALL_NOTIFY
, acpi_wmi_event_handler
, sc
);
396 if (ACPI_FAILURE(rv
)) {
397 aprint_error_dev(sc
->sc_dev
, "failed to install notify "
398 "handler: %s\n", AcpiFormatException(rv
));
402 /* Enable possible expensive events. */
403 SIMPLEQ_FOREACH(wmi
, &sc
->wmi_head
, wmi_link
) {
405 if ((wmi
->guid
.flags
& ACPI_WMI_FLAG_EVENT
) &&
406 (wmi
->guid
.flags
& ACPI_WMI_FLAG_EXPENSIVE
)) {
408 rv
= acpi_wmi_enable(sc
->sc_node
->ad_handle
,
409 wmi
->guid
.oid
, false, true);
411 if (ACPI_SUCCESS(rv
)) {
416 aprint_error_dev(sc
->sc_dev
, "failed to enable "
417 "expensive WExx: %s\n", AcpiFormatException(rv
));
423 * Removes the internal event handler.
426 acpi_wmi_event_del(struct acpi_wmi_softc
*sc
)
431 rv
= AcpiRemoveNotifyHandler(sc
->sc_node
->ad_handle
,
432 ACPI_ALL_NOTIFY
, acpi_wmi_event_handler
);
434 if (ACPI_FAILURE(rv
)) {
435 aprint_debug_dev(sc
->sc_dev
, "failed to remove notify "
436 "handler: %s\n", AcpiFormatException(rv
));
440 SIMPLEQ_FOREACH(wmi
, &sc
->wmi_head
, wmi_link
) {
442 if (wmi
->eevent
!= true)
445 KASSERT(wmi
->guid
.flags
& ACPI_WMI_FLAG_EVENT
);
446 KASSERT(wmi
->guid
.flags
& ACPI_WMI_FLAG_EXPENSIVE
);
448 rv
= acpi_wmi_enable(sc
->sc_node
->ad_handle
,
449 wmi
->guid
.oid
, false, false);
451 if (ACPI_SUCCESS(rv
)) {
456 aprint_error_dev(sc
->sc_dev
, "failed to disable "
457 "expensive WExx: %s\n", AcpiFormatException(rv
));
462 * Returns extra information possibly associated with an event.
465 acpi_wmi_event_get(device_t self
, uint32_t event
, ACPI_BUFFER
*obuf
)
467 struct acpi_wmi_softc
*sc
= device_private(self
);
469 ACPI_OBJECT_LIST arg
;
472 if (sc
== NULL
|| obuf
== NULL
)
473 return AE_BAD_PARAMETER
;
475 if (sc
->sc_handler
== NULL
)
476 return AE_ABORT_METHOD
;
478 obj
.Type
= ACPI_TYPE_INTEGER
;
479 obj
.Integer
.Value
= event
;
484 obuf
->Pointer
= NULL
;
485 obuf
->Length
= ACPI_ALLOCATE_LOCAL_BUFFER
;
487 SIMPLEQ_FOREACH(wmi
, &sc
->wmi_head
, wmi_link
) {
489 if (!(wmi
->guid
.flags
& ACPI_WMI_FLAG_EVENT
))
492 if (wmi
->guid
.nid
!= event
)
495 return AcpiEvaluateObject(sc
->sc_node
->ad_handle
, "_WED",
503 * Forwards events to the external handler through the internal one.
506 acpi_wmi_event_handler(ACPI_HANDLE hdl
, uint32_t evt
, void *aux
)
509 struct acpi_wmi_softc
*sc
= device_private(self
);
511 if (sc
->sc_handler
== NULL
)
514 (*sc
->sc_handler
)(NULL
, evt
, sc
->sc_child
);
518 * Adds or removes (NULL) the external event handler.
521 acpi_wmi_event_register(device_t self
, ACPI_NOTIFY_HANDLER handler
)
523 struct acpi_wmi_softc
*sc
= device_private(self
);
526 return AE_BAD_PARAMETER
;
528 if (handler
!= NULL
&& sc
->sc_handler
!= NULL
)
529 return AE_ALREADY_EXISTS
;
531 sc
->sc_handler
= handler
;
537 * As there is no prior knowledge about the expensive
538 * events that cause "significant overhead", try to
539 * disable (enable) these before suspending (resuming).
542 acpi_wmi_suspend(device_t self
, pmf_qual_t qual
)
544 struct acpi_wmi_softc
*sc
= device_private(self
);
546 acpi_wmi_event_del(sc
);
552 acpi_wmi_resume(device_t self
, pmf_qual_t qual
)
554 struct acpi_wmi_softc
*sc
= device_private(self
);
556 acpi_wmi_event_add(sc
);
562 * Enables or disables data collection (WCxx) or an event (WExx).
565 acpi_wmi_enable(ACPI_HANDLE hdl
, const char *oid
, bool data
, bool flag
)
570 str
= (data
!= false) ? "WC" : "WE";
572 (void)strlcpy(path
, str
, sizeof(path
));
573 (void)strlcat(path
, oid
, sizeof(path
));
575 return acpi_eval_set_integer(hdl
, path
, (flag
!= false) ? 0x01 : 0x00);
579 acpi_wmi_input(struct wmi_t
*wmi
, uint8_t flag
, uint8_t idx
)
582 if (!(wmi
->guid
.flags
& flag
))
585 if (wmi
->guid
.count
== 0x00 || wmi
->guid
.flags
== 0x00)
588 if (wmi
->guid
.count
< idx
)
595 * Makes a WMI data block query (WQxx). The corresponding control
596 * method for data collection will be invoked if it is available.
599 acpi_wmi_data_query(device_t self
, const char *guid
,
600 uint8_t idx
, ACPI_BUFFER
*obuf
)
602 struct acpi_wmi_softc
*sc
= device_private(self
);
605 ACPI_OBJECT_LIST arg
;
606 ACPI_STATUS rv
, rvxx
;
612 return AE_BAD_PARAMETER
;
614 rv
= acpi_wmi_guid_get(sc
, guid
, &wmi
);
616 if (ACPI_FAILURE(rv
))
619 if (acpi_wmi_input(wmi
, ACPI_WMI_FLAG_DATA
, idx
) != true)
622 (void)strlcat(path
, wmi
->guid
.oid
, sizeof(path
));
624 obj
.Type
= ACPI_TYPE_INTEGER
;
625 obj
.Integer
.Value
= idx
;
630 obuf
->Pointer
= NULL
;
631 obuf
->Length
= ACPI_ALLOCATE_LOCAL_BUFFER
;
634 * If the expensive flag is set, we should enable
635 * data collection before evaluating the WQxx buffer.
637 if (wmi
->guid
.flags
& ACPI_WMI_FLAG_EXPENSIVE
) {
639 rvxx
= acpi_wmi_enable(sc
->sc_node
->ad_handle
,
640 wmi
->guid
.oid
, true, true);
643 rv
= AcpiEvaluateObject(sc
->sc_node
->ad_handle
, path
, &arg
, obuf
);
645 /* No longer needed. */
646 if (ACPI_SUCCESS(rvxx
)) {
648 (void)acpi_wmi_enable(sc
->sc_node
->ad_handle
,
649 wmi
->guid
.oid
, true, false);
654 * XXX: It appears that quite a few laptops have WQxx
655 * methods that are declared as expensive, but lack the
656 * corresponding WCxx control method.
658 * -- Acer Aspire One is one example <jruohonen@iki.fi>.
660 if (ACPI_FAILURE(rvxx
) && rvxx
!= AE_SUPPORT
)
661 aprint_error_dev(sc
->sc_dev
, "failed to evaluate WCxx "
662 "for %s: %s\n", path
, AcpiFormatException(rvxx
));
668 * Writes to a data block (WSxx).
671 acpi_wmi_data_write(device_t self
, const char *guid
,
672 uint8_t idx
, ACPI_BUFFER
*ibuf
)
674 struct acpi_wmi_softc
*sc
= device_private(self
);
676 ACPI_OBJECT_LIST arg
;
682 return AE_BAD_PARAMETER
;
684 rv
= acpi_wmi_guid_get(sc
, guid
, &wmi
);
686 if (ACPI_FAILURE(rv
))
689 if (acpi_wmi_input(wmi
, ACPI_WMI_FLAG_DATA
, idx
) != true)
692 (void)strlcat(path
, wmi
->guid
.oid
, sizeof(path
));
694 obj
[0].Integer
.Value
= idx
;
695 obj
[0].Type
= ACPI_TYPE_INTEGER
;
697 obj
[1].Buffer
.Length
= ibuf
->Length
;
698 obj
[1].Buffer
.Pointer
= ibuf
->Pointer
;
700 obj
[1].Type
= (wmi
->guid
.flags
& ACPI_WMI_FLAG_STRING
) ?
701 ACPI_TYPE_STRING
: ACPI_TYPE_INTEGER
;
706 return AcpiEvaluateObject(sc
->sc_node
->ad_handle
, path
, &arg
, NULL
);
710 * Executes a method (WMxx).
713 acpi_wmi_method(device_t self
, const char *guid
, uint8_t idx
,
714 uint32_t mid
, ACPI_BUFFER
*ibuf
, ACPI_BUFFER
*obuf
)
716 struct acpi_wmi_softc
*sc
= device_private(self
);
718 ACPI_OBJECT_LIST arg
;
723 if (ibuf
== NULL
|| obuf
== NULL
)
724 return AE_BAD_PARAMETER
;
726 rv
= acpi_wmi_guid_get(sc
, guid
, &wmi
);
728 if (ACPI_FAILURE(rv
))
731 if (acpi_wmi_input(wmi
, ACPI_WMI_FLAG_METHOD
, idx
) != true)
734 (void)strlcat(path
, wmi
->guid
.oid
, sizeof(path
));
736 obj
[0].Integer
.Value
= idx
;
737 obj
[1].Integer
.Value
= mid
;
738 obj
[0].Type
= obj
[1].Type
= ACPI_TYPE_INTEGER
;
740 obj
[2].Buffer
.Length
= ibuf
->Length
;
741 obj
[2].Buffer
.Pointer
= ibuf
->Pointer
;
743 obj
[2].Type
= (wmi
->guid
.flags
& ACPI_WMI_FLAG_STRING
) ?
744 ACPI_TYPE_STRING
: ACPI_TYPE_INTEGER
;
749 obuf
->Pointer
= NULL
;
750 obuf
->Length
= ACPI_ALLOCATE_LOCAL_BUFFER
;
752 return AcpiEvaluateObject(sc
->sc_node
->ad_handle
, path
, &arg
, obuf
);