1 /* $NetBSD: sysmon_envsys_events.c,v 1.73 2010/01/01 15:41:25 pgoyette Exp $ */
4 * Copyright (c) 2007, 2008 Juan Romero Pardines.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
29 * sysmon_envsys(9) events framework.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: sysmon_envsys_events.c,v 1.73 2010/01/01 15:41:25 pgoyette Exp $");
35 #include <sys/param.h>
36 #include <sys/types.h>
38 #include <sys/errno.h>
39 #include <sys/kernel.h>
40 #include <sys/systm.h>
42 #include <sys/mutex.h>
44 #include <sys/callout.h>
46 /* #define ENVSYS_DEBUG */
48 #include <dev/sysmon/sysmonvar.h>
49 #include <dev/sysmon/sysmon_envsysvar.h>
51 struct sme_sensor_event
{
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 { ENVSYS_BATTERY_CAPACITY_NORMAL
, PENVSYS_EVENT_NORMAL
},
63 { ENVSYS_BATTERY_CAPACITY_WARNING
, PENVSYS_EVENT_BATT_WARN
},
64 { ENVSYS_BATTERY_CAPACITY_CRITICAL
, PENVSYS_EVENT_BATT_CRIT
},
68 static bool sysmon_low_power
;
70 #define SME_EVTIMO (SME_EVENTS_DEFTIMEOUT * hz)
72 static bool sme_event_check_low_power(void);
73 static bool sme_battery_check(void);
74 static bool sme_battery_critical(envsys_data_t
*);
75 static bool sme_acadapter_check(void);
80 * + Registers a new sysmon envsys event or updates any event
81 * already in the queue.
84 sme_event_register(prop_dictionary_t sdict
, envsys_data_t
*edata
,
85 struct sysmon_envsys
*sme
, sysmon_envsys_lim_t
*lims
,
86 int crittype
, int powertype
)
88 sme_event_t
*see
= NULL
, *osee
= NULL
;
93 KASSERT(sdict
!= NULL
|| edata
!= NULL
|| sme
!= NULL
);
96 * check if the event is already on the list and return
97 * EEXIST if value provided hasn't been changed.
99 mutex_enter(&sme
->sme_mtx
);
100 LIST_FOREACH(osee
, &sme
->sme_events_list
, see_list
) {
101 if (strcmp(edata
->desc
, osee
->see_pes
.pes_sensname
) != 0)
103 if (crittype
!= osee
->see_type
)
106 DPRINTF(("%s: dev %s sensor %s lim_flags 0x%04x event exists\n",
107 __func__
, sme
->sme_name
, edata
->desc
, lims
->sel_flags
));
110 if (lims
->sel_flags
& PROP_CRITMAX
) {
111 if (lims
->sel_critmax
== see
->see_lims
.sel_critmax
) {
112 DPRINTF(("%s: type=%d (critmax exists)\n",
113 __func__
, crittype
));
115 lims
->sel_flags
&= ~PROP_CRITMAX
;
118 if (lims
->sel_flags
& PROP_WARNMAX
) {
119 if (lims
->sel_warnmax
== see
->see_lims
.sel_warnmax
) {
120 DPRINTF(("%s: type=%d (warnmax exists)\n",
121 __func__
, crittype
));
123 lims
->sel_flags
&= ~PROP_WARNMAX
;
126 if (lims
->sel_flags
& (PROP_WARNMIN
| PROP_BATTWARN
)) {
127 if (lims
->sel_warnmin
== see
->see_lims
.sel_warnmin
) {
128 DPRINTF(("%s: type=%d (warnmin exists)\n",
129 __func__
, crittype
));
131 lims
->sel_flags
&= ~(PROP_WARNMIN
| PROP_BATTWARN
);
134 if (lims
->sel_flags
& (PROP_CRITMIN
| PROP_BATTCAP
)) {
135 if (lims
->sel_critmin
== see
->see_lims
.sel_critmin
) {
136 DPRINTF(("%s: type=%d (critmin exists)\n",
137 __func__
, crittype
));
139 lims
->sel_flags
&= ~(PROP_CRITMIN
| PROP_BATTCAP
);
146 * New event requested - allocate a sysmon_envsys event.
148 see
= kmem_zalloc(sizeof(*see
), KM_SLEEP
);
152 DPRINTF(("%s: dev %s sensor %s lim_flags 0x%04x new event\n",
153 __func__
, sme
->sme_name
, edata
->desc
, lims
->sel_flags
));
155 see
->see_type
= crittype
;
157 see
->see_edata
= edata
;
159 /* Initialize sensor type and previously-sent state */
161 see
->see_pes
.pes_type
= powertype
;
164 case PENVSYS_EVENT_LIMITS
:
165 see
->see_evsent
= ENVSYS_SVALID
;
167 case PENVSYS_EVENT_CAPACITY
:
168 see
->see_evsent
= ENVSYS_BATTERY_CAPACITY_NORMAL
;
170 case PENVSYS_EVENT_STATE_CHANGED
:
171 if (edata
->units
== ENVSYS_BATTERY_CAPACITY
)
172 see
->see_evsent
= ENVSYS_BATTERY_CAPACITY_NORMAL
;
173 else if (edata
->units
== ENVSYS_DRIVE
)
174 see
->see_evsent
= ENVSYS_DRIVE_EMPTY
;
176 panic("%s: bad units for "
177 "PENVSYS_EVENT_STATE_CHANGED", __func__
);
179 case PENVSYS_EVENT_CRITICAL
:
185 (void)strlcpy(see
->see_pes
.pes_dvname
, sme
->sme_name
,
186 sizeof(see
->see_pes
.pes_dvname
));
187 (void)strlcpy(see
->see_pes
.pes_sensname
, edata
->desc
,
188 sizeof(see
->see_pes
.pes_sensname
));
192 * Limit operation requested.
194 if (lims
->sel_flags
& PROP_CRITMAX
) {
195 objkey
= "critical-max";
196 obj
= prop_dictionary_get(sdict
, objkey
);
197 if (obj
&& prop_object_type(obj
) != PROP_TYPE_NUMBER
) {
198 DPRINTF(("%s: (%s) %s object not TYPE_NUMBER\n",
199 __func__
, sme
->sme_name
, objkey
));
202 see
->see_lims
.sel_critmax
= lims
->sel_critmax
;
203 error
= sme_sensor_upint32(sdict
, objkey
,
205 DPRINTF(("%s: (%s) event [sensor=%s type=%d] "
206 "(%s updated)\n", __func__
, sme
->sme_name
,
207 edata
->desc
, crittype
, objkey
));
209 if (error
&& error
!= EEXIST
)
211 see
->see_edata
->upropset
|= PROP_CRITMAX
;
212 see
->see_lims
.sel_flags
|= PROP_CRITMAX
;
215 if (lims
->sel_flags
& PROP_WARNMAX
) {
216 objkey
= "warning-max";
217 obj
= prop_dictionary_get(sdict
, objkey
);
218 if (obj
&& prop_object_type(obj
) != PROP_TYPE_NUMBER
) {
219 DPRINTF(("%s: (%s) %s object not TYPE_NUMBER\n",
220 __func__
, sme
->sme_name
, objkey
));
223 see
->see_lims
.sel_warnmax
= lims
->sel_warnmax
;
224 error
= sme_sensor_upint32(sdict
, objkey
,
226 DPRINTF(("%s: (%s) event [sensor=%s type=%d] "
227 "(%s updated)\n", __func__
, sme
->sme_name
,
228 edata
->desc
, crittype
, objkey
));
230 if (error
&& error
!= EEXIST
)
232 see
->see_edata
->upropset
|= PROP_WARNMAX
;
233 see
->see_lims
.sel_flags
|= PROP_WARNMAX
;
236 if (lims
->sel_flags
& PROP_WARNMIN
) {
237 objkey
= "warning-min";
238 obj
= prop_dictionary_get(sdict
, objkey
);
239 if (obj
&& prop_object_type(obj
) != PROP_TYPE_NUMBER
) {
240 DPRINTF(("%s: (%s) %s object not TYPE_NUMBER\n",
241 __func__
, sme
->sme_name
, objkey
));
244 see
->see_lims
.sel_warnmin
= lims
->sel_warnmin
;
245 error
= sme_sensor_upint32(sdict
, objkey
,
247 DPRINTF(("%s: (%s) event [sensor=%s type=%d] "
248 "(%s updated)\n", __func__
, sme
->sme_name
,
249 edata
->desc
, crittype
, objkey
));
251 if (error
&& error
!= EEXIST
)
253 see
->see_edata
->upropset
|= PROP_WARNMIN
;
254 see
->see_lims
.sel_flags
|= PROP_WARNMIN
;
257 if (lims
->sel_flags
& PROP_CRITMIN
) {
258 objkey
= "critical-min";
259 obj
= prop_dictionary_get(sdict
, objkey
);
260 if (obj
&& prop_object_type(obj
) != PROP_TYPE_NUMBER
) {
261 DPRINTF(("%s: (%s) %s object not TYPE_NUMBER\n",
262 __func__
, sme
->sme_name
, objkey
));
265 see
->see_lims
.sel_critmin
= lims
->sel_critmin
;
266 error
= sme_sensor_upint32(sdict
, objkey
,
268 DPRINTF(("%s: (%s) event [sensor=%s type=%d] "
269 "(%s updated)\n", __func__
, sme
->sme_name
,
270 edata
->desc
, crittype
, objkey
));
272 if (error
&& error
!= EEXIST
)
274 see
->see_edata
->upropset
|= PROP_CRITMIN
;
275 see
->see_lims
.sel_flags
|= PROP_CRITMIN
;
278 if (lims
->sel_flags
& PROP_BATTWARN
) {
279 objkey
= "warning-capacity";
280 obj
= prop_dictionary_get(sdict
, objkey
);
281 if (obj
&& prop_object_type(obj
) != PROP_TYPE_NUMBER
) {
282 DPRINTF(("%s: (%s) %s object not TYPE_NUMBER\n",
283 __func__
, sme
->sme_name
, objkey
));
286 see
->see_lims
.sel_warnmin
= lims
->sel_warnmin
;
287 error
= sme_sensor_upint32(sdict
, objkey
,
289 DPRINTF(("%s: (%s) event [sensor=%s type=%d] "
290 "(%s updated)\n", __func__
, sme
->sme_name
,
291 edata
->desc
, crittype
, objkey
));
293 if (error
&& error
!= EEXIST
)
295 see
->see_edata
->upropset
|= PROP_BATTWARN
;
296 see
->see_lims
.sel_flags
|= PROP_BATTWARN
;
299 if (lims
->sel_flags
& PROP_BATTCAP
) {
300 objkey
= "critical-capacity";
301 obj
= prop_dictionary_get(sdict
, objkey
);
302 if (obj
&& prop_object_type(obj
) != PROP_TYPE_NUMBER
) {
303 DPRINTF(("%s: (%s) %s object not TYPE_NUMBER\n",
304 __func__
, sme
->sme_name
, objkey
));
307 see
->see_lims
.sel_critmin
= lims
->sel_critmin
;
308 error
= sme_sensor_upint32(sdict
, objkey
,
310 DPRINTF(("%s: (%s) event [sensor=%s type=%d] "
311 "(%s updated)\n", __func__
, sme
->sme_name
,
312 edata
->desc
, crittype
, objkey
));
314 if (error
&& error
!= EEXIST
)
316 see
->see_edata
->upropset
|= PROP_BATTCAP
;
317 see
->see_lims
.sel_flags
|= PROP_BATTCAP
;
320 if (lims
->sel_flags
& PROP_DRIVER_LIMITS
)
321 see
->see_lims
.sel_flags
|= PROP_DRIVER_LIMITS
;
323 DPRINTF(("%s: (%s) event registered (sensor=%s snum=%d type=%d "
324 "critmin=%" PRIu32
" warnmin=%" PRIu32
" warnmax=%" PRIu32
325 " critmax=%" PRIu32
" props 0x%04x)\n", __func__
,
326 see
->see_sme
->sme_name
, see
->see_pes
.pes_sensname
,
327 see
->see_edata
->sensor
, see
->see_type
, see
->see_lims
.sel_critmin
,
328 see
->see_lims
.sel_warnmin
, see
->see_lims
.sel_warnmax
,
329 see
->see_lims
.sel_critmax
, see
->see_edata
->upropset
));
331 * Initialize the events framework if it wasn't initialized before.
333 if ((sme
->sme_flags
& SME_CALLOUT_INITIALIZED
) == 0)
334 error
= sme_events_init(sme
);
337 * If driver requested notification, advise it of new
340 if (sme
->sme_set_limits
) {
341 see
->see_lims
.sel_flags
= see
->see_edata
->upropset
&
343 (*sme
->sme_set_limits
)(sme
, edata
, &(see
->see_lims
));
347 if ((error
== 0 || error
== EEXIST
) && osee
== NULL
)
348 LIST_INSERT_HEAD(&sme
->sme_events_list
, see
, see_list
);
350 mutex_exit(&sme
->sme_mtx
);
356 * sme_event_unregister_all:
358 * + Unregisters all events associated with a sysmon envsys device.
361 sme_event_unregister_all(struct sysmon_envsys
*sme
)
366 KASSERT(sme
!= NULL
);
368 mutex_enter(&sme
->sme_mtx
);
369 LIST_FOREACH(see
, &sme
->sme_events_list
, see_list
) {
370 while (see
->see_flags
& SEE_EVENT_WORKING
)
371 cv_wait(&sme
->sme_condvar
, &sme
->sme_mtx
);
373 if (strcmp(see
->see_pes
.pes_dvname
, sme
->sme_name
) == 0)
377 DPRINTF(("%s: total events %d (%s)\n", __func__
,
378 evcounter
, sme
->sme_name
));
380 while ((see
= LIST_FIRST(&sme
->sme_events_list
))) {
384 if (strcmp(see
->see_pes
.pes_dvname
, sme
->sme_name
) == 0) {
385 LIST_REMOVE(see
, see_list
);
386 DPRINTF(("%s: event %s %d removed (%s)\n", __func__
,
387 see
->see_pes
.pes_sensname
, see
->see_type
,
389 kmem_free(see
, sizeof(*see
));
394 if (LIST_EMPTY(&sme
->sme_events_list
))
395 if (sme
->sme_flags
& SME_CALLOUT_INITIALIZED
)
396 sme_events_destroy(sme
);
397 mutex_exit(&sme
->sme_mtx
);
401 * sme_event_unregister:
403 * + Unregisters an event from the specified sysmon envsys device.
406 sme_event_unregister(struct sysmon_envsys
*sme
, const char *sensor
, int type
)
411 KASSERT(sensor
!= NULL
);
413 mutex_enter(&sme
->sme_mtx
);
414 LIST_FOREACH(see
, &sme
->sme_events_list
, see_list
) {
415 if (strcmp(see
->see_pes
.pes_sensname
, sensor
) == 0) {
416 if (see
->see_type
== type
) {
424 mutex_exit(&sme
->sme_mtx
);
429 * Wait for the event to finish its work, remove from the list
430 * and release resouces.
432 while (see
->see_flags
& SEE_EVENT_WORKING
)
433 cv_wait(&sme
->sme_condvar
, &sme
->sme_mtx
);
435 DPRINTF(("%s: removed dev=%s sensor=%s type=%d\n",
436 __func__
, see
->see_pes
.pes_dvname
, sensor
, type
));
437 LIST_REMOVE(see
, see_list
);
439 * So the events list is empty, we'll do the following:
441 * - stop and destroy the callout.
442 * - destroy the workqueue.
444 if (LIST_EMPTY(&sme
->sme_events_list
))
445 sme_events_destroy(sme
);
446 mutex_exit(&sme
->sme_mtx
);
448 kmem_free(see
, sizeof(*see
));
455 * + Registers a new event for a device that had enabled any of
456 * the monitoring flags in the driver.
459 sme_event_drvadd(void *arg
)
461 sme_event_drv_t
*sed_t
= arg
;
462 sysmon_envsys_lim_t lims
;
465 KASSERT(sed_t
!= NULL
);
467 #define SEE_REGEVENT(a, b, c) \
469 if (sed_t->sed_edata->flags & (a)) { \
470 char str[ENVSYS_DESCLEN] = "monitoring-state-"; \
472 error = sme_event_register(sed_t->sed_sdict, \
477 sed_t->sed_powertype); \
478 if (error && error != EEXIST) \
479 printf("%s: failed to add event! " \
480 "error=%d sensor=%s event=%s\n", \
482 sed_t->sed_edata->desc, (c)); \
484 (void)strlcat(str, (c), sizeof(str)); \
485 prop_dictionary_set_bool(sed_t->sed_sdict, \
490 } while (/* CONSTCOND */ 0)
493 * If driver provides a method to retrieve its internal limit
494 * values, call it and use those returned values as initial
495 * limits for event monitoring.
498 if (sed_t
->sed_edata
->flags
& ENVSYS_FMONLIMITS
)
499 if (sed_t
->sed_sme
->sme_get_limits
)
500 (*sed_t
->sed_sme
->sme_get_limits
)(sed_t
->sed_sme
,
504 * If no values returned, don't create the event monitor at
505 * this time. We'll get another chance later when the user
506 * provides us with limits.
508 if (lims
.sel_flags
== 0)
509 sed_t
->sed_edata
->flags
&= ~ENVSYS_FMONLIMITS
;
512 * If driver doesn't provide a way to "absorb" user-specified
513 * limit values, we must monitor all limits ourselves
515 else if (sed_t
->sed_sme
->sme_set_limits
== NULL
)
516 lims
.sel_flags
&= ~PROP_DRIVER_LIMITS
;
518 /* Register the events that were specified */
520 SEE_REGEVENT(ENVSYS_FMONCRITICAL
,
521 PENVSYS_EVENT_CRITICAL
,
524 SEE_REGEVENT(ENVSYS_FMONSTCHANGED
,
525 PENVSYS_EVENT_STATE_CHANGED
,
528 SEE_REGEVENT(ENVSYS_FMONLIMITS
,
529 PENVSYS_EVENT_LIMITS
,
533 * we are done, free memory now.
535 kmem_free(sed_t
, sizeof(*sed_t
));
541 * + Initialize the events framework for this device.
544 sme_events_init(struct sysmon_envsys
*sme
)
549 KASSERT(sme
!= NULL
);
550 KASSERT(mutex_owned(&sme
->sme_mtx
));
552 if (sme
->sme_events_timeout
)
553 timo
= sme
->sme_events_timeout
* hz
;
557 error
= workqueue_create(&sme
->sme_wq
, sme
->sme_name
,
558 sme_events_worker
, sme
, PRI_NONE
, IPL_SOFTCLOCK
, WQ_MPSAFE
);
562 mutex_init(&sme
->sme_callout_mtx
, MUTEX_DEFAULT
, IPL_SOFTCLOCK
);
563 callout_init(&sme
->sme_callout
, CALLOUT_MPSAFE
);
564 callout_setfunc(&sme
->sme_callout
, sme_events_check
, sme
);
565 callout_schedule(&sme
->sme_callout
, timo
);
566 sme
->sme_flags
|= SME_CALLOUT_INITIALIZED
;
567 DPRINTF(("%s: events framework initialized for '%s'\n",
568 __func__
, sme
->sme_name
));
574 * sme_events_destroy:
576 * + Destroys the event framework for this device: callout
577 * stopped, workqueue destroyed and callout mutex destroyed.
580 sme_events_destroy(struct sysmon_envsys
*sme
)
582 KASSERT(mutex_owned(&sme
->sme_mtx
));
584 callout_stop(&sme
->sme_callout
);
585 workqueue_destroy(sme
->sme_wq
);
586 mutex_destroy(&sme
->sme_callout_mtx
);
587 callout_destroy(&sme
->sme_callout
);
588 sme
->sme_flags
&= ~SME_CALLOUT_INITIALIZED
;
589 DPRINTF(("%s: events framework destroyed for '%s'\n",
590 __func__
, sme
->sme_name
));
596 * + Passes the events to the workqueue thread and stops
597 * the callout if the 'low-power' condition is triggered.
600 sme_events_check(void *arg
)
602 struct sysmon_envsys
*sme
= arg
;
606 KASSERT(sme
!= NULL
);
608 mutex_enter(&sme
->sme_callout_mtx
);
609 LIST_FOREACH(see
, &sme
->sme_events_list
, see_list
) {
610 workqueue_enqueue(sme
->sme_wq
, &see
->see_wk
, NULL
);
611 see
->see_edata
->flags
|= ENVSYS_FNEED_REFRESH
;
613 if (sme
->sme_events_timeout
)
614 timo
= sme
->sme_events_timeout
* hz
;
617 if (!sysmon_low_power
)
618 callout_schedule(&sme
->sme_callout
, timo
);
619 mutex_exit(&sme
->sme_callout_mtx
);
625 * + workqueue thread that checks if there's a critical condition
626 * and sends an event if it was triggered.
629 sme_events_worker(struct work
*wk
, void *arg
)
631 const struct sme_description_table
*sdt
= NULL
;
632 const struct sme_sensor_event
*sse
= sme_sensor_event
;
633 sme_event_t
*see
= (void *)wk
;
634 struct sysmon_envsys
*sme
= see
->see_sme
;
635 envsys_data_t
*edata
= see
->see_edata
;
638 KASSERT(wk
== &see
->see_wk
);
639 KASSERT(sme
!= NULL
|| edata
!= NULL
);
641 mutex_enter(&sme
->sme_mtx
);
642 if ((see
->see_flags
& SEE_EVENT_WORKING
) == 0)
643 see
->see_flags
|= SEE_EVENT_WORKING
;
645 * sme_events_check marks the sensors to make us refresh them here.
646 * Don't refresh if the driver uses its own method for refreshing.
648 if ((sme
->sme_flags
& SME_DISABLE_REFRESH
) == 0) {
649 if ((edata
->flags
& ENVSYS_FNEED_REFRESH
) != 0) {
650 /* refresh sensor in device */
651 (*sme
->sme_refresh
)(sme
, edata
);
652 edata
->flags
&= ~ENVSYS_FNEED_REFRESH
;
656 DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d type=%d state=%d units=%d "
657 "value_cur=%d\n", __func__
, sme
->sme_name
, edata
->desc
,
658 edata
->sensor
, see
->see_type
, edata
->state
, edata
->units
,
661 /* skip the event if current sensor is in invalid state */
662 if (edata
->state
== ENVSYS_SINVALID
)
665 switch (see
->see_type
) {
667 * For range limits, if the driver claims responsibility for
668 * limit/range checking, just user driver-supplied status.
669 * Else calculate our own status. Note that driver must
670 * relinquish responsibility for ALL limits if there is even
671 * one limit that it cannot handle!
673 case PENVSYS_EVENT_LIMITS
:
674 case PENVSYS_EVENT_CAPACITY
:
675 #define __EXCEED_LIM(valid, lim, rel) \
676 ((see->see_lims.sel_flags & (valid)) && \
677 (edata->value_cur rel (see->see_lims.lim)))
679 if ((see
->see_lims
.sel_flags
& PROP_DRIVER_LIMITS
) == 0) {
680 if __EXCEED_LIM(PROP_CRITMIN
| PROP_BATTCAP
,
682 edata
->state
= ENVSYS_SCRITUNDER
;
683 else if __EXCEED_LIM(PROP_WARNMIN
| PROP_BATTWARN
,
685 edata
->state
= ENVSYS_SWARNUNDER
;
686 else if __EXCEED_LIM(PROP_CRITMAX
, sel_critmax
, >)
687 edata
->state
= ENVSYS_SCRITOVER
;
688 else if __EXCEED_LIM(PROP_WARNMAX
, sel_warnmax
, >)
689 edata
->state
= ENVSYS_SWARNOVER
;
694 * Send event if state has changed
696 if (edata
->state
== see
->see_evsent
)
699 for (i
= 0; sse
[i
].state
!= -1; i
++)
700 if (sse
[i
].state
== edata
->state
)
703 if (sse
[i
].state
== -1)
706 if (edata
->state
== ENVSYS_SVALID
)
707 sysmon_penvsys_event(&see
->see_pes
,
708 PENVSYS_EVENT_NORMAL
);
710 sysmon_penvsys_event(&see
->see_pes
, sse
[i
].event
);
712 see
->see_evsent
= edata
->state
;
717 * Send PENVSYS_EVENT_CRITICAL event if:
718 * State has gone from non-CRITICAL to CRITICAL,
719 * State remains CRITICAL and value has changed, or
720 * State has returned from CRITICAL to non-CRITICAL
722 case PENVSYS_EVENT_CRITICAL
:
723 if (edata
->state
== ENVSYS_SVALID
&&
724 see
->see_evsent
!= 0) {
725 sysmon_penvsys_event(&see
->see_pes
,
726 PENVSYS_EVENT_NORMAL
);
728 } else if (edata
->state
== ENVSYS_SCRITICAL
&&
729 see
->see_evsent
!= edata
->value_cur
) {
730 sysmon_penvsys_event(&see
->see_pes
,
731 PENVSYS_EVENT_CRITICAL
);
732 see
->see_evsent
= edata
->value_cur
;
737 * if value_cur is not normal (battery) or online (drive),
740 case PENVSYS_EVENT_STATE_CHANGED
:
742 * the state has not been changed, just ignore the event.
744 if (edata
->value_cur
== see
->see_evsent
)
747 switch (edata
->units
) {
749 sdt
= sme_get_description_table(SME_DESC_DRIVE_STATES
);
750 state
= ENVSYS_DRIVE_ONLINE
;
752 case ENVSYS_BATTERY_CAPACITY
:
753 sdt
= sme_get_description_table(
754 SME_DESC_BATTERY_CAPACITY
);
755 state
= ENVSYS_BATTERY_CAPACITY_NORMAL
;
758 panic("%s: bad units for PENVSYS_EVENT_STATE_CHANGED",
762 for (i
= 0; sdt
[i
].type
!= -1; i
++)
763 if (sdt
[i
].type
== edata
->value_cur
)
766 if (sdt
[i
].type
== -1)
770 * copy current state description.
772 (void)strlcpy(see
->see_pes
.pes_statedesc
, sdt
[i
].desc
,
773 sizeof(see
->see_pes
.pes_statedesc
));
775 if (edata
->value_cur
== state
)
777 * state returned to normal condition
779 sysmon_penvsys_event(&see
->see_pes
,
780 PENVSYS_EVENT_NORMAL
);
783 * state changed to abnormal condition
785 sysmon_penvsys_event(&see
->see_pes
, see
->see_type
);
787 see
->see_evsent
= edata
->value_cur
;
790 * There's no need to continue if it's a drive sensor.
792 if (edata
->units
== ENVSYS_DRIVE
)
796 * Check if the system is running in low power and send the
797 * event to powerd (if running) or shutdown the system
800 if (!sysmon_low_power
&& sme_event_check_low_power()) {
801 struct penvsys_state pes
;
804 * Stop the callout and send the 'low-power' event.
806 sysmon_low_power
= true;
807 callout_stop(&sme
->sme_callout
);
808 pes
.pes_type
= PENVSYS_TYPE_BATTERY
;
809 sysmon_penvsys_event(&pes
, PENVSYS_EVENT_LOW_POWER
);
813 panic("%s: invalid event type %d", __func__
, see
->see_type
);
817 see
->see_flags
&= ~SEE_EVENT_WORKING
;
818 cv_broadcast(&sme
->sme_condvar
);
819 mutex_exit(&sme
->sme_mtx
);
823 * Returns true if the system is in low power state: an AC adapter
824 * is OFF and all batteries are in LOW/CRITICAL state.
827 sme_event_check_low_power(void)
829 if (!sme_acadapter_check())
832 return sme_battery_check();
836 * Called with the sysmon_envsys device mtx held through the
840 sme_acadapter_check(void)
842 struct sysmon_envsys
*sme
;
843 envsys_data_t
*edata
;
844 bool dev
= false, sensor
= false;
846 LIST_FOREACH(sme
, &sysmon_envsys_list
, sme_list
) {
847 if (sme
->sme_class
== SME_CLASS_ACADAPTER
) {
854 * No AC Adapter devices were found.
860 * Check if there's an AC adapter device connected.
862 TAILQ_FOREACH(edata
, &sme
->sme_sensors_list
, sensors_head
) {
863 if (edata
->units
== ENVSYS_INDICATOR
) {
865 /* refresh current sensor */
866 (*sme
->sme_refresh
)(sme
, edata
);
867 if (edata
->value_cur
)
876 * AC adapter found and not connected.
882 * Called with the sysmon_envsys device mtx held through the
886 sme_battery_check(void)
888 struct sysmon_envsys
*sme
;
889 envsys_data_t
*edata
;
890 int batteriesfound
= 0;
891 bool present
, batterycap
, batterycharge
;
894 * Check for battery devices and its state.
896 LIST_FOREACH(sme
, &sysmon_envsys_list
, sme_list
) {
897 if (sme
->sme_class
!= SME_CLASS_BATTERY
)
901 TAILQ_FOREACH(edata
, &sme
->sme_sensors_list
, sensors_head
) {
902 if (edata
->units
== ENVSYS_INDICATOR
&&
911 * We've found a battery device...
914 batterycap
= batterycharge
= false;
915 TAILQ_FOREACH(edata
, &sme
->sme_sensors_list
, sensors_head
) {
916 if (edata
->units
== ENVSYS_BATTERY_CAPACITY
) {
918 if (!sme_battery_critical(edata
))
920 } else if (edata
->units
== ENVSYS_BATTERY_CHARGE
) {
921 batterycharge
= true;
922 if (edata
->value_cur
)
926 if (!batterycap
|| !batterycharge
)
934 * All batteries in low/critical capacity and discharging.
940 sme_battery_critical(envsys_data_t
*edata
)
942 if (edata
->value_cur
== ENVSYS_BATTERY_CAPACITY_CRITICAL
||
943 edata
->value_cur
== ENVSYS_BATTERY_CAPACITY_LOW
)