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