1 /* $NetBSD: sysmon_power.c,v 1.60 2019/03/01 11:06:56 pgoyette 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.60 2019/03/01 11:06:56 pgoyette 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 mutex_enter(&sysmon_power_event_queue_mtx); 335 336 switch (event) { 337 /* 338 * Power switch events. 339 */ 340 case PSWITCH_EVENT_PRESSED: 341 case PSWITCH_EVENT_RELEASED: 342 { 343 344 struct sysmon_pswitch *pswitch = 345 (struct sysmon_pswitch *)pev_data; 346 347 pev.pev_type = POWER_EVENT_SWITCH_STATE_CHANGE; 348 349 MODULE_HOOK_CALL_VOID(compat_sysmon_power_40_hook, 350 (&pev, pswitch, event), __nothing); 351 352 error = sysmon_power_make_dictionary(ped->dict, 353 pswitch, 354 event, 355 pev.pev_type); 356 if (error) { 357 mutex_exit(&sysmon_power_event_queue_mtx); 358 goto out; 359 } 360 361 break; 362 } 363 364 /* 365 * ENVSYS events. 366 */ 367 case PENVSYS_EVENT_NORMAL: 368 case PENVSYS_EVENT_CRITICAL: 369 case PENVSYS_EVENT_CRITUNDER: 370 case PENVSYS_EVENT_CRITOVER: 371 case PENVSYS_EVENT_WARNUNDER: 372 case PENVSYS_EVENT_WARNOVER: 373 case PENVSYS_EVENT_BATT_CRIT: 374 case PENVSYS_EVENT_BATT_WARN: 375 case PENVSYS_EVENT_BATT_HIGH: 376 case PENVSYS_EVENT_BATT_MAX: 377 case PENVSYS_EVENT_STATE_CHANGED: 378 case PENVSYS_EVENT_LOW_POWER: 379 { 380 struct penvsys_state *penvsys = 381 (struct penvsys_state *)pev_data; 382 383 pev.pev_type = POWER_EVENT_ENVSYS_STATE_CHANGE; 384 385 error = sysmon_power_make_dictionary(ped->dict, 386 penvsys, 387 event, 388 pev.pev_type); 389 if (error) { 390 mutex_exit(&sysmon_power_event_queue_mtx); 391 goto out; 392 } 393 394 break; 395 } 396 default: 397 error = ENOTTY; 398 mutex_exit(&sysmon_power_event_queue_mtx); 399 goto out; 400 } 401 402 /* 403 * Enqueue the event. 404 */ 405 rv = sysmon_queue_power_event(&pev); 406 if (rv == 0) { 407 printf("%s: WARNING: state change event %d lost; " 408 "queue full\n", __func__, pev.pev_type); 409 mutex_exit(&sysmon_power_event_queue_mtx); 410 error = EINVAL; 411 goto out; 412 } else { 413 /* 414 * Notify the daemon that an event is ready and its 415 * dictionary is ready to be fetched. 416 */ 417 ped->flags |= SYSMON_POWER_DICTIONARY_READY; 418 SIMPLEQ_INSERT_TAIL(&pev_dict_list, ped, pev_dict_head); 419 cv_broadcast(&sysmon_power_event_queue_cv); 420 mutex_exit(&sysmon_power_event_queue_mtx); 421 selnotify(&sysmon_power_event_queue_selinfo, 0, 0); 422 } 423 424 out: 425 return error; 426 } 427 428 /* 429 * sysmonopen_power: 430 * 431 * Open the system monitor device. 432 */ 433 int 434 sysmonopen_power(dev_t dev, int flag, int mode, struct lwp *l) 435 { 436 int error = 0; 437 438 mutex_enter(&sysmon_power_event_queue_mtx); 439 if (sysmon_power_daemon != NULL) 440 error = EBUSY; 441 else { 442 sysmon_power_daemon = l; 443 sysmon_power_event_queue_flush(); 444 } 445 mutex_exit(&sysmon_power_event_queue_mtx); 446 447 return error; 448 } 449 450 /* 451 * sysmonclose_power: 452 * 453 * Close the system monitor device. 454 */ 455 int 456 sysmonclose_power(dev_t dev, int flag, int mode, struct lwp *l) 457 { 458 int count; 459 460 mutex_enter(&sysmon_power_event_queue_mtx); 461 count = sysmon_power_event_queue_count; 462 sysmon_power_daemon = NULL; 463 sysmon_power_event_queue_flush(); 464 mutex_exit(&sysmon_power_event_queue_mtx); 465 466 if (count) 467 printf("WARNING: %d power event%s lost by exiting daemon\n", 468 count, count > 1 ? "s" : ""); 469 470 return 0; 471 } 472 473 /* 474 * sysmonread_power: 475 * 476 * Read the system monitor device. 477 */ 478 int 479 sysmonread_power(dev_t dev, struct uio *uio, int flags) 480 { 481 power_event_t pev; 482 int rv; 483 484 /* We only allow one event to be read at a time. */ 485 if (uio->uio_resid != POWER_EVENT_MSG_SIZE) 486 return EINVAL; 487 488 mutex_enter(&sysmon_power_event_queue_mtx); 489 for (;;) { 490 if (sysmon_get_power_event(&pev)) { 491 rv = uiomove(&pev, POWER_EVENT_MSG_SIZE, uio); 492 break; 493 } 494 495 if (flags & IO_NDELAY) { 496 rv = EWOULDBLOCK; 497 break; 498 } 499 500 cv_wait(&sysmon_power_event_queue_cv, 501 &sysmon_power_event_queue_mtx); 502 } 503 mutex_exit(&sysmon_power_event_queue_mtx); 504 505 return rv; 506 } 507 508 /* 509 * sysmonpoll_power: 510 * 511 * Poll the system monitor device. 512 */ 513 int 514 sysmonpoll_power(dev_t dev, int events, struct lwp *l) 515 { 516 int revents; 517 518 revents = events & (POLLOUT | POLLWRNORM); 519 520 /* Attempt to save some work. */ 521 if ((events & (POLLIN | POLLRDNORM)) == 0) 522 return revents; 523 524 mutex_enter(&sysmon_power_event_queue_mtx); 525 if (sysmon_power_event_queue_count) 526 revents |= events & (POLLIN | POLLRDNORM); 527 else 528 selrecord(l, &sysmon_power_event_queue_selinfo); 529 mutex_exit(&sysmon_power_event_queue_mtx); 530 531 return revents; 532 } 533 534 static void 535 filt_sysmon_power_rdetach(struct knote *kn) 536 { 537 538 mutex_enter(&sysmon_power_event_queue_mtx); 539 SLIST_REMOVE(&sysmon_power_event_queue_selinfo.sel_klist, 540 kn, knote, kn_selnext); 541 mutex_exit(&sysmon_power_event_queue_mtx); 542 } 543 544 static int 545 filt_sysmon_power_read(struct knote *kn, long hint) 546 { 547 548 mutex_enter(&sysmon_power_event_queue_mtx); 549 kn->kn_data = sysmon_power_event_queue_count; 550 mutex_exit(&sysmon_power_event_queue_mtx); 551 552 return kn->kn_data > 0; 553 } 554 555 static const struct filterops sysmon_power_read_filtops = { 556 .f_isfd = 1, 557 .f_attach = NULL, 558 .f_detach = filt_sysmon_power_rdetach, 559 .f_event = filt_sysmon_power_read, 560 }; 561 562 static const struct filterops sysmon_power_write_filtops = { 563 .f_isfd = 1, 564 .f_attach = NULL, 565 .f_detach = filt_sysmon_power_rdetach, 566 .f_event = filt_seltrue, 567 }; 568 569 /* 570 * sysmonkqfilter_power: 571 * 572 * Kqueue filter for the system monitor device. 573 */ 574 int 575 sysmonkqfilter_power(dev_t dev, struct knote *kn) 576 { 577 struct klist *klist; 578 579 switch (kn->kn_filter) { 580 case EVFILT_READ: 581 klist = &sysmon_power_event_queue_selinfo.sel_klist; 582 kn->kn_fop = &sysmon_power_read_filtops; 583 break; 584 585 case EVFILT_WRITE: 586 klist = &sysmon_power_event_queue_selinfo.sel_klist; 587 kn->kn_fop = &sysmon_power_write_filtops; 588 break; 589 590 default: 591 return EINVAL; 592 } 593 594 mutex_enter(&sysmon_power_event_queue_mtx); 595 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 596 mutex_exit(&sysmon_power_event_queue_mtx); 597 598 return 0; 599 } 600 601 /* 602 * sysmonioctl_power: 603 * 604 * Perform a power management control request. 605 */ 606 int 607 sysmonioctl_power(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 608 { 609 int error = 0; 610 611 switch (cmd) { 612 case POWER_IOC_GET_TYPE: 613 case POWER_IOC_GET_TYPE_WITH_LOSSAGE: 614 { 615 struct power_type *power_type = (void *) data; 616 617 (void)strlcpy(power_type->power_type, 618 sysmon_power_type, 619 sizeof(power_type->power_type)); 620 break; 621 } 622 case POWER_EVENT_RECVDICT: 623 { 624 struct plistref *plist = (struct plistref *)data; 625 struct power_event_dictionary *ped; 626 627 /* 628 * Get the first dictionary enqueued and mark it 629 * as busy. 630 */ 631 mutex_enter(&sysmon_power_event_queue_mtx); 632 ped = SIMPLEQ_FIRST(&pev_dict_list); 633 if (!ped || !ped->dict) { 634 mutex_exit(&sysmon_power_event_queue_mtx); 635 error = ENOTSUP; 636 break; 637 } 638 639 if ((ped->flags & SYSMON_POWER_DICTIONARY_READY) == 0) { 640 mutex_exit(&sysmon_power_event_queue_mtx); 641 error = EINVAL; 642 break; 643 } 644 645 if (ped->flags & SYSMON_POWER_DICTIONARY_BUSY) { 646 mutex_exit(&sysmon_power_event_queue_mtx); 647 error = EBUSY; 648 break; 649 } 650 651 ped->flags |= SYSMON_POWER_DICTIONARY_BUSY; 652 mutex_exit(&sysmon_power_event_queue_mtx); 653 654 /* 655 * Send it now. 656 */ 657 error = prop_dictionary_copyout_ioctl(plist, 658 cmd, 659 ped->dict); 660 661 /* 662 * Remove the dictionary now that we don't need it. 663 */ 664 mutex_enter(&sysmon_power_event_queue_mtx); 665 ped->flags &= ~SYSMON_POWER_DICTIONARY_BUSY; 666 ped->flags &= ~SYSMON_POWER_DICTIONARY_READY; 667 SIMPLEQ_REMOVE_HEAD(&pev_dict_list, pev_dict_head); 668 mutex_exit(&sysmon_power_event_queue_mtx); 669 sysmon_power_destroy_dictionary(ped); 670 671 break; 672 } 673 default: 674 error = ENOTTY; 675 } 676 677 return error; 678 } 679 680 /* 681 * sysmon_power_make_dictionary: 682 * 683 * Adds the properties for an event in a dictionary. 684 */ 685 int 686 sysmon_power_make_dictionary(prop_dictionary_t dict, void *power_data, 687 int event, int type) 688 { 689 int i; 690 691 KASSERT(mutex_owned(&sysmon_power_event_queue_mtx)); 692 693 switch (type) { 694 /* 695 * create the dictionary for a power switch event. 696 */ 697 case POWER_EVENT_SWITCH_STATE_CHANGE: 698 { 699 const struct power_event_description *peevent = 700 pswitch_event_desc; 701 const struct power_event_description *petype = 702 pswitch_type_desc; 703 struct sysmon_pswitch *smpsw = 704 (struct sysmon_pswitch *)power_data; 705 const char *pwrtype = "pswitch"; 706 707 #define SETPROP(key, str) \ 708 do { \ 709 if ((str) != NULL && !prop_dictionary_set_cstring(dict, \ 710 (key), \ 711 (str))) { \ 712 printf("%s: failed to set %s\n", __func__, (str)); \ 713 return EINVAL; \ 714 } \ 715 } while (/* CONSTCOND */ 0) 716 717 718 SETPROP("driver-name", smpsw->smpsw_name); 719 720 for (i = 0; peevent[i].type != -1; i++) 721 if (peevent[i].type == event) 722 break; 723 724 SETPROP("powerd-event-name", peevent[i].desc); 725 726 for (i = 0; petype[i].type != -1; i++) 727 if (petype[i].type == smpsw->smpsw_type) 728 break; 729 730 SETPROP("powerd-script-name", petype[i].desc); 731 SETPROP("power-type", pwrtype); 732 break; 733 } 734 /* 735 * create a dictionary for power envsys event. 736 */ 737 case POWER_EVENT_ENVSYS_STATE_CHANGE: 738 { 739 const struct power_event_description *peevent = 740 penvsys_event_desc; 741 const struct power_event_description *petype = 742 penvsys_type_desc; 743 struct penvsys_state *pes = 744 (struct penvsys_state *)power_data; 745 const char *pwrtype = "envsys"; 746 747 SETPROP("driver-name", pes->pes_dvname); 748 SETPROP("sensor-name", pes->pes_sensname); 749 SETPROP("state-description", pes->pes_statedesc); 750 751 for (i = 0; peevent[i].type != -1; i++) 752 if (peevent[i].type == event) 753 break; 754 755 SETPROP("powerd-event-name", peevent[i].desc); 756 757 for (i = 0; petype[i].type != -1; i++) 758 if (petype[i].type == pes->pes_type) 759 break; 760 761 SETPROP("powerd-script-name", petype[i].desc); 762 SETPROP("power-type", pwrtype); 763 break; 764 } 765 default: 766 return ENOTSUP; 767 } 768 769 return 0; 770 } 771 772 /* 773 * sysmon_power_destroy_dictionary: 774 * 775 * Destroys a power_event_dictionary object and all its 776 * properties in the dictionary. 777 */ 778 static void 779 sysmon_power_destroy_dictionary(struct power_event_dictionary *ped) 780 { 781 prop_object_iterator_t iter; 782 prop_object_t obj; 783 784 KASSERT(ped != NULL); 785 KASSERT((ped->flags & SYSMON_POWER_DICTIONARY_BUSY) == 0); 786 787 iter = prop_dictionary_iterator(ped->dict); 788 if (iter == NULL) 789 return; 790 791 while ((obj = prop_object_iterator_next(iter)) != NULL) { 792 prop_dictionary_remove(ped->dict, 793 prop_dictionary_keysym_cstring_nocopy(obj)); 794 prop_object_iterator_reset(iter); 795 } 796 797 prop_object_iterator_release(iter); 798 prop_object_release(ped->dict); 799 800 kmem_free(ped, sizeof(*ped)); 801 } 802 803 /* 804 * sysmon_power_settype: 805 * 806 * Sets the back-end power management type. This information can 807 * be used by the power management daemon. 808 */ 809 void 810 sysmon_power_settype(const char *type) 811 { 812 813 /* 814 * Don't bother locking this; it's going to be set 815 * during autoconfiguration, and then only read from 816 * then on. 817 */ 818 (void)strlcpy(sysmon_power_type, type, sizeof(sysmon_power_type)); 819 } 820 821 #define PENVSYS_SHOWSTATE(str) \ 822 do { \ 823 printf("%s: %s limit on '%s'\n", \ 824 pes->pes_dvname, (str), pes->pes_sensname); \ 825 } while (/* CONSTCOND */ 0) 826 827 /* 828 * sysmon_penvsys_event: 829 * 830 * Puts an event onto the sysmon power queue and sends the 831 * appropriate event if the daemon is running, otherwise a 832 * message is shown. 833 */ 834 void 835 sysmon_penvsys_event(struct penvsys_state *pes, int event) 836 { 837 struct power_event_dictionary *ped; 838 const char *mystr = NULL; 839 840 KASSERT(pes != NULL); 841 842 rnd_add_uint32(&sysmon_rndsource, pes->pes_type); 843 844 if (sysmon_power_daemon != NULL) { 845 /* 846 * Create a dictionary for the new event. 847 */ 848 ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP); 849 if (!ped) 850 return; 851 ped->dict = prop_dictionary_create(); 852 853 if (sysmon_power_daemon_task(ped, pes, event) == 0) 854 return; 855 /* We failed */ 856 prop_object_release(ped->dict); 857 kmem_free(ped, sizeof(*ped)); 858 } 859 860 switch (pes->pes_type) { 861 case PENVSYS_TYPE_BATTERY: 862 switch (event) { 863 case PENVSYS_EVENT_LOW_POWER: 864 printf("sysmon: LOW POWER! SHUTTING DOWN.\n"); 865 cpu_reboot(RB_POWERDOWN, NULL); 866 break; 867 case PENVSYS_EVENT_STATE_CHANGED: 868 printf("%s: state changed on '%s' to '%s'\n", 869 pes->pes_dvname, pes->pes_sensname, 870 pes->pes_statedesc); 871 break; 872 case PENVSYS_EVENT_BATT_CRIT: 873 mystr = "critical capacity"; 874 PENVSYS_SHOWSTATE(mystr); 875 break; 876 case PENVSYS_EVENT_BATT_WARN: 877 mystr = "warning capacity"; 878 PENVSYS_SHOWSTATE(mystr); 879 break; 880 case PENVSYS_EVENT_BATT_HIGH: 881 mystr = "high capacity"; 882 PENVSYS_SHOWSTATE(mystr); 883 break; 884 case PENVSYS_EVENT_BATT_MAX: 885 mystr = "maximum capacity"; 886 PENVSYS_SHOWSTATE(mystr); 887 break; 888 case PENVSYS_EVENT_NORMAL: 889 printf("%s: normal capacity on '%s'\n", 890 pes->pes_dvname, pes->pes_sensname); 891 break; 892 } 893 break; 894 case PENVSYS_TYPE_FAN: 895 case PENVSYS_TYPE_INDICATOR: 896 case PENVSYS_TYPE_TEMP: 897 case PENVSYS_TYPE_POWER: 898 case PENVSYS_TYPE_RESISTANCE: 899 case PENVSYS_TYPE_VOLTAGE: 900 switch (event) { 901 case PENVSYS_EVENT_CRITICAL: 902 mystr = "critical"; 903 PENVSYS_SHOWSTATE(mystr); 904 break; 905 case PENVSYS_EVENT_CRITOVER: 906 mystr = "critical over"; 907 PENVSYS_SHOWSTATE(mystr); 908 break; 909 case PENVSYS_EVENT_CRITUNDER: 910 mystr = "critical under"; 911 PENVSYS_SHOWSTATE(mystr); 912 break; 913 case PENVSYS_EVENT_WARNOVER: 914 mystr = "warning over"; 915 PENVSYS_SHOWSTATE(mystr); 916 break; 917 case PENVSYS_EVENT_WARNUNDER: 918 mystr = "warning under"; 919 PENVSYS_SHOWSTATE(mystr); 920 break; 921 case PENVSYS_EVENT_NORMAL: 922 printf("%s: normal state on '%s'\n", 923 pes->pes_dvname, pes->pes_sensname); 924 break; 925 default: 926 printf("%s: unknown event\n", __func__); 927 } 928 break; 929 case PENVSYS_TYPE_DRIVE: 930 switch (event) { 931 case PENVSYS_EVENT_STATE_CHANGED: 932 printf("%s: state changed on '%s' to '%s'\n", 933 pes->pes_dvname, pes->pes_sensname, 934 pes->pes_statedesc); 935 break; 936 case PENVSYS_EVENT_NORMAL: 937 printf("%s: normal state on '%s' (%s)\n", 938 pes->pes_dvname, pes->pes_sensname, 939 pes->pes_statedesc); 940 break; 941 } 942 break; 943 default: 944 printf("%s: unknown power type\n", __func__); 945 break; 946 } 947 } 948 949 /* 950 * sysmon_pswitch_register: 951 * 952 * Register a power switch device. 953 */ 954 int 955 sysmon_pswitch_register(struct sysmon_pswitch *smpsw) 956 { 957 (void)RUN_ONCE(&once_power, power_preinit); 958 959 return 0; 960 } 961 962 /* 963 * sysmon_pswitch_unregister: 964 * 965 * Unregister a power switch device. 966 */ 967 void 968 sysmon_pswitch_unregister(struct sysmon_pswitch *smpsw) 969 { 970 /* nada */ 971 } 972 973 /* 974 * sysmon_pswitch_event: 975 * 976 * Register an event on a power switch device. 977 */ 978 void 979 sysmon_pswitch_event(struct sysmon_pswitch *smpsw, int event) 980 { 981 struct power_event_dictionary *ped = NULL; 982 983 KASSERT(smpsw != NULL); 984 985 /* 986 * For pnp specific events, we don't care if the power daemon 987 * is running or not 988 */ 989 if (smpsw->smpsw_type == PSWITCH_TYPE_LID) { 990 switch (event) { 991 case PSWITCH_EVENT_PRESSED: 992 pmf_event_inject(NULL, PMFE_CHASSIS_LID_CLOSE); 993 break; 994 case PSWITCH_EVENT_RELEASED: 995 pmf_event_inject(NULL, PMFE_CHASSIS_LID_OPEN); 996 break; 997 default: 998 break; 999 } 1000 } 1001 1002 if (sysmon_power_daemon != NULL) { 1003 /* 1004 * Create a new dictionary for the event. 1005 */ 1006 ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP); 1007 if (!ped) 1008 return; 1009 ped->dict = prop_dictionary_create(); 1010 1011 if (sysmon_power_daemon_task(ped, smpsw, event) == 0) 1012 return; 1013 /* We failed */ 1014 prop_object_release(ped->dict); 1015 kmem_free(ped, sizeof(*ped)); 1016 } 1017 1018 switch (smpsw->smpsw_type) { 1019 case PSWITCH_TYPE_POWER: 1020 if (event != PSWITCH_EVENT_PRESSED) { 1021 /* just ignore it */ 1022 return; 1023 } 1024 1025 /* 1026 * Attempt a somewhat graceful shutdown of the system, 1027 * as if the user has issued a reboot(2) call with 1028 * RB_POWERDOWN. 1029 */ 1030 printf("%s: power button pressed, shutting down!\n", 1031 smpsw->smpsw_name); 1032 cpu_reboot(RB_POWERDOWN, NULL); 1033 break; 1034 1035 case PSWITCH_TYPE_RESET: 1036 if (event != PSWITCH_EVENT_PRESSED) { 1037 /* just ignore it */ 1038 return; 1039 } 1040 1041 /* 1042 * Attempt a somewhat graceful reboot of the system, 1043 * as if the user had issued a reboot(2) call. 1044 */ 1045 printf("%s: reset button pressed, rebooting!\n", 1046 smpsw->smpsw_name); 1047 cpu_reboot(0, NULL); 1048 break; 1049 1050 case PSWITCH_TYPE_SLEEP: 1051 if (event != PSWITCH_EVENT_PRESSED) { 1052 /* just ignore it */ 1053 return; 1054 } 1055 1056 /* 1057 * Try to enter a "sleep" state. 1058 */ 1059 /* XXX */ 1060 printf("%s: sleep button pressed.\n", smpsw->smpsw_name); 1061 break; 1062 1063 case PSWITCH_TYPE_HOTKEY: 1064 /* 1065 * Eat up the event, there's nothing we can do 1066 */ 1067 break; 1068 1069 case PSWITCH_TYPE_LID: 1070 switch (event) { 1071 case PSWITCH_EVENT_PRESSED: 1072 /* 1073 * Try to enter a "standby" state. 1074 */ 1075 /* XXX */ 1076 printf("%s: lid closed.\n", smpsw->smpsw_name); 1077 break; 1078 1079 case PSWITCH_EVENT_RELEASED: 1080 /* 1081 * Come out of "standby" state. 1082 */ 1083 /* XXX */ 1084 printf("%s: lid opened.\n", smpsw->smpsw_name); 1085 break; 1086 1087 default: 1088 printf("%s: unknown lid switch event: %d\n", 1089 smpsw->smpsw_name, event); 1090 } 1091 break; 1092 1093 case PSWITCH_TYPE_ACADAPTER: 1094 switch (event) { 1095 case PSWITCH_EVENT_PRESSED: 1096 /* 1097 * Come out of power-save state. 1098 */ 1099 aprint_normal("%s: AC adapter online.\n", 1100 smpsw->smpsw_name); 1101 break; 1102 1103 case PSWITCH_EVENT_RELEASED: 1104 /* 1105 * Try to enter a power-save state. 1106 */ 1107 aprint_normal("%s: AC adapter offline.\n", 1108 smpsw->smpsw_name); 1109 break; 1110 } 1111 break; 1112 1113 } 1114 } 1115 1116 static 1117 int 1118 sysmon_power_modcmd(modcmd_t cmd, void *arg) 1119 { 1120 int ret; 1121 1122 switch (cmd) { 1123 case MODULE_CMD_INIT: 1124 ret = sysmon_power_init(); 1125 break; 1126 1127 case MODULE_CMD_FINI: 1128 ret = sysmon_power_fini(); 1129 break; 1130 1131 case MODULE_CMD_STAT: 1132 default: 1133 ret = ENOTTY; 1134 } 1135 1136 return ret; 1137 } 1138 1139