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