1 /* $NetBSD: sysmon_power.c,v 1.68 2021/09/26 16:24:21 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 Juan Romero Pardines. 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 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /* 29 * Copyright (c) 2003 Wasabi Systems, Inc. 30 * All rights reserved. 31 * 32 * Written by Jason R. Thorpe for Wasabi Systems, Inc. 33 * 34 * Redistribution and use in source and binary forms, with or without 35 * modification, are permitted provided that the following conditions 36 * are met: 37 * 1. Redistributions of source code must retain the above copyright 38 * notice, this list of conditions and the following disclaimer. 39 * 2. Redistributions in binary form must reproduce the above copyright 40 * notice, this list of conditions and the following disclaimer in the 41 * documentation and/or other materials provided with the distribution. 42 * 3. All advertising materials mentioning features or use of this software 43 * must display the following acknowledgement: 44 * This product includes software developed for the NetBSD Project by 45 * Wasabi Systems, Inc. 46 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 47 * or promote products derived from this software without specific prior 48 * written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 52 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 54 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 55 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 56 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 57 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 58 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 59 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 60 * POSSIBILITY OF SUCH DAMAGE. 61 */ 62 63 /* 64 * Power management framework for sysmon. 65 * 66 * We defer to a power management daemon running in userspace, since 67 * power management is largely a policy issue. This merely provides 68 * for power management event notification to that daemon. 69 */ 70 71 #include <sys/cdefs.h> 72 __KERNEL_RCSID(0, "$NetBSD: sysmon_power.c,v 1.68 2021/09/26 16:24:21 thorpej Exp $"); 73 74 #ifdef _KERNEL_OPT 75 #include "opt_compat_netbsd.h" 76 #endif 77 78 #include <sys/param.h> 79 #include <sys/reboot.h> 80 #include <sys/systm.h> 81 #include <sys/poll.h> 82 #include <sys/select.h> 83 #include <sys/vnode.h> 84 #include <sys/condvar.h> 85 #include <sys/mutex.h> 86 #include <sys/kmem.h> 87 #include <sys/proc.h> 88 #include <sys/device.h> 89 #include <sys/rndsource.h> 90 #include <sys/module.h> 91 #include <sys/once.h> 92 #include <sys/compat_stub.h> 93 94 #include <dev/sysmon/sysmonvar.h> 95 #include <prop/proplib.h> 96 97 MODULE(MODULE_CLASS_DRIVER, sysmon_power, "sysmon"); 98 99 /* 100 * Singly linked list for dictionaries to be stored/sent. 101 */ 102 struct power_event_dictionary { 103 SIMPLEQ_ENTRY(power_event_dictionary) pev_dict_head; 104 prop_dictionary_t dict; 105 int flags; 106 }; 107 108 struct power_event_description { 109 int type; 110 const char *desc; 111 }; 112 113 /* 114 * Available events for power switches. 115 */ 116 static const struct power_event_description pswitch_event_desc[] = { 117 { PSWITCH_EVENT_PRESSED, "pressed" }, 118 { PSWITCH_EVENT_RELEASED, "released" }, 119 { -1, NULL } 120 }; 121 122 /* 123 * Available script names for power switches. 124 */ 125 static const struct power_event_description pswitch_type_desc[] = { 126 { PSWITCH_TYPE_POWER, "power_button" }, 127 { PSWITCH_TYPE_SLEEP, "sleep_button" }, 128 { PSWITCH_TYPE_LID, "lid_switch" }, 129 { PSWITCH_TYPE_RESET, "reset_button" }, 130 { PSWITCH_TYPE_ACADAPTER, "acadapter" }, 131 { PSWITCH_TYPE_HOTKEY, "hotkey_button" }, 132 { PSWITCH_TYPE_RADIO, "radio_button" }, 133 { -1, NULL } 134 }; 135 136 /* 137 * Available events for envsys(4). 138 */ 139 static const struct power_event_description penvsys_event_desc[] = { 140 { PENVSYS_EVENT_NORMAL, "normal" }, 141 { PENVSYS_EVENT_CRITICAL, "critical" }, 142 { PENVSYS_EVENT_CRITOVER, "critical-over" }, 143 { PENVSYS_EVENT_CRITUNDER, "critical-under" }, 144 { PENVSYS_EVENT_WARNOVER, "warning-over" }, 145 { PENVSYS_EVENT_WARNUNDER, "warning-under" }, 146 { PENVSYS_EVENT_BATT_CRIT, "critical-capacity" }, 147 { PENVSYS_EVENT_BATT_WARN, "warning-capacity" }, 148 { PENVSYS_EVENT_BATT_HIGH, "high-capacity" }, 149 { PENVSYS_EVENT_BATT_MAX, "maximum-capacity" }, 150 { PENVSYS_EVENT_STATE_CHANGED, "state-changed" }, 151 { PENVSYS_EVENT_LOW_POWER, "low-power" }, 152 { -1, NULL } 153 }; 154 155 /* 156 * Available script names for envsys(4). 157 */ 158 static const struct power_event_description penvsys_type_desc[] = { 159 { PENVSYS_TYPE_BATTERY, "sensor_battery" }, 160 { PENVSYS_TYPE_DRIVE, "sensor_drive" }, 161 { PENVSYS_TYPE_FAN, "sensor_fan" }, 162 { PENVSYS_TYPE_INDICATOR, "sensor_indicator" }, 163 { PENVSYS_TYPE_POWER, "sensor_power" }, 164 { PENVSYS_TYPE_RESISTANCE, "sensor_resistance" }, 165 { PENVSYS_TYPE_TEMP, "sensor_temperature" }, 166 { PENVSYS_TYPE_VOLTAGE, "sensor_voltage" }, 167 { -1, NULL } 168 }; 169 170 #define SYSMON_MAX_POWER_EVENTS 32 171 #define SYSMON_POWER_DICTIONARY_BUSY 0x01 172 #define SYSMON_POWER_DICTIONARY_READY 0x02 173 174 static power_event_t sysmon_power_event_queue[SYSMON_MAX_POWER_EVENTS]; 175 static int sysmon_power_event_queue_head; 176 static int sysmon_power_event_queue_tail; 177 static int sysmon_power_event_queue_count; 178 179 static krndsource_t sysmon_rndsource; 180 181 static SIMPLEQ_HEAD(, power_event_dictionary) pev_dict_list = 182 SIMPLEQ_HEAD_INITIALIZER(pev_dict_list); 183 184 static struct selinfo sysmon_power_event_queue_selinfo; 185 static struct lwp *sysmon_power_daemon; 186 187 static kmutex_t sysmon_power_event_queue_mtx; 188 static kcondvar_t sysmon_power_event_queue_cv; 189 190 static char sysmon_power_type[32]; 191 192 static int sysmon_power_make_dictionary(prop_dictionary_t, void *, int, int); 193 static int sysmon_power_daemon_task(struct power_event_dictionary *, 194 void *, int); 195 static void sysmon_power_destroy_dictionary(struct power_event_dictionary *); 196 197 static struct sysmon_opvec sysmon_power_opvec = { 198 sysmonopen_power, sysmonclose_power, sysmonioctl_power, 199 sysmonread_power, sysmonpoll_power, sysmonkqfilter_power 200 }; 201 202 #define SYSMON_NEXT_EVENT(x) (((x) + 1) % SYSMON_MAX_POWER_EVENTS) 203 204 ONCE_DECL(once_power); 205 206 static int 207 power_preinit(void) 208 { 209 210 mutex_init(&sysmon_power_event_queue_mtx, MUTEX_DEFAULT, IPL_NONE); 211 cv_init(&sysmon_power_event_queue_cv, "smpower"); 212 213 return 0; 214 } 215 216 /* 217 * sysmon_power_init: 218 * 219 * Initializes the mutexes and condition variables in the 220 * boot process via module initialization process. 221 */ 222 int 223 sysmon_power_init(void) 224 { 225 int error; 226 227 (void)RUN_ONCE(&once_power, power_preinit); 228 229 selinit(&sysmon_power_event_queue_selinfo); 230 231 rnd_attach_source(&sysmon_rndsource, "system-power", 232 RND_TYPE_POWER, RND_FLAG_DEFAULT); 233 234 error = sysmon_attach_minor(SYSMON_MINOR_POWER, &sysmon_power_opvec); 235 236 return error; 237 } 238 239 int 240 sysmon_power_fini(void) 241 { 242 int error; 243 244 if (sysmon_power_daemon != NULL) 245 error = EBUSY; 246 else 247 error = sysmon_attach_minor(SYSMON_MINOR_POWER, NULL); 248 249 if (error == 0) { 250 rnd_detach_source(&sysmon_rndsource); 251 seldestroy(&sysmon_power_event_queue_selinfo); 252 cv_destroy(&sysmon_power_event_queue_cv); 253 mutex_destroy(&sysmon_power_event_queue_mtx); 254 } 255 256 return error; 257 } 258 259 /* 260 * sysmon_queue_power_event: 261 * 262 * Enqueue a power event for the power management daemon. Returns 263 * non-zero if we were able to enqueue a power event. 264 */ 265 static int 266 sysmon_queue_power_event(power_event_t *pev) 267 { 268 KASSERT(mutex_owned(&sysmon_power_event_queue_mtx)); 269 270 if (sysmon_power_event_queue_count == SYSMON_MAX_POWER_EVENTS) 271 return 0; 272 273 sysmon_power_event_queue[sysmon_power_event_queue_head] = *pev; 274 sysmon_power_event_queue_head = 275 SYSMON_NEXT_EVENT(sysmon_power_event_queue_head); 276 sysmon_power_event_queue_count++; 277 278 return 1; 279 } 280 281 /* 282 * sysmon_get_power_event: 283 * 284 * Get a power event from the queue. Returns non-zero if there 285 * is an event available. 286 */ 287 static int 288 sysmon_get_power_event(power_event_t *pev) 289 { 290 KASSERT(mutex_owned(&sysmon_power_event_queue_mtx)); 291 292 if (sysmon_power_event_queue_count == 0) 293 return 0; 294 295 *pev = sysmon_power_event_queue[sysmon_power_event_queue_tail]; 296 sysmon_power_event_queue_tail = 297 SYSMON_NEXT_EVENT(sysmon_power_event_queue_tail); 298 sysmon_power_event_queue_count--; 299 300 return 1; 301 } 302 303 /* 304 * sysmon_power_event_queue_flush: 305 * 306 * Flush the event queue, and reset all state. 307 */ 308 static void 309 sysmon_power_event_queue_flush(void) 310 { 311 KASSERT(mutex_owned(&sysmon_power_event_queue_mtx)); 312 313 sysmon_power_event_queue_head = 0; 314 sysmon_power_event_queue_tail = 0; 315 sysmon_power_event_queue_count = 0; 316 } 317 318 /* 319 * sysmon_power_daemon_task: 320 * 321 * Assign required power event members and sends a signal 322 * to the process to notify that an event was enqueued successfully. 323 */ 324 static int 325 sysmon_power_daemon_task(struct power_event_dictionary *ped, 326 void *pev_data, int event) 327 { 328 power_event_t pev; 329 int rv, error = 0; 330 331 if (!ped || !ped->dict || !pev_data) 332 return EINVAL; 333 334 memset(&pev, 0, sizeof(pev)); 335 336 mutex_enter(&sysmon_power_event_queue_mtx); 337 338 switch (event) { 339 /* 340 * Power switch events. 341 */ 342 case PSWITCH_EVENT_PRESSED: 343 case PSWITCH_EVENT_RELEASED: 344 { 345 346 struct sysmon_pswitch *pswitch = 347 (struct sysmon_pswitch *)pev_data; 348 349 pev.pev_type = POWER_EVENT_SWITCH_STATE_CHANGE; 350 351 MODULE_HOOK_CALL_VOID(compat_sysmon_power_40_hook, 352 (&pev, pswitch, event), __nothing); 353 354 error = sysmon_power_make_dictionary(ped->dict, 355 pswitch, 356 event, 357 pev.pev_type); 358 if (error) { 359 mutex_exit(&sysmon_power_event_queue_mtx); 360 goto out; 361 } 362 363 break; 364 } 365 366 /* 367 * ENVSYS events. 368 */ 369 case PENVSYS_EVENT_NORMAL: 370 case PENVSYS_EVENT_CRITICAL: 371 case PENVSYS_EVENT_CRITUNDER: 372 case PENVSYS_EVENT_CRITOVER: 373 case PENVSYS_EVENT_WARNUNDER: 374 case PENVSYS_EVENT_WARNOVER: 375 case PENVSYS_EVENT_BATT_CRIT: 376 case PENVSYS_EVENT_BATT_WARN: 377 case PENVSYS_EVENT_BATT_HIGH: 378 case PENVSYS_EVENT_BATT_MAX: 379 case PENVSYS_EVENT_STATE_CHANGED: 380 case PENVSYS_EVENT_LOW_POWER: 381 { 382 struct penvsys_state *penvsys = 383 (struct penvsys_state *)pev_data; 384 385 pev.pev_type = POWER_EVENT_ENVSYS_STATE_CHANGE; 386 387 error = sysmon_power_make_dictionary(ped->dict, 388 penvsys, 389 event, 390 pev.pev_type); 391 if (error) { 392 mutex_exit(&sysmon_power_event_queue_mtx); 393 goto out; 394 } 395 396 break; 397 } 398 default: 399 error = ENOTTY; 400 mutex_exit(&sysmon_power_event_queue_mtx); 401 goto out; 402 } 403 404 /* 405 * Enqueue the event. 406 */ 407 rv = sysmon_queue_power_event(&pev); 408 if (rv == 0) { 409 printf("%s: WARNING: state change event %d lost; " 410 "queue full\n", __func__, pev.pev_type); 411 mutex_exit(&sysmon_power_event_queue_mtx); 412 error = EINVAL; 413 goto out; 414 } else { 415 /* 416 * Notify the daemon that an event is ready and its 417 * dictionary is ready to be fetched. 418 */ 419 ped->flags |= SYSMON_POWER_DICTIONARY_READY; 420 SIMPLEQ_INSERT_TAIL(&pev_dict_list, ped, pev_dict_head); 421 cv_broadcast(&sysmon_power_event_queue_cv); 422 selnotify(&sysmon_power_event_queue_selinfo, 423 POLLIN | POLLRDNORM, NOTE_SUBMIT); 424 mutex_exit(&sysmon_power_event_queue_mtx); 425 } 426 427 out: 428 return error; 429 } 430 431 /* 432 * sysmonopen_power: 433 * 434 * Open the system monitor device. 435 */ 436 int 437 sysmonopen_power(dev_t dev, int flag, int mode, struct lwp *l) 438 { 439 int error = 0; 440 441 mutex_enter(&sysmon_power_event_queue_mtx); 442 if (sysmon_power_daemon != NULL) 443 error = EBUSY; 444 else { 445 sysmon_power_daemon = l; 446 sysmon_power_event_queue_flush(); 447 } 448 mutex_exit(&sysmon_power_event_queue_mtx); 449 450 return error; 451 } 452 453 /* 454 * sysmonclose_power: 455 * 456 * Close the system monitor device. 457 */ 458 int 459 sysmonclose_power(dev_t dev, int flag, int mode, struct lwp *l) 460 { 461 int count; 462 463 mutex_enter(&sysmon_power_event_queue_mtx); 464 count = sysmon_power_event_queue_count; 465 sysmon_power_daemon = NULL; 466 sysmon_power_event_queue_flush(); 467 mutex_exit(&sysmon_power_event_queue_mtx); 468 469 if (count) 470 printf("WARNING: %d power event%s lost by exiting daemon\n", 471 count, count > 1 ? "s" : ""); 472 473 return 0; 474 } 475 476 /* 477 * sysmonread_power: 478 * 479 * Read the system monitor device. 480 */ 481 int 482 sysmonread_power(dev_t dev, struct uio *uio, int flags) 483 { 484 power_event_t pev; 485 int rv; 486 487 /* We only allow one event to be read at a time. */ 488 if (uio->uio_resid != POWER_EVENT_MSG_SIZE) 489 return EINVAL; 490 491 mutex_enter(&sysmon_power_event_queue_mtx); 492 for (;;) { 493 if (sysmon_get_power_event(&pev)) { 494 rv = uiomove(&pev, POWER_EVENT_MSG_SIZE, uio); 495 break; 496 } 497 498 if (flags & IO_NDELAY) { 499 rv = EWOULDBLOCK; 500 break; 501 } 502 503 cv_wait(&sysmon_power_event_queue_cv, 504 &sysmon_power_event_queue_mtx); 505 } 506 mutex_exit(&sysmon_power_event_queue_mtx); 507 508 return rv; 509 } 510 511 /* 512 * sysmonpoll_power: 513 * 514 * Poll the system monitor device. 515 */ 516 int 517 sysmonpoll_power(dev_t dev, int events, struct lwp *l) 518 { 519 int revents; 520 521 revents = events & (POLLOUT | POLLWRNORM); 522 523 /* Attempt to save some work. */ 524 if ((events & (POLLIN | POLLRDNORM)) == 0) 525 return revents; 526 527 mutex_enter(&sysmon_power_event_queue_mtx); 528 if (sysmon_power_event_queue_count) 529 revents |= events & (POLLIN | POLLRDNORM); 530 else 531 selrecord(l, &sysmon_power_event_queue_selinfo); 532 mutex_exit(&sysmon_power_event_queue_mtx); 533 534 return revents; 535 } 536 537 static void 538 filt_sysmon_power_rdetach(struct knote *kn) 539 { 540 541 mutex_enter(&sysmon_power_event_queue_mtx); 542 selremove_knote(&sysmon_power_event_queue_selinfo, kn); 543 mutex_exit(&sysmon_power_event_queue_mtx); 544 } 545 546 static int 547 filt_sysmon_power_read(struct knote *kn, long hint) 548 { 549 550 if (hint & NOTE_SUBMIT) { 551 KASSERT(mutex_owned(&sysmon_power_event_queue_mtx)); 552 } else { 553 mutex_enter(&sysmon_power_event_queue_mtx); 554 } 555 556 kn->kn_data = sysmon_power_event_queue_count; 557 558 if ((hint & NOTE_SUBMIT) == 0) { 559 mutex_exit(&sysmon_power_event_queue_mtx); 560 } 561 562 return kn->kn_data > 0; 563 } 564 565 static const struct filterops sysmon_power_read_filtops = { 566 .f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE, 567 .f_attach = NULL, 568 .f_detach = filt_sysmon_power_rdetach, 569 .f_event = filt_sysmon_power_read, 570 }; 571 572 /* 573 * sysmonkqfilter_power: 574 * 575 * Kqueue filter for the system monitor device. 576 */ 577 int 578 sysmonkqfilter_power(dev_t dev, struct knote *kn) 579 { 580 581 switch (kn->kn_filter) { 582 case EVFILT_READ: 583 kn->kn_fop = &sysmon_power_read_filtops; 584 mutex_enter(&sysmon_power_event_queue_mtx); 585 selrecord_knote(&sysmon_power_event_queue_selinfo, kn); 586 mutex_exit(&sysmon_power_event_queue_mtx); 587 break; 588 589 case EVFILT_WRITE: 590 kn->kn_fop = &seltrue_filtops; 591 break; 592 593 default: 594 return EINVAL; 595 } 596 597 return 0; 598 } 599 600 /* 601 * sysmonioctl_power: 602 * 603 * Perform a power management control request. 604 */ 605 int 606 sysmonioctl_power(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 607 { 608 int error = 0; 609 610 switch (cmd) { 611 case POWER_IOC_GET_TYPE: 612 case POWER_IOC_GET_TYPE_WITH_LOSSAGE: 613 { 614 struct power_type *power_type = (void *) data; 615 616 (void)strlcpy(power_type->power_type, 617 sysmon_power_type, 618 sizeof(power_type->power_type)); 619 break; 620 } 621 case POWER_EVENT_RECVDICT: 622 { 623 struct plistref *plist = (struct plistref *)data; 624 struct power_event_dictionary *ped; 625 626 /* 627 * Get the first dictionary enqueued and mark it 628 * as busy. 629 */ 630 mutex_enter(&sysmon_power_event_queue_mtx); 631 ped = SIMPLEQ_FIRST(&pev_dict_list); 632 if (!ped || !ped->dict) { 633 mutex_exit(&sysmon_power_event_queue_mtx); 634 error = ENOTSUP; 635 break; 636 } 637 638 if ((ped->flags & SYSMON_POWER_DICTIONARY_READY) == 0) { 639 mutex_exit(&sysmon_power_event_queue_mtx); 640 error = EINVAL; 641 break; 642 } 643 644 if (ped->flags & SYSMON_POWER_DICTIONARY_BUSY) { 645 mutex_exit(&sysmon_power_event_queue_mtx); 646 error = EBUSY; 647 break; 648 } 649 650 ped->flags |= SYSMON_POWER_DICTIONARY_BUSY; 651 mutex_exit(&sysmon_power_event_queue_mtx); 652 653 /* 654 * Send it now. 655 */ 656 error = prop_dictionary_copyout_ioctl(plist, 657 cmd, 658 ped->dict); 659 660 /* 661 * Remove the dictionary now that we don't need it. 662 */ 663 mutex_enter(&sysmon_power_event_queue_mtx); 664 ped->flags &= ~SYSMON_POWER_DICTIONARY_BUSY; 665 ped->flags &= ~SYSMON_POWER_DICTIONARY_READY; 666 SIMPLEQ_REMOVE_HEAD(&pev_dict_list, pev_dict_head); 667 mutex_exit(&sysmon_power_event_queue_mtx); 668 sysmon_power_destroy_dictionary(ped); 669 670 break; 671 } 672 default: 673 error = ENOTTY; 674 } 675 676 return error; 677 } 678 679 /* 680 * sysmon_power_make_dictionary: 681 * 682 * Adds the properties for an event in a dictionary. 683 */ 684 int 685 sysmon_power_make_dictionary(prop_dictionary_t dict, void *power_data, 686 int event, int type) 687 { 688 int i; 689 690 KASSERT(mutex_owned(&sysmon_power_event_queue_mtx)); 691 692 switch (type) { 693 /* 694 * create the dictionary for a power switch event. 695 */ 696 case POWER_EVENT_SWITCH_STATE_CHANGE: 697 { 698 const struct power_event_description *peevent = 699 pswitch_event_desc; 700 const struct power_event_description *petype = 701 pswitch_type_desc; 702 struct sysmon_pswitch *smpsw = 703 (struct sysmon_pswitch *)power_data; 704 const char *pwrtype = "pswitch"; 705 706 #define SETPROP(key, str) \ 707 do { \ 708 if ((str) != NULL && !prop_dictionary_set_string(dict, \ 709 (key), \ 710 (str))) { \ 711 printf("%s: failed to set %s\n", __func__, (str)); \ 712 return EINVAL; \ 713 } \ 714 } while (/* CONSTCOND */ 0) 715 716 717 SETPROP("driver-name", smpsw->smpsw_name); 718 719 for (i = 0; peevent[i].type != -1; i++) 720 if (peevent[i].type == event) 721 break; 722 723 SETPROP("powerd-event-name", peevent[i].desc); 724 725 for (i = 0; petype[i].type != -1; i++) 726 if (petype[i].type == smpsw->smpsw_type) 727 break; 728 729 SETPROP("powerd-script-name", petype[i].desc); 730 SETPROP("power-type", pwrtype); 731 break; 732 } 733 /* 734 * create a dictionary for power envsys event. 735 */ 736 case POWER_EVENT_ENVSYS_STATE_CHANGE: 737 { 738 const struct power_event_description *peevent = 739 penvsys_event_desc; 740 const struct power_event_description *petype = 741 penvsys_type_desc; 742 struct penvsys_state *pes = 743 (struct penvsys_state *)power_data; 744 const char *pwrtype = "envsys"; 745 746 SETPROP("driver-name", pes->pes_dvname); 747 SETPROP("sensor-name", pes->pes_sensname); 748 SETPROP("state-description", pes->pes_statedesc); 749 750 for (i = 0; peevent[i].type != -1; i++) 751 if (peevent[i].type == event) 752 break; 753 754 SETPROP("powerd-event-name", peevent[i].desc); 755 756 for (i = 0; petype[i].type != -1; i++) 757 if (petype[i].type == pes->pes_type) 758 break; 759 760 SETPROP("powerd-script-name", petype[i].desc); 761 SETPROP("power-type", pwrtype); 762 break; 763 } 764 default: 765 return ENOTSUP; 766 } 767 768 return 0; 769 } 770 771 /* 772 * sysmon_power_destroy_dictionary: 773 * 774 * Destroys a power_event_dictionary object and all its 775 * properties in the dictionary. 776 */ 777 static void 778 sysmon_power_destroy_dictionary(struct power_event_dictionary *ped) 779 { 780 prop_object_iterator_t iter; 781 prop_object_t obj; 782 783 KASSERT(ped != NULL); 784 KASSERT((ped->flags & SYSMON_POWER_DICTIONARY_BUSY) == 0); 785 786 iter = prop_dictionary_iterator(ped->dict); 787 if (iter == NULL) 788 return; 789 790 while ((obj = prop_object_iterator_next(iter)) != NULL) { 791 prop_dictionary_remove(ped->dict, 792 prop_dictionary_keysym_value(obj)); 793 prop_object_iterator_reset(iter); 794 } 795 796 prop_object_iterator_release(iter); 797 prop_object_release(ped->dict); 798 799 kmem_free(ped, sizeof(*ped)); 800 } 801 802 /* 803 * sysmon_power_settype: 804 * 805 * Sets the back-end power management type. This information can 806 * be used by the power management daemon. 807 */ 808 void 809 sysmon_power_settype(const char *type) 810 { 811 812 /* 813 * Don't bother locking this; it's going to be set 814 * during autoconfiguration, and then only read from 815 * then on. 816 */ 817 (void)strlcpy(sysmon_power_type, type, sizeof(sysmon_power_type)); 818 } 819 820 #define PENVSYS_SHOWSTATE(str) \ 821 do { \ 822 printf("%s: %s limit on '%s'\n", \ 823 pes->pes_dvname, (str), pes->pes_sensname); \ 824 } while (/* CONSTCOND */ 0) 825 826 /* 827 * sysmon_penvsys_event: 828 * 829 * Puts an event onto the sysmon power queue and sends the 830 * appropriate event if the daemon is running, otherwise a 831 * message is shown. 832 */ 833 void 834 sysmon_penvsys_event(struct penvsys_state *pes, int event) 835 { 836 struct power_event_dictionary *ped; 837 const char *mystr = NULL; 838 839 KASSERT(pes != NULL); 840 841 rnd_add_uint32(&sysmon_rndsource, pes->pes_type); 842 843 if (sysmon_power_daemon != NULL) { 844 /* 845 * Create a dictionary for the new event. 846 */ 847 ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP); 848 if (!ped) 849 return; 850 ped->dict = prop_dictionary_create(); 851 852 if (sysmon_power_daemon_task(ped, pes, event) == 0) 853 return; 854 /* We failed */ 855 prop_object_release(ped->dict); 856 kmem_free(ped, sizeof(*ped)); 857 } 858 859 switch (pes->pes_type) { 860 case PENVSYS_TYPE_BATTERY: 861 switch (event) { 862 case PENVSYS_EVENT_LOW_POWER: 863 printf("sysmon: LOW POWER! SHUTTING DOWN.\n"); 864 kern_reboot(RB_POWERDOWN, NULL); 865 break; 866 case PENVSYS_EVENT_STATE_CHANGED: 867 printf("%s: state changed on '%s' to '%s'\n", 868 pes->pes_dvname, pes->pes_sensname, 869 pes->pes_statedesc); 870 break; 871 case PENVSYS_EVENT_BATT_CRIT: 872 mystr = "critical capacity"; 873 PENVSYS_SHOWSTATE(mystr); 874 break; 875 case PENVSYS_EVENT_BATT_WARN: 876 mystr = "warning capacity"; 877 PENVSYS_SHOWSTATE(mystr); 878 break; 879 case PENVSYS_EVENT_BATT_HIGH: 880 mystr = "high capacity"; 881 PENVSYS_SHOWSTATE(mystr); 882 break; 883 case PENVSYS_EVENT_BATT_MAX: 884 mystr = "maximum capacity"; 885 PENVSYS_SHOWSTATE(mystr); 886 break; 887 case PENVSYS_EVENT_NORMAL: 888 printf("%s: normal capacity on '%s'\n", 889 pes->pes_dvname, pes->pes_sensname); 890 break; 891 } 892 break; 893 case PENVSYS_TYPE_FAN: 894 case PENVSYS_TYPE_INDICATOR: 895 case PENVSYS_TYPE_TEMP: 896 case PENVSYS_TYPE_POWER: 897 case PENVSYS_TYPE_RESISTANCE: 898 case PENVSYS_TYPE_VOLTAGE: 899 switch (event) { 900 case PENVSYS_EVENT_CRITICAL: 901 mystr = "critical"; 902 PENVSYS_SHOWSTATE(mystr); 903 break; 904 case PENVSYS_EVENT_CRITOVER: 905 mystr = "critical over"; 906 PENVSYS_SHOWSTATE(mystr); 907 break; 908 case PENVSYS_EVENT_CRITUNDER: 909 mystr = "critical under"; 910 PENVSYS_SHOWSTATE(mystr); 911 break; 912 case PENVSYS_EVENT_WARNOVER: 913 mystr = "warning over"; 914 PENVSYS_SHOWSTATE(mystr); 915 break; 916 case PENVSYS_EVENT_WARNUNDER: 917 mystr = "warning under"; 918 PENVSYS_SHOWSTATE(mystr); 919 break; 920 case PENVSYS_EVENT_NORMAL: 921 printf("%s: normal state on '%s'\n", 922 pes->pes_dvname, pes->pes_sensname); 923 break; 924 default: 925 printf("%s: unknown event\n", __func__); 926 } 927 break; 928 case PENVSYS_TYPE_DRIVE: 929 switch (event) { 930 case PENVSYS_EVENT_STATE_CHANGED: 931 printf("%s: state changed on '%s' to '%s'\n", 932 pes->pes_dvname, pes->pes_sensname, 933 pes->pes_statedesc); 934 break; 935 case PENVSYS_EVENT_NORMAL: 936 printf("%s: normal state on '%s' (%s)\n", 937 pes->pes_dvname, pes->pes_sensname, 938 pes->pes_statedesc); 939 break; 940 } 941 break; 942 default: 943 printf("%s: unknown power type\n", __func__); 944 break; 945 } 946 } 947 948 /* 949 * sysmon_pswitch_register: 950 * 951 * Register a power switch device. 952 */ 953 int 954 sysmon_pswitch_register(struct sysmon_pswitch *smpsw) 955 { 956 (void)RUN_ONCE(&once_power, power_preinit); 957 958 return 0; 959 } 960 961 /* 962 * sysmon_pswitch_unregister: 963 * 964 * Unregister a power switch device. 965 */ 966 void 967 sysmon_pswitch_unregister(struct sysmon_pswitch *smpsw) 968 { 969 /* nada */ 970 } 971 972 /* 973 * sysmon_pswitch_event: 974 * 975 * Register an event on a power switch device. 976 */ 977 void 978 sysmon_pswitch_event(struct sysmon_pswitch *smpsw, int event) 979 { 980 struct power_event_dictionary *ped = NULL; 981 982 KASSERT(smpsw != NULL); 983 984 /* 985 * For pnp specific events, we don't care if the power daemon 986 * is running or not 987 */ 988 if (smpsw->smpsw_type == PSWITCH_TYPE_LID) { 989 switch (event) { 990 case PSWITCH_EVENT_PRESSED: 991 pmf_event_inject(NULL, PMFE_CHASSIS_LID_CLOSE); 992 break; 993 case PSWITCH_EVENT_RELEASED: 994 pmf_event_inject(NULL, PMFE_CHASSIS_LID_OPEN); 995 break; 996 default: 997 break; 998 } 999 } 1000 1001 if (sysmon_power_daemon != NULL) { 1002 /* 1003 * Create a new dictionary for the event. 1004 */ 1005 ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP); 1006 if (!ped) 1007 return; 1008 ped->dict = prop_dictionary_create(); 1009 1010 if (sysmon_power_daemon_task(ped, smpsw, event) == 0) 1011 return; 1012 /* We failed */ 1013 prop_object_release(ped->dict); 1014 kmem_free(ped, sizeof(*ped)); 1015 } 1016 1017 switch (smpsw->smpsw_type) { 1018 case PSWITCH_TYPE_POWER: 1019 if (event != PSWITCH_EVENT_PRESSED) { 1020 /* just ignore it */ 1021 return; 1022 } 1023 1024 /* 1025 * Attempt a somewhat graceful shutdown of the system, 1026 * as if the user has issued a reboot(2) call with 1027 * RB_POWERDOWN. 1028 */ 1029 printf("%s: power button pressed, shutting down!\n", 1030 smpsw->smpsw_name); 1031 kern_reboot(RB_POWERDOWN, NULL); 1032 break; 1033 1034 case PSWITCH_TYPE_RESET: 1035 if (event != PSWITCH_EVENT_PRESSED) { 1036 /* just ignore it */ 1037 return; 1038 } 1039 1040 /* 1041 * Attempt a somewhat graceful reboot of the system, 1042 * as if the user had issued a reboot(2) call. 1043 */ 1044 printf("%s: reset button pressed, rebooting!\n", 1045 smpsw->smpsw_name); 1046 kern_reboot(0, NULL); 1047 break; 1048 1049 case PSWITCH_TYPE_SLEEP: 1050 if (event != PSWITCH_EVENT_PRESSED) { 1051 /* just ignore it */ 1052 return; 1053 } 1054 1055 /* 1056 * Try to enter a "sleep" state. 1057 */ 1058 /* XXX */ 1059 printf("%s: sleep button pressed.\n", smpsw->smpsw_name); 1060 break; 1061 1062 case PSWITCH_TYPE_HOTKEY: 1063 /* 1064 * Eat up the event, there's nothing we can do 1065 */ 1066 break; 1067 1068 case PSWITCH_TYPE_LID: 1069 switch (event) { 1070 case PSWITCH_EVENT_PRESSED: 1071 /* 1072 * Try to enter a "standby" state. 1073 */ 1074 /* XXX */ 1075 printf("%s: lid closed.\n", smpsw->smpsw_name); 1076 break; 1077 1078 case PSWITCH_EVENT_RELEASED: 1079 /* 1080 * Come out of "standby" state. 1081 */ 1082 /* XXX */ 1083 printf("%s: lid opened.\n", smpsw->smpsw_name); 1084 break; 1085 1086 default: 1087 printf("%s: unknown lid switch event: %d\n", 1088 smpsw->smpsw_name, event); 1089 } 1090 break; 1091 1092 case PSWITCH_TYPE_ACADAPTER: 1093 switch (event) { 1094 case PSWITCH_EVENT_PRESSED: 1095 /* 1096 * Come out of power-save state. 1097 */ 1098 aprint_normal("%s: AC adapter online.\n", 1099 smpsw->smpsw_name); 1100 break; 1101 1102 case PSWITCH_EVENT_RELEASED: 1103 /* 1104 * Try to enter a power-save state. 1105 */ 1106 aprint_normal("%s: AC adapter offline.\n", 1107 smpsw->smpsw_name); 1108 break; 1109 } 1110 break; 1111 1112 } 1113 } 1114 1115 static 1116 int 1117 sysmon_power_modcmd(modcmd_t cmd, void *arg) 1118 { 1119 int ret; 1120 1121 switch (cmd) { 1122 case MODULE_CMD_INIT: 1123 ret = sysmon_power_init(); 1124 break; 1125 1126 case MODULE_CMD_FINI: 1127 ret = sysmon_power_fini(); 1128 break; 1129 1130 case MODULE_CMD_STAT: 1131 default: 1132 ret = ENOTTY; 1133 } 1134 1135 return ret; 1136 } 1137 1138