wmi_acpi.c revision 1.10
1/* $NetBSD: wmi_acpi.c,v 1.10 2010/10/25 15:38:05 jruoho Exp $ */ 2 3/*- 4 * Copyright (c) 2009, 2010 Jukka Ruohonen <jruohonen@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.10 2010/10/25 15:38:05 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#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 44ACPI_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 55static int acpi_wmi_match(device_t, cfdata_t, void *); 56static void acpi_wmi_attach(device_t, device_t, void *); 57static int acpi_wmi_detach(device_t, int); 58static int acpi_wmi_rescan(device_t, const char *, const int *); 59static void acpi_wmi_childdet(device_t, device_t); 60static int acpi_wmi_print(void *, const char *); 61static bool acpi_wmi_init(struct acpi_wmi_softc *); 62static bool acpi_wmi_add(struct acpi_wmi_softc *, ACPI_OBJECT *); 63static void acpi_wmi_del(struct acpi_wmi_softc *); 64static void acpi_wmi_dump(struct acpi_wmi_softc *); 65 66static ACPI_STATUS acpi_wmi_guid_get(struct acpi_wmi_softc *, 67 const char *, struct wmi_t **); 68static void acpi_wmi_event_add(struct acpi_wmi_softc *); 69static void acpi_wmi_event_del(struct acpi_wmi_softc *); 70static void acpi_wmi_event_handler(ACPI_HANDLE, uint32_t, void *); 71static bool acpi_wmi_suspend(device_t, const pmf_qual_t *); 72static bool acpi_wmi_resume(device_t, const pmf_qual_t *); 73static ACPI_STATUS acpi_wmi_enable(ACPI_HANDLE, const char *, bool, bool); 74static bool acpi_wmi_input(struct wmi_t *, uint8_t, uint8_t); 75 76const char * const acpi_wmi_ids[] = { 77 "PNP0C14", 78 "pnp0c14", 79 NULL 80}; 81 82CFATTACH_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 86static int 87acpi_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 97static void 98acpi_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 123static int 124acpi_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 139static int 140acpi_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 151static void 152acpi_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 160static int 161acpi_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 170static bool 171acpi_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 207fail: 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 217static bool 218acpi_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 245fail: 246 ACPI_FREE(obj); 247 acpi_wmi_del(sc); 248 249 return false; 250} 251 252static void 253acpi_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 271static void 272acpi_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 294static ACPI_STATUS 295acpi_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 */ 347int 348acpi_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 */ 364static void 365acpi_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 */ 398static void 399acpi_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 */ 430ACPI_STATUS 431acpi_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 */ 473static void 474acpi_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 490ACPI_STATUS 491acpi_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 506ACPI_STATUS 507acpi_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 */ 517static bool 518acpi_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 527static bool 528acpi_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 */ 540static ACPI_STATUS 541acpi_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 554static bool 555acpi_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 */ 574ACPI_STATUS 575acpi_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@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 */ 646ACPI_STATUS 647acpi_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 */ 688ACPI_STATUS 689acpi_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 733MODULE(MODULE_CLASS_DRIVER, acpiwmi, NULL); 734CFDRIVER_DECL(acpiwmi, DV_DULL, NULL); 735 736static int acpiwmiloc[] = { -1 }; 737extern struct cfattach acpiwmi_ca; 738 739static struct cfparent acpiparent = { 740 "acpinodebus", NULL, DVUNIT_ANY 741}; 742 743static 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, NULL, 0, 0, NULL, 0, NULL } 755}; 756 757static int 758acpiwmi_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