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