xref: /netbsd-src/sys/dev/sysmon/sysmon_envsys_events.c (revision 4b71a66d0f279143147d63ebfcfd8a59499a3684)
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