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