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