1 /* $NetBSD: sysmon_power.c,v 1.57 2015/12/14 01:08:47 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.57 2015/12/14 01:08:47 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 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 { 1, NULL, filt_sysmon_power_rdetach, filt_sysmon_power_read }; 562 563 static const struct filterops sysmon_power_write_filtops = 564 { 1, NULL, filt_sysmon_power_rdetach, filt_seltrue }; 565 566 /* 567 * sysmonkqfilter_power: 568 * 569 * Kqueue filter for the system monitor device. 570 */ 571 int 572 sysmonkqfilter_power(dev_t dev, struct knote *kn) 573 { 574 struct klist *klist; 575 576 switch (kn->kn_filter) { 577 case EVFILT_READ: 578 klist = &sysmon_power_event_queue_selinfo.sel_klist; 579 kn->kn_fop = &sysmon_power_read_filtops; 580 break; 581 582 case EVFILT_WRITE: 583 klist = &sysmon_power_event_queue_selinfo.sel_klist; 584 kn->kn_fop = &sysmon_power_write_filtops; 585 break; 586 587 default: 588 return EINVAL; 589 } 590 591 mutex_enter(&sysmon_power_event_queue_mtx); 592 SLIST_INSERT_HEAD(klist, kn, kn_selnext); 593 mutex_exit(&sysmon_power_event_queue_mtx); 594 595 return 0; 596 } 597 598 /* 599 * sysmonioctl_power: 600 * 601 * Perform a power management control request. 602 */ 603 int 604 sysmonioctl_power(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 605 { 606 int error = 0; 607 608 switch (cmd) { 609 case POWER_IOC_GET_TYPE: 610 case POWER_IOC_GET_TYPE_WITH_LOSSAGE: 611 { 612 struct power_type *power_type = (void *) data; 613 614 (void)strlcpy(power_type->power_type, 615 sysmon_power_type, 616 sizeof(power_type->power_type)); 617 break; 618 } 619 case POWER_EVENT_RECVDICT: 620 { 621 struct plistref *plist = (struct plistref *)data; 622 struct power_event_dictionary *ped; 623 624 /* 625 * Get the first dictionary enqueued and mark it 626 * as busy. 627 */ 628 mutex_enter(&sysmon_power_event_queue_mtx); 629 ped = SIMPLEQ_FIRST(&pev_dict_list); 630 if (!ped || !ped->dict) { 631 mutex_exit(&sysmon_power_event_queue_mtx); 632 error = ENOTSUP; 633 break; 634 } 635 636 if ((ped->flags & SYSMON_POWER_DICTIONARY_READY) == 0) { 637 mutex_exit(&sysmon_power_event_queue_mtx); 638 error = EINVAL; 639 break; 640 } 641 642 if (ped->flags & SYSMON_POWER_DICTIONARY_BUSY) { 643 mutex_exit(&sysmon_power_event_queue_mtx); 644 error = EBUSY; 645 break; 646 } 647 648 ped->flags |= SYSMON_POWER_DICTIONARY_BUSY; 649 mutex_exit(&sysmon_power_event_queue_mtx); 650 651 /* 652 * Send it now. 653 */ 654 error = prop_dictionary_copyout_ioctl(plist, 655 cmd, 656 ped->dict); 657 658 /* 659 * Remove the dictionary now that we don't need it. 660 */ 661 mutex_enter(&sysmon_power_event_queue_mtx); 662 ped->flags &= ~SYSMON_POWER_DICTIONARY_BUSY; 663 ped->flags &= ~SYSMON_POWER_DICTIONARY_READY; 664 SIMPLEQ_REMOVE_HEAD(&pev_dict_list, pev_dict_head); 665 mutex_exit(&sysmon_power_event_queue_mtx); 666 sysmon_power_destroy_dictionary(ped); 667 668 break; 669 } 670 default: 671 error = ENOTTY; 672 } 673 674 return error; 675 } 676 677 /* 678 * sysmon_power_make_dictionary: 679 * 680 * Adds the properties for an event in a dictionary. 681 */ 682 int 683 sysmon_power_make_dictionary(prop_dictionary_t dict, void *power_data, 684 int event, int type) 685 { 686 int i; 687 688 KASSERT(mutex_owned(&sysmon_power_event_queue_mtx)); 689 690 switch (type) { 691 /* 692 * create the dictionary for a power switch event. 693 */ 694 case POWER_EVENT_SWITCH_STATE_CHANGE: 695 { 696 const struct power_event_description *peevent = 697 pswitch_event_desc; 698 const struct power_event_description *petype = 699 pswitch_type_desc; 700 struct sysmon_pswitch *smpsw = 701 (struct sysmon_pswitch *)power_data; 702 const char *pwrtype = "pswitch"; 703 704 #define SETPROP(key, str) \ 705 do { \ 706 if ((str) != NULL && !prop_dictionary_set_cstring(dict, \ 707 (key), \ 708 (str))) { \ 709 printf("%s: failed to set %s\n", __func__, (str)); \ 710 return EINVAL; \ 711 } \ 712 } while (/* CONSTCOND */ 0) 713 714 715 SETPROP("driver-name", smpsw->smpsw_name); 716 717 for (i = 0; peevent[i].type != -1; i++) 718 if (peevent[i].type == event) 719 break; 720 721 SETPROP("powerd-event-name", peevent[i].desc); 722 723 for (i = 0; petype[i].type != -1; i++) 724 if (petype[i].type == smpsw->smpsw_type) 725 break; 726 727 SETPROP("powerd-script-name", petype[i].desc); 728 SETPROP("power-type", pwrtype); 729 break; 730 } 731 /* 732 * create a dictionary for power envsys event. 733 */ 734 case POWER_EVENT_ENVSYS_STATE_CHANGE: 735 { 736 const struct power_event_description *peevent = 737 penvsys_event_desc; 738 const struct power_event_description *petype = 739 penvsys_type_desc; 740 struct penvsys_state *pes = 741 (struct penvsys_state *)power_data; 742 const char *pwrtype = "envsys"; 743 744 SETPROP("driver-name", pes->pes_dvname); 745 SETPROP("sensor-name", pes->pes_sensname); 746 SETPROP("state-description", pes->pes_statedesc); 747 748 for (i = 0; peevent[i].type != -1; i++) 749 if (peevent[i].type == event) 750 break; 751 752 SETPROP("powerd-event-name", peevent[i].desc); 753 754 for (i = 0; petype[i].type != -1; i++) 755 if (petype[i].type == pes->pes_type) 756 break; 757 758 SETPROP("powerd-script-name", petype[i].desc); 759 SETPROP("power-type", pwrtype); 760 break; 761 } 762 default: 763 return ENOTSUP; 764 } 765 766 return 0; 767 } 768 769 /* 770 * sysmon_power_destroy_dictionary: 771 * 772 * Destroys a power_event_dictionary object and all its 773 * properties in the dictionary. 774 */ 775 static void 776 sysmon_power_destroy_dictionary(struct power_event_dictionary *ped) 777 { 778 prop_object_iterator_t iter; 779 prop_object_t obj; 780 781 KASSERT(ped != NULL); 782 KASSERT((ped->flags & SYSMON_POWER_DICTIONARY_BUSY) == 0); 783 784 iter = prop_dictionary_iterator(ped->dict); 785 if (iter == NULL) 786 return; 787 788 while ((obj = prop_object_iterator_next(iter)) != NULL) { 789 prop_dictionary_remove(ped->dict, 790 prop_dictionary_keysym_cstring_nocopy(obj)); 791 prop_object_iterator_reset(iter); 792 } 793 794 prop_object_iterator_release(iter); 795 prop_object_release(ped->dict); 796 797 kmem_free(ped, sizeof(*ped)); 798 } 799 800 /* 801 * sysmon_power_settype: 802 * 803 * Sets the back-end power management type. This information can 804 * be used by the power management daemon. 805 */ 806 void 807 sysmon_power_settype(const char *type) 808 { 809 810 /* 811 * Don't bother locking this; it's going to be set 812 * during autoconfiguration, and then only read from 813 * then on. 814 */ 815 (void)strlcpy(sysmon_power_type, type, sizeof(sysmon_power_type)); 816 } 817 818 #define PENVSYS_SHOWSTATE(str) \ 819 do { \ 820 printf("%s: %s limit on '%s'\n", \ 821 pes->pes_dvname, (str), pes->pes_sensname); \ 822 } while (/* CONSTCOND */ 0) 823 824 /* 825 * sysmon_penvsys_event: 826 * 827 * Puts an event onto the sysmon power queue and sends the 828 * appropriate event if the daemon is running, otherwise a 829 * message is shown. 830 */ 831 void 832 sysmon_penvsys_event(struct penvsys_state *pes, int event) 833 { 834 struct power_event_dictionary *ped; 835 const char *mystr = NULL; 836 837 KASSERT(pes != NULL); 838 839 rnd_add_uint32(&sysmon_rndsource, pes->pes_type); 840 841 if (sysmon_power_daemon != NULL) { 842 /* 843 * Create a dictionary for the new event. 844 */ 845 ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP); 846 if (!ped) 847 return; 848 ped->dict = prop_dictionary_create(); 849 850 if (sysmon_power_daemon_task(ped, pes, event) == 0) 851 return; 852 /* We failed */ 853 prop_object_release(ped->dict); 854 kmem_free(ped, sizeof(*ped)); 855 } 856 857 switch (pes->pes_type) { 858 case PENVSYS_TYPE_BATTERY: 859 switch (event) { 860 case PENVSYS_EVENT_LOW_POWER: 861 printf("sysmon: LOW POWER! SHUTTING DOWN.\n"); 862 cpu_reboot(RB_POWERDOWN, NULL); 863 break; 864 case PENVSYS_EVENT_STATE_CHANGED: 865 printf("%s: state changed on '%s' to '%s'\n", 866 pes->pes_dvname, pes->pes_sensname, 867 pes->pes_statedesc); 868 break; 869 case PENVSYS_EVENT_BATT_CRIT: 870 mystr = "critical capacity"; 871 PENVSYS_SHOWSTATE(mystr); 872 break; 873 case PENVSYS_EVENT_BATT_WARN: 874 mystr = "warning capacity"; 875 PENVSYS_SHOWSTATE(mystr); 876 break; 877 case PENVSYS_EVENT_BATT_HIGH: 878 mystr = "high capacity"; 879 PENVSYS_SHOWSTATE(mystr); 880 break; 881 case PENVSYS_EVENT_BATT_MAX: 882 mystr = "maximum capacity"; 883 PENVSYS_SHOWSTATE(mystr); 884 break; 885 case PENVSYS_EVENT_NORMAL: 886 printf("%s: normal capacity on '%s'\n", 887 pes->pes_dvname, pes->pes_sensname); 888 break; 889 } 890 break; 891 case PENVSYS_TYPE_FAN: 892 case PENVSYS_TYPE_INDICATOR: 893 case PENVSYS_TYPE_TEMP: 894 case PENVSYS_TYPE_POWER: 895 case PENVSYS_TYPE_RESISTANCE: 896 case PENVSYS_TYPE_VOLTAGE: 897 switch (event) { 898 case PENVSYS_EVENT_CRITICAL: 899 mystr = "critical"; 900 PENVSYS_SHOWSTATE(mystr); 901 break; 902 case PENVSYS_EVENT_CRITOVER: 903 mystr = "critical over"; 904 PENVSYS_SHOWSTATE(mystr); 905 break; 906 case PENVSYS_EVENT_CRITUNDER: 907 mystr = "critical under"; 908 PENVSYS_SHOWSTATE(mystr); 909 break; 910 case PENVSYS_EVENT_WARNOVER: 911 mystr = "warning over"; 912 PENVSYS_SHOWSTATE(mystr); 913 break; 914 case PENVSYS_EVENT_WARNUNDER: 915 mystr = "warning under"; 916 PENVSYS_SHOWSTATE(mystr); 917 break; 918 case PENVSYS_EVENT_NORMAL: 919 printf("%s: normal state on '%s'\n", 920 pes->pes_dvname, pes->pes_sensname); 921 break; 922 default: 923 printf("%s: unknown event\n", __func__); 924 } 925 break; 926 case PENVSYS_TYPE_DRIVE: 927 switch (event) { 928 case PENVSYS_EVENT_STATE_CHANGED: 929 printf("%s: state changed on '%s' to '%s'\n", 930 pes->pes_dvname, pes->pes_sensname, 931 pes->pes_statedesc); 932 break; 933 case PENVSYS_EVENT_NORMAL: 934 printf("%s: normal state on '%s' (%s)\n", 935 pes->pes_dvname, pes->pes_sensname, 936 pes->pes_statedesc); 937 break; 938 } 939 break; 940 default: 941 printf("%s: unknown power type\n", __func__); 942 break; 943 } 944 } 945 946 /* 947 * sysmon_pswitch_register: 948 * 949 * Register a power switch device. 950 */ 951 int 952 sysmon_pswitch_register(struct sysmon_pswitch *smpsw) 953 { 954 (void)RUN_ONCE(&once_power, power_preinit); 955 956 return 0; 957 } 958 959 /* 960 * sysmon_pswitch_unregister: 961 * 962 * Unregister a power switch device. 963 */ 964 void 965 sysmon_pswitch_unregister(struct sysmon_pswitch *smpsw) 966 { 967 /* nada */ 968 } 969 970 /* 971 * sysmon_pswitch_event: 972 * 973 * Register an event on a power switch device. 974 */ 975 void 976 sysmon_pswitch_event(struct sysmon_pswitch *smpsw, int event) 977 { 978 struct power_event_dictionary *ped = NULL; 979 980 KASSERT(smpsw != NULL); 981 982 /* 983 * For pnp specific events, we don't care if the power daemon 984 * is running or not 985 */ 986 if (smpsw->smpsw_type == PSWITCH_TYPE_LID) { 987 switch (event) { 988 case PSWITCH_EVENT_PRESSED: 989 pmf_event_inject(NULL, PMFE_CHASSIS_LID_CLOSE); 990 break; 991 case PSWITCH_EVENT_RELEASED: 992 pmf_event_inject(NULL, PMFE_CHASSIS_LID_OPEN); 993 break; 994 default: 995 break; 996 } 997 } 998 999 if (sysmon_power_daemon != NULL) { 1000 /* 1001 * Create a new dictionary for the event. 1002 */ 1003 ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP); 1004 if (!ped) 1005 return; 1006 ped->dict = prop_dictionary_create(); 1007 1008 if (sysmon_power_daemon_task(ped, smpsw, event) == 0) 1009 return; 1010 /* We failed */ 1011 prop_object_release(ped->dict); 1012 kmem_free(ped, sizeof(*ped)); 1013 } 1014 1015 switch (smpsw->smpsw_type) { 1016 case PSWITCH_TYPE_POWER: 1017 if (event != PSWITCH_EVENT_PRESSED) { 1018 /* just ignore it */ 1019 return; 1020 } 1021 1022 /* 1023 * Attempt a somewhat graceful shutdown of the system, 1024 * as if the user has issued a reboot(2) call with 1025 * RB_POWERDOWN. 1026 */ 1027 printf("%s: power button pressed, shutting down!\n", 1028 smpsw->smpsw_name); 1029 cpu_reboot(RB_POWERDOWN, NULL); 1030 break; 1031 1032 case PSWITCH_TYPE_RESET: 1033 if (event != PSWITCH_EVENT_PRESSED) { 1034 /* just ignore it */ 1035 return; 1036 } 1037 1038 /* 1039 * Attempt a somewhat graceful reboot of the system, 1040 * as if the user had issued a reboot(2) call. 1041 */ 1042 printf("%s: reset button pressed, rebooting!\n", 1043 smpsw->smpsw_name); 1044 cpu_reboot(0, NULL); 1045 break; 1046 1047 case PSWITCH_TYPE_SLEEP: 1048 if (event != PSWITCH_EVENT_PRESSED) { 1049 /* just ignore it */ 1050 return; 1051 } 1052 1053 /* 1054 * Try to enter a "sleep" state. 1055 */ 1056 /* XXX */ 1057 printf("%s: sleep button pressed.\n", smpsw->smpsw_name); 1058 break; 1059 1060 case PSWITCH_TYPE_HOTKEY: 1061 /* 1062 * Eat up the event, there's nothing we can do 1063 */ 1064 break; 1065 1066 case PSWITCH_TYPE_LID: 1067 switch (event) { 1068 case PSWITCH_EVENT_PRESSED: 1069 /* 1070 * Try to enter a "standby" state. 1071 */ 1072 /* XXX */ 1073 printf("%s: lid closed.\n", smpsw->smpsw_name); 1074 break; 1075 1076 case PSWITCH_EVENT_RELEASED: 1077 /* 1078 * Come out of "standby" state. 1079 */ 1080 /* XXX */ 1081 printf("%s: lid opened.\n", smpsw->smpsw_name); 1082 break; 1083 1084 default: 1085 printf("%s: unknown lid switch event: %d\n", 1086 smpsw->smpsw_name, event); 1087 } 1088 break; 1089 1090 case PSWITCH_TYPE_ACADAPTER: 1091 switch (event) { 1092 case PSWITCH_EVENT_PRESSED: 1093 /* 1094 * Come out of power-save state. 1095 */ 1096 aprint_normal("%s: AC adapter online.\n", 1097 smpsw->smpsw_name); 1098 break; 1099 1100 case PSWITCH_EVENT_RELEASED: 1101 /* 1102 * Try to enter a power-save state. 1103 */ 1104 aprint_normal("%s: AC adapter offline.\n", 1105 smpsw->smpsw_name); 1106 break; 1107 } 1108 break; 1109 1110 } 1111 } 1112 1113 static 1114 int 1115 sysmon_power_modcmd(modcmd_t cmd, void *arg) 1116 { 1117 int ret; 1118 1119 switch (cmd) { 1120 case MODULE_CMD_INIT: 1121 ret = sysmon_power_init(); 1122 break; 1123 1124 case MODULE_CMD_FINI: 1125 ret = sysmon_power_fini(); 1126 break; 1127 1128 case MODULE_CMD_STAT: 1129 default: 1130 ret = ENOTTY; 1131 } 1132 1133 return ret; 1134 } 1135 1136