1 /* $NetBSD: acpi_power.c,v 1.22 2010/08/06 18:10:40 jruoho Exp $ */ 2 3 /*- 4 * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jukka Ruohonen. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /*- 33 * Copyright (c) 2001 Michael Smith 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55 * SUCH DAMAGE. 56 */ 57 58 #include <sys/cdefs.h> 59 __KERNEL_RCSID(0, "$NetBSD: acpi_power.c,v 1.22 2010/08/06 18:10:40 jruoho Exp $"); 60 61 #include <sys/param.h> 62 #include <sys/kmem.h> 63 #include <sys/mutex.h> 64 #include <sys/sysctl.h> 65 66 #include <dev/acpi/acpireg.h> 67 #include <dev/acpi/acpivar.h> 68 #include <dev/acpi/acpi_power.h> 69 70 #define _COMPONENT ACPI_BUS_COMPONENT 71 ACPI_MODULE_NAME ("acpi_power") 72 73 #define ACPI_STA_POW_OFF 0x00 74 #define ACPI_STA_POW_ON 0x01 75 76 /* 77 * References. 78 */ 79 struct acpi_power_ref { 80 ACPI_HANDLE ref_handle; 81 82 SIMPLEQ_ENTRY(acpi_power_ref) ref_list; 83 }; 84 85 /* 86 * Resources. 87 */ 88 struct acpi_power_res { 89 ACPI_HANDLE res_handle; 90 ACPI_INTEGER res_level; 91 ACPI_INTEGER res_order; 92 char res_name[5]; 93 kmutex_t res_mutex; 94 95 TAILQ_ENTRY(acpi_power_res) res_list; 96 SIMPLEQ_HEAD(, acpi_power_ref) ref_head; 97 }; 98 99 static TAILQ_HEAD(, acpi_power_res) res_head = 100 TAILQ_HEAD_INITIALIZER(res_head); 101 102 static const struct sysctlnode *anode = NULL; 103 104 static struct acpi_power_res *acpi_power_res_init(ACPI_HANDLE); 105 static struct acpi_power_res *acpi_power_res_get(ACPI_HANDLE); 106 107 static ACPI_STATUS acpi_power_get_direct(struct acpi_devnode *); 108 static ACPI_STATUS acpi_power_get_indirect(struct acpi_devnode *); 109 static ACPI_STATUS acpi_power_switch(struct acpi_devnode *, 110 int, bool); 111 static ACPI_STATUS acpi_power_res_ref(struct acpi_power_res *, 112 ACPI_HANDLE); 113 static ACPI_STATUS acpi_power_res_deref(struct acpi_power_res *, 114 ACPI_HANDLE); 115 static ACPI_STATUS acpi_power_res_sta(ACPI_OBJECT *, void *); 116 117 static ACPI_OBJECT *acpi_power_pkg_get(ACPI_HANDLE, int); 118 static int acpi_power_sysctl(SYSCTLFN_PROTO); 119 static const char *acpi_xname(ACPI_HANDLE); 120 121 static struct acpi_power_res * 122 acpi_power_res_init(ACPI_HANDLE hdl) 123 { 124 struct acpi_power_res *tmp = NULL; 125 struct acpi_power_res *res = NULL; 126 ACPI_OBJECT *obj; 127 ACPI_BUFFER buf; 128 ACPI_STATUS rv; 129 130 rv = acpi_eval_struct(hdl, NULL, &buf); 131 132 if (ACPI_FAILURE(rv)) 133 goto out; 134 135 obj = buf.Pointer; 136 137 if (obj->Type != ACPI_TYPE_POWER) { 138 rv = AE_TYPE; 139 goto out; 140 } 141 142 res = kmem_zalloc(sizeof(*res), KM_SLEEP); 143 144 if (res == NULL) { 145 rv = AE_NO_MEMORY; 146 goto out; 147 } 148 149 res->res_handle = hdl; 150 res->res_level = obj->PowerResource.SystemLevel; 151 res->res_order = obj->PowerResource.ResourceOrder; 152 153 (void)strlcpy(res->res_name, 154 acpi_xname(hdl), sizeof(res->res_name)); 155 156 SIMPLEQ_INIT(&res->ref_head); 157 mutex_init(&res->res_mutex, MUTEX_DEFAULT, IPL_NONE); 158 159 /* 160 * Power resources should be ordered. 161 * 162 * These *should* be enabled from low values to high 163 * values and disabled from high values to low values. 164 */ 165 TAILQ_FOREACH(tmp, &res_head, res_list) { 166 167 if (res->res_order < tmp->res_order) { 168 TAILQ_INSERT_BEFORE(tmp, res, res_list); 169 break; 170 } 171 } 172 173 if (tmp == NULL) 174 TAILQ_INSERT_TAIL(&res_head, res, res_list); 175 176 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s added to the " 177 "power resource queue\n", res->res_name)); 178 179 out: 180 if (buf.Pointer != NULL) 181 ACPI_FREE(buf.Pointer); 182 183 return res; 184 } 185 186 static struct acpi_power_res * 187 acpi_power_res_get(ACPI_HANDLE hdl) 188 { 189 struct acpi_power_res *res; 190 191 TAILQ_FOREACH(res, &res_head, res_list) { 192 193 if (res->res_handle == hdl) 194 return res; 195 } 196 197 return acpi_power_res_init(hdl); 198 } 199 200 bool 201 acpi_power_register(ACPI_HANDLE hdl) 202 { 203 struct acpi_devnode *ad = acpi_get_node(hdl); 204 205 if (ad == NULL) 206 return false; 207 208 if ((ad->ad_flags & ACPI_DEVICE_POWER) == 0) 209 return false; 210 211 return true; 212 } 213 214 void 215 acpi_power_deregister(ACPI_HANDLE hdl) 216 { 217 struct acpi_devnode *ad = acpi_get_node(hdl); 218 struct acpi_power_res *res; 219 220 if (ad == NULL) 221 return; 222 223 if ((ad->ad_flags & ACPI_DEVICE_POWER) == 0) 224 return; 225 226 /* 227 * Remove all references in each resource. 228 */ 229 TAILQ_FOREACH(res, &res_head, res_list) 230 (void)acpi_power_res_deref(res, ad->ad_handle); 231 } 232 233 /* 234 * Get the D-state of an ACPI device node. 235 */ 236 bool 237 acpi_power_get(ACPI_HANDLE hdl, int *state) 238 { 239 struct acpi_devnode *ad = acpi_get_node(hdl); 240 ACPI_STATUS rv; 241 242 if (ad == NULL) 243 return false; 244 245 if ((ad->ad_flags & ACPI_DEVICE_POWER) == 0) { 246 rv = AE_SUPPORT; 247 goto fail; 248 } 249 250 /* 251 * Because the _PSC control method, like _STA, 252 * is known to be implemented incorrectly in 253 * some systems, we first try to retrieve the 254 * power state indirectly via power resources. 255 */ 256 rv = acpi_power_get_indirect(ad); 257 258 if (ACPI_FAILURE(rv)) 259 rv = acpi_power_get_direct(ad); 260 261 if (ACPI_FAILURE(rv)) 262 goto fail; 263 264 KASSERT(ad->ad_state != ACPI_STATE_ERROR); 265 266 if (ad->ad_state < ACPI_STATE_D0 || ad->ad_state > ACPI_STATE_D3) { 267 rv = AE_BAD_VALUE; 268 goto fail; 269 } 270 271 if (state != NULL) 272 *state = ad->ad_state; 273 274 return true; 275 276 fail: 277 ad->ad_state = ACPI_STATE_ERROR; 278 279 if (state != NULL) 280 *state = ad->ad_state; 281 282 aprint_error_dev(ad->ad_root, "failed to get power state " 283 "for %s: %s\n", ad->ad_name, AcpiFormatException(rv)); 284 285 return false; 286 } 287 288 static ACPI_STATUS 289 acpi_power_get_direct(struct acpi_devnode *ad) 290 { 291 ACPI_INTEGER val = 0; 292 ACPI_STATUS rv; 293 294 rv = acpi_eval_integer(ad->ad_handle, "_PSC", &val); 295 296 KDASSERT((uint64_t)val < INT_MAX); 297 298 ad->ad_state = (int)val; 299 300 return rv; 301 } 302 303 static ACPI_STATUS 304 acpi_power_get_indirect(struct acpi_devnode *ad) 305 { 306 ACPI_OBJECT *pkg; 307 ACPI_STATUS rv; 308 int i; 309 310 CTASSERT(ACPI_STATE_D0 == 0 && ACPI_STATE_D1 == 1); 311 CTASSERT(ACPI_STATE_D2 == 2 && ACPI_STATE_D3 == 3); 312 313 /* 314 * The device is in a given D-state if all resources are on. 315 * To derive this, evaluate all elements in each _PRx package 316 * (x = 0 ... 3) and break if the noted condition becomes true. 317 */ 318 for (ad->ad_state = ACPI_STATE_D3, i = 0; i < ACPI_STATE_D3; i++) { 319 320 pkg = acpi_power_pkg_get(ad->ad_handle, i); 321 322 if (pkg == NULL) 323 continue; 324 325 /* 326 * For each element in the _PRx package, evaluate _STA 327 * and return AE_OK only if all power resources are on. 328 */ 329 rv = acpi_foreach_package_object(pkg, acpi_power_res_sta, ad); 330 331 if (ACPI_FAILURE(rv) && rv != AE_CTRL_FALSE) 332 goto out; 333 334 if (ACPI_SUCCESS(rv)) { 335 ad->ad_state = i; 336 goto out; 337 } 338 339 ACPI_FREE(pkg); pkg = NULL; 340 } 341 342 KASSERT(ad->ad_state == ACPI_STATE_D3); 343 344 return AE_OK; 345 346 out: 347 ACPI_FREE(pkg); 348 349 return rv; 350 } 351 352 /* 353 * Set the D-state of an ACPI device node. 354 */ 355 bool 356 acpi_power_set(ACPI_HANDLE hdl, int state) 357 { 358 struct acpi_devnode *ad = acpi_get_node(hdl); 359 ACPI_STATUS rv; 360 char path[5]; 361 int old; 362 363 if (ad == NULL) 364 return false; 365 366 if ((ad->ad_flags & ACPI_DEVICE_POWER) == 0) { 367 rv = AE_SUPPORT; 368 goto fail; 369 } 370 371 if (state < ACPI_STATE_D0 || state > ACPI_STATE_D3) { 372 rv = AE_BAD_PARAMETER; 373 goto fail; 374 } 375 376 if (acpi_power_get(ad->ad_handle, &old) != true) { 377 rv = AE_NOT_FOUND; 378 goto fail; 379 } 380 381 KASSERT(ad->ad_state == old); 382 KASSERT(ad->ad_state != ACPI_STATE_ERROR); 383 384 if (ad->ad_state == state) { 385 rv = AE_ALREADY_EXISTS; 386 goto fail; 387 } 388 389 /* 390 * It is only possible to go to D0 ("on") from D3 ("off"). 391 */ 392 if (ad->ad_state == ACPI_STATE_D3 && state != ACPI_STATE_D0) { 393 rv = AE_BAD_PARAMETER; 394 goto fail; 395 } 396 397 /* 398 * As noted in ACPI 4.0 (appendix A.2.1), the bus power state 399 * should never be lower than the highest state of one of its 400 * devices. Consequently, we cannot set the state to a lower 401 * (i.e. higher power) state than the parent device's state. 402 */ 403 if ((ad->ad_parent != NULL) && 404 (ad->ad_parent->ad_flags & ACPI_DEVICE_POWER) != 0) { 405 406 if (ad->ad_parent->ad_state > state) { 407 rv = AE_ABORT_METHOD; 408 goto fail; 409 } 410 } 411 412 /* 413 * We first sweep through the resources required for the target 414 * state, turning things on and building references. After this 415 * we dereference the resources required for the current state, 416 * turning the resources off as we go. 417 */ 418 rv = acpi_power_switch(ad, state, true); 419 420 if (ACPI_FAILURE(rv) && rv != AE_CTRL_CONTINUE) 421 goto fail; 422 423 rv = acpi_power_switch(ad, ad->ad_state, false); 424 425 if (ACPI_FAILURE(rv) && rv != AE_CTRL_CONTINUE) 426 goto fail; 427 428 /* 429 * Last but not least, invoke the power state switch method, 430 * if available. Because some systems use only _PSx for the 431 * power state transitions, we do this even if there is no _PRx. 432 */ 433 (void)snprintf(path, sizeof(path), "_PS%d", state); 434 (void)AcpiEvaluateObject(ad->ad_handle, path, NULL, NULL); 435 436 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s turned from " 437 "D%d to D%d\n", ad->ad_name, old, state)); 438 439 ad->ad_state = state; 440 441 return true; 442 443 fail: 444 ad->ad_state = ACPI_STATE_ERROR; 445 446 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "failed to set power state to D%d " 447 "for %s: %s\n", state, ad->ad_name, AcpiFormatException(rv))); 448 449 return false; 450 } 451 452 static ACPI_STATUS 453 acpi_power_switch(struct acpi_devnode *ad, int state, bool on) 454 { 455 ACPI_OBJECT *elm, *pkg; 456 ACPI_STATUS rv = AE_OK; 457 ACPI_HANDLE hdl; 458 uint32_t i, n; 459 460 /* 461 * For each element in the _PRx package, fetch 462 * the reference handle, search for this handle 463 * from the power resource queue, and turn the 464 * resource behind the handle on or off. 465 */ 466 pkg = acpi_power_pkg_get(ad->ad_handle, state); 467 468 if (pkg == NULL) 469 return AE_CTRL_CONTINUE; 470 471 n = pkg->Package.Count; 472 473 for (i = 0; i < n; i++) { 474 475 elm = &pkg->Package.Elements[i]; 476 rv = acpi_eval_reference_handle(elm, &hdl); 477 478 if (ACPI_FAILURE(rv)) 479 continue; 480 481 (void)acpi_power_res(hdl, ad->ad_handle, on); 482 } 483 484 ACPI_FREE(pkg); 485 486 return rv; 487 } 488 489 ACPI_STATUS 490 acpi_power_res(ACPI_HANDLE hdl, ACPI_HANDLE ref, bool on) 491 { 492 struct acpi_power_res *res; 493 const char *str; 494 ACPI_STATUS rv; 495 496 /* 497 * Search for the resource. 498 */ 499 res = acpi_power_res_get(hdl); 500 501 if (res == NULL) 502 return AE_NOT_FOUND; 503 504 /* 505 * (De)reference the resource. 506 */ 507 switch (on) { 508 509 case true: 510 rv = acpi_power_res_ref(res, ref); 511 str = "_ON"; 512 break; 513 514 case false: 515 rv = acpi_power_res_deref(res, ref); 516 str = "_OFF"; 517 break; 518 519 default: 520 return AE_BAD_PARAMETER; 521 } 522 523 if (ACPI_FAILURE(rv)) 524 return rv; 525 526 /* 527 * Turn the resource on or off. 528 */ 529 return AcpiEvaluateObject(res->res_handle, str, NULL, NULL); 530 } 531 532 static ACPI_STATUS 533 acpi_power_res_ref(struct acpi_power_res *res, ACPI_HANDLE hdl) 534 { 535 struct acpi_power_ref *ref, *tmp; 536 537 ref = kmem_zalloc(sizeof(*ref), KM_SLEEP); 538 539 if (ref == NULL) 540 return AE_NO_MEMORY; 541 542 mutex_enter(&res->res_mutex); 543 544 SIMPLEQ_FOREACH(tmp, &res->ref_head, ref_list) { 545 546 if (tmp->ref_handle == hdl) 547 goto out; 548 } 549 550 ref->ref_handle = hdl; 551 SIMPLEQ_INSERT_TAIL(&res->ref_head, ref, ref_list); 552 mutex_exit(&res->res_mutex); 553 554 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s referenced " 555 "by %s\n", res->res_name, acpi_xname(hdl))); 556 557 return AE_OK; 558 559 out: 560 mutex_exit(&res->res_mutex); 561 kmem_free(ref, sizeof(*ref)); 562 563 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s already referenced " 564 "by %s?\n", res->res_name, acpi_xname(hdl))); 565 566 return AE_OK; 567 } 568 569 static ACPI_STATUS 570 acpi_power_res_deref(struct acpi_power_res *res, ACPI_HANDLE hdl) 571 { 572 struct acpi_power_ref *ref; 573 574 mutex_enter(&res->res_mutex); 575 576 if (SIMPLEQ_EMPTY(&res->ref_head) != 0) { 577 mutex_exit(&res->res_mutex); 578 return AE_OK; 579 } 580 581 SIMPLEQ_FOREACH(ref, &res->ref_head, ref_list) { 582 583 if (ref->ref_handle == hdl) { 584 SIMPLEQ_REMOVE(&res->ref_head, 585 ref, acpi_power_ref, ref_list); 586 mutex_exit(&res->res_mutex); 587 kmem_free(ref, sizeof(*ref)); 588 mutex_enter(&res->res_mutex); 589 break; 590 } 591 } 592 593 /* 594 * If the queue remains non-empty, 595 * something else is using the resource 596 * and hence it can not be turned off. 597 */ 598 if (SIMPLEQ_EMPTY(&res->ref_head) == 0) { 599 mutex_exit(&res->res_mutex); 600 return AE_ABORT_METHOD; 601 } 602 603 mutex_exit(&res->res_mutex); 604 605 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s dereferenced " 606 "by %s\n", res->res_name, acpi_xname(hdl))); 607 608 return AE_OK; 609 } 610 611 static ACPI_STATUS 612 acpi_power_res_sta(ACPI_OBJECT *elm, void *arg) 613 { 614 ACPI_INTEGER val; 615 ACPI_HANDLE hdl; 616 ACPI_STATUS rv; 617 618 rv = acpi_eval_reference_handle(elm, &hdl); 619 620 if (ACPI_FAILURE(rv)) 621 goto fail; 622 623 rv = acpi_eval_integer(hdl, "_STA", &val); 624 625 if (ACPI_FAILURE(rv)) 626 goto fail; 627 628 KDASSERT((uint64_t)val < INT_MAX); 629 630 if ((int)val != ACPI_STA_POW_ON && (int)val != ACPI_STA_POW_OFF) 631 return AE_BAD_VALUE; 632 633 if ((int)val != ACPI_STA_POW_ON) 634 return AE_CTRL_FALSE; /* XXX: Not an error. */ 635 636 return AE_OK; 637 638 fail: 639 if (rv == AE_CTRL_FALSE) 640 rv = AE_ERROR; 641 642 ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT, "failed to evaluate _STA " 643 "for %s: %s\n", acpi_xname(hdl), AcpiFormatException(rv))); 644 645 return rv; 646 } 647 648 static ACPI_OBJECT * 649 acpi_power_pkg_get(ACPI_HANDLE hdl, int state) 650 { 651 char path[5] = "_PR?"; 652 ACPI_OBJECT *obj; 653 ACPI_BUFFER buf; 654 ACPI_STATUS rv; 655 656 path[3] = '0' + state; 657 658 rv = acpi_eval_struct(hdl, path, &buf); 659 660 if (ACPI_FAILURE(rv)) 661 goto fail; 662 663 if (buf.Length == 0) { 664 rv = AE_LIMIT; 665 goto fail; 666 } 667 668 obj = buf.Pointer; 669 670 if (obj->Type != ACPI_TYPE_PACKAGE) { 671 rv = AE_TYPE; 672 goto fail; 673 } 674 675 if (obj->Package.Count == 0) { 676 rv = AE_LIMIT; 677 goto fail; 678 } 679 680 return obj; 681 682 fail: 683 if (buf.Pointer != NULL) 684 ACPI_FREE(buf.Pointer); 685 686 ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT, "failed to evaluate %s for " 687 "%s: %s\n", path, acpi_xname(hdl), AcpiFormatException(rv))); 688 689 return NULL; 690 } 691 692 SYSCTL_SETUP(sysctl_acpi_power_setup, "sysctl hw.acpi.power subtree setup") 693 { 694 int err; 695 696 err = sysctl_createv(NULL, 0, NULL, &anode, 697 CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", 698 NULL, NULL, 0, NULL, 0, 699 CTL_HW, CTL_EOL); 700 701 if (err != 0) 702 goto fail; 703 704 err = sysctl_createv(NULL, 0, &anode, &anode, 705 CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", 706 NULL, NULL, 0, NULL, 0, 707 CTL_CREATE, CTL_EOL); 708 709 if (err != 0) 710 goto fail; 711 712 err = sysctl_createv(NULL, 0, &anode, &anode, 713 CTLFLAG_PERMANENT, CTLTYPE_NODE, 714 "power", SYSCTL_DESCR("ACPI device power states"), 715 NULL, 0, NULL, 0, 716 CTL_CREATE, CTL_EOL); 717 718 if (err != 0) 719 goto fail; 720 721 return; 722 723 fail: 724 anode = NULL; 725 } 726 727 void 728 acpi_power_add(struct acpi_devnode *ad) 729 { 730 int err; 731 732 KASSERT(ad != NULL && ad->ad_root != NULL); 733 KASSERT((ad->ad_flags & ACPI_DEVICE_POWER) != 0); 734 735 if (anode == NULL) 736 return; 737 738 /* 739 * Make this read-only: because a single power resource 740 * may power multiple devices, it is unclear whether 741 * power resources should be controllable by an user. 742 */ 743 err = sysctl_createv(NULL, 0, &anode, NULL, 744 CTLFLAG_READONLY, CTLTYPE_STRING, ad->ad_name, 745 NULL, acpi_power_sysctl, 0, ad, 0, 746 CTL_CREATE, CTL_EOL); 747 748 if (err != 0) 749 aprint_error_dev(ad->ad_root, "sysctl_createv" 750 "(hw.acpi.power.%s) failed (err %d)\n", ad->ad_name, err); 751 } 752 753 static int 754 acpi_power_sysctl(SYSCTLFN_ARGS) 755 { 756 struct acpi_devnode *ad; 757 struct sysctlnode node; 758 int err, state; 759 char t[3]; 760 761 node = *rnode; 762 ad = rnode->sysctl_data; 763 764 if (acpi_power_get(ad->ad_handle, &state) != true) 765 state = 0; 766 767 (void)memset(t, '\0', sizeof(t)); 768 (void)snprintf(t, sizeof(t), "D%d", state); 769 770 node.sysctl_data = &t; 771 772 err = sysctl_lookup(SYSCTLFN_CALL(&node)); 773 774 if (err || newp == NULL) 775 return err; 776 777 return 0; 778 } 779 780 /* 781 * XXX: Move this to acpi_util.c by refactoring 782 * acpi_name() to optionally return a single name. 783 */ 784 static const char * 785 acpi_xname(ACPI_HANDLE hdl) 786 { 787 static char str[5]; 788 ACPI_BUFFER buf; 789 ACPI_STATUS rv; 790 791 buf.Pointer = str; 792 buf.Length = sizeof(str); 793 794 rv = AcpiGetName(hdl, ACPI_SINGLE_NAME, &buf); 795 796 if (ACPI_FAILURE(rv)) 797 return "????"; 798 799 return str; 800 } 801