wmi_acpi.c revision 1.3 1 /* $NetBSD: wmi_acpi.c,v 1.3 2010/04/09 04:48:23 jruoho Exp $ */
2
3 /*-
4 * Copyright (c) 2009, 2010 Jukka Ruohonen <jruohonen (at) iki.fi>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
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.
16 *
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
27 * SUCH DAMAGE.
28 */
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: wmi_acpi.c,v 1.3 2010/04/09 04:48:23 jruoho Exp $");
31
32 #include <sys/param.h>
33 #include <sys/device.h>
34 #include <sys/endian.h>
35 #include <sys/kmem.h>
36 #include <sys/systm.h>
37
38 #include <dev/acpi/acpireg.h>
39 #include <dev/acpi/acpivar.h>
40 #include <dev/acpi/wmi/wmi_acpivar.h>
41
42 #define _COMPONENT ACPI_RESOURCE_COMPONENT
43 ACPI_MODULE_NAME ("wmi_acpi")
44
45 /*
46 * This implements something called "Microsoft Windows Management
47 * Instrumentation" (WMI). This subset of ACPI is desribed in:
48 *
49 * http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx
50 *
51 * (Obtained on Thu Feb 12 18:21:44 EET 2009.)
52 */
53 struct guid_t {
54
55 /*
56 * The GUID itself. The used format is the usual 32-16-16-64-bit
57 * representation. All except the fourth field are in native byte
58 * order. A 32-16-16-16-48-bit hexadecimal notation with hyphens
59 * is used for human-readable GUIDs.
60 */
61 struct {
62 uint32_t data1;
63 uint16_t data2;
64 uint16_t data3;
65 uint8_t data4[8];
66 } __packed;
67
68 union {
69 char oid[2]; /* ACPI object ID. */
70
71 struct {
72 uint8_t nid; /* Notification value. */
73 uint8_t res; /* Reserved. */
74 } __packed;
75 } __packed;
76
77 uint8_t count; /* Number of instances. */
78 uint8_t flags; /* Additional flags. */
79
80 } __packed;
81
82 struct wmi_t {
83 struct guid_t guid;
84 bool eevent;
85
86 SIMPLEQ_ENTRY(wmi_t) wmi_link;
87 };
88
89 struct acpi_wmi_softc {
90 device_t sc_dev;
91 device_t sc_child;
92 ACPI_NOTIFY_HANDLER sc_handler;
93 struct acpi_devnode *sc_node;
94
95 SIMPLEQ_HEAD(, wmi_t) wmi_head;
96 };
97
98 #define ACPI_WMI_FLAG_EXPENSIVE 0x01
99 #define ACPI_WMI_FLAG_METHOD 0x02
100 #define ACPI_WMI_FLAG_STRING 0x04
101 #define ACPI_WMI_FLAG_EVENT 0x08
102 #define ACPI_WMI_FLAG_DATA (ACPI_WMI_FLAG_EXPENSIVE | \
103 ACPI_WMI_FLAG_STRING)
104
105 #define UGET16(x) (*(uint16_t *)(x))
106 #define UGET64(x) (*(uint64_t *)(x))
107
108 #define HEXCHAR(x) (((x) >= '0' && (x) <= '9') || \
109 ((x) >= 'a' && (x) <= 'f') || \
110 ((x) >= 'A' && (x) <= 'F'))
111
112 #define GUIDCMP(a, b) \
113 ((a)->data1 == (b)->data1 && \
114 (a)->data2 == (b)->data2 && \
115 (a)->data3 == (b)->data3 && \
116 UGET64((a)->data4) == UGET64((b)->data4))
117
118 static int acpi_wmi_match(device_t, cfdata_t, void *);
119 static void acpi_wmi_attach(device_t, device_t, void *);
120 static int acpi_wmi_detach(device_t, int);
121 static int acpi_wmi_print(void *, const char *);
122 static bool acpi_wmi_init(struct acpi_wmi_softc *);
123 static bool acpi_wmi_add(struct acpi_wmi_softc *, ACPI_OBJECT *);
124 static void acpi_wmi_del(struct acpi_wmi_softc *);
125
126 #ifdef ACPIVERBOSE
127 static void acpi_wmi_dump(struct acpi_wmi_softc *);
128 #endif
129
130 static ACPI_STATUS acpi_wmi_guid_get(struct acpi_wmi_softc *,
131 const char *, struct wmi_t **);
132 static ACPI_STATUS acpi_wmi_event_add(struct acpi_wmi_softc *);
133 static ACPI_STATUS acpi_wmi_event_del(struct acpi_wmi_softc *);
134 static void acpi_wmi_event_handler(ACPI_HANDLE, uint32_t, void *);
135 static bool acpi_wmi_suspend(device_t, const pmf_qual_t *);
136 static bool acpi_wmi_resume(device_t, const pmf_qual_t *);
137 static ACPI_STATUS acpi_wmi_enable(ACPI_HANDLE, const char *, bool, bool);
138 static bool acpi_wmi_input(struct wmi_t *, uint8_t, uint8_t);
139
140 const char * const acpi_wmi_ids[] = {
141 "PNP0C14",
142 NULL
143 };
144
145 CFATTACH_DECL_NEW(acpiwmi, sizeof(struct acpi_wmi_softc),
146 acpi_wmi_match, acpi_wmi_attach, acpi_wmi_detach, NULL);
147
148 static int
149 acpi_wmi_match(device_t parent, cfdata_t match, void *aux)
150 {
151 struct acpi_attach_args *aa = aux;
152
153 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
154 return 0;
155
156 return acpi_match_hid(aa->aa_node->ad_devinfo, acpi_wmi_ids);
157 }
158
159 static void
160 acpi_wmi_attach(device_t parent, device_t self, void *aux)
161 {
162 struct acpi_wmi_softc *sc = device_private(self);
163 struct acpi_attach_args *aa = aux;
164
165 sc->sc_dev = self;
166 sc->sc_node = aa->aa_node;
167
168 sc->sc_child = NULL;
169 sc->sc_handler = NULL;
170
171 aprint_naive("\n");
172 aprint_normal(": ACPI WMI Interface\n");
173
174 if (acpi_wmi_init(sc) != true)
175 return;
176
177 #ifdef ACPIVERBOSE
178 acpi_wmi_dump(sc);
179 #endif
180
181 (void)acpi_wmi_event_add(sc);
182 (void)pmf_device_register(self, acpi_wmi_suspend, acpi_wmi_resume);
183
184 sc->sc_child = config_found_ia(self, "acpiwmibus",
185 NULL, acpi_wmi_print);
186 }
187
188 static int
189 acpi_wmi_detach(device_t self, int flags)
190 {
191 struct acpi_wmi_softc *sc = device_private(self);
192 ACPI_STATUS rv;
193
194 rv = acpi_wmi_event_del(sc);
195
196 if (ACPI_FAILURE(rv))
197 return EBUSY;
198
199 if (sc->sc_child != NULL)
200 (void)config_detach(sc->sc_child, flags);
201
202 acpi_wmi_del(sc);
203 pmf_device_deregister(self);
204
205 return 0;
206 }
207
208 static int
209 acpi_wmi_print(void *aux, const char *pnp)
210 {
211
212 if (pnp != NULL)
213 aprint_normal("acpiwmibus at %s", pnp);
214
215 return UNCONF;
216 }
217
218 static bool
219 acpi_wmi_init(struct acpi_wmi_softc *sc)
220 {
221 ACPI_OBJECT *obj;
222 ACPI_BUFFER buf;
223 ACPI_STATUS rv;
224 uint32_t len;
225
226 rv = acpi_eval_struct(sc->sc_node->ad_handle, "_WDG", &buf);
227
228 if (ACPI_FAILURE(rv))
229 goto fail;
230
231 obj = buf.Pointer;
232
233 if (obj->Type != ACPI_TYPE_BUFFER) {
234 rv = AE_TYPE;
235 goto fail;
236 }
237
238 len = obj->Buffer.Length;
239
240 if (len != obj->Package.Count) {
241 rv = AE_BAD_VALUE;
242 goto fail;
243 }
244
245 CTASSERT(sizeof(struct guid_t) == 20);
246
247 if (len < sizeof(struct guid_t) ||
248 len % sizeof(struct guid_t) != 0) {
249 rv = AE_BAD_DATA;
250 goto fail;
251 }
252
253 return acpi_wmi_add(sc, obj);
254
255 fail:
256 aprint_error_dev(sc->sc_dev, "failed to evaluate _WDG: %s\n",
257 AcpiFormatException(rv));
258
259 if (buf.Pointer != NULL)
260 ACPI_FREE(buf.Pointer);
261
262 return false;
263 }
264
265 static bool
266 acpi_wmi_add(struct acpi_wmi_softc *sc, ACPI_OBJECT *obj)
267 {
268 struct wmi_t *wmi;
269 size_t i, n, offset, siz;
270
271 siz = sizeof(struct guid_t);
272 n = obj->Buffer.Length / siz;
273
274 SIMPLEQ_INIT(&sc->wmi_head);
275
276 for (i = offset = 0; i < n; ++i) {
277
278 if ((wmi = kmem_zalloc(sizeof(*wmi), KM_NOSLEEP)) == NULL)
279 goto fail;
280
281 (void)memcpy(&wmi->guid, obj->Buffer.Pointer + offset, siz);
282
283 wmi->eevent = false;
284 offset = offset + siz;
285
286 SIMPLEQ_INSERT_TAIL(&sc->wmi_head, wmi, wmi_link);
287 }
288
289 ACPI_FREE(obj);
290
291 return true;
292
293 fail:
294 ACPI_FREE(obj);
295 acpi_wmi_del(sc);
296
297 return false;
298 }
299
300 static void
301 acpi_wmi_del(struct acpi_wmi_softc *sc)
302 {
303 struct wmi_t *wmi;
304
305 if (SIMPLEQ_EMPTY(&sc->wmi_head) != 0)
306 return;
307
308 while (SIMPLEQ_FIRST(&sc->wmi_head) != NULL) {
309
310 wmi = SIMPLEQ_FIRST(&sc->wmi_head);
311 SIMPLEQ_REMOVE_HEAD(&sc->wmi_head, wmi_link);
312
313 KASSERT(wmi != NULL);
314
315 kmem_free(wmi, sizeof(*wmi));
316 }
317 }
318
319 #ifdef ACPIVERBOSE
320 static void
321 acpi_wmi_dump(struct acpi_wmi_softc *sc)
322 {
323 struct wmi_t *wmi;
324
325 KASSERT(SIMPLEQ_EMPTY(&sc->wmi_head) == 0);
326
327 SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) {
328
329 aprint_normal_dev(sc->sc_dev, "{%08X-%04X-%04X-",
330 wmi->guid.data1, wmi->guid.data2, wmi->guid.data3);
331
332 aprint_normal("%02X%02X-%02X%02X%02X%02X%02X%02X} ",
333 wmi->guid.data4[0], wmi->guid.data4[1],
334 wmi->guid.data4[2], wmi->guid.data4[3],
335 wmi->guid.data4[4], wmi->guid.data4[5],
336 wmi->guid.data4[6], wmi->guid.data4[7]);
337
338 aprint_normal("oid %04X count %02X flags %02X\n",
339 UGET16(wmi->guid.oid), wmi->guid.count, wmi->guid.flags);
340 }
341 }
342 #endif
343
344 static ACPI_STATUS
345 acpi_wmi_guid_get(struct acpi_wmi_softc *sc,
346 const char *src, struct wmi_t **out)
347 {
348 struct wmi_t *wmi;
349 struct guid_t *guid;
350 char bin[16];
351 char hex[2];
352 const char *ptr;
353 uint8_t i;
354
355 if (sc == NULL || src == NULL || strlen(src) != 36)
356 return AE_BAD_PARAMETER;
357
358 for (ptr = src, i = 0; i < 16; i++) {
359
360 if (*ptr == '-')
361 ptr++;
362
363 (void)memcpy(hex, ptr, 2);
364
365 if (HEXCHAR(hex[0]) == 0 || HEXCHAR(hex[1]) == 0)
366 return AE_BAD_HEX_CONSTANT;
367
368 bin[i] = strtoul(hex, NULL, 16) & 0xFF;
369
370 ptr++;
371 ptr++;
372 }
373
374 guid = (struct guid_t *)bin;
375 guid->data1 = be32toh(guid->data1);
376 guid->data2 = be16toh(guid->data2);
377 guid->data3 = be16toh(guid->data3);
378
379 SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) {
380
381 if (GUIDCMP(guid, &wmi->guid) != 0) {
382
383 if (out != NULL)
384 *out = wmi;
385
386 return AE_OK;
387 }
388 }
389
390 return AE_NOT_FOUND;
391 }
392
393 /*
394 * Checks if a GUID is present. Child devices
395 * can use this in their autoconf(9) routines.
396 */
397 int
398 acpi_wmi_guid_match(device_t self, const char *guid)
399 {
400 struct acpi_wmi_softc *sc = device_private(self);
401 ACPI_STATUS rv;
402
403 rv = acpi_wmi_guid_get(sc, guid, NULL);
404
405 if (ACPI_SUCCESS(rv))
406 return 1;
407
408 return 0;
409 }
410
411 /*
412 * Adds internal event handler.
413 */
414 static ACPI_STATUS
415 acpi_wmi_event_add(struct acpi_wmi_softc *sc)
416 {
417 struct wmi_t *wmi;
418 ACPI_STATUS rv;
419
420 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle,
421 ACPI_ALL_NOTIFY, acpi_wmi_event_handler, sc);
422
423 if (ACPI_FAILURE(rv)) {
424 aprint_error_dev(sc->sc_dev, "failed to install notify "
425 "handler: %s\n", AcpiFormatException(rv));
426 return rv;
427 }
428
429 /* Enable possible expensive events. */
430 SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) {
431
432 if ((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) != 0 &&
433 (wmi->guid.flags & ACPI_WMI_FLAG_EXPENSIVE) != 0) {
434
435 rv = acpi_wmi_enable(sc->sc_node->ad_handle,
436 wmi->guid.oid, false, true);
437
438 if (ACPI_SUCCESS(rv)) {
439 wmi->eevent = true;
440 continue;
441 }
442
443 aprint_error_dev(sc->sc_dev, "failed to enable "
444 "expensive WExx: %s\n", AcpiFormatException(rv));
445 }
446 }
447
448 return AE_OK;
449 }
450
451 /*
452 * Removes the internal event handler.
453 */
454 static ACPI_STATUS
455 acpi_wmi_event_del(struct acpi_wmi_softc *sc)
456 {
457 struct wmi_t *wmi;
458 ACPI_STATUS rv;
459
460 rv = AcpiRemoveNotifyHandler(sc->sc_node->ad_handle,
461 ACPI_ALL_NOTIFY, acpi_wmi_event_handler);
462
463 if (ACPI_FAILURE(rv)) {
464 aprint_debug_dev(sc->sc_dev, "failed to remove notify "
465 "handler: %s\n", AcpiFormatException(rv));
466 return rv;
467 }
468
469 SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) {
470
471 if (wmi->eevent != true)
472 continue;
473
474 KASSERT((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) != 0);
475 KASSERT((wmi->guid.flags & ACPI_WMI_FLAG_EXPENSIVE) != 0);
476
477 rv = acpi_wmi_enable(sc->sc_node->ad_handle,
478 wmi->guid.oid, false, false);
479
480 if (ACPI_SUCCESS(rv)) {
481 wmi->eevent = false;
482 continue;
483 }
484
485 aprint_error_dev(sc->sc_dev, "failed to disable "
486 "expensive WExx: %s\n", AcpiFormatException(rv));
487 }
488
489 return AE_OK;
490 }
491
492 /*
493 * Returns extra information possibly associated with an event.
494 */
495 ACPI_STATUS
496 acpi_wmi_event_get(device_t self, uint32_t event, ACPI_BUFFER *obuf)
497 {
498 struct acpi_wmi_softc *sc = device_private(self);
499 struct wmi_t *wmi;
500 ACPI_OBJECT_LIST arg;
501 ACPI_OBJECT obj;
502 ACPI_HANDLE hdl;
503
504 if (sc == NULL || obuf == NULL)
505 return AE_BAD_PARAMETER;
506
507 if (sc->sc_handler == NULL)
508 return AE_ABORT_METHOD;
509
510 hdl = sc->sc_node->ad_handle;
511
512 obj.Type = ACPI_TYPE_INTEGER;
513 obj.Integer.Value = event;
514
515 arg.Count = 0x01;
516 arg.Pointer = &obj;
517
518 obuf->Pointer = NULL;
519 obuf->Length = ACPI_ALLOCATE_LOCAL_BUFFER;
520
521 SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) {
522
523 if ((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) == 0)
524 continue;
525
526 if (wmi->guid.nid != event)
527 continue;
528
529 return AcpiEvaluateObject(hdl, "_WED", &arg, obuf);
530 }
531
532 return AE_NOT_FOUND;
533 }
534
535 /*
536 * Forwards events to the external handler through the internal one.
537 */
538 static void
539 acpi_wmi_event_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux)
540 {
541 struct acpi_wmi_softc *sc = aux;
542
543 if (sc->sc_child == NULL)
544 return;
545
546 if (sc->sc_handler == NULL)
547 return;
548
549 (*sc->sc_handler)(NULL, evt, sc->sc_child);
550 }
551
552 ACPI_STATUS
553 acpi_wmi_event_register(device_t self, ACPI_NOTIFY_HANDLER handler)
554 {
555 struct acpi_wmi_softc *sc = device_private(self);
556
557 if (sc == NULL)
558 return AE_BAD_PARAMETER;
559
560 if (handler != NULL && sc->sc_handler != NULL)
561 return AE_ALREADY_EXISTS;
562
563 sc->sc_handler = handler;
564
565 return AE_OK;
566 }
567
568 ACPI_STATUS
569 acpi_wmi_event_deregister(device_t self)
570 {
571 return acpi_wmi_event_register(self, NULL);
572 }
573
574 /*
575 * As there is no prior knowledge about the expensive
576 * events that cause "significant overhead", try to
577 * disable (enable) these before suspending (resuming).
578 */
579 static bool
580 acpi_wmi_suspend(device_t self, const pmf_qual_t *qual)
581 {
582 struct acpi_wmi_softc *sc = device_private(self);
583
584 acpi_wmi_event_del(sc);
585
586 return true;
587 }
588
589 static bool
590 acpi_wmi_resume(device_t self, const pmf_qual_t *qual)
591 {
592 struct acpi_wmi_softc *sc = device_private(self);
593
594 acpi_wmi_event_add(sc);
595
596 return true;
597 }
598
599 /*
600 * Enables or disables data collection (WCxx) or an event (WExx).
601 */
602 static ACPI_STATUS
603 acpi_wmi_enable(ACPI_HANDLE hdl, const char *oid, bool data, bool flag)
604 {
605 char path[5];
606 const char *str;
607
608 str = (data != false) ? "WC" : "WE";
609
610 (void)strlcpy(path, str, sizeof(path));
611 (void)strlcat(path, oid, sizeof(path));
612
613 return acpi_eval_set_integer(hdl, path, (flag != false) ? 0x01 : 0x00);
614 }
615
616 static bool
617 acpi_wmi_input(struct wmi_t *wmi, uint8_t flag, uint8_t idx)
618 {
619
620 if ((wmi->guid.flags & flag) == 0)
621 return false;
622
623 if (wmi->guid.count == 0x00)
624 return false;
625
626 if (wmi->guid.count < idx)
627 return false;
628
629 return true;
630 }
631
632 /*
633 * Makes a WMI data block query (WQxx). The corresponding control
634 * method for data collection will be invoked if it is available.
635 */
636 ACPI_STATUS
637 acpi_wmi_data_query(device_t self, const char *guid,
638 uint8_t idx, ACPI_BUFFER *obuf)
639 {
640 struct acpi_wmi_softc *sc = device_private(self);
641 struct wmi_t *wmi;
642 char path[5] = "WQ";
643 ACPI_OBJECT_LIST arg;
644 ACPI_STATUS rv, rvxx;
645 ACPI_OBJECT obj;
646
647 rvxx = AE_SUPPORT;
648
649 if (obuf == NULL)
650 return AE_BAD_PARAMETER;
651
652 rv = acpi_wmi_guid_get(sc, guid, &wmi);
653
654 if (ACPI_FAILURE(rv))
655 return rv;
656
657 if (acpi_wmi_input(wmi, ACPI_WMI_FLAG_DATA, idx) != true)
658 return AE_BAD_DATA;
659
660 (void)strlcat(path, wmi->guid.oid, sizeof(path));
661
662 obj.Type = ACPI_TYPE_INTEGER;
663 obj.Integer.Value = idx;
664
665 arg.Count = 0x01;
666 arg.Pointer = &obj;
667
668 obuf->Pointer = NULL;
669 obuf->Length = ACPI_ALLOCATE_LOCAL_BUFFER;
670
671 /*
672 * If the expensive flag is set, we should enable
673 * data collection before evaluating the WQxx buffer.
674 */
675 if ((wmi->guid.flags & ACPI_WMI_FLAG_EXPENSIVE) != 0) {
676
677 rvxx = acpi_wmi_enable(sc->sc_node->ad_handle,
678 wmi->guid.oid, true, true);
679 }
680
681 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, obuf);
682
683 /* No longer needed. */
684 if (ACPI_SUCCESS(rvxx)) {
685
686 (void)acpi_wmi_enable(sc->sc_node->ad_handle,
687 wmi->guid.oid, true, false);
688 }
689
690 #ifdef DIAGNOSTIC
691 /*
692 * XXX: It appears that quite a few laptops have WQxx
693 * methods that are declared as expensive, but lack the
694 * corresponding WCxx control method.
695 *
696 * -- Acer Aspire One is one example <jruohonen (at) iki.fi>.
697 */
698 if (ACPI_FAILURE(rvxx) && rvxx != AE_SUPPORT)
699 aprint_error_dev(sc->sc_dev, "failed to evaluate WCxx "
700 "for %s: %s\n", path, AcpiFormatException(rvxx));
701 #endif
702 return rv;
703 }
704
705 /*
706 * Writes to a data block (WSxx).
707 */
708 ACPI_STATUS
709 acpi_wmi_data_write(device_t self, const char *guid,
710 uint8_t idx, ACPI_BUFFER *ibuf)
711 {
712 struct acpi_wmi_softc *sc = device_private(self);
713 struct wmi_t *wmi;
714 ACPI_OBJECT_LIST arg;
715 ACPI_OBJECT obj[2];
716 char path[5] = "WS";
717 ACPI_STATUS rv;
718
719 if (ibuf == NULL)
720 return AE_BAD_PARAMETER;
721
722 rv = acpi_wmi_guid_get(sc, guid, &wmi);
723
724 if (ACPI_FAILURE(rv))
725 return rv;
726
727 if (acpi_wmi_input(wmi, ACPI_WMI_FLAG_DATA, idx) != true)
728 return AE_BAD_DATA;
729
730 (void)strlcat(path, wmi->guid.oid, sizeof(path));
731
732 obj[0].Integer.Value = idx;
733 obj[0].Type = ACPI_TYPE_INTEGER;
734
735 obj[1].Buffer.Length = ibuf->Length;
736 obj[1].Buffer.Pointer = ibuf->Pointer;
737
738 obj[1].Type = ((wmi->guid.flags & ACPI_WMI_FLAG_STRING) != 0) ?
739 ACPI_TYPE_STRING : ACPI_TYPE_BUFFER;
740
741 arg.Count = 0x02;
742 arg.Pointer = obj;
743
744 return AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, NULL);
745 }
746
747 /*
748 * Executes a method (WMxx).
749 */
750 ACPI_STATUS
751 acpi_wmi_method(device_t self, const char *guid, uint8_t idx,
752 uint32_t mid, ACPI_BUFFER *ibuf, ACPI_BUFFER *obuf)
753 {
754 struct acpi_wmi_softc *sc = device_private(self);
755 struct wmi_t *wmi;
756 ACPI_OBJECT_LIST arg;
757 ACPI_OBJECT obj[3];
758 char path[5] = "WM";
759 ACPI_STATUS rv;
760
761 if (ibuf == NULL || obuf == NULL)
762 return AE_BAD_PARAMETER;
763
764 rv = acpi_wmi_guid_get(sc, guid, &wmi);
765
766 if (ACPI_FAILURE(rv))
767 return rv;
768
769 if (acpi_wmi_input(wmi, ACPI_WMI_FLAG_METHOD, idx) != true)
770 return AE_BAD_DATA;
771
772 (void)strlcat(path, wmi->guid.oid, sizeof(path));
773
774 obj[0].Integer.Value = idx;
775 obj[1].Integer.Value = mid;
776 obj[0].Type = obj[1].Type = ACPI_TYPE_INTEGER;
777
778 obj[2].Buffer.Length = ibuf->Length;
779 obj[2].Buffer.Pointer = ibuf->Pointer;
780
781 obj[2].Type = ((wmi->guid.flags & ACPI_WMI_FLAG_STRING) != 0) ?
782 ACPI_TYPE_STRING : ACPI_TYPE_BUFFER;
783
784 arg.Count = 0x03;
785 arg.Pointer = obj;
786
787 obuf->Pointer = NULL;
788 obuf->Length = ACPI_ALLOCATE_LOCAL_BUFFER;
789
790 return AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, obuf);
791 }
792