1 /* $NetBSD: sysmon_envsys_events.c,v 1.62 2008/11/11 19:03:06 pgoyette Exp $ */ 2 3 /*- 4 * Copyright (c) 2007, 2008 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 * sysmon_envsys(9) events framework. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: sysmon_envsys_events.c,v 1.62 2008/11/11 19:03:06 pgoyette Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/types.h> 37 #include <sys/conf.h> 38 #include <sys/errno.h> 39 #include <sys/kernel.h> 40 #include <sys/systm.h> 41 #include <sys/proc.h> 42 #include <sys/mutex.h> 43 #include <sys/kmem.h> 44 #include <sys/callout.h> 45 46 /* #define ENVSYS_DEBUG */ 47 48 #include <dev/sysmon/sysmonvar.h> 49 #include <dev/sysmon/sysmon_envsysvar.h> 50 51 struct sme_sensor_event { 52 int state; 53 int event; 54 }; 55 56 static const struct sme_sensor_event sme_sensor_event[] = { 57 { ENVSYS_SVALID, PENVSYS_EVENT_NORMAL }, 58 { ENVSYS_SCRITOVER, PENVSYS_EVENT_CRITOVER }, 59 { ENVSYS_SCRITUNDER, PENVSYS_EVENT_CRITUNDER }, 60 { ENVSYS_SWARNOVER, PENVSYS_EVENT_WARNOVER }, 61 { ENVSYS_SWARNUNDER, PENVSYS_EVENT_WARNUNDER }, 62 { -1, -1 } 63 }; 64 65 static bool sysmon_low_power; 66 67 #define SME_EVTIMO (SME_EVENTS_DEFTIMEOUT * hz) 68 69 static bool sme_event_check_low_power(void); 70 static bool sme_battery_check(void); 71 static bool sme_battery_critical(envsys_data_t *); 72 static bool sme_acadapter_check(void); 73 74 /* 75 * sme_event_register: 76 * 77 * + Registers a new sysmon envsys event or updates any event 78 * already in the queue. 79 */ 80 int 81 sme_event_register(prop_dictionary_t sdict, envsys_data_t *edata, 82 struct sysmon_envsys *sme, const char *objkey, 83 int32_t critval, int crittype, int powertype) 84 { 85 sme_event_t *see = NULL, *osee = NULL; 86 prop_object_t obj; 87 bool critvalup = false; 88 int error = 0; 89 int real_crittype; 90 int32_t o_critval; 91 92 KASSERT(sdict != NULL || edata != NULL || sme != NULL); 93 /* 94 * Allocate a new sysmon_envsys event. 95 */ 96 see = kmem_zalloc(sizeof(*see), KM_SLEEP); 97 if (see == NULL) 98 return ENOMEM; 99 100 /* 101 * Map user-types to kernel types 102 */ 103 switch (crittype) { 104 case PENVSYS_EVENT_USER_CRITMAX: 105 case PENVSYS_EVENT_USER_CRITMIN: 106 case PENVSYS_EVENT_USER_WARNMAX: 107 case PENVSYS_EVENT_USER_WARNMIN: 108 real_crittype = PENVSYS_EVENT_USER_LIMITS; 109 break; 110 case PENVSYS_EVENT_BATT_USERCAP: 111 case PENVSYS_EVENT_BATT_USERWARN: 112 real_crittype = PENVSYS_EVENT_BATT_USER_LIMITS; 113 break; 114 default: 115 real_crittype = crittype; 116 } 117 118 /* 119 * check if the event is already on the list and return 120 * EEXIST if value provided hasn't been changed. 121 */ 122 mutex_enter(&sme->sme_mtx); 123 LIST_FOREACH(osee, &sme->sme_events_list, see_list) { 124 if (strcmp(edata->desc, osee->see_pes.pes_sensname) == 0) 125 if (real_crittype == osee->see_type) { 126 switch (crittype) { 127 case PENVSYS_EVENT_USER_CRITMAX: 128 case PENVSYS_EVENT_BATT_USERCAP: 129 o_critval = osee->see_critmax; 130 break; 131 case PENVSYS_EVENT_USER_WARNMAX: 132 case PENVSYS_EVENT_BATT_USERWARN: 133 o_critval = osee->see_warnmax; 134 break; 135 case PENVSYS_EVENT_USER_WARNMIN: 136 o_critval = osee->see_warnmin; 137 break; 138 case PENVSYS_EVENT_USER_CRITMIN: 139 default: 140 o_critval = osee->see_critmin; 141 break; 142 } 143 if (o_critval == critval) { 144 DPRINTF(("%s: dev=%s sensor=%s type=%d " 145 "(already exists)\n", __func__, 146 osee->see_pes.pes_dvname, 147 osee->see_pes.pes_sensname, 148 osee->see_type)); 149 error = EEXIST; 150 goto out; 151 } 152 critvalup = true; 153 break; 154 } 155 } 156 157 /* 158 * Critical condition operation requested by userland. 159 */ 160 if (objkey && critval && critvalup) { 161 obj = prop_dictionary_get(sdict, objkey); 162 if (obj && prop_object_type(obj) == PROP_TYPE_NUMBER) { 163 /* 164 * object is already in dictionary and value 165 * provided is not the same than we have 166 * currently, update the critical value. 167 */ 168 switch (crittype) { 169 case PENVSYS_EVENT_USER_CRITMAX: 170 case PENVSYS_EVENT_BATT_USERCAP: 171 osee->see_critmax = critval; 172 break; 173 case PENVSYS_EVENT_USER_WARNMAX: 174 case PENVSYS_EVENT_BATT_USERWARN: 175 osee->see_warnmax = critval; 176 break; 177 case PENVSYS_EVENT_USER_WARNMIN: 178 osee->see_warnmin = critval; 179 break; 180 case PENVSYS_EVENT_USER_CRITMIN: 181 default: 182 osee->see_critmin = critval; 183 break; 184 } 185 DPRINTF(("%s: (%s) event [sensor=%s type=%d] " 186 "(critval updated)\n", __func__, sme->sme_name, 187 edata->desc, osee->see_type)); 188 error = sme_sensor_upint32(sdict, objkey, critval); 189 goto out; 190 } 191 } 192 193 /* 194 * New limit defined for existing event 195 */ 196 if (osee != NULL) { 197 osee->see_edata = edata; 198 switch (crittype) { 199 case PENVSYS_EVENT_USER_CRITMAX: 200 case PENVSYS_EVENT_BATT_USERCAP: 201 osee->see_critmax = critval; 202 break; 203 case PENVSYS_EVENT_USER_WARNMAX: 204 case PENVSYS_EVENT_BATT_USERWARN: 205 osee->see_warnmax = critval; 206 break; 207 case PENVSYS_EVENT_USER_WARNMIN: 208 osee->see_warnmin = critval; 209 break; 210 case PENVSYS_EVENT_USER_CRITMIN: 211 default: 212 osee->see_critmin = critval; 213 break; 214 } 215 if (objkey && critval) { 216 error = sme_sensor_upint32(sdict, objkey, critval); 217 if (error) 218 goto out; 219 } 220 DPRINTF(("%s: (%s) new limit added to existing event, type %d " 221 "critmin=%" PRIu32 " warnmin=%" PRIu32 " warnmax=%" 222 PRIu32 " critmax=%d\n", __func__, osee->see_sme->sme_name, 223 osee->see_type, osee->see_critmin, osee->see_warnmin, 224 osee->see_warnmax, osee->see_critmax)); 225 goto out; 226 } 227 /* 228 * New event requested. 229 */ 230 see->see_edata = edata; 231 switch (crittype) { 232 case PENVSYS_EVENT_USER_CRITMAX: 233 case PENVSYS_EVENT_BATT_USERCAP: 234 see->see_critmax = critval; 235 break; 236 case PENVSYS_EVENT_USER_WARNMAX: 237 case PENVSYS_EVENT_BATT_USERWARN: 238 see->see_warnmax = critval; 239 break; 240 case PENVSYS_EVENT_USER_WARNMIN: 241 see->see_warnmin = critval; 242 break; 243 case PENVSYS_EVENT_USER_CRITMIN: 244 default: 245 see->see_critmin = critval; 246 break; 247 } 248 see->see_type = real_crittype; 249 see->see_sme = sme; 250 251 /* Initialize sensor type and previously-sent state */ 252 253 see->see_pes.pes_type = powertype; 254 switch (real_crittype) { 255 case PENVSYS_EVENT_HW_LIMITS: 256 case PENVSYS_EVENT_USER_LIMITS: 257 case PENVSYS_EVENT_BATT_USER_LIMITS: 258 see->see_evsent = ENVSYS_SVALID; 259 break; 260 case PENVSYS_EVENT_STATE_CHANGED: 261 if (edata->units == ENVSYS_BATTERY_CAPACITY) 262 see->see_evsent = ENVSYS_BATTERY_CAPACITY_NORMAL; 263 else if (edata->units == ENVSYS_DRIVE) 264 see->see_evsent = ENVSYS_DRIVE_EMPTY; 265 #ifdef DIAGNOSTIC 266 else 267 panic("%s: bad units for " 268 "PENVSYS_EVENT_STATE_CHANGED", __func__); 269 #endif 270 break; 271 case PENVSYS_EVENT_CRITICAL: 272 default: 273 see->see_evsent = 0; 274 break; 275 } 276 277 (void)strlcpy(see->see_pes.pes_dvname, sme->sme_name, 278 sizeof(see->see_pes.pes_dvname)); 279 (void)strlcpy(see->see_pes.pes_sensname, edata->desc, 280 sizeof(see->see_pes.pes_sensname)); 281 282 LIST_INSERT_HEAD(&sme->sme_events_list, see, see_list); 283 if (objkey && critval) { 284 error = sme_sensor_upint32(sdict, objkey, critval); 285 if (error) 286 goto out; 287 } 288 DPRINTF(("%s: (%s) event registered (sensor=%s snum=%d type=%d " 289 "critmin=%" PRIu32 " warnmin=%" PRIu32 " warnmax=%" PRIu32 290 " crixmax=%" PRIu32 ")\n", __func__, 291 see->see_sme->sme_name, see->see_pes.pes_sensname, 292 see->see_edata->sensor, see->see_type, see->see_critmin, 293 see->see_warnmin, see->see_warnmax, see->see_critmax)); 294 /* 295 * Initialize the events framework if it wasn't initialized before. 296 */ 297 if ((sme->sme_flags & SME_CALLOUT_INITIALIZED) == 0) 298 error = sme_events_init(sme); 299 out: 300 mutex_exit(&sme->sme_mtx); 301 if (error || critvalup) 302 kmem_free(see, sizeof(*see)); 303 return error; 304 } 305 306 /* 307 * sme_event_unregister_all: 308 * 309 * + Unregisters all events associated with a sysmon envsys device. 310 */ 311 void 312 sme_event_unregister_all(struct sysmon_envsys *sme) 313 { 314 sme_event_t *see; 315 int evcounter = 0; 316 317 KASSERT(sme != NULL); 318 319 mutex_enter(&sme->sme_mtx); 320 LIST_FOREACH(see, &sme->sme_events_list, see_list) { 321 while (see->see_flags & SEE_EVENT_WORKING) 322 cv_wait(&sme->sme_condvar, &sme->sme_mtx); 323 324 if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0) 325 evcounter++; 326 } 327 328 DPRINTF(("%s: total events %d (%s)\n", __func__, 329 evcounter, sme->sme_name)); 330 331 while ((see = LIST_FIRST(&sme->sme_events_list))) { 332 if (evcounter == 0) 333 break; 334 335 if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0) { 336 LIST_REMOVE(see, see_list); 337 DPRINTF(("%s: event %s %d removed (%s)\n", __func__, 338 see->see_pes.pes_sensname, see->see_type, 339 sme->sme_name)); 340 kmem_free(see, sizeof(*see)); 341 evcounter--; 342 } 343 } 344 345 if (LIST_EMPTY(&sme->sme_events_list)) 346 if (sme->sme_flags & SME_CALLOUT_INITIALIZED) 347 sme_events_destroy(sme); 348 mutex_exit(&sme->sme_mtx); 349 } 350 351 /* 352 * sme_event_unregister: 353 * 354 * + Unregisters an event from the specified sysmon envsys device. 355 */ 356 int 357 sme_event_unregister(struct sysmon_envsys *sme, const char *sensor, int type) 358 { 359 sme_event_t *see; 360 bool found = false; 361 362 KASSERT(sensor != NULL); 363 364 mutex_enter(&sme->sme_mtx); 365 LIST_FOREACH(see, &sme->sme_events_list, see_list) { 366 if (strcmp(see->see_pes.pes_sensname, sensor) == 0) { 367 if (see->see_type == type) { 368 found = true; 369 break; 370 } 371 } 372 } 373 374 if (!found) { 375 mutex_exit(&sme->sme_mtx); 376 return EINVAL; 377 } 378 379 /* 380 * Wait for the event to finish its work, remove from the list 381 * and release resouces. 382 */ 383 while (see->see_flags & SEE_EVENT_WORKING) 384 cv_wait(&sme->sme_condvar, &sme->sme_mtx); 385 386 DPRINTF(("%s: removed dev=%s sensor=%s type=%d\n", 387 __func__, see->see_pes.pes_dvname, sensor, type)); 388 LIST_REMOVE(see, see_list); 389 /* 390 * So the events list is empty, we'll do the following: 391 * 392 * - stop and destroy the callout. 393 * - destroy the workqueue. 394 */ 395 if (LIST_EMPTY(&sme->sme_events_list)) 396 sme_events_destroy(sme); 397 mutex_exit(&sme->sme_mtx); 398 399 kmem_free(see, sizeof(*see)); 400 return 0; 401 } 402 403 /* 404 * sme_event_drvadd: 405 * 406 * + Registers a new event for a device that had enabled any of 407 * the monitoring flags in the driver. 408 */ 409 void 410 sme_event_drvadd(void *arg) 411 { 412 sme_event_drv_t *sed_t = arg; 413 int error = 0; 414 415 KASSERT(sed_t != NULL); 416 417 #define SEE_REGEVENT(a, b, c) \ 418 do { \ 419 if (sed_t->sed_edata->flags & (a)) { \ 420 char str[ENVSYS_DESCLEN] = "monitoring-state-"; \ 421 \ 422 error = sme_event_register(sed_t->sed_sdict, \ 423 sed_t->sed_edata, \ 424 sed_t->sed_sme, \ 425 NULL, \ 426 0, \ 427 (b), \ 428 sed_t->sed_powertype); \ 429 if (error && error != EEXIST) \ 430 printf("%s: failed to add event! " \ 431 "error=%d sensor=%s event=%s\n", \ 432 __func__, error, \ 433 sed_t->sed_edata->desc, (c)); \ 434 else { \ 435 (void)strlcat(str, (c), sizeof(str)); \ 436 prop_dictionary_set_bool(sed_t->sed_sdict, \ 437 str, \ 438 true); \ 439 } \ 440 } \ 441 } while (/* CONSTCOND */ 0) 442 443 SEE_REGEVENT(ENVSYS_FMONCRITICAL, 444 PENVSYS_EVENT_CRITICAL, 445 "critical"); 446 447 SEE_REGEVENT(ENVSYS_FMONCRITUNDER | ENVSYS_FMONCRITOVER | 448 ENVSYS_FMONWARNUNDER | ENVSYS_FMONWARNOVER, 449 PENVSYS_EVENT_HW_LIMITS, 450 "hw-range-limits"); 451 452 SEE_REGEVENT(ENVSYS_FMONSTCHANGED, 453 PENVSYS_EVENT_STATE_CHANGED, 454 "state-changed"); 455 456 /* 457 * we are done, free memory now. 458 */ 459 kmem_free(sed_t, sizeof(*sed_t)); 460 } 461 462 /* 463 * sme_events_init: 464 * 465 * + Initialize the events framework for this device. 466 */ 467 int 468 sme_events_init(struct sysmon_envsys *sme) 469 { 470 int error = 0; 471 uint64_t timo; 472 473 KASSERT(sme != NULL); 474 KASSERT(mutex_owned(&sme->sme_mtx)); 475 476 if (sme->sme_events_timeout) 477 timo = sme->sme_events_timeout * hz; 478 else 479 timo = SME_EVTIMO; 480 481 error = workqueue_create(&sme->sme_wq, sme->sme_name, 482 sme_events_worker, sme, PRI_NONE, IPL_SOFTCLOCK, WQ_MPSAFE); 483 if (error) 484 return error; 485 486 mutex_init(&sme->sme_callout_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK); 487 callout_init(&sme->sme_callout, CALLOUT_MPSAFE); 488 callout_setfunc(&sme->sme_callout, sme_events_check, sme); 489 callout_schedule(&sme->sme_callout, timo); 490 sme->sme_flags |= SME_CALLOUT_INITIALIZED; 491 DPRINTF(("%s: events framework initialized for '%s'\n", 492 __func__, sme->sme_name)); 493 494 return error; 495 } 496 497 /* 498 * sme_events_destroy: 499 * 500 * + Destroys the event framework for this device: callout 501 * stopped, workqueue destroyed and callout mutex destroyed. 502 */ 503 void 504 sme_events_destroy(struct sysmon_envsys *sme) 505 { 506 KASSERT(mutex_owned(&sme->sme_mtx)); 507 508 callout_stop(&sme->sme_callout); 509 workqueue_destroy(sme->sme_wq); 510 mutex_destroy(&sme->sme_callout_mtx); 511 callout_destroy(&sme->sme_callout); 512 sme->sme_flags &= ~SME_CALLOUT_INITIALIZED; 513 DPRINTF(("%s: events framework destroyed for '%s'\n", 514 __func__, sme->sme_name)); 515 } 516 517 /* 518 * sme_events_check: 519 * 520 * + Passes the events to the workqueue thread and stops 521 * the callout if the 'low-power' condition is triggered. 522 */ 523 void 524 sme_events_check(void *arg) 525 { 526 struct sysmon_envsys *sme = arg; 527 sme_event_t *see; 528 uint64_t timo; 529 530 KASSERT(sme != NULL); 531 532 mutex_enter(&sme->sme_callout_mtx); 533 LIST_FOREACH(see, &sme->sme_events_list, see_list) { 534 workqueue_enqueue(sme->sme_wq, &see->see_wk, NULL); 535 see->see_edata->flags |= ENVSYS_FNEED_REFRESH; 536 } 537 if (sme->sme_events_timeout) 538 timo = sme->sme_events_timeout * hz; 539 else 540 timo = SME_EVTIMO; 541 if (!sysmon_low_power) 542 callout_schedule(&sme->sme_callout, timo); 543 mutex_exit(&sme->sme_callout_mtx); 544 } 545 546 /* 547 * sme_events_worker: 548 * 549 * + workqueue thread that checks if there's a critical condition 550 * and sends an event if it was triggered. 551 */ 552 void 553 sme_events_worker(struct work *wk, void *arg) 554 { 555 const struct sme_description_table *sdt = NULL; 556 const struct sme_sensor_event *sse = sme_sensor_event; 557 sme_event_t *see = (void *)wk; 558 struct sysmon_envsys *sme = see->see_sme; 559 envsys_data_t *edata = see->see_edata; 560 int i, state = 0; 561 562 KASSERT(wk == &see->see_wk); 563 KASSERT(sme != NULL || edata != NULL); 564 565 mutex_enter(&sme->sme_mtx); 566 if ((see->see_flags & SEE_EVENT_WORKING) == 0) 567 see->see_flags |= SEE_EVENT_WORKING; 568 /* 569 * sme_events_check marks the first event for the device to 570 * mak us refresh it here. Don't refresh if the driver uses 571 * its own method for refreshing. 572 */ 573 if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) { 574 if ((see->see_edata->flags & ENVSYS_FNEED_REFRESH) != 0) { 575 /* refresh sensor in device */ 576 (*sme->sme_refresh)(sme, edata); 577 see->see_edata->flags &= ~ENVSYS_FNEED_REFRESH; 578 } 579 } 580 581 DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d type=%d state=%d units=%d " 582 "value_cur=%d\n", __func__, sme->sme_name, edata->desc, 583 edata->sensor, see->see_type, edata->state, edata->units, 584 edata->value_cur)); 585 586 /* skip the event if current sensor is in invalid state */ 587 if (edata->state == ENVSYS_SINVALID) 588 goto out; 589 590 switch (see->see_type) { 591 /* 592 * For user range limits, calculate a new state first 593 * State based on user limits will override any hardware 594 * detected state. 595 */ 596 case PENVSYS_EVENT_USER_LIMITS: 597 case PENVSYS_EVENT_BATT_USER_LIMITS: 598 #define __EXCEEDED_LIMIT(lim, rel) ((lim) && edata->value_cur rel (lim)) 599 if __EXCEEDED_LIMIT(see->see_critmin, <) 600 edata->state = ENVSYS_SCRITUNDER; 601 else if __EXCEEDED_LIMIT(see->see_warnmin, <) 602 edata->state = ENVSYS_SWARNUNDER; 603 else if __EXCEEDED_LIMIT(see->see_warnmax, >) 604 edata->state = ENVSYS_SWARNOVER; 605 else if __EXCEEDED_LIMIT(see->see_critmax, >) 606 edata->state = ENVSYS_SCRITOVER; 607 /* FALLTHROUGH */ 608 #undef __EXCEED_LIMIT 609 /* 610 * For hardware and user range limits, send event if state has changed 611 */ 612 case PENVSYS_EVENT_HW_LIMITS: 613 if (edata->state == see->see_evsent) 614 break; 615 616 for (i = 0; sse[i].state != -1; i++) 617 if (sse[i].state == edata->state) 618 break; 619 620 if (sse[i].state == -1) 621 break; 622 623 if (edata->state == ENVSYS_SVALID) 624 sysmon_penvsys_event(&see->see_pes, 625 PENVSYS_EVENT_NORMAL); 626 else 627 sysmon_penvsys_event(&see->see_pes, sse[i].event); 628 629 see->see_evsent = edata->state; 630 631 break; 632 633 /* 634 * Send PENVSYS_EVENT_CRITICAL event if: 635 * State has gone from non-CRITICAL to CRITICAL, 636 * State remains CRITICAL and value has changed, or 637 * State has returned from CRITICAL to non-CRITICAL 638 */ 639 case PENVSYS_EVENT_CRITICAL: 640 if (edata->state == ENVSYS_SVALID && 641 see->see_evsent != 0) { 642 sysmon_penvsys_event(&see->see_pes, 643 PENVSYS_EVENT_NORMAL); 644 see->see_evsent = 0; 645 } else if (edata->state == ENVSYS_SCRITICAL && 646 see->see_evsent != edata->value_cur) { 647 sysmon_penvsys_event(&see->see_pes, 648 PENVSYS_EVENT_CRITICAL); 649 see->see_evsent = edata->value_cur; 650 } 651 break; 652 653 /* 654 * if value_cur is not normal (battery) or online (drive), 655 * send the event... 656 */ 657 case PENVSYS_EVENT_STATE_CHANGED: 658 /* 659 * the state has not been changed, just ignore the event. 660 */ 661 if (edata->value_cur == see->see_evsent) 662 break; 663 664 switch (edata->units) { 665 case ENVSYS_DRIVE: 666 sdt = sme_get_description_table(SME_DESC_DRIVE_STATES); 667 state = ENVSYS_DRIVE_ONLINE; 668 break; 669 case ENVSYS_BATTERY_CAPACITY: 670 sdt = sme_get_description_table( 671 SME_DESC_BATTERY_CAPACITY); 672 state = ENVSYS_BATTERY_CAPACITY_NORMAL; 673 break; 674 default: 675 panic("%s: invalid units for ENVSYS_FMONSTCHANGED", 676 __func__); 677 } 678 679 for (i = 0; sdt[i].type != -1; i++) 680 if (sdt[i].type == edata->value_cur) 681 break; 682 683 if (sdt[i].type == -1) 684 break; 685 686 /* 687 * copy current state description. 688 */ 689 (void)strlcpy(see->see_pes.pes_statedesc, sdt[i].desc, 690 sizeof(see->see_pes.pes_statedesc)); 691 692 /* 693 * state is ok again... send a normal event. 694 */ 695 if (see->see_evsent && edata->value_cur == state) { 696 sysmon_penvsys_event(&see->see_pes, 697 PENVSYS_EVENT_NORMAL); 698 see->see_evsent = false; 699 } 700 701 /* 702 * state has been changed... send event. 703 */ 704 if (see->see_evsent || edata->value_cur != state) { 705 /* 706 * save current drive state. 707 */ 708 see->see_evsent = edata->value_cur; 709 sysmon_penvsys_event(&see->see_pes, see->see_type); 710 } 711 712 /* 713 * There's no need to continue if it's a drive sensor. 714 */ 715 if (edata->units == ENVSYS_DRIVE) 716 break; 717 718 /* 719 * Check if the system is running in low power and send the 720 * event to powerd (if running) or shutdown the system 721 * otherwise. 722 */ 723 if (!sysmon_low_power && sme_event_check_low_power()) { 724 struct penvsys_state pes; 725 726 /* 727 * Stop the callout and send the 'low-power' event. 728 */ 729 sysmon_low_power = true; 730 callout_stop(&sme->sme_callout); 731 pes.pes_type = PENVSYS_TYPE_BATTERY; 732 sysmon_penvsys_event(&pes, PENVSYS_EVENT_LOW_POWER); 733 } 734 break; 735 default: 736 panic("%s: invalid event type %d", __func__, see->see_type); 737 } 738 739 out: 740 see->see_flags &= ~SEE_EVENT_WORKING; 741 cv_broadcast(&sme->sme_condvar); 742 mutex_exit(&sme->sme_mtx); 743 } 744 745 /* 746 * Returns true if the system is in low power state: an AC adapter 747 * is OFF and all batteries are in LOW/CRITICAL state. 748 */ 749 static bool 750 sme_event_check_low_power(void) 751 { 752 if (!sme_acadapter_check()) 753 return false; 754 755 return sme_battery_check(); 756 } 757 758 /* 759 * Called with the sysmon_envsys device mtx held through the 760 * workqueue thread. 761 */ 762 static bool 763 sme_acadapter_check(void) 764 { 765 struct sysmon_envsys *sme; 766 envsys_data_t *edata; 767 bool dev = false, sensor = false; 768 769 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 770 if (sme->sme_class == SME_CLASS_ACADAPTER) { 771 dev = true; 772 break; 773 } 774 } 775 776 /* 777 * No AC Adapter devices were found. 778 */ 779 if (!dev) 780 return false; 781 782 /* 783 * Check if there's an AC adapter device connected. 784 */ 785 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 786 if (edata->units == ENVSYS_INDICATOR) { 787 sensor = true; 788 /* refresh current sensor */ 789 (*sme->sme_refresh)(sme, edata); 790 if (edata->value_cur) 791 return false; 792 } 793 } 794 795 if (!sensor) 796 return false; 797 798 /* 799 * AC adapter found and not connected. 800 */ 801 return true; 802 } 803 804 /* 805 * Called with the sysmon_envsys device mtx held through the 806 * workqueue thread. 807 */ 808 static bool 809 sme_battery_check(void) 810 { 811 struct sysmon_envsys *sme; 812 envsys_data_t *edata; 813 int batteriesfound = 0; 814 bool present, batterycap, batterycharge; 815 816 /* 817 * Check for battery devices and its state. 818 */ 819 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 820 if (sme->sme_class != SME_CLASS_BATTERY) 821 continue; 822 823 present = true; 824 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 825 if (edata->units == ENVSYS_INDICATOR && 826 !edata->value_cur) { 827 present = false; 828 break; 829 } 830 } 831 if (!present) 832 continue; 833 /* 834 * We've found a battery device... 835 */ 836 batteriesfound++; 837 batterycap = batterycharge = false; 838 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 839 if (edata->units == ENVSYS_BATTERY_CAPACITY) { 840 batterycap = true; 841 if (!sme_battery_critical(edata)) 842 return false; 843 } else if (edata->units == ENVSYS_BATTERY_CHARGE) { 844 batterycharge = true; 845 if (edata->value_cur) 846 return false; 847 } 848 } 849 if (!batterycap || !batterycharge) 850 return false; 851 } 852 853 if (!batteriesfound) 854 return false; 855 856 /* 857 * All batteries in low/critical capacity and discharging. 858 */ 859 return true; 860 } 861 862 static bool 863 sme_battery_critical(envsys_data_t *edata) 864 { 865 if (edata->value_cur == ENVSYS_BATTERY_CAPACITY_CRITICAL || 866 edata->value_cur == ENVSYS_BATTERY_CAPACITY_LOW) 867 return true; 868 869 return false; 870 } 871