sysmon_envsys_events.c revision 1.105 1 /* $NetBSD: sysmon_envsys_events.c,v 1.105 2012/09/06 12:21:40 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.105 2012/09/06 12:21:40 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 #include <dev/sysmon/sysmonvar.h>
47 #include <dev/sysmon/sysmon_envsysvar.h>
48
49 struct sme_sensor_event {
50 int state;
51 int event;
52 };
53
54 static const struct sme_sensor_event sme_sensor_event[] = {
55 { ENVSYS_SVALID, PENVSYS_EVENT_NORMAL },
56 { ENVSYS_SCRITOVER, PENVSYS_EVENT_CRITOVER },
57 { ENVSYS_SCRITUNDER, PENVSYS_EVENT_CRITUNDER },
58 { ENVSYS_SWARNOVER, PENVSYS_EVENT_WARNOVER },
59 { ENVSYS_SWARNUNDER, PENVSYS_EVENT_WARNUNDER },
60 { ENVSYS_BATTERY_CAPACITY_NORMAL, PENVSYS_EVENT_NORMAL },
61 { ENVSYS_BATTERY_CAPACITY_WARNING, PENVSYS_EVENT_BATT_WARN },
62 { ENVSYS_BATTERY_CAPACITY_CRITICAL, PENVSYS_EVENT_BATT_CRIT },
63 { ENVSYS_BATTERY_CAPACITY_HIGH, PENVSYS_EVENT_BATT_HIGH },
64 { ENVSYS_BATTERY_CAPACITY_MAX, PENVSYS_EVENT_BATT_MAX },
65 { -1, -1 }
66 };
67
68 static bool sysmon_low_power;
69
70 #define SME_EVTIMO (SME_EVENTS_DEFTIMEOUT * hz)
71
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);
76
77 static void sme_remove_event(sme_event_t *, struct sysmon_envsys *);
78
79 /*
80 * sme_event_register:
81 *
82 * + Registers a new sysmon envsys event or updates any event
83 * already in the queue.
84 */
85 int
86 sme_event_register(prop_dictionary_t sdict, envsys_data_t *edata,
87 struct sysmon_envsys *sme, sysmon_envsys_lim_t *lims,
88 uint32_t props, int crittype, int powertype)
89 {
90 sme_event_t *see = NULL, *osee = NULL;
91 prop_object_t obj;
92 int error = 0;
93 const char *objkey;
94
95 KASSERT(sdict != NULL);
96 KASSERT(edata != NULL);
97 KASSERT(sme != NULL);
98 KASSERT(lims != NULL);
99
100 /*
101 * Some validation first for limit-checking events
102 *
103 * 1. Limits are not permitted if the units is ENVSYS_INDICATOR
104 * or ENVSYS_BATTERY_CHARGE.
105 *
106 * 2. Capacity limits are permitted only if the sensor has the
107 * ENVSYS_FPERCENT flag set and value_max is set.
108 *
109 * 3. It is not permissible for both capacity and value limits
110 * to coexist.
111 *
112 * Note that it permissible for a sensor to have value limits
113 * even if its ENVSYS_FPERCENT flag and value_max are set.
114 */
115
116 DPRINTF(("%s: units %d props 0x%04x upropset 0x%04x max_val %d"
117 " edata-flags 0x%04x\n", __func__, edata->units, props,
118 edata->upropset, edata->value_max, edata->flags));
119
120 if (props)
121 if (edata->units == ENVSYS_INDICATOR ||
122 edata->units == ENVSYS_BATTERY_CHARGE)
123 return ENOTSUP;
124
125 if ((props & PROP_CAP_LIMITS) &&
126 ((edata->value_max == 0) ||
127 !(edata->flags & ENVSYS_FPERCENT) ||
128 (props & PROP_VAL_LIMITS) ||
129 (edata->upropset & PROP_VAL_LIMITS)))
130 props = 0;
131
132 if ((props & PROP_VAL_LIMITS) && (edata->upropset & PROP_CAP_LIMITS))
133 props = 0;
134
135 /*
136 * check if the event is already on the list and return
137 * EEXIST if value provided hasn't been changed.
138 */
139 mutex_enter(&sme->sme_mtx);
140 LIST_FOREACH(osee, &sme->sme_events_list, see_list) {
141 if (strcmp(edata->desc, osee->see_pes.pes_sensname) != 0)
142 continue;
143 if (crittype != osee->see_type &&
144 osee->see_type != PENVSYS_EVENT_NULL)
145 continue;
146
147 /*
148 * We found an existing event for this sensor. Make
149 * sure it references the correct edata
150 */
151 KASSERT(edata == osee->see_edata);
152
153 DPRINTF(("%s: dev %s sensor %s: event type %d exists\n",
154 __func__, sme->sme_name, edata->desc, crittype));
155
156 see = osee;
157 if (props & edata->upropset & (PROP_CRITMAX | PROP_BATTMAX)) {
158 if (lims->sel_critmax == edata->limits.sel_critmax) {
159 DPRINTF(("%s: critmax exists\n", __func__));
160 error = EEXIST;
161 props &= ~(PROP_CRITMAX | PROP_BATTMAX);
162 }
163 }
164 if (props & edata->upropset & (PROP_WARNMAX | PROP_BATTHIGH)) {
165 if (lims->sel_warnmax == edata->limits.sel_warnmax) {
166 DPRINTF(("%s: warnmax exists\n", __func__));
167 error = EEXIST;
168 props &= ~(PROP_WARNMAX | PROP_BATTHIGH);
169 }
170 }
171 if (props & edata->upropset & (PROP_WARNMIN | PROP_BATTWARN)) {
172 if (lims->sel_warnmin == edata->limits.sel_warnmin) {
173 DPRINTF(("%s: warnmin exists\n", __func__));
174 error = EEXIST;
175 props &= ~(PROP_WARNMIN | PROP_BATTWARN);
176 }
177 }
178 if (props & edata->upropset & (PROP_CRITMIN | PROP_BATTCAP)) {
179 if (lims->sel_critmin == edata->limits.sel_critmin) {
180 DPRINTF(("%s: critmin exists\n", __func__));
181 error = EEXIST;
182 props &= ~(PROP_CRITMIN | PROP_BATTCAP);
183 }
184 }
185 if (props && see->see_type == PENVSYS_EVENT_NULL)
186 see->see_type = crittype;
187
188 break;
189 }
190 if (crittype == PENVSYS_EVENT_NULL && see != NULL) {
191 mutex_exit(&sme->sme_mtx);
192 return EEXIST;
193 }
194
195 if (see == NULL) {
196 /*
197 * New event requested - allocate a sysmon_envsys event.
198 */
199 see = kmem_zalloc(sizeof(*see), KM_SLEEP);
200 if (see == NULL)
201 return ENOMEM;
202
203 DPRINTF(("%s: dev %s sensor %s: new event\n",
204 __func__, sme->sme_name, edata->desc));
205
206 see->see_type = crittype;
207 see->see_sme = sme;
208 see->see_edata = edata;
209
210 /* Initialize sensor type and previously-sent state */
211
212 see->see_pes.pes_type = powertype;
213
214 switch (crittype) {
215 case PENVSYS_EVENT_CAPACITY:
216 see->see_evstate = ENVSYS_BATTERY_CAPACITY_NORMAL;
217 break;
218 case PENVSYS_EVENT_STATE_CHANGED:
219 if (edata->units == ENVSYS_BATTERY_CAPACITY)
220 see->see_evstate =
221 ENVSYS_BATTERY_CAPACITY_NORMAL;
222 else if (edata->units == ENVSYS_DRIVE)
223 see->see_evstate = ENVSYS_DRIVE_EMPTY;
224 else if (edata->units == ENVSYS_INDICATOR)
225 see->see_evstate = ENVSYS_SVALID;
226 else
227 panic("%s: bad units for "
228 "PENVSYS_EVENT_STATE_CHANGED", __func__);
229 break;
230 case PENVSYS_EVENT_CRITICAL:
231 case PENVSYS_EVENT_LIMITS:
232 default:
233 see->see_evstate = ENVSYS_SVALID;
234 break;
235 }
236 see->see_evvalue = 0;
237
238 (void)strlcpy(see->see_pes.pes_dvname, sme->sme_name,
239 sizeof(see->see_pes.pes_dvname));
240 (void)strlcpy(see->see_pes.pes_sensname, edata->desc,
241 sizeof(see->see_pes.pes_sensname));
242 }
243
244 /*
245 * Limit operation requested.
246 */
247 #define LIMIT_OP(k, l, p) \
248 if (props & p) { \
249 objkey = k; \
250 obj = prop_dictionary_get(sdict, objkey); \
251 if (obj != NULL && \
252 prop_object_type(obj) != PROP_TYPE_NUMBER) { \
253 DPRINTF(("%s: (%s) %s object no TYPE_NUMBER\n", \
254 __func__, sme->sme_name, objkey)); \
255 error = ENOTSUP; \
256 } else { \
257 edata->limits.l = lims->l; \
258 error = sme_sensor_upint32(sdict, objkey,lims->l); \
259 DPRINTF(("%s: (%s) event [sensor=%s type=%d] " \
260 "(%s updated)\n", __func__, sme->sme_name, \
261 edata->desc, crittype, objkey)); \
262 } \
263 if (error && error != EEXIST) \
264 goto out; \
265 edata->upropset |= p; \
266 }
267
268 /* Value-based limits */
269 LIMIT_OP("critical-max", sel_critmax, PROP_CRITMAX);
270 LIMIT_OP("warning-max", sel_warnmax, PROP_WARNMAX);
271 LIMIT_OP("warning-min", sel_warnmin, PROP_WARNMIN);
272 LIMIT_OP("critical-min", sel_critmin, PROP_CRITMIN);
273
274 /* %Capacity-based limits */
275 LIMIT_OP("maximum-capacity", sel_critmax, PROP_BATTMAX);
276 LIMIT_OP("high-capacity", sel_warnmax, PROP_BATTHIGH);
277 LIMIT_OP("warning-capacity", sel_warnmin, PROP_BATTWARN);
278 LIMIT_OP("critical-capacity", sel_critmin, PROP_BATTCAP);
279
280 #undef LIMIT_OP
281
282 if (props & PROP_DRIVER_LIMITS)
283 edata->upropset |= PROP_DRIVER_LIMITS;
284 else
285 edata->upropset &= ~PROP_DRIVER_LIMITS;
286
287 DPRINTF(("%s: (%s) event registered (sensor=%s snum=%d type=%d "
288 "critmin=%" PRIu32 " warnmin=%" PRIu32 " warnmax=%" PRIu32
289 " critmax=%" PRIu32 " props 0x%04x)\n", __func__,
290 see->see_sme->sme_name, see->see_pes.pes_sensname,
291 edata->sensor, see->see_type, edata->limits.sel_critmin,
292 edata->limits.sel_warnmin, edata->limits.sel_warnmax,
293 edata->limits.sel_critmax, edata->upropset));
294 /*
295 * Initialize the events framework if it wasn't initialized before.
296 */
297 if ((sme->sme_flags & SME_CALLOUT_INITIALIZED) == 0)
298 error = sme_events_init(sme);
299
300 /*
301 * If driver requested notification, advise it of new
302 * limit values
303 */
304 if (sme->sme_set_limits)
305 (*sme->sme_set_limits)(sme, edata, &(edata->limits),
306 &(edata->upropset));
307
308 out:
309 if ((error == 0 || error == EEXIST) && osee == NULL)
310 LIST_INSERT_HEAD(&sme->sme_events_list, see, see_list);
311
312 mutex_exit(&sme->sme_mtx);
313
314 return error;
315 }
316
317 /*
318 * sme_event_unregister_all:
319 *
320 * + Unregisters all events associated with a sysmon envsys device.
321 */
322 void
323 sme_event_unregister_all(struct sysmon_envsys *sme)
324 {
325 sme_event_t *see;
326 int evcounter = 0;
327
328 KASSERT(sme != NULL);
329
330 mutex_enter(&sme->sme_mtx);
331 LIST_FOREACH(see, &sme->sme_events_list, see_list) {
332 while (see->see_flags & SEE_EVENT_WORKING)
333 cv_wait(&sme->sme_condvar, &sme->sme_mtx);
334
335 if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0)
336 evcounter++;
337 }
338
339 DPRINTF(("%s: total events %d (%s)\n", __func__,
340 evcounter, sme->sme_name));
341
342 while ((see = LIST_FIRST(&sme->sme_events_list))) {
343 if (evcounter == 0)
344 break;
345
346 if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0) {
347 DPRINTF(("%s: event %s %d removed (%s)\n", __func__,
348 see->see_pes.pes_sensname, see->see_type,
349 sme->sme_name));
350 sme_remove_event(see, sme);
351
352 evcounter--;
353 }
354 }
355
356 if (LIST_EMPTY(&sme->sme_events_list))
357 if (sme->sme_flags & SME_CALLOUT_INITIALIZED)
358 sme_events_destroy(sme);
359 mutex_exit(&sme->sme_mtx);
360 }
361
362 /*
363 * sme_event_unregister:
364 *
365 * + Unregisters an event from the specified sysmon envsys device.
366 */
367 int
368 sme_event_unregister(struct sysmon_envsys *sme, const char *sensor, int type)
369 {
370 sme_event_t *see;
371 bool found = false;
372
373 KASSERT(sensor != NULL);
374
375 mutex_enter(&sme->sme_mtx);
376 LIST_FOREACH(see, &sme->sme_events_list, see_list) {
377 if (strcmp(see->see_pes.pes_sensname, sensor) == 0) {
378 if (see->see_type == type) {
379 found = true;
380 break;
381 }
382 }
383 }
384
385 if (!found) {
386 mutex_exit(&sme->sme_mtx);
387 return EINVAL;
388 }
389
390 /*
391 * Wait for the event to finish its work, remove from the list
392 * and release resouces.
393 */
394 while (see->see_flags & SEE_EVENT_WORKING)
395 cv_wait(&sme->sme_condvar, &sme->sme_mtx);
396
397 DPRINTF(("%s: removed dev=%s sensor=%s type=%d\n",
398 __func__, see->see_pes.pes_dvname, sensor, type));
399
400 sme_remove_event(see, sme);
401
402 mutex_exit(&sme->sme_mtx);
403 return 0;
404 }
405
406 /*
407 * sme_event_unregister_sensor:
408 *
409 * + Unregisters any event associated with a specific sensor
410 * The caller must already own the sme_mtx.
411 */
412 int
413 sme_event_unregister_sensor(struct sysmon_envsys *sme, envsys_data_t *edata)
414 {
415 sme_event_t *see;
416 bool found = false;
417
418 KASSERT(mutex_owned(&sme->sme_mtx));
419 LIST_FOREACH(see, &sme->sme_events_list, see_list) {
420 if (see->see_edata == edata) {
421 found = true;
422 break;
423 }
424 }
425 if (!found)
426 return EINVAL;
427
428 /*
429 * Wait for the event to finish its work, remove from the list
430 * and release resouces.
431 */
432 while (see->see_flags & SEE_EVENT_WORKING)
433 cv_wait(&sme->sme_condvar, &sme->sme_mtx);
434
435 DPRINTF(("%s: removed dev=%s sensor=%s\n",
436 __func__, see->see_pes.pes_dvname, edata->desc));
437
438 sme_remove_event(see, sme);
439
440 return 0;
441 }
442
443 static void
444 sme_remove_event(sme_event_t *see, struct sysmon_envsys *sme)
445 {
446
447 KASSERT(mutex_owned(&sme->sme_mtx));
448
449 if (see->see_edata->flags & ENVSYS_FHAS_ENTROPY)
450 rnd_detach_source(&see->see_edata->rnd_src);
451 LIST_REMOVE(see, see_list);
452 /*
453 * So the events list is empty, we'll do the following:
454 *
455 * - stop and destroy the callout.
456 * - destroy the workqueue.
457 */
458 if (LIST_EMPTY(&sme->sme_events_list))
459 sme_events_destroy(sme);
460
461 kmem_free(see, sizeof(*see));
462 }
463
464 /*
465 * sme_event_drvadd:
466 *
467 * + Registers a new event for a device that had enabled any of
468 * the monitoring flags in the driver.
469 */
470 void
471 sme_event_drvadd(void *arg)
472 {
473 sme_event_drv_t *sed_t = arg;
474 sysmon_envsys_lim_t lims;
475 uint32_t props;
476 int error = 0;
477
478 KASSERT(sed_t != NULL);
479
480 #define SEE_REGEVENT(a, b, c) \
481 do { \
482 if (sed_t->sed_edata->flags & (a)) { \
483 char str[ENVSYS_DESCLEN] = "monitoring-state-"; \
484 \
485 error = sme_event_register(sed_t->sed_sdict, \
486 sed_t->sed_edata, \
487 sed_t->sed_sme, \
488 &lims, props, \
489 (b), \
490 sed_t->sed_powertype); \
491 if (error && error != EEXIST) \
492 printf("%s: failed to add event! " \
493 "error=%d sensor=%s event=%s\n", \
494 __func__, error, \
495 sed_t->sed_edata->desc, (c)); \
496 else { \
497 (void)strlcat(str, (c), sizeof(str)); \
498 prop_dictionary_set_bool(sed_t->sed_sdict, \
499 str, \
500 true); \
501 } \
502 } \
503 } while (/* CONSTCOND */ 0)
504
505 /*
506 * If driver provides a method to retrieve its internal limit
507 * values, call it and use those returned values as initial
508 * limits for event monitoring.
509 */
510 props = 0;
511 if (sed_t->sed_edata->flags & ENVSYS_FMONLIMITS)
512 if (sed_t->sed_sme->sme_get_limits)
513 (*sed_t->sed_sme->sme_get_limits)(sed_t->sed_sme,
514 sed_t->sed_edata,
515 &lims, &props);
516 /*
517 * If driver doesn't provide a way to "absorb" user-specified
518 * limit values, we must monitor all limits ourselves
519 */
520 if (sed_t->sed_sme->sme_set_limits == NULL)
521 props &= ~PROP_DRIVER_LIMITS;
522
523 /* Register the events that were specified */
524
525 SEE_REGEVENT(ENVSYS_FMONCRITICAL,
526 PENVSYS_EVENT_CRITICAL,
527 "critical");
528
529 SEE_REGEVENT(ENVSYS_FMONSTCHANGED,
530 PENVSYS_EVENT_STATE_CHANGED,
531 "state-changed");
532
533 SEE_REGEVENT(ENVSYS_FMONLIMITS,
534 PENVSYS_EVENT_LIMITS,
535 "hw-range-limits");
536
537 SEE_REGEVENT(ENVSYS_FHAS_ENTROPY,
538 PENVSYS_EVENT_NULL,
539 "refresh-event");
540
541 /*
542 * we are done, free memory now.
543 */
544 kmem_free(sed_t, sizeof(*sed_t));
545 }
546
547 /*
548 * sme_events_init:
549 *
550 * + Initialize the events framework for this device.
551 */
552 int
553 sme_events_init(struct sysmon_envsys *sme)
554 {
555 int error = 0;
556
557 KASSERT(sme != NULL);
558 KASSERT(mutex_owned(&sme->sme_mtx));
559
560 error = workqueue_create(&sme->sme_wq, sme->sme_name,
561 sme_events_worker, sme, PRI_NONE, IPL_SOFTCLOCK, WQ_MPSAFE);
562 if (error)
563 return error;
564
565 mutex_init(&sme->sme_callout_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
566 callout_init(&sme->sme_callout, CALLOUT_MPSAFE);
567 callout_setfunc(&sme->sme_callout, sme_events_check, sme);
568 sme->sme_flags |= SME_CALLOUT_INITIALIZED;
569 sme_schedule_callout(sme);
570 DPRINTF(("%s: events framework initialized for '%s'\n",
571 __func__, sme->sme_name));
572
573 return error;
574 }
575
576 /*
577 * sme_schedule_callout
578 *
579 * (Re)-schedule the device's callout timer
580 */
581 void
582 sme_schedule_callout(struct sysmon_envsys *sme)
583 {
584 uint64_t timo;
585
586 KASSERT(sme != NULL);
587
588 if ((sme->sme_flags & SME_CALLOUT_INITIALIZED) == 0)
589 return;
590
591 if (sme->sme_events_timeout)
592 timo = sme->sme_events_timeout * hz;
593 else
594 timo = SME_EVTIMO;
595
596 callout_stop(&sme->sme_callout);
597 callout_schedule(&sme->sme_callout, timo);
598 }
599
600 /*
601 * sme_events_destroy:
602 *
603 * + Destroys the event framework for this device: callout
604 * stopped, workqueue destroyed and callout mutex destroyed.
605 */
606 void
607 sme_events_destroy(struct sysmon_envsys *sme)
608 {
609 KASSERT(mutex_owned(&sme->sme_mtx));
610
611 callout_stop(&sme->sme_callout);
612 workqueue_destroy(sme->sme_wq);
613 mutex_destroy(&sme->sme_callout_mtx);
614 callout_destroy(&sme->sme_callout);
615 sme->sme_flags &= ~SME_CALLOUT_INITIALIZED;
616 DPRINTF(("%s: events framework destroyed for '%s'\n",
617 __func__, sme->sme_name));
618 }
619
620 /*
621 * sysmon_envsys_update_limits
622 *
623 * + If a driver needs to update the limits that it is providing,
624 * we need to update the dictionary data as well as the limits.
625 * This only makes sense if the driver is capable of providing
626 * its limits, and if there is a limits event-monitor.
627 */
628 int
629 sysmon_envsys_update_limits(struct sysmon_envsys *sme, envsys_data_t *edata)
630 {
631 int err;
632
633 sysmon_envsys_acquire(sme, false);
634 if (sme->sme_get_limits == NULL ||
635 (edata->flags & ENVSYS_FMONLIMITS) == 0)
636 err = EINVAL;
637 else
638 err = sme_update_limits(sme, edata);
639 sysmon_envsys_release(sme, false);
640
641 return err;
642 }
643
644 /*
645 * sme_update_limits
646 *
647 * + Internal version of sysmon_envsys_update_limits() to be used
648 * when the device has already been sysmon_envsys_acquire()d.
649 */
650
651 int
652 sme_update_limits(struct sysmon_envsys *sme, envsys_data_t *edata)
653 {
654 prop_dictionary_t sdict = NULL;
655 prop_array_t array = NULL;
656 sysmon_envsys_lim_t lims;
657 sme_event_t *see;
658 uint32_t props = 0;
659
660 /* Find the dictionary for this sensor */
661 array = prop_dictionary_get(sme_propd, sme->sme_name);
662 if (array == NULL ||
663 prop_object_type(array) != PROP_TYPE_ARRAY) {
664 DPRINTF(("%s: array device failed\n", __func__));
665 return EINVAL;
666 }
667
668 sdict = prop_array_get(array, edata->sensor);
669 if (sdict == NULL) {
670 return EINVAL;
671 }
672
673 /* Find the event definition to get its powertype */
674 LIST_FOREACH(see, &sme->sme_events_list, see_list) {
675 if (edata == see->see_edata &&
676 see->see_type == PENVSYS_EVENT_LIMITS)
677 break;
678 }
679 if (see == NULL)
680 return EINVAL;
681
682 /* Update limit values from driver if possible */
683 if (sme->sme_get_limits != NULL)
684 (*sme->sme_get_limits)(sme, edata, &lims, &props);
685
686 /* Update event and dictionary */
687 sme_event_register(sdict, edata, sme, &lims, props,
688 PENVSYS_EVENT_LIMITS, see->see_pes.pes_type);
689
690 return 0;
691 }
692
693 /*
694 * sme_events_check:
695 *
696 * + Passes the events to the workqueue thread and stops
697 * the callout if the 'low-power' condition is triggered.
698 */
699 void
700 sme_events_check(void *arg)
701 {
702 struct sysmon_envsys *sme = arg;
703 sme_event_t *see;
704 uint64_t timo;
705
706 KASSERT(sme != NULL);
707
708 mutex_enter(&sme->sme_callout_mtx);
709 LIST_FOREACH(see, &sme->sme_events_list, see_list) {
710 workqueue_enqueue(sme->sme_wq, &see->see_wk, NULL);
711 see->see_edata->flags |= ENVSYS_FNEED_REFRESH;
712 }
713 if (sme->sme_events_timeout)
714 timo = sme->sme_events_timeout * hz;
715 else
716 timo = SME_EVTIMO;
717 if (!sysmon_low_power)
718 sme_schedule_callout(sme);
719 mutex_exit(&sme->sme_callout_mtx);
720 }
721
722 /*
723 * sme_events_worker:
724 *
725 * + workqueue thread that checks if there's a critical condition
726 * and sends an event if it was triggered.
727 */
728 void
729 sme_events_worker(struct work *wk, void *arg)
730 {
731 sme_event_t *see = (void *)wk;
732 struct sysmon_envsys *sme = see->see_sme;
733 envsys_data_t *edata = see->see_edata;
734
735 KASSERT(wk == &see->see_wk);
736 KASSERT(sme != NULL || edata != NULL);
737
738 mutex_enter(&sme->sme_mtx);
739 see->see_flags |= SEE_EVENT_WORKING;
740 /*
741 * sme_events_check marks the sensors to make us refresh them here.
742 * sme_envsys_refresh_sensor will not call the driver if the driver
743 * does its own setting of the sensor value.
744 */
745 if ((edata->flags & ENVSYS_FNEED_REFRESH) != 0) {
746 /* refresh sensor in device */
747 sysmon_envsys_refresh_sensor(sme, edata);
748 edata->flags &= ~ENVSYS_FNEED_REFRESH;
749 }
750
751 DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d type=%d state=%d units=%d "
752 "value_cur=%d upropset=%d\n", __func__, sme->sme_name, edata->desc,
753 edata->sensor, see->see_type, edata->state, edata->units,
754 edata->value_cur, edata->upropset));
755
756 /* skip the event if current sensor is in invalid state */
757 if (edata->state == ENVSYS_SINVALID)
758 goto out;
759
760 /*
761 * For range limits, if the driver claims responsibility for
762 * limit/range checking, just user driver-supplied status.
763 * Else calculate our own status. Note that driver must
764 * relinquish responsibility for ALL limits if there is even
765 * one limit that it cannot handle!
766 *
767 * If this is a CAPACITY monitor, but the sensor's max_value
768 * is not set, treat it as though the monitor does not exist.
769 */
770 if ((see->see_type == PENVSYS_EVENT_LIMITS ||
771 see->see_type == PENVSYS_EVENT_CAPACITY) &&
772 (edata->upropset & PROP_DRIVER_LIMITS) == 0) {
773 if ((see->see_type == PENVSYS_EVENT_CAPACITY) &&
774 (edata->value_max == 0))
775 edata->state = ENVSYS_SVALID;
776 else if ((edata->upropset & (PROP_CRITMIN | PROP_BATTCAP)) &&
777 (edata->value_cur < edata->limits.sel_critmin))
778 edata->state = ENVSYS_SCRITUNDER;
779 else if ((edata->upropset & (PROP_WARNMIN | PROP_BATTWARN)) &&
780 (edata->value_cur < edata->limits.sel_warnmin))
781 edata->state = ENVSYS_SWARNUNDER;
782 else if ((edata->upropset & (PROP_CRITMAX | PROP_BATTMAX)) &&
783 (edata->value_cur > edata->limits.sel_critmax))
784 edata->state = ENVSYS_SCRITOVER;
785 else if ((edata->upropset & (PROP_WARNMAX | PROP_BATTHIGH)) &&
786 (edata->value_cur > edata->limits.sel_warnmax))
787 edata->state = ENVSYS_SWARNOVER;
788 else
789 edata->state = ENVSYS_SVALID;
790 }
791 sme_deliver_event(see);
792
793 out:
794 see->see_flags &= ~SEE_EVENT_WORKING;
795 cv_broadcast(&sme->sme_condvar);
796 mutex_exit(&sme->sme_mtx);
797 }
798
799 /*
800 * sysmon_envsys_sensor_event
801 *
802 * + Find the monitor event of a particular type for a given sensor
803 * on a device and deliver the event if one is required. If
804 * no event type is specified, deliver all events for the sensor.
805 */
806 void
807 sysmon_envsys_sensor_event(struct sysmon_envsys *sme, envsys_data_t *edata,
808 int ev_type)
809 {
810 sme_event_t *see;
811
812 mutex_enter(&sme->sme_mtx);
813 LIST_FOREACH(see, &sme->sme_events_list, see_list) {
814 if (edata != see->see_edata)
815 continue;
816 if (ev_type == 0 ||
817 ev_type == see->see_type) {
818 sme_deliver_event(see);
819 if (ev_type != 0)
820 break;
821 }
822 }
823 mutex_exit(&sme->sme_mtx);
824 }
825
826 /*
827 * sme_deliver_event:
828 *
829 * + If new sensor state requires it, send an event to powerd
830 *
831 * Must be called with the device's sysmon mutex held
832 * see->see_sme->sme_mtx
833 */
834 void
835 sme_deliver_event(sme_event_t *see)
836 {
837 envsys_data_t *edata = see->see_edata;
838 const struct sme_descr_entry *sdt = NULL;
839 const struct sme_sensor_event *sse = sme_sensor_event;
840 int i, state = 0;
841
842 switch (see->see_type) {
843 case PENVSYS_EVENT_LIMITS:
844 case PENVSYS_EVENT_CAPACITY:
845 /*
846 * Send event if state has changed
847 */
848 if (edata->state == see->see_evstate)
849 break;
850
851 for (i = 0; sse[i].state != -1; i++)
852 if (sse[i].state == edata->state)
853 break;
854
855 if (sse[i].state == -1)
856 break;
857
858 if (edata->state == ENVSYS_SVALID)
859 sysmon_penvsys_event(&see->see_pes,
860 PENVSYS_EVENT_NORMAL);
861 else
862 sysmon_penvsys_event(&see->see_pes, sse[i].event);
863
864 see->see_evstate = edata->state;
865 DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d state=%d send_ev=%d\n",
866 __func__, see->see_sme->sme_name, edata->desc,
867 edata->sensor, edata->state,
868 (edata->state == ENVSYS_SVALID) ? PENVSYS_EVENT_NORMAL :
869 sse[i].event));
870
871 break;
872
873 /*
874 * Send PENVSYS_EVENT_CRITICAL event if:
875 * State has gone from non-CRITICAL to CRITICAL,
876 * State remains CRITICAL and value has changed, or
877 * State has returned from CRITICAL to non-CRITICAL
878 */
879 case PENVSYS_EVENT_CRITICAL:
880 DPRINTF(("%s: CRITICAL: old/new state %d/%d, old/new value "
881 "%d/%d\n", __func__, see->see_evstate, edata->state,
882 see->see_evvalue, edata->value_cur));
883 if (edata->state == ENVSYS_SVALID &&
884 see->see_evstate != ENVSYS_SVALID) {
885 sysmon_penvsys_event(&see->see_pes,
886 PENVSYS_EVENT_NORMAL);
887 see->see_evstate = ENVSYS_SVALID;
888 break;
889 } else if (edata->state != ENVSYS_SCRITICAL)
890 break;
891 if (see->see_evstate != ENVSYS_SCRITICAL ||
892 see->see_evvalue != edata->value_cur) {
893 sysmon_penvsys_event(&see->see_pes,
894 PENVSYS_EVENT_CRITICAL);
895 see->see_evstate = ENVSYS_SCRITICAL;
896 }
897 see->see_evvalue = edata->value_cur;
898 break;
899
900 /*
901 * if value_cur is not normal (battery) or online (drive),
902 * send the event...
903 */
904 case PENVSYS_EVENT_STATE_CHANGED:
905 /*
906 * the state has not been changed, just ignore the event.
907 */
908 if (edata->value_cur == see->see_evvalue)
909 break;
910
911 switch (edata->units) {
912 case ENVSYS_DRIVE:
913 sdt = sme_find_table_entry(SME_DESC_DRIVE_STATES,
914 edata->value_cur);
915 state = ENVSYS_DRIVE_ONLINE;
916 break;
917 case ENVSYS_BATTERY_CAPACITY:
918 sdt = sme_find_table_entry(SME_DESC_BATTERY_CAPACITY,
919 edata->value_cur);
920 state = ENVSYS_BATTERY_CAPACITY_NORMAL;
921 break;
922 case ENVSYS_INDICATOR:
923 sdt = sme_find_table_entry(SME_DESC_INDICATOR,
924 edata->value_cur);
925 state = see->see_evvalue; /* force state change */
926 break;
927 default:
928 panic("%s: bad units for PENVSYS_EVENT_STATE_CHANGED",
929 __func__);
930 }
931
932 if (sdt->type == -1)
933 break;
934
935 /*
936 * copy current state description.
937 */
938 (void)strlcpy(see->see_pes.pes_statedesc, sdt->desc,
939 sizeof(see->see_pes.pes_statedesc));
940
941 if (edata->value_cur == state)
942 /*
943 * state returned to normal condition
944 */
945 sysmon_penvsys_event(&see->see_pes,
946 PENVSYS_EVENT_NORMAL);
947 else
948 /*
949 * state changed to abnormal condition
950 */
951 sysmon_penvsys_event(&see->see_pes, see->see_type);
952
953 see->see_evvalue = edata->value_cur;
954
955 /*
956 * There's no need to continue if it's a drive sensor.
957 */
958 if (edata->units == ENVSYS_DRIVE)
959 break;
960
961 /*
962 * Check if the system is running in low power and send the
963 * event to powerd (if running) or shutdown the system
964 * otherwise.
965 */
966 if (!sysmon_low_power && sme_event_check_low_power()) {
967 struct penvsys_state pes;
968
969 /*
970 * Stop the callout and send the 'low-power' event.
971 */
972 sysmon_low_power = true;
973 callout_stop(&see->see_sme->sme_callout);
974 pes.pes_type = PENVSYS_TYPE_BATTERY;
975 sysmon_penvsys_event(&pes, PENVSYS_EVENT_LOW_POWER);
976 }
977 break;
978 case PENVSYS_EVENT_NULL:
979 break;
980 default:
981 panic("%s: invalid event type %d", __func__, see->see_type);
982 }
983 }
984
985 /*
986 * Returns true if the system is in low power state: an AC adapter
987 * is OFF and all batteries are in LOW/CRITICAL state.
988 */
989 static bool
990 sme_event_check_low_power(void)
991 {
992 if (!sme_acadapter_check())
993 return false;
994
995 return sme_battery_check();
996 }
997
998 /*
999 * Called with the sysmon_envsys device mtx held through the
1000 * workqueue thread.
1001 */
1002 static bool
1003 sme_acadapter_check(void)
1004 {
1005 struct sysmon_envsys *sme;
1006 envsys_data_t *edata;
1007 bool dev = false, sensor = false;
1008
1009 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) {
1010 if (sme->sme_class == SME_CLASS_ACADAPTER) {
1011 dev = true;
1012 break;
1013 }
1014 }
1015
1016 /*
1017 * No AC Adapter devices were found.
1018 */
1019 if (!dev)
1020 return false;
1021
1022 /*
1023 * Check if there's an AC adapter device connected.
1024 */
1025 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
1026 if (edata->units == ENVSYS_INDICATOR) {
1027 sensor = true;
1028 /* refresh current sensor */
1029 sysmon_envsys_refresh_sensor(sme, edata);
1030 if (edata->value_cur)
1031 return false;
1032 }
1033 }
1034
1035 if (!sensor)
1036 return false;
1037
1038 /*
1039 * AC adapter found and not connected.
1040 */
1041 return true;
1042 }
1043
1044 /*
1045 * Called with the sysmon_envsys device mtx held through the
1046 * workqueue thread.
1047 */
1048 static bool
1049 sme_battery_check(void)
1050 {
1051 struct sysmon_envsys *sme;
1052 envsys_data_t *edata;
1053 int batteriesfound = 0;
1054 bool present, batterycap, batterycharge;
1055
1056 /*
1057 * Check for battery devices and its state.
1058 */
1059 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) {
1060 if (sme->sme_class != SME_CLASS_BATTERY)
1061 continue;
1062
1063 present = true;
1064 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
1065 if (edata->units == ENVSYS_INDICATOR &&
1066 !edata->value_cur) {
1067 present = false;
1068 break;
1069 }
1070 }
1071 if (!present)
1072 continue;
1073 /*
1074 * We've found a battery device...
1075 */
1076 batteriesfound++;
1077 batterycap = batterycharge = false;
1078 TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
1079 if (edata->units == ENVSYS_BATTERY_CAPACITY) {
1080 batterycap = true;
1081 if (!sme_battery_critical(edata))
1082 return false;
1083 } else if (edata->units == ENVSYS_BATTERY_CHARGE) {
1084 batterycharge = true;
1085 if (edata->value_cur)
1086 return false;
1087 }
1088 }
1089 if (!batterycap || !batterycharge)
1090 return false;
1091 }
1092
1093 if (!batteriesfound)
1094 return false;
1095
1096 /*
1097 * All batteries in low/critical capacity and discharging.
1098 */
1099 return true;
1100 }
1101
1102 static bool
1103 sme_battery_critical(envsys_data_t *edata)
1104 {
1105 if (edata->value_cur == ENVSYS_BATTERY_CAPACITY_CRITICAL ||
1106 edata->value_cur == ENVSYS_BATTERY_CAPACITY_LOW)
1107 return true;
1108
1109 return false;
1110 }
1111