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