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