1 /* $NetBSD: sysmon_envsys_events.c,v 1.54 2008/04/02 11:19:22 xtraeme 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.54 2008/04/02 11:19:22 xtraeme 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 #include <dev/sysmon/sysmonvar.h> 48 #include <dev/sysmon/sysmon_envsysvar.h> 49 50 struct sme_sensor_event { 51 int state; 52 int event; 53 }; 54 55 static const struct sme_sensor_event sme_sensor_event[] = { 56 { ENVSYS_SVALID, PENVSYS_EVENT_NORMAL }, 57 { ENVSYS_SCRITICAL, PENVSYS_EVENT_CRITICAL }, 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 90 KASSERT(sdict != NULL || edata != NULL || sme != NULL); 91 /* 92 * Allocate a new sysmon_envsys event. 93 */ 94 see = kmem_zalloc(sizeof(*see), KM_SLEEP); 95 if (see == NULL) 96 return ENOMEM; 97 98 /* 99 * check if the event is already on the list and return 100 * EEXIST if value provided hasn't been changed. 101 */ 102 mutex_enter(&sme->sme_mtx); 103 LIST_FOREACH(osee, &sme->sme_events_list, see_list) { 104 if (strcmp(edata->desc, osee->see_pes.pes_sensname) == 0) 105 if (crittype == osee->see_type) { 106 if (osee->see_critval == critval) { 107 DPRINTF(("%s: dev=%s sensor=%s type=%d " 108 "(already exists)\n", __func__, 109 osee->see_pes.pes_dvname, 110 osee->see_pes.pes_sensname, 111 osee->see_type)); 112 error = EEXIST; 113 goto out; 114 } 115 critvalup = true; 116 break; 117 } 118 } 119 120 /* 121 * Critical condition operation requested by userland. 122 */ 123 if (objkey && critval && critvalup) { 124 obj = prop_dictionary_get(sdict, objkey); 125 if (obj && prop_object_type(obj) == PROP_TYPE_NUMBER) { 126 /* 127 * object is already in dictionary and value 128 * provided is not the same than we have 129 * currently, update the critical value. 130 */ 131 osee->see_critval = critval; 132 DPRINTF(("%s: (%s) event [sensor=%s type=%d] " 133 "(critval updated)\n", __func__, sme->sme_name, 134 edata->desc, osee->see_type)); 135 error = sme_sensor_upint32(sdict, objkey, critval); 136 goto out; 137 } 138 } 139 140 /* 141 * New event requested. 142 */ 143 see->see_edata = edata; 144 see->see_critval = critval; 145 see->see_type = crittype; 146 see->see_sme = sme; 147 (void)strlcpy(see->see_pes.pes_dvname, sme->sme_name, 148 sizeof(see->see_pes.pes_dvname)); 149 see->see_pes.pes_type = powertype; 150 (void)strlcpy(see->see_pes.pes_sensname, edata->desc, 151 sizeof(see->see_pes.pes_sensname)); 152 153 LIST_INSERT_HEAD(&sme->sme_events_list, see, see_list); 154 if (objkey && critval) { 155 error = sme_sensor_upint32(sdict, objkey, critval); 156 if (error) 157 goto out; 158 } 159 DPRINTF(("%s: (%s) event registered (sensor=%s snum=%d type=%d " 160 "critval=%" PRIu32 ")\n", __func__, 161 see->see_sme->sme_name, see->see_pes.pes_sensname, 162 see->see_edata->sensor, see->see_type, see->see_critval)); 163 /* 164 * Initialize the events framework if it wasn't initialized before. 165 */ 166 if ((sme->sme_flags & SME_CALLOUT_INITIALIZED) == 0) 167 error = sme_events_init(sme); 168 out: 169 mutex_exit(&sme->sme_mtx); 170 if (error || critvalup) 171 kmem_free(see, sizeof(*see)); 172 return error; 173 } 174 175 /* 176 * sme_event_unregister_all: 177 * 178 * + Unregisters all events associated with a sysmon envsys device. 179 */ 180 void 181 sme_event_unregister_all(struct sysmon_envsys *sme) 182 { 183 sme_event_t *see; 184 int evcounter = 0; 185 186 KASSERT(sme != NULL); 187 188 mutex_enter(&sme->sme_mtx); 189 LIST_FOREACH(see, &sme->sme_events_list, see_list) { 190 while (see->see_flags & SME_EVENT_WORKING) 191 cv_wait(&sme->sme_condvar, &sme->sme_mtx); 192 193 if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0) 194 evcounter++; 195 } 196 197 DPRINTF(("%s: total events %d (%s)\n", __func__, 198 evcounter, sme->sme_name)); 199 200 while ((see = LIST_FIRST(&sme->sme_events_list))) { 201 if (evcounter == 0) 202 break; 203 204 if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0) { 205 LIST_REMOVE(see, see_list); 206 DPRINTF(("%s: event %s %d removed (%s)\n", __func__, 207 see->see_pes.pes_sensname, see->see_type, 208 sme->sme_name)); 209 kmem_free(see, sizeof(*see)); 210 evcounter--; 211 } 212 } 213 214 if (LIST_EMPTY(&sme->sme_events_list)) 215 if (sme->sme_flags & SME_CALLOUT_INITIALIZED) 216 sme_events_destroy(sme); 217 mutex_exit(&sme->sme_mtx); 218 } 219 220 /* 221 * sme_event_unregister: 222 * 223 * + Unregisters an event from the specified sysmon envsys device. 224 */ 225 int 226 sme_event_unregister(struct sysmon_envsys *sme, const char *sensor, int type) 227 { 228 sme_event_t *see; 229 bool found = false; 230 231 KASSERT(sensor != NULL); 232 233 mutex_enter(&sme->sme_mtx); 234 LIST_FOREACH(see, &sme->sme_events_list, see_list) { 235 if (strcmp(see->see_pes.pes_sensname, sensor) == 0) { 236 if (see->see_type == type) { 237 found = true; 238 break; 239 } 240 } 241 } 242 243 if (!found) { 244 mutex_exit(&sme->sme_mtx); 245 return EINVAL; 246 } 247 248 /* 249 * Wait for the event to finish its work, remove from the list 250 * and release resouces. 251 */ 252 while (see->see_flags & SME_EVENT_WORKING) 253 cv_wait(&sme->sme_condvar, &sme->sme_mtx); 254 255 DPRINTF(("%s: removed dev=%s sensor=%s type=%d\n", 256 __func__, see->see_pes.pes_dvname, sensor, type)); 257 LIST_REMOVE(see, see_list); 258 /* 259 * So the events list is empty, we'll do the following: 260 * 261 * - stop and destroy the callout. 262 * - destroy the workqueue. 263 */ 264 if (LIST_EMPTY(&sme->sme_events_list)) 265 sme_events_destroy(sme); 266 mutex_exit(&sme->sme_mtx); 267 268 kmem_free(see, sizeof(*see)); 269 return 0; 270 } 271 272 /* 273 * sme_event_drvadd: 274 * 275 * + Registers a new event for a device that had enabled any of 276 * the monitoring flags in the driver. 277 */ 278 void 279 sme_event_drvadd(void *arg) 280 { 281 sme_event_drv_t *sed_t = arg; 282 int error = 0; 283 284 KASSERT(sed_t != NULL); 285 286 #define SEE_REGEVENT(a, b, c) \ 287 do { \ 288 if (sed_t->sed_edata->flags & (a)) { \ 289 char str[ENVSYS_DESCLEN] = "monitoring-state-"; \ 290 \ 291 error = sme_event_register(sed_t->sed_sdict, \ 292 sed_t->sed_edata, \ 293 sed_t->sed_sme, \ 294 NULL, \ 295 0, \ 296 (b), \ 297 sed_t->sed_powertype); \ 298 if (error && error != EEXIST) \ 299 printf("%s: failed to add event! " \ 300 "error=%d sensor=%s event=%s\n", \ 301 __func__, error, \ 302 sed_t->sed_edata->desc, (c)); \ 303 else { \ 304 (void)strlcat(str, (c), sizeof(str)); \ 305 prop_dictionary_set_bool(sed_t->sed_sdict, \ 306 str, \ 307 true); \ 308 } \ 309 } \ 310 } while (/* CONSTCOND */ 0) 311 312 SEE_REGEVENT(ENVSYS_FMONCRITICAL, 313 PENVSYS_EVENT_CRITICAL, 314 "critical"); 315 316 SEE_REGEVENT(ENVSYS_FMONCRITUNDER, 317 PENVSYS_EVENT_CRITUNDER, 318 "critunder"); 319 320 SEE_REGEVENT(ENVSYS_FMONCRITOVER, 321 PENVSYS_EVENT_CRITOVER, 322 "critover"); 323 324 SEE_REGEVENT(ENVSYS_FMONWARNUNDER, 325 PENVSYS_EVENT_WARNUNDER, 326 "warnunder"); 327 328 SEE_REGEVENT(ENVSYS_FMONWARNOVER, 329 PENVSYS_EVENT_WARNOVER, 330 "warnover"); 331 332 SEE_REGEVENT(ENVSYS_FMONSTCHANGED, 333 PENVSYS_EVENT_STATE_CHANGED, 334 "state-changed"); 335 336 /* 337 * we are done, free memory now. 338 */ 339 kmem_free(sed_t, sizeof(*sed_t)); 340 } 341 342 /* 343 * sme_events_init: 344 * 345 * + Initialize the events framework for this device. 346 */ 347 int 348 sme_events_init(struct sysmon_envsys *sme) 349 { 350 int error = 0; 351 uint64_t timo; 352 353 KASSERT(sme != NULL); 354 KASSERT(mutex_owned(&sme->sme_mtx)); 355 356 if (sme->sme_events_timeout) 357 timo = sme->sme_events_timeout * hz; 358 else 359 timo = SME_EVTIMO; 360 361 error = workqueue_create(&sme->sme_wq, sme->sme_name, 362 sme_events_worker, sme, PRI_NONE, IPL_SOFTCLOCK, WQ_MPSAFE); 363 if (error) 364 return error; 365 366 mutex_init(&sme->sme_callout_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK); 367 callout_init(&sme->sme_callout, CALLOUT_MPSAFE); 368 callout_setfunc(&sme->sme_callout, sme_events_check, sme); 369 callout_schedule(&sme->sme_callout, timo); 370 sme->sme_flags |= SME_CALLOUT_INITIALIZED; 371 DPRINTF(("%s: events framework initialized for '%s'\n", 372 __func__, sme->sme_name)); 373 374 return error; 375 } 376 377 /* 378 * sme_events_destroy: 379 * 380 * + Destroys the event framework for this device: callout 381 * stopped, workqueue destroyed and callout mutex destroyed. 382 */ 383 void 384 sme_events_destroy(struct sysmon_envsys *sme) 385 { 386 KASSERT(mutex_owned(&sme->sme_mtx)); 387 388 callout_stop(&sme->sme_callout); 389 workqueue_destroy(sme->sme_wq); 390 mutex_destroy(&sme->sme_callout_mtx); 391 callout_destroy(&sme->sme_callout); 392 sme->sme_flags &= ~SME_CALLOUT_INITIALIZED; 393 DPRINTF(("%s: events framework destroyed for '%s'\n", 394 __func__, sme->sme_name)); 395 } 396 397 /* 398 * sme_events_check: 399 * 400 * + Passes the events to the workqueue thread and stops 401 * the callout if the 'low-power' condition is triggered. 402 */ 403 void 404 sme_events_check(void *arg) 405 { 406 struct sysmon_envsys *sme = arg; 407 sme_event_t *see; 408 uint64_t timo; 409 410 KASSERT(sme != NULL); 411 412 mutex_enter(&sme->sme_callout_mtx); 413 LIST_FOREACH(see, &sme->sme_events_list, see_list) { 414 workqueue_enqueue(sme->sme_wq, &see->see_wk, NULL); 415 see->see_flags &= ~SME_EVENT_REFRESHED; 416 } 417 if (sme->sme_events_timeout) 418 timo = sme->sme_events_timeout * hz; 419 else 420 timo = SME_EVTIMO; 421 if (!sysmon_low_power) 422 callout_schedule(&sme->sme_callout, timo); 423 mutex_exit(&sme->sme_callout_mtx); 424 } 425 426 /* 427 * sme_events_worker: 428 * 429 * + workqueue thread that checks if there's a critical condition 430 * and sends an event if it was triggered. 431 */ 432 void 433 sme_events_worker(struct work *wk, void *arg) 434 { 435 const struct sme_description_table *sdt = NULL; 436 const struct sme_sensor_event *sse = sme_sensor_event; 437 sme_event_t *see = (void *)wk; 438 struct sysmon_envsys *sme = see->see_sme; 439 envsys_data_t *edata = see->see_edata; 440 int i, state = 0; 441 442 KASSERT(wk == &see->see_wk); 443 KASSERT(sme != NULL || edata != NULL); 444 445 mutex_enter(&sme->sme_mtx); 446 if ((see->see_flags & SME_EVENT_WORKING) == 0) 447 see->see_flags |= SME_EVENT_WORKING; 448 /* 449 * refresh the sensor that was marked with a critical event 450 * only if it wasn't refreshed before or if the driver doesn't 451 * use its own method for refreshing. 452 */ 453 if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) { 454 if ((see->see_flags & SME_EVENT_REFRESHED) == 0) { 455 /* refresh sensor in device */ 456 (*sme->sme_refresh)(sme, edata); 457 see->see_flags |= SME_EVENT_REFRESHED; 458 } 459 } 460 461 DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d state=%d units=%d " 462 "value_cur=%d\n", __func__, sme->sme_name, edata->desc, 463 edata->sensor, edata->state, edata->units, edata->value_cur)); 464 465 /* skip the event if current sensor is in invalid state */ 466 if (edata->state == ENVSYS_SINVALID) 467 goto out; 468 469 #define SME_SEND_NORMALEVENT() \ 470 do { \ 471 see->see_evsent = false; \ 472 sysmon_penvsys_event(&see->see_pes, PENVSYS_EVENT_NORMAL); \ 473 } while (/* CONSTCOND */ 0) 474 475 #define SME_SEND_EVENT(type) \ 476 do { \ 477 see->see_evsent = true; \ 478 sysmon_penvsys_event(&see->see_pes, (type)); \ 479 } while (/* CONSTCOND */ 0) 480 481 482 switch (see->see_type) { 483 /* 484 * if state is the same than the one that matches sse[i].state, 485 * send the event... 486 */ 487 case PENVSYS_EVENT_CRITICAL: 488 case PENVSYS_EVENT_CRITUNDER: 489 case PENVSYS_EVENT_CRITOVER: 490 case PENVSYS_EVENT_WARNUNDER: 491 case PENVSYS_EVENT_WARNOVER: 492 for (i = 0; sse[i].state != -1; i++) 493 if (sse[i].event == see->see_type) 494 break; 495 496 if (sse[i].state == -1) 497 break; 498 499 if (see->see_evsent && edata->state == ENVSYS_SVALID) 500 SME_SEND_NORMALEVENT(); 501 502 if (!see->see_evsent && edata->state == sse[i].state) 503 SME_SEND_EVENT(see->see_type); 504 505 break; 506 /* 507 * if value_cur is lower than the limit, send the event... 508 */ 509 case PENVSYS_EVENT_BATT_USERCAP: 510 case PENVSYS_EVENT_USER_CRITMIN: 511 if (see->see_evsent && edata->value_cur > see->see_critval) 512 SME_SEND_NORMALEVENT(); 513 514 if (!see->see_evsent && edata->value_cur < see->see_critval) 515 SME_SEND_EVENT(see->see_type); 516 517 break; 518 /* 519 * if value_cur is higher than the limit, send the event... 520 */ 521 case PENVSYS_EVENT_USER_CRITMAX: 522 if (see->see_evsent && edata->value_cur < see->see_critval) 523 SME_SEND_NORMALEVENT(); 524 525 if (!see->see_evsent && edata->value_cur > see->see_critval) 526 SME_SEND_EVENT(see->see_type); 527 528 break; 529 /* 530 * if value_cur is not normal (battery) or online (drive), 531 * send the event... 532 */ 533 case PENVSYS_EVENT_STATE_CHANGED: 534 /* 535 * the state has not been changed, just ignore the event. 536 */ 537 if (edata->value_cur == see->see_evsent) 538 break; 539 540 switch (edata->units) { 541 case ENVSYS_DRIVE: 542 sdt = sme_get_description_table(SME_DESC_DRIVE_STATES); 543 state = ENVSYS_DRIVE_ONLINE; 544 break; 545 case ENVSYS_BATTERY_CAPACITY: 546 sdt = sme_get_description_table( 547 SME_DESC_BATTERY_CAPACITY); 548 state = ENVSYS_BATTERY_CAPACITY_NORMAL; 549 break; 550 default: 551 panic("%s: invalid units for ENVSYS_FMONSTCHANGED", 552 __func__); 553 } 554 555 for (i = 0; sdt[i].type != -1; i++) 556 if (sdt[i].type == edata->value_cur) 557 break; 558 559 if (sdt[i].type == -1) 560 break; 561 562 /* 563 * copy current state description. 564 */ 565 (void)strlcpy(see->see_pes.pes_statedesc, sdt[i].desc, 566 sizeof(see->see_pes.pes_statedesc)); 567 568 /* 569 * state is ok again... send a normal event. 570 */ 571 if (see->see_evsent && edata->value_cur == state) 572 SME_SEND_NORMALEVENT(); 573 574 /* 575 * state has been changed... send event. 576 */ 577 if (see->see_evsent || edata->value_cur != state) { 578 /* 579 * save current drive state. 580 */ 581 see->see_evsent = edata->value_cur; 582 sysmon_penvsys_event(&see->see_pes, see->see_type); 583 } 584 585 /* 586 * There's no need to continue if it's a drive sensor. 587 */ 588 if (edata->units == ENVSYS_DRIVE) 589 break; 590 591 /* 592 * Check if the system is running in low power and send the 593 * event to powerd (if running) or shutdown the system 594 * otherwise. 595 */ 596 if (!sysmon_low_power && sme_event_check_low_power()) { 597 struct penvsys_state pes; 598 599 /* 600 * Stop the callout and send the 'low-power' event. 601 */ 602 sysmon_low_power = true; 603 callout_stop(&sme->sme_callout); 604 pes.pes_type = PENVSYS_TYPE_BATTERY; 605 sysmon_penvsys_event(&pes, PENVSYS_EVENT_LOW_POWER); 606 } 607 break; 608 } 609 610 out: 611 see->see_flags &= ~SME_EVENT_WORKING; 612 cv_broadcast(&sme->sme_condvar); 613 mutex_exit(&sme->sme_mtx); 614 } 615 616 /* 617 * Returns true if the system is in low power state: an AC adapter 618 * is OFF and all batteries are in LOW/CRITICAL state. 619 */ 620 static bool 621 sme_event_check_low_power(void) 622 { 623 if (!sme_acadapter_check()) 624 return false; 625 626 return sme_battery_check(); 627 } 628 629 /* 630 * Called with the sysmon_envsys device mtx held through the 631 * workqueue thread. 632 */ 633 static bool 634 sme_acadapter_check(void) 635 { 636 struct sysmon_envsys *sme; 637 envsys_data_t *edata; 638 bool dev = false, sensor = false; 639 640 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 641 if (sme->sme_class == SME_CLASS_ACADAPTER) { 642 dev = true; 643 break; 644 } 645 } 646 647 /* 648 * No AC Adapter devices were found. 649 */ 650 if (!dev) 651 return false; 652 653 /* 654 * Check if there's an AC adapter device connected. 655 */ 656 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 657 if (edata->units == ENVSYS_INDICATOR) { 658 sensor = true; 659 /* refresh current sensor */ 660 (*sme->sme_refresh)(sme, edata); 661 if (edata->value_cur) 662 return false; 663 } 664 } 665 666 if (!sensor) 667 return false; 668 669 /* 670 * AC adapter found and not connected. 671 */ 672 return true; 673 } 674 675 /* 676 * Called with the sysmon_envsys device mtx held through the 677 * workqueue thread. 678 */ 679 static bool 680 sme_battery_check(void) 681 { 682 struct sysmon_envsys *sme; 683 envsys_data_t *edata; 684 bool battery, batterycap, batterycharge; 685 686 battery = batterycap = batterycharge = false; 687 688 /* 689 * Check for battery devices and its state. 690 */ 691 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) { 692 if (sme->sme_class != SME_CLASS_BATTERY) 693 continue; 694 695 /* 696 * We've found a battery device... 697 */ 698 battery = true; 699 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) { 700 if (edata->units == ENVSYS_BATTERY_CAPACITY) { 701 batterycap = true; 702 if (!sme_battery_critical(edata)) 703 return false; 704 } else if (edata->units == ENVSYS_BATTERY_CHARGE) { 705 batterycharge = true; 706 if (edata->value_cur) 707 return false; 708 } 709 } 710 } 711 712 if (!battery || !batterycap || !batterycharge) 713 return false; 714 715 /* 716 * All batteries in low/critical capacity and discharging. 717 */ 718 return true; 719 } 720 721 static bool 722 sme_battery_critical(envsys_data_t *edata) 723 { 724 if (edata->value_cur == ENVSYS_BATTERY_CAPACITY_CRITICAL || 725 edata->value_cur == ENVSYS_BATTERY_CAPACITY_LOW) 726 return true; 727 728 return false; 729 } 730