sysmon_envsys_events.c revision 1.74 1 /* $NetBSD: sysmon_envsys_events.c,v 1.74 2010/01/02 19:02:39 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.74 2010/01/02 19:02:39 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_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 }
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 /*
78 * sme_event_register:
79 *
80 * + Registers a new sysmon envsys event or updates any event
81 * already in the queue.
82 */
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)
87 {
88 sme_event_t *see = NULL, *osee = NULL;
89 prop_object_t obj;
90 int error = 0;
91 const char *objkey;
92
93 KASSERT(sdict != NULL || edata != NULL || sme != NULL);
94
95 /*
96 * check if the event is already on the list and return
97 * EEXIST if value provided hasn't been changed.
98 */
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;
105
106 DPRINTF(("%s: dev %s sensor %s lim_flags 0x%04x event exists\n",
107 __func__, sme->sme_name, edata->desc, lims->sel_flags));
108
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;
116 }
117 }
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;
124 }
125 }
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);
132 }
133 }
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);
140 }
141 }
142 break;
143 }
144 if (see == NULL) {
145 /*
146 * New event requested - allocate a sysmon_envsys event.
147 */
148 see = kmem_zalloc(sizeof(*see), KM_SLEEP);
149 if (see == NULL)
150 return ENOMEM;
151
152 DPRINTF(("%s: dev %s sensor %s lim_flags 0x%04x new event\n",
153 __func__, sme->sme_name, edata->desc, lims->sel_flags));
154
155 see->see_type = crittype;
156 see->see_sme = sme;
157 see->see_edata = edata;
158
159 /* Initialize sensor type and previously-sent state */
160
161 see->see_pes.pes_type = powertype;
162
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;
183 }
184
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));
189 }
190
191 /*
192 * Limit operation requested.
193 */
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));
208 }
209 if (error && error != EEXIST)
210 goto out;
211 see->see_edata->upropset |= PROP_CRITMAX;
212 see->see_lims.sel_flags |= PROP_CRITMAX;
213 }
214
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));
229 }
230 if (error && error != EEXIST)
231 goto out;
232 see->see_edata->upropset |= PROP_WARNMAX;
233 see->see_lims.sel_flags |= PROP_WARNMAX;
234 }
235
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));
250 }
251 if (error && error != EEXIST)
252 goto out;
253 see->see_edata->upropset |= PROP_WARNMIN;
254 see->see_lims.sel_flags |= PROP_WARNMIN;
255 }
256
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));
271 }
272 if (error && error != EEXIST)
273 goto out;
274 see->see_edata->upropset |= PROP_CRITMIN;
275 see->see_lims.sel_flags |= PROP_CRITMIN;
276 }
277
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));
292 }
293 if (error && error != EEXIST)
294 goto out;
295 see->see_edata->upropset |= PROP_BATTWARN;
296 see->see_lims.sel_flags |= PROP_BATTWARN;
297 }
298
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));
313 }
314 if (error && error != EEXIST)
315 goto out;
316 see->see_edata->upropset |= PROP_BATTCAP;
317 see->see_lims.sel_flags |= PROP_BATTCAP;
318 }
319
320 if (lims->sel_flags & PROP_DRIVER_LIMITS)
321 see->see_lims.sel_flags |= PROP_DRIVER_LIMITS;
322
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));
330 /*
331 * Initialize the events framework if it wasn't initialized before.
332 */
333 if ((sme->sme_flags & SME_CALLOUT_INITIALIZED) == 0)
334 error = sme_events_init(sme);
335
336 /*
337 * If driver requested notification, advise it of new
338 * limit values
339 */
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));
344 }
345
346 out:
347 if ((error == 0 || error == EEXIST) && osee == NULL)
348 LIST_INSERT_HEAD(&sme->sme_events_list, see, see_list);
349
350 mutex_exit(&sme->sme_mtx);
351
352 return error;
353 }
354
355 /*
356 * sme_event_unregister_all:
357 *
358 * + Unregisters all events associated with a sysmon envsys device.
359 */
360 void
361 sme_event_unregister_all(struct sysmon_envsys *sme)
362 {
363 sme_event_t *see;
364 int evcounter = 0;
365
366 KASSERT(sme != NULL);
367
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);
372
373 if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0)
374 evcounter++;
375 }
376
377 DPRINTF(("%s: total events %d (%s)\n", __func__,
378 evcounter, sme->sme_name));
379
380 while ((see = LIST_FIRST(&sme->sme_events_list))) {
381 if (evcounter == 0)
382 break;
383
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--;
391 }
392 }
393
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);
398 }
399
400 /*
401 * sme_event_unregister:
402 *
403 * + Unregisters an event from the specified sysmon envsys device.
404 */
405 int
406 sme_event_unregister(struct sysmon_envsys *sme, const char *sensor, int type)
407 {
408 sme_event_t *see;
409 bool found = false;
410
411 KASSERT(sensor != NULL);
412
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;
419 }
420 }
421 }
422
423 if (!found) {
424 mutex_exit(&sme->sme_mtx);
425 return EINVAL;
426 }
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 type=%d\n",
436 __func__, see->see_pes.pes_dvname, sensor, type));
437 LIST_REMOVE(see, see_list);
438 /*
439 * So the events list is empty, we'll do the following:
440 *
441 * - stop and destroy the callout.
442 * - destroy the workqueue.
443 */
444 if (LIST_EMPTY(&sme->sme_events_list))
445 sme_events_destroy(sme);
446 mutex_exit(&sme->sme_mtx);
447
448 kmem_free(see, sizeof(*see));
449 return 0;
450 }
451
452 /*
453 * sme_event_drvadd:
454 *
455 * + Registers a new event for a device that had enabled any of
456 * the monitoring flags in the driver.
457 */
458 void
459 sme_event_drvadd(void *arg)
460 {
461 sme_event_drv_t *sed_t = arg;
462 sysmon_envsys_lim_t lims;
463 int error = 0;
464
465 KASSERT(sed_t != NULL);
466
467 #define SEE_REGEVENT(a, b, c) \
468 do { \
469 if (sed_t->sed_edata->flags & (a)) { \
470 char str[ENVSYS_DESCLEN] = "monitoring-state-"; \
471 \
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); \
488 } \
489 } \
490 } while (/* CONSTCOND */ 0)
491
492 /*
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.
496 */
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);
503 /*
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.
507 */
508 if (lims.sel_flags == 0)
509 sed_t->sed_edata->flags &= ~ENVSYS_FMONLIMITS;
510
511 /*
512 * If driver doesn't provide a way to "absorb" user-specified
513 * limit values, we must monitor all limits ourselves
514 */
515 else if (sed_t->sed_sme->sme_set_limits == NULL)
516 lims.sel_flags &= ~PROP_DRIVER_LIMITS;
517
518 /* Register the events that were specified */
519
520 SEE_REGEVENT(ENVSYS_FMONCRITICAL,
521 PENVSYS_EVENT_CRITICAL,
522 "critical");
523
524 SEE_REGEVENT(ENVSYS_FMONSTCHANGED,
525 PENVSYS_EVENT_STATE_CHANGED,
526 "state-changed");
527
528 SEE_REGEVENT(ENVSYS_FMONLIMITS,
529 PENVSYS_EVENT_LIMITS,
530 "hw-range-limits");
531
532 /*
533 * we are done, free memory now.
534 */
535 kmem_free(sed_t, sizeof(*sed_t));
536 }
537
538 /*
539 * sme_events_init:
540 *
541 * + Initialize the events framework for this device.
542 */
543 int
544 sme_events_init(struct sysmon_envsys *sme)
545 {
546 int error = 0;
547 uint64_t timo;
548
549 KASSERT(sme != NULL);
550 KASSERT(mutex_owned(&sme->sme_mtx));
551
552 if (sme->sme_events_timeout)
553 timo = sme->sme_events_timeout * hz;
554 else
555 timo = SME_EVTIMO;
556
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;
561
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));
569
570 return error;
571 }
572
573 /*
574 * sme_events_destroy:
575 *
576 * + Destroys the event framework for this device: callout
577 * stopped, workqueue destroyed and callout mutex destroyed.
578 */
579 void
580 sme_events_destroy(struct sysmon_envsys *sme)
581 {
582 KASSERT(mutex_owned(&sme->sme_mtx));
583
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));
591 }
592
593 /*
594 * sme_events_check:
595 *
596 * + Passes the events to the workqueue thread and stops
597 * the callout if the 'low-power' condition is triggered.
598 */
599 void
600 sme_events_check(void *arg)
601 {
602 struct sysmon_envsys *sme = arg;
603 sme_event_t *see;
604 uint64_t timo;
605
606 KASSERT(sme != NULL);
607
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;
612 }
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);
620 }
621
622 /*
623 * sme_events_worker:
624 *
625 * + workqueue thread that checks if there's a critical condition
626 * and sends an event if it was triggered.
627 */
628 void
629 sme_events_worker(struct work *wk, void *arg)
630 {
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;
637
638 KASSERT(wk == &see->see_wk);
639 KASSERT(sme != NULL || edata != NULL);
640
641 mutex_enter(&sme->sme_mtx);
642 if ((see->see_flags & SEE_EVENT_WORKING) == 0)
643 see->see_flags |= SEE_EVENT_WORKING;
644 /*
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.
647 */
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;
653 }
654 }
655
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));
660
661 /* skip the event if current sensor is in invalid state */
662 if (edata->state == ENVSYS_SINVALID)
663 goto out;
664
665 switch (see->see_type) {
666 /*
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!
672 */
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)))
678
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;
690 }
691 #undef __EXCEED_LIM
692
693 /*
694 * Send event if state has changed
695 */
696 if (edata->state == see->see_evsent)
697 break;
698
699 for (i = 0; sse[i].state != -1; i++)
700 if (sse[i].state == edata->state)
701 break;
702
703 if (sse[i].state == -1)
704 break;
705
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);
711
712 see->see_evsent = edata->state;
713
714 break;
715
716 /*
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
721 */
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;
733 }
734 break;
735
736 /*
737 * if value_cur is not normal (battery) or online (drive),
738 * send the event...
739 */
740 case PENVSYS_EVENT_STATE_CHANGED:
741 /*
742 * the state has not been changed, just ignore the event.
743 */
744 if (edata->value_cur == see->see_evsent)
745 break;
746
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__);
760 }
761
762 for (i = 0; sdt[i].type != -1; i++)
763 if (sdt[i].type == edata->value_cur)
764 break;
765
766 if (sdt[i].type == -1)
767 break;
768
769 /*
770 * copy current state description.
771 */
772 (void)strlcpy(see->see_pes.pes_statedesc, sdt[i].desc,
773 sizeof(see->see_pes.pes_statedesc));
774
775 if (edata->value_cur == state)
776 /*
777 * state returned to normal condition
778 */
779 sysmon_penvsys_event(&see->see_pes,
780 PENVSYS_EVENT_NORMAL);
781 else
782 /*
783 * state changed to abnormal condition
784 */
785 sysmon_penvsys_event(&see->see_pes, see->see_type);
786
787 see->see_evsent = edata->value_cur;
788
789 /*
790 * There's no need to continue if it's a drive sensor.
791 */
792 if (edata->units == ENVSYS_DRIVE)
793 break;
794
795 /*
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.
799 */
800 if (!sysmon_low_power && sme_event_check_low_power()) {
801 struct penvsys_state pes;
802
803 /*
804 * Stop the callout and send the 'low-power' event.
805 */
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);
810 }
811 break;
812 default:
813 panic("%s: invalid event type %d", __func__, see->see_type);
814 }
815
816 out:
817 see->see_flags &= ~SEE_EVENT_WORKING;
818 cv_broadcast(&sme->sme_condvar);
819 mutex_exit(&sme->sme_mtx);
820 }
821
822 /*
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.
825 */
826 static bool
827 sme_event_check_low_power(void)
828 {
829 if (!sme_acadapter_check())
830 return false;
831
832 return sme_battery_check();
833 }
834
835 /*
836 * Called with the sysmon_envsys device mtx held through the
837 * workqueue thread.
838 */
839 static bool
840 sme_acadapter_check(void)
841 {
842 struct sysmon_envsys *sme;
843 envsys_data_t *edata;
844 bool dev = false, sensor = false;
845
846 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) {
847 if (sme->sme_class == SME_CLASS_ACADAPTER) {
848 dev = true;
849 break;
850 }
851 }
852
853 /*
854 * No AC Adapter devices were found.
855 */
856 if (!dev)
857 return false;
858
859 /*
860 * Check if there's an AC adapter device connected.
861 */
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;
869 }
870 }
871
872 if (!sensor)
873 return false;
874
875 /*
876 * AC adapter found and not connected.
877 */
878 return true;
879 }
880
881 /*
882 * Called with the sysmon_envsys device mtx held through the
883 * workqueue thread.
884 */
885 static bool
886 sme_battery_check(void)
887 {
888 struct sysmon_envsys *sme;
889 envsys_data_t *edata;
890 int batteriesfound = 0;
891 bool present, batterycap, batterycharge;
892
893 /*
894 * Check for battery devices and its state.
895 */
896 LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) {
897 if (sme->sme_class != SME_CLASS_BATTERY)
898 continue;
899
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;
906 }
907 }
908 if (!present)
909 continue;
910 /*
911 * We've found a battery device...
912 */
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;
924 }
925 }
926 if (!batterycap || !batterycharge)
927 return false;
928 }
929
930 if (!batteriesfound)
931 return false;
932
933 /*
934 * All batteries in low/critical capacity and discharging.
935 */
936 return true;
937 }
938
939 static bool
940 sme_battery_critical(envsys_data_t *edata)
941 {
942 if (edata->value_cur == ENVSYS_BATTERY_CAPACITY_CRITICAL ||
943 edata->value_cur == ENVSYS_BATTERY_CAPACITY_LOW)
944 return true;
945
946 return false;
947 }
948