sysmon_envsys_events.c revision 1.54 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