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