sysmon_envsys_events.c revision 1.56 1 /* $NetBSD: sysmon_envsys_events.c,v 1.56 2008/08/22 11:27:50 pgoyette 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.56 2008/08/22 11:27:50 pgoyette 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
48 #include <dev/sysmon/sysmonvar.h>
49 #include <dev/sysmon/sysmon_envsysvar.h>
50
51 struct sme_sensor_event {
52 int state;
53 int event;
54 };
55
56 static const struct sme_sensor_event sme_sensor_event[] = {
57 { ENVSYS_SVALID, PENVSYS_EVENT_NORMAL },
58 { ENVSYS_SCRITICAL, PENVSYS_EVENT_CRITICAL },
59 { ENVSYS_SCRITOVER, PENVSYS_EVENT_CRITOVER },
60 { ENVSYS_SCRITUNDER, PENVSYS_EVENT_CRITUNDER },
61 { ENVSYS_SWARNOVER, PENVSYS_EVENT_WARNOVER },
62 { ENVSYS_SWARNUNDER, PENVSYS_EVENT_WARNUNDER },
63 { -1, -1 }
64 };
65
66 static bool sysmon_low_power;
67
68 #define SME_EVTIMO (SME_EVENTS_DEFTIMEOUT * hz)
69
70 static bool sme_event_check_low_power(void);
71 static bool sme_battery_check(void);
72 static bool sme_battery_critical(envsys_data_t *);
73 static bool sme_acadapter_check(void);
74
75 /*
76 * sme_event_register:
77 *
78 * + Registers a new sysmon envsys event or updates any event
79 * already in the queue.
80 */
81 int
82 sme_event_register(prop_dictionary_t sdict, envsys_data_t *edata,
83 struct sysmon_envsys *sme, const char *objkey,
84 int32_t critval, int crittype, int powertype)
85 {
86 sme_event_t *see = NULL, *osee = NULL;
87 prop_object_t obj;
88 bool critvalup = false;
89 int error = 0;
90 int real_crittype;
91 int32_t o_critval;
92
93 KASSERT(sdict != NULL || edata != NULL || sme != NULL);
94 /*
95 * Allocate a new sysmon_envsys event.
96 */
97 see = kmem_zalloc(sizeof(*see), KM_SLEEP);
98 if (see == NULL)
99 return ENOMEM;
100
101 /*
102 * Map user-types to kernel types
103 */
104 switch (crittype) {
105 case PENVSYS_EVENT_USER_CRITMAX:
106 case PENVSYS_EVENT_USER_CRITMIN:
107 case PENVSYS_EVENT_USER_WARNMAX:
108 case PENVSYS_EVENT_USER_WARNMIN:
109 real_crittype = PENVSYS_EVENT_USER_LIMITS;
110 break;
111 case PENVSYS_EVENT_BATT_USERCAP:
112 case PENVSYS_EVENT_BATT_USERWARN:
113 real_crittype = PENVSYS_EVENT_BATT_USER_LIMITS;
114 break;
115 default:
116 real_crittype = crittype;
117 }
118
119 /*
120 * check if the event is already on the list and return
121 * EEXIST if value provided hasn't been changed.
122 */
123 mutex_enter(&sme->sme_mtx);
124 LIST_FOREACH(osee, &sme->sme_events_list, see_list) {
125 if (strcmp(edata->desc, osee->see_pes.pes_sensname) == 0)
126 if (real_crittype == osee->see_type) {
127 switch (crittype) {
128 case PENVSYS_EVENT_USER_CRITMAX:
129 case PENVSYS_EVENT_BATT_USERCAP:
130 o_critval = osee->see_critmax;
131 break;
132 case PENVSYS_EVENT_USER_WARNMAX:
133 case PENVSYS_EVENT_BATT_USERWARN:
134 o_critval = osee->see_warnmax;
135 break;
136 case PENVSYS_EVENT_USER_WARNMIN:
137 o_critval = osee->see_warnmin;
138 break;
139 case PENVSYS_EVENT_USER_CRITMIN:
140 default:
141 o_critval = osee->see_critmin;
142 break;
143 }
144 if (o_critval == critval) {
145 DPRINTF(("%s: dev=%s sensor=%s type=%d "
146 "(already exists)\n", __func__,
147 osee->see_pes.pes_dvname,
148 osee->see_pes.pes_sensname,
149 osee->see_type));
150 error = EEXIST;
151 goto out;
152 }
153 critvalup = true;
154 break;
155 }
156 }
157
158 /*
159 * Critical condition operation requested by userland.
160 */
161 if (objkey && critval && critvalup) {
162 obj = prop_dictionary_get(sdict, objkey);
163 if (obj && prop_object_type(obj) == PROP_TYPE_NUMBER) {
164 /*
165 * object is already in dictionary and value
166 * provided is not the same than we have
167 * currently, update the critical value.
168 */
169 switch (crittype) {
170 case PENVSYS_EVENT_USER_CRITMAX:
171 case PENVSYS_EVENT_BATT_USERCAP:
172 osee->see_critmax = critval;
173 break;
174 case PENVSYS_EVENT_USER_WARNMAX:
175 case PENVSYS_EVENT_BATT_USERWARN:
176 osee->see_warnmax = critval;
177 break;
178 case PENVSYS_EVENT_USER_WARNMIN:
179 osee->see_warnmin = critval;
180 break;
181 case PENVSYS_EVENT_USER_CRITMIN:
182 default:
183 osee->see_critmin = critval;
184 break;
185 }
186 DPRINTF(("%s: (%s) event [sensor=%s type=%d] "
187 "(critval updated)\n", __func__, sme->sme_name,
188 edata->desc, osee->see_type));
189 error = sme_sensor_upint32(sdict, objkey, critval);
190 goto out;
191 }
192 }
193
194 /*
195 * New limit defined for existing event
196 */
197 if (osee != NULL) {
198 osee->see_edata = edata;
199 switch (crittype) {
200 case PENVSYS_EVENT_USER_CRITMAX:
201 case PENVSYS_EVENT_BATT_USERCAP:
202 osee->see_critmax = critval;
203 break;
204 case PENVSYS_EVENT_USER_WARNMAX:
205 case PENVSYS_EVENT_BATT_USERWARN:
206 osee->see_warnmax = critval;
207 break;
208 case PENVSYS_EVENT_USER_WARNMIN:
209 osee->see_warnmin = critval;
210 break;
211 case PENVSYS_EVENT_USER_CRITMIN:
212 default:
213 osee->see_critmin = critval;
214 break;
215 }
216 if (objkey && critval) {
217 error = sme_sensor_upint32(sdict, objkey, critval);
218 if (error)
219 goto out;
220 }
221 DPRINTF(("%s: (%s) new limit added to existing event, type %d "
222 "critmin=%" PRIu32 " warnmin=%" PRIu32 " warnmax=%"
223 PRIu32 " critmax=%d\n", __func__, osee->see_sme->sme_name,
224 osee->see_type, osee->see_critmin, osee->see_warnmin,
225 osee->see_warnmax, osee->see_critmax));
226 goto out;
227 }
228 /*
229 * New event requested.
230 */
231 see->see_edata = edata;
232 switch (crittype) {
233 case PENVSYS_EVENT_USER_CRITMAX:
234 case PENVSYS_EVENT_BATT_USERCAP:
235 see->see_critmax = critval;
236 break;
237 case PENVSYS_EVENT_USER_WARNMAX:
238 case PENVSYS_EVENT_BATT_USERWARN:
239 see->see_warnmax = critval;
240 break;
241 case PENVSYS_EVENT_USER_WARNMIN:
242 see->see_warnmin = critval;
243 break;
244 case PENVSYS_EVENT_USER_CRITMIN:
245 default:
246 see->see_critmin = critval;
247 break;
248 }
249 see->see_type = real_crittype;
250 see->see_sme = sme;
251 (void)strlcpy(see->see_pes.pes_dvname, sme->sme_name,
252 sizeof(see->see_pes.pes_dvname));
253 see->see_pes.pes_type = powertype;
254 (void)strlcpy(see->see_pes.pes_sensname, edata->desc,
255 sizeof(see->see_pes.pes_sensname));
256
257 LIST_INSERT_HEAD(&sme->sme_events_list, see, see_list);
258 if (objkey && critval) {
259 error = sme_sensor_upint32(sdict, objkey, critval);
260 if (error)
261 goto out;
262 }
263 DPRINTF(("%s: (%s) event registered (sensor=%s snum=%d type=%d "
264 "critmin=%" PRIu32 " warnmin=%" PRIu32 " warnmax=%" PRIu32
265 " crixmax=%" PRIu32 ")\n", __func__,
266 see->see_sme->sme_name, see->see_pes.pes_sensname,
267 see->see_edata->sensor, see->see_type, see->see_critmin,
268 see->see_warnmin, see->see_warnmax, see->see_critmax));
269 /*
270 * Initialize the events framework if it wasn't initialized before.
271 */
272 if ((sme->sme_flags & SME_CALLOUT_INITIALIZED) == 0)
273 error = sme_events_init(sme);
274 out:
275 mutex_exit(&sme->sme_mtx);
276 if (error || critvalup)
277 kmem_free(see, sizeof(*see));
278 return error;
279 }
280
281 /*
282 * sme_event_unregister_all:
283 *
284 * + Unregisters all events associated with a sysmon envsys device.
285 */
286 void
287 sme_event_unregister_all(struct sysmon_envsys *sme)
288 {
289 sme_event_t *see;
290 int evcounter = 0;
291
292 KASSERT(sme != NULL);
293
294 mutex_enter(&sme->sme_mtx);
295 LIST_FOREACH(see, &sme->sme_events_list, see_list) {
296 while (see->see_flags & SEE_EVENT_WORKING)
297 cv_wait(&sme->sme_condvar, &sme->sme_mtx);
298
299 if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0)
300 evcounter++;
301 }
302
303 DPRINTF(("%s: total events %d (%s)\n", __func__,
304 evcounter, sme->sme_name));
305
306 while ((see = LIST_FIRST(&sme->sme_events_list))) {
307 if (evcounter == 0)
308 break;
309
310 if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0) {
311 LIST_REMOVE(see, see_list);
312 DPRINTF(("%s: event %s %d removed (%s)\n", __func__,
313 see->see_pes.pes_sensname, see->see_type,
314 sme->sme_name));
315 kmem_free(see, sizeof(*see));
316 evcounter--;
317 }
318 }
319
320 if (LIST_EMPTY(&sme->sme_events_list))
321 if (sme->sme_flags & SME_CALLOUT_INITIALIZED)
322 sme_events_destroy(sme);
323 mutex_exit(&sme->sme_mtx);
324 }
325
326 /*
327 * sme_event_unregister:
328 *
329 * + Unregisters an event from the specified sysmon envsys device.
330 */
331 int
332 sme_event_unregister(struct sysmon_envsys *sme, const char *sensor, int type)
333 {
334 sme_event_t *see;
335 bool found = false;
336
337 KASSERT(sensor != NULL);
338
339 mutex_enter(&sme->sme_mtx);
340 LIST_FOREACH(see, &sme->sme_events_list, see_list) {
341 if (strcmp(see->see_pes.pes_sensname, sensor) == 0) {
342 if (see->see_type == type) {
343 found = true;
344 break;
345 }
346 }
347 }
348
349 if (!found) {
350 mutex_exit(&sme->sme_mtx);
351 return EINVAL;
352 }
353
354 /*
355 * Wait for the event to finish its work, remove from the list
356 * and release resouces.
357 */
358 while (see->see_flags & SEE_EVENT_WORKING)
359 cv_wait(&sme->sme_condvar, &sme->sme_mtx);
360
361 DPRINTF(("%s: removed dev=%s sensor=%s type=%d\n",
362 __func__, see->see_pes.pes_dvname, sensor, type));
363 LIST_REMOVE(see, see_list);
364 /*
365 * So the events list is empty, we'll do the following:
366 *
367 * - stop and destroy the callout.
368 * - destroy the workqueue.
369 */
370 if (LIST_EMPTY(&sme->sme_events_list))
371 sme_events_destroy(sme);
372 mutex_exit(&sme->sme_mtx);
373
374 kmem_free(see, sizeof(*see));
375 return 0;
376 }
377
378 /*
379 * sme_event_drvadd:
380 *
381 * + Registers a new event for a device that had enabled any of
382 * the monitoring flags in the driver.
383 */
384 void
385 sme_event_drvadd(void *arg)
386 {
387 sme_event_drv_t *sed_t = arg;
388 int error = 0;
389
390 KASSERT(sed_t != NULL);
391
392 #define SEE_REGEVENT(a, b, c) \
393 do { \
394 if (sed_t->sed_edata->flags & (a)) { \
395 char str[ENVSYS_DESCLEN] = "monitoring-state-"; \
396 \
397 error = sme_event_register(sed_t->sed_sdict, \
398 sed_t->sed_edata, \
399 sed_t->sed_sme, \
400 NULL, \
401 0, \
402 (b), \
403 sed_t->sed_powertype); \
404 if (error && error != EEXIST) \
405 printf("%s: failed to add event! " \
406 "error=%d sensor=%s event=%s\n", \
407 __func__, error, \
408 sed_t->sed_edata->desc, (c)); \
409 else { \
410 (void)strlcat(str, (c), sizeof(str)); \
411 prop_dictionary_set_bool(sed_t->sed_sdict, \
412 str, \
413 true); \
414 } \
415 } \
416 } while (/* CONSTCOND */ 0)
417
418 SEE_REGEVENT(ENVSYS_FMONCRITICAL,
419 PENVSYS_EVENT_CRITICAL,
420 "critical");
421
422 SEE_REGEVENT(ENVSYS_FMONCRITUNDER | ENVSYS_FMONCRITOVER |
423 ENVSYS_FMONWARNUNDER | ENVSYS_FMONWARNOVER,
424 PENVSYS_EVENT_HW_LIMITS,
425 "hw-range-limits");
426
427 SEE_REGEVENT(ENVSYS_FMONSTCHANGED,
428 PENVSYS_EVENT_STATE_CHANGED,
429 "state-changed");
430
431 /*
432 * we are done, free memory now.
433 */
434 kmem_free(sed_t, sizeof(*sed_t));
435 }
436
437 /*
438 * sme_events_init:
439 *
440 * + Initialize the events framework for this device.
441 */
442 int
443 sme_events_init(struct sysmon_envsys *sme)
444 {
445 int error = 0;
446 uint64_t timo;
447
448 KASSERT(sme != NULL);
449 KASSERT(mutex_owned(&sme->sme_mtx));
450
451 if (sme->sme_events_timeout)
452 timo = sme->sme_events_timeout * hz;
453 else
454 timo = SME_EVTIMO;
455
456 error = workqueue_create(&sme->sme_wq, sme->sme_name,
457 sme_events_worker, sme, PRI_NONE, IPL_SOFTCLOCK, WQ_MPSAFE);
458 if (error)
459 return error;
460
461 mutex_init(&sme->sme_callout_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
462 callout_init(&sme->sme_callout, CALLOUT_MPSAFE);
463 callout_setfunc(&sme->sme_callout, sme_events_check, sme);
464 callout_schedule(&sme->sme_callout, timo);
465 sme->sme_flags |= SME_CALLOUT_INITIALIZED;
466 DPRINTF(("%s: events framework initialized for '%s'\n",
467 __func__, sme->sme_name));
468
469 return error;
470 }
471
472 /*
473 * sme_events_destroy:
474 *
475 * + Destroys the event framework for this device: callout
476 * stopped, workqueue destroyed and callout mutex destroyed.
477 */
478 void
479 sme_events_destroy(struct sysmon_envsys *sme)
480 {
481 KASSERT(mutex_owned(&sme->sme_mtx));
482
483 callout_stop(&sme->sme_callout);
484 workqueue_destroy(sme->sme_wq);
485 mutex_destroy(&sme->sme_callout_mtx);
486 callout_destroy(&sme->sme_callout);
487 sme->sme_flags &= ~SME_CALLOUT_INITIALIZED;
488 DPRINTF(("%s: events framework destroyed for '%s'\n",
489 __func__, sme->sme_name));
490 }
491
492 /*
493 * sme_events_check:
494 *
495 * + Passes the events to the workqueue thread and stops
496 * the callout if the 'low-power' condition is triggered.
497 */
498 void
499 sme_events_check(void *arg)
500 {
501 struct sysmon_envsys *sme = arg;
502 sme_event_t *see;
503 uint64_t timo;
504
505 KASSERT(sme != NULL);
506
507 mutex_enter(&sme->sme_callout_mtx);
508 LIST_FOREACH(see, &sme->sme_events_list, see_list) {
509 workqueue_enqueue(sme->sme_wq, &see->see_wk, NULL);
510 see->see_edata->flags |= ENVSYS_FNEED_REFRESH;
511 }
512 if (sme->sme_events_timeout)
513 timo = sme->sme_events_timeout * hz;
514 else
515 timo = SME_EVTIMO;
516 if (!sysmon_low_power)
517 callout_schedule(&sme->sme_callout, timo);
518 mutex_exit(&sme->sme_callout_mtx);
519 }
520
521 /*
522 * sme_events_worker:
523 *
524 * + workqueue thread that checks if there's a critical condition
525 * and sends an event if it was triggered.
526 */
527 void
528 sme_events_worker(struct work *wk, void *arg)
529 {
530 const struct sme_description_table *sdt = NULL;
531 const struct sme_sensor_event *sse = sme_sensor_event;
532 sme_event_t *see = (void *)wk;
533 struct sysmon_envsys *sme = see->see_sme;
534 envsys_data_t *edata = see->see_edata;
535 int i, state = 0;
536
537 KASSERT(wk == &see->see_wk);
538 KASSERT(sme != NULL || edata != NULL);
539
540 mutex_enter(&sme->sme_mtx);
541 if ((see->see_flags & SEE_EVENT_WORKING) == 0)
542 see->see_flags |= SEE_EVENT_WORKING;
543 /*
544 * sme_events_check marks the first event for the device to
545 * mak us refresh it here. Don't refresh if the driver uses
546 * its own method for refreshing.
547 */
548 if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) {
549 if ((see->see_edata->flags & ENVSYS_FNEED_REFRESH) != 0) {
550 /* refresh sensor in device */
551 (*sme->sme_refresh)(sme, edata);
552 see->see_edata->flags &= ~ENVSYS_FNEED_REFRESH;
553 }
554 }
555
556 DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d type=%d state=%d units=%d "
557 "value_cur=%d\n", __func__, sme->sme_name, edata->desc,
558 edata->sensor, see->see_type, edata->state, edata->units,
559 edata->value_cur));
560
561 /* skip the event if current sensor is in invalid state */
562 if (edata->state == ENVSYS_SINVALID)
563 goto out;
564
565 switch (see->see_type) {
566 /*
567 * For user range limits, calculate a new state first
568 * State based on user limits will override any hardware
569 * detected state.
570 */
571 case PENVSYS_EVENT_USER_LIMITS:
572 case PENVSYS_EVENT_BATT_USER_LIMITS:
573 #define __EXCEEDED_LIMIT(lim, rel) ((lim) && edata->value_cur rel (lim))
574 if __EXCEEDED_LIMIT(see->see_critmin, <)
575 edata->state = ENVSYS_SCRITUNDER;
576 else if __EXCEEDED_LIMIT(see->see_warnmin, <)
577 edata->state = ENVSYS_SWARNUNDER;
578 else if __EXCEEDED_LIMIT(see->see_warnmax, >)
579 edata->state = ENVSYS_SWARNOVER;
580 else if __EXCEEDED_LIMIT(see->see_critmax, >)
581 edata->state = ENVSYS_SCRITOVER;
582 /* FALLTHROUGH */
583 #undef __EXCEED_LIMIT
584 /*
585 * For hardware and user range limits, send event if state has changed
586 */
587 case PENVSYS_EVENT_CRITICAL:
588 case PENVSYS_EVENT_HW_LIMITS:
589 if (edata->state != see->see_evsent) {
590 for (i = 0; sse[i].state != -1; i++)
591 if (sse[i].state == edata->state)
592 break;
593
594 if (sse[i].state == -1)
595 break;
596
597 if (edata->state == ENVSYS_SVALID)
598 sysmon_penvsys_event(&see->see_pes,
599 PENVSYS_EVENT_NORMAL);
600 else
601 sysmon_penvsys_event(&see->see_pes,
602 sse[i].event);
603
604 see->see_evsent = edata->state;
605 }
606 break;
607
608 /*
609 * if value_cur is not normal (battery) or online (drive),
610 * send the event...
611 */
612 case PENVSYS_EVENT_STATE_CHANGED:
613 /*
614 * the state has not been changed, just ignore the event.
615 */
616 if (edata->value_cur == see->see_evsent)
617 break;
618
619 switch (edata->units) {
620 case ENVSYS_DRIVE:
621 sdt = sme_get_description_table(SME_DESC_DRIVE_STATES);
622 state = ENVSYS_DRIVE_ONLINE;
623 break;
624 case ENVSYS_BATTERY_CAPACITY:
625 sdt = sme_get_description_table(
626 SME_DESC_BATTERY_CAPACITY);
627 state = ENVSYS_BATTERY_CAPACITY_NORMAL;
628 break;
629 default:
630 panic("%s: invalid units for ENVSYS_FMONSTCHANGED",
631 __func__);
632 }
633
634 for (i = 0; sdt[i].type != -1; i++)
635 if (sdt[i].type == edata->value_cur)
636 break;
637
638 if (sdt[i].type == -1)
639 break;
640
641 /*
642 * copy current state description.
643 */
644 (void)strlcpy(see->see_pes.pes_statedesc, sdt[i].desc,
645 sizeof(see->see_pes.pes_statedesc));
646
647 /*
648 * state is ok again... send a normal event.
649 */
650 if (see->see_evsent && edata->value_cur == state) {
651 sysmon_penvsys_event(&see->see_pes,
652 PENVSYS_EVENT_NORMAL);
653 see->see_evsent = false;
654 }
655
656 /*
657 * state has been changed... send event.
658 */
659 if (see->see_evsent || edata->value_cur != state) {
660 /*
661 * save current drive state.
662 */
663 see->see_evsent = edata->value_cur;
664 sysmon_penvsys_event(&see->see_pes, see->see_type);
665 }
666
667 /*
668 * There's no need to continue if it's a drive sensor.
669 */
670 if (edata->units == ENVSYS_DRIVE)
671 break;
672
673 /*
674 * Check if the system is running in low power and send the
675 * event to powerd (if running) or shutdown the system
676 * otherwise.
677 */
678 if (!sysmon_low_power && sme_event_check_low_power()) {
679 struct penvsys_state pes;
680
681 /*
682 * Stop the callout and send the 'low-power' event.
683 */
684 sysmon_low_power = true;
685 callout_stop(&sme->sme_callout);
686 pes.pes_type = PENVSYS_TYPE_BATTERY;
687 sysmon_penvsys_event(&pes, PENVSYS_EVENT_LOW_POWER);
688 }
689 break;
690 default:
691 panic("%s: invalid event type %d", __func__, see->see_type);
692 }
693
694 out:
695 see->see_flags &= ~SEE_EVENT_WORKING;
696 cv_broadcast(&sme->sme_condvar);
697 mutex_exit(&sme->sme_mtx);
698 }
699
700 /*
701 * Returns true if the system is in low power state: an AC adapter
702 * is OFF and all batteries are in LOW/CRITICAL state.
703 */
704 static bool
705 sme_event_check_low_power(void)
706 {
707 if (!sme_acadapter_check())
708 return false;
709
710 return sme_battery_check();
711 }
712
713 /*
714 * Called with the sysmon_envsys device mtx held through the
715 * workqueue thread.
716 */
717 static bool
718 sme_acadapter_check(void)
719 {
720 struct sysmon_envsys *sme;
721 envsys_data_t *edata;
722 bool dev = false, sensor = false;
723
724 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) {
725 if (sme->sme_class == SME_CLASS_ACADAPTER) {
726 dev = true;
727 break;
728 }
729 }
730
731 /*
732 * No AC Adapter devices were found.
733 */
734 if (!dev)
735 return false;
736
737 /*
738 * Check if there's an AC adapter device connected.
739 */
740 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
741 if (edata->units == ENVSYS_INDICATOR) {
742 sensor = true;
743 /* refresh current sensor */
744 (*sme->sme_refresh)(sme, edata);
745 if (edata->value_cur)
746 return false;
747 }
748 }
749
750 if (!sensor)
751 return false;
752
753 /*
754 * AC adapter found and not connected.
755 */
756 return true;
757 }
758
759 /*
760 * Called with the sysmon_envsys device mtx held through the
761 * workqueue thread.
762 */
763 static bool
764 sme_battery_check(void)
765 {
766 struct sysmon_envsys *sme;
767 envsys_data_t *edata;
768 int batteriesfound = 0;
769 bool present, batterycap, batterycharge;
770
771 /*
772 * Check for battery devices and its state.
773 */
774 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) {
775 if (sme->sme_class != SME_CLASS_BATTERY)
776 continue;
777
778 present = true;
779 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
780 if (edata->units == ENVSYS_INDICATOR &&
781 !edata->value_cur) {
782 present = false;
783 break;
784 }
785 }
786 if (!present)
787 continue;
788 /*
789 * We've found a battery device...
790 */
791 batteriesfound++;
792 batterycap = batterycharge = false;
793 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
794 if (edata->units == ENVSYS_BATTERY_CAPACITY) {
795 batterycap = true;
796 if (!sme_battery_critical(edata))
797 return false;
798 } else if (edata->units == ENVSYS_BATTERY_CHARGE) {
799 batterycharge = true;
800 if (edata->value_cur)
801 return false;
802 }
803 }
804 if (!batterycap || !batterycharge)
805 return false;
806 }
807
808 if (!batteriesfound)
809 return false;
810
811 /*
812 * All batteries in low/critical capacity and discharging.
813 */
814 return true;
815 }
816
817 static bool
818 sme_battery_critical(envsys_data_t *edata)
819 {
820 if (edata->value_cur == ENVSYS_BATTERY_CAPACITY_CRITICAL ||
821 edata->value_cur == ENVSYS_BATTERY_CAPACITY_LOW)
822 return true;
823
824 return false;
825 }
826