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