Expand PMF_FN_* macros.
[netbsd-mini2440.git] / sys / dev / sysmon / sysmon_envsys_events.c
blob26cf2ca9320267c8701ab4e7cc7973eb73497734
1 /* $NetBSD: sysmon_envsys_events.c,v 1.73 2010/01/01 15:41:25 pgoyette Exp $ */
3 /*-
4 * Copyright (c) 2007, 2008 Juan Romero Pardines.
5 * All rights reserved.
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.
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>
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>
46 /* #define ENVSYS_DEBUG */
48 #include <dev/sysmon/sysmonvar.h>
49 #include <dev/sysmon/sysmon_envsysvar.h>
51 struct sme_sensor_event {
52 int state;
53 int 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 },
65 { -1, -1 }
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);
78 * sme_event_register:
80 * + Registers a new sysmon envsys event or updates any event
81 * already in the queue.
83 int
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;
89 prop_object_t obj;
90 int error = 0;
91 const char *objkey;
93 KASSERT(sdict != NULL || edata != NULL || sme != NULL);
95 /*
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)
102 continue;
103 if (crittype != osee->see_type)
104 continue;
106 DPRINTF(("%s: dev %s sensor %s lim_flags 0x%04x event exists\n",
107 __func__, sme->sme_name, edata->desc, lims->sel_flags));
109 see = osee;
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));
114 error = EEXIST;
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));
122 error = EEXIST;
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));
130 error = EEXIST;
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));
138 error = EEXIST;
139 lims->sel_flags &= ~(PROP_CRITMIN | PROP_BATTCAP);
142 break;
144 if (see == NULL) {
146 * New event requested - allocate a sysmon_envsys event.
148 see = kmem_zalloc(sizeof(*see), KM_SLEEP);
149 if (see == NULL)
150 return ENOMEM;
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;
156 see->see_sme = sme;
157 see->see_edata = edata;
159 /* Initialize sensor type and previously-sent state */
161 see->see_pes.pes_type = powertype;
163 switch (crittype) {
164 case PENVSYS_EVENT_LIMITS:
165 see->see_evsent = ENVSYS_SVALID;
166 break;
167 case PENVSYS_EVENT_CAPACITY:
168 see->see_evsent = ENVSYS_BATTERY_CAPACITY_NORMAL;
169 break;
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;
175 else
176 panic("%s: bad units for "
177 "PENVSYS_EVENT_STATE_CHANGED", __func__);
178 break;
179 case PENVSYS_EVENT_CRITICAL:
180 default:
181 see->see_evsent = 0;
182 break;
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));
200 error = ENOTSUP;
201 } else {
202 see->see_lims.sel_critmax = lims->sel_critmax;
203 error = sme_sensor_upint32(sdict, objkey,
204 lims->sel_critmax);
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)
210 goto out;
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));
221 error = ENOTSUP;
222 } else {
223 see->see_lims.sel_warnmax = lims->sel_warnmax;
224 error = sme_sensor_upint32(sdict, objkey,
225 lims->sel_warnmax);
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)
231 goto out;
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));
242 error = ENOTSUP;
243 } else {
244 see->see_lims.sel_warnmin = lims->sel_warnmin;
245 error = sme_sensor_upint32(sdict, objkey,
246 lims->sel_warnmin);
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)
252 goto out;
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));
263 error = ENOTSUP;
264 } else {
265 see->see_lims.sel_critmin = lims->sel_critmin;
266 error = sme_sensor_upint32(sdict, objkey,
267 lims->sel_critmin);
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)
273 goto out;
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));
284 error = ENOTSUP;
285 } else {
286 see->see_lims.sel_warnmin = lims->sel_warnmin;
287 error = sme_sensor_upint32(sdict, objkey,
288 lims->sel_warnmin);
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)
294 goto out;
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));
305 error = ENOTSUP;
306 } else {
307 see->see_lims.sel_critmin = lims->sel_critmin;
308 error = sme_sensor_upint32(sdict, objkey,
309 lims->sel_critmin);
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)
315 goto out;
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
338 * limit values
340 if (sme->sme_set_limits) {
341 see->see_lims.sel_flags = see->see_edata->upropset &
342 PROP_LIMITS;
343 (*sme->sme_set_limits)(sme, edata, &(see->see_lims));
346 out:
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);
352 return error;
356 * sme_event_unregister_all:
358 * + Unregisters all events associated with a sysmon envsys device.
360 void
361 sme_event_unregister_all(struct sysmon_envsys *sme)
363 sme_event_t *see;
364 int evcounter = 0;
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)
374 evcounter++;
377 DPRINTF(("%s: total events %d (%s)\n", __func__,
378 evcounter, sme->sme_name));
380 while ((see = LIST_FIRST(&sme->sme_events_list))) {
381 if (evcounter == 0)
382 break;
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,
388 sme->sme_name));
389 kmem_free(see, sizeof(*see));
390 evcounter--;
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)
408 sme_event_t *see;
409 bool found = false;
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) {
417 found = true;
418 break;
423 if (!found) {
424 mutex_exit(&sme->sme_mtx);
425 return EINVAL;
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));
449 return 0;
453 * sme_event_drvadd:
455 * + Registers a new event for a device that had enabled any of
456 * the monitoring flags in the driver.
458 void
459 sme_event_drvadd(void *arg)
461 sme_event_drv_t *sed_t = arg;
462 sysmon_envsys_lim_t lims;
463 int error = 0;
465 KASSERT(sed_t != NULL);
467 #define SEE_REGEVENT(a, b, c) \
468 do { \
469 if (sed_t->sed_edata->flags & (a)) { \
470 char str[ENVSYS_DESCLEN] = "monitoring-state-"; \
472 error = sme_event_register(sed_t->sed_sdict, \
473 sed_t->sed_edata, \
474 sed_t->sed_sme, \
475 &lims, \
476 (b), \
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", \
481 __func__, error, \
482 sed_t->sed_edata->desc, (c)); \
483 else { \
484 (void)strlcat(str, (c), sizeof(str)); \
485 prop_dictionary_set_bool(sed_t->sed_sdict, \
486 str, \
487 true); \
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.
497 lims.sel_flags = 0;
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,
501 sed_t->sed_edata,
502 &lims);
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,
522 "critical");
524 SEE_REGEVENT(ENVSYS_FMONSTCHANGED,
525 PENVSYS_EVENT_STATE_CHANGED,
526 "state-changed");
528 SEE_REGEVENT(ENVSYS_FMONLIMITS,
529 PENVSYS_EVENT_LIMITS,
530 "hw-range-limits");
533 * we are done, free memory now.
535 kmem_free(sed_t, sizeof(*sed_t));
539 * sme_events_init:
541 * + Initialize the events framework for this device.
544 sme_events_init(struct sysmon_envsys *sme)
546 int error = 0;
547 uint64_t timo;
549 KASSERT(sme != NULL);
550 KASSERT(mutex_owned(&sme->sme_mtx));
552 if (sme->sme_events_timeout)
553 timo = sme->sme_events_timeout * hz;
554 else
555 timo = SME_EVTIMO;
557 error = workqueue_create(&sme->sme_wq, sme->sme_name,
558 sme_events_worker, sme, PRI_NONE, IPL_SOFTCLOCK, WQ_MPSAFE);
559 if (error)
560 return error;
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));
570 return error;
574 * sme_events_destroy:
576 * + Destroys the event framework for this device: callout
577 * stopped, workqueue destroyed and callout mutex destroyed.
579 void
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));
594 * sme_events_check:
596 * + Passes the events to the workqueue thread and stops
597 * the callout if the 'low-power' condition is triggered.
599 void
600 sme_events_check(void *arg)
602 struct sysmon_envsys *sme = arg;
603 sme_event_t *see;
604 uint64_t timo;
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;
615 else
616 timo = SME_EVTIMO;
617 if (!sysmon_low_power)
618 callout_schedule(&sme->sme_callout, timo);
619 mutex_exit(&sme->sme_callout_mtx);
623 * sme_events_worker:
625 * + workqueue thread that checks if there's a critical condition
626 * and sends an event if it was triggered.
628 void
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;
636 int i, state = 0;
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,
659 edata->value_cur));
661 /* skip the event if current sensor is in invalid state */
662 if (edata->state == ENVSYS_SINVALID)
663 goto out;
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,
681 sel_critmin, <)
682 edata->state = ENVSYS_SCRITUNDER;
683 else if __EXCEED_LIM(PROP_WARNMIN | PROP_BATTWARN,
684 sel_warnmin, <)
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;
691 #undef __EXCEED_LIM
694 * Send event if state has changed
696 if (edata->state == see->see_evsent)
697 break;
699 for (i = 0; sse[i].state != -1; i++)
700 if (sse[i].state == edata->state)
701 break;
703 if (sse[i].state == -1)
704 break;
706 if (edata->state == ENVSYS_SVALID)
707 sysmon_penvsys_event(&see->see_pes,
708 PENVSYS_EVENT_NORMAL);
709 else
710 sysmon_penvsys_event(&see->see_pes, sse[i].event);
712 see->see_evsent = edata->state;
714 break;
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);
727 see->see_evsent = 0;
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;
734 break;
737 * if value_cur is not normal (battery) or online (drive),
738 * send the event...
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)
745 break;
747 switch (edata->units) {
748 case ENVSYS_DRIVE:
749 sdt = sme_get_description_table(SME_DESC_DRIVE_STATES);
750 state = ENVSYS_DRIVE_ONLINE;
751 break;
752 case ENVSYS_BATTERY_CAPACITY:
753 sdt = sme_get_description_table(
754 SME_DESC_BATTERY_CAPACITY);
755 state = ENVSYS_BATTERY_CAPACITY_NORMAL;
756 break;
757 default:
758 panic("%s: bad units for PENVSYS_EVENT_STATE_CHANGED",
759 __func__);
762 for (i = 0; sdt[i].type != -1; i++)
763 if (sdt[i].type == edata->value_cur)
764 break;
766 if (sdt[i].type == -1)
767 break;
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);
781 else
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)
793 break;
796 * Check if the system is running in low power and send the
797 * event to powerd (if running) or shutdown the system
798 * otherwise.
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);
811 break;
812 default:
813 panic("%s: invalid event type %d", __func__, see->see_type);
816 out:
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.
826 static bool
827 sme_event_check_low_power(void)
829 if (!sme_acadapter_check())
830 return false;
832 return sme_battery_check();
836 * Called with the sysmon_envsys device mtx held through the
837 * workqueue thread.
839 static bool
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) {
848 dev = true;
849 break;
854 * No AC Adapter devices were found.
856 if (!dev)
857 return false;
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) {
864 sensor = true;
865 /* refresh current sensor */
866 (*sme->sme_refresh)(sme, edata);
867 if (edata->value_cur)
868 return false;
872 if (!sensor)
873 return false;
876 * AC adapter found and not connected.
878 return true;
882 * Called with the sysmon_envsys device mtx held through the
883 * workqueue thread.
885 static bool
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)
898 continue;
900 present = true;
901 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
902 if (edata->units == ENVSYS_INDICATOR &&
903 !edata->value_cur) {
904 present = false;
905 break;
908 if (!present)
909 continue;
911 * We've found a battery device...
913 batteriesfound++;
914 batterycap = batterycharge = false;
915 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
916 if (edata->units == ENVSYS_BATTERY_CAPACITY) {
917 batterycap = true;
918 if (!sme_battery_critical(edata))
919 return false;
920 } else if (edata->units == ENVSYS_BATTERY_CHARGE) {
921 batterycharge = true;
922 if (edata->value_cur)
923 return false;
926 if (!batterycap || !batterycharge)
927 return false;
930 if (!batteriesfound)
931 return false;
934 * All batteries in low/critical capacity and discharging.
936 return true;
939 static bool
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)
944 return true;
946 return false;