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