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