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