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