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