Home | History | Annotate | Line # | Download | only in sysmon
      1 /* $NetBSD: sysmon_envsys_events.c,v 1.123 2021/12/31 14:30:04 riastradh 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.123 2021/12/31 14:30:04 riastradh 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 		mutex_enter(&sme->sme_work_mtx);
    330 		LIST_INSERT_HEAD(&sme->sme_events_list, see, see_list);
    331 		mutex_exit(&sme->sme_work_mtx);
    332 	}
    333 
    334 	mutex_exit(&sme->sme_mtx);
    335 
    336 	return error;
    337 }
    338 
    339 /*
    340  * sme_event_unregister_all:
    341  *
    342  * 	+ Unregisters all events associated with a sysmon envsys device.
    343  */
    344 void
    345 sme_event_unregister_all(struct sysmon_envsys *sme)
    346 {
    347 	sme_event_t *see;
    348 	int evcounter = 0;
    349 	bool destroy = false;
    350 
    351 	KASSERT(sme != NULL);
    352 
    353 	mutex_enter(&sme->sme_mtx);
    354 	LIST_FOREACH(see, &sme->sme_events_list, see_list) {
    355 		while (see->see_flags & SEE_EVENT_WORKING)
    356 			cv_wait(&sme->sme_condvar, &sme->sme_mtx);
    357 
    358 		if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0)
    359 			evcounter++;
    360 	}
    361 
    362 	DPRINTF(("%s: total events %d (%s)\n", __func__,
    363 	    evcounter, sme->sme_name));
    364 
    365 	while ((see = LIST_FIRST(&sme->sme_events_list))) {
    366 		if (evcounter == 0)
    367 			break;
    368 
    369 		if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0) {
    370 			DPRINTF(("%s: event %s %d removed (%s)\n", __func__,
    371 			    see->see_pes.pes_sensname, see->see_type,
    372 			    sme->sme_name));
    373 			sme_remove_event(see, sme);
    374 
    375 			evcounter--;
    376 		}
    377 	}
    378 
    379 	mutex_enter(&sme->sme_work_mtx);
    380 	if (LIST_EMPTY(&sme->sme_events_list) &&
    381 	    sme->sme_callout_state == SME_CALLOUT_READY) {
    382 		sme_events_halt_callout(sme);
    383 		destroy = true;
    384 	}
    385 	mutex_exit(&sme->sme_work_mtx);
    386 	mutex_exit(&sme->sme_mtx);
    387 
    388 	if (destroy)
    389 		sme_events_destroy(sme);
    390 }
    391 
    392 /*
    393  * sme_event_unregister:
    394  *
    395  * 	+ Unregisters an event from the specified sysmon envsys device.
    396  */
    397 int
    398 sme_event_unregister(struct sysmon_envsys *sme, const char *sensor, int type)
    399 {
    400 	sme_event_t *see;
    401 	bool found = false;
    402 	bool destroy = false;
    403 
    404 	KASSERT(sensor != NULL);
    405 
    406 	mutex_enter(&sme->sme_mtx);
    407 	LIST_FOREACH(see, &sme->sme_events_list, see_list) {
    408 		if (strcmp(see->see_pes.pes_sensname, sensor) == 0) {
    409 			if (see->see_type == type) {
    410 				found = true;
    411 				break;
    412 			}
    413 		}
    414 	}
    415 
    416 	if (!found) {
    417 		mutex_exit(&sme->sme_mtx);
    418 		return EINVAL;
    419 	}
    420 
    421 	/*
    422 	 * Wait for the event to finish its work, remove it from the list
    423 	 * and release resources.
    424 	 */
    425 	while (see->see_flags & SEE_EVENT_WORKING)
    426 		cv_wait(&sme->sme_condvar, &sme->sme_mtx);
    427 
    428 	DPRINTF(("%s: removed dev=%s sensor=%s type=%d\n",
    429 	    __func__, see->see_pes.pes_dvname, sensor, type));
    430 
    431 	sme_remove_event(see, sme);
    432 
    433 	mutex_enter(&sme->sme_work_mtx);
    434 	if (LIST_EMPTY(&sme->sme_events_list)) {
    435 		sme_events_halt_callout(sme);
    436 		destroy = true;
    437 	}
    438 	mutex_exit(&sme->sme_work_mtx);
    439 	mutex_exit(&sme->sme_mtx);
    440 
    441 	if (destroy)
    442 		sme_events_destroy(sme);
    443 
    444 	return 0;
    445 }
    446 
    447 /*
    448  * sme_event_unregister_sensor:
    449  *
    450  *	+ Unregisters any event associated with a specific sensor
    451  *	  The caller must already own the sme_mtx.
    452  */
    453 int
    454 sme_event_unregister_sensor(struct sysmon_envsys *sme, envsys_data_t *edata)
    455 {
    456 	sme_event_t *see;
    457 	bool found = false;
    458 
    459 	KASSERT(mutex_owned(&sme->sme_mtx));
    460 	LIST_FOREACH(see, &sme->sme_events_list, see_list) {
    461 		if (see->see_edata == edata) {
    462 			found = true;
    463 			break;
    464 		}
    465 	}
    466 	if (!found)
    467 		return EINVAL;
    468 
    469 	/*
    470 	 * Wait for the event to finish its work, remove it from the list
    471 	 * and release resources.
    472 	 */
    473 	while (see->see_flags & SEE_EVENT_WORKING)
    474 		cv_wait(&sme->sme_condvar, &sme->sme_mtx);
    475 
    476 	DPRINTF(("%s: removed dev=%s sensor=%s\n",
    477 	    __func__, see->see_pes.pes_dvname, edata->desc));
    478 
    479 	sme_remove_event(see, sme);
    480 
    481 	return 0;
    482 }
    483 
    484 static void
    485 sme_remove_event(sme_event_t *see, struct sysmon_envsys *sme)
    486 {
    487 
    488 	KASSERT(mutex_owned(&sme->sme_mtx));
    489 
    490 	mutex_enter(&sme->sme_work_mtx);
    491 	LIST_REMOVE(see, see_list);
    492 	mutex_exit(&sme->sme_work_mtx);
    493 
    494 	kmem_free(see, sizeof(*see));
    495 }
    496 
    497 /*
    498  * sme_event_drvadd:
    499  *
    500  * 	+ Registers a new event for a device that had enabled any of
    501  * 	  the monitoring flags in the driver.
    502  */
    503 void
    504 sme_event_drvadd(void *arg)
    505 {
    506 	sme_event_drv_t *sed_t = arg;
    507 	sysmon_envsys_lim_t lims;
    508 	uint32_t props;
    509 	int error = 0;
    510 	const struct ev_reg_t *reg;
    511 
    512 	KASSERT(sed_t != NULL);
    513 
    514 	/*
    515 	 * If driver provides a method to retrieve its internal limit
    516 	 * values, call it and use those returned values as initial
    517 	 * limits for event monitoring.
    518 	 */
    519 	props = 0;
    520 	if (sed_t->sed_edata->flags & ENVSYS_FMONLIMITS)
    521 		if (sed_t->sed_sme->sme_get_limits)
    522 			(*sed_t->sed_sme->sme_get_limits)(sed_t->sed_sme,
    523 							  sed_t->sed_edata,
    524 							  &lims, &props);
    525 	/*
    526 	 * If driver doesn't provide a way to "absorb" user-specified
    527 	 * limit values, we must monitor all limits ourselves
    528 	 */
    529 	if (sed_t->sed_sme->sme_set_limits == NULL)
    530 		props &= ~PROP_DRIVER_LIMITS;
    531 
    532 	/* Register the events that were specified */
    533 
    534 	for (reg = reg_events; reg->name != NULL; reg++) {
    535 		if (sed_t->sed_edata->flags & reg->crittype) {
    536 
    537 			error = sme_event_register(sed_t->sed_sdict,
    538 					      sed_t->sed_edata,
    539 					      sed_t->sed_sme,
    540 					      &lims, props,
    541 					      reg->powertype,
    542 					      sed_t->sed_powertype);
    543 			if (error && error != EEXIST)
    544 				printf("%s: failed to add event! "
    545 				    "error=%d sensor=%s event=%s\n",
    546 				    __func__, error,
    547 				    sed_t->sed_edata->desc, reg->name);
    548 			else {
    549 				char str[ENVSYS_DESCLEN] = "monitoring-state-";
    550 				(void)strlcat(str, reg->name, sizeof(str));
    551 				prop_dictionary_set_bool(sed_t->sed_sdict,
    552 							 str, true);
    553 			}
    554 		}
    555 	}
    556 
    557 	/*
    558 	 * we are done, free memory now.
    559 	 */
    560 	kmem_free(sed_t, sizeof(*sed_t));
    561 }
    562 
    563 /*
    564  * sme_events_init:
    565  *
    566  * 	+ Initialize the events framework for this device.
    567  */
    568 int
    569 sme_events_init(struct sysmon_envsys *sme)
    570 {
    571 	int error = 0;
    572 
    573 	KASSERT(sme != NULL);
    574 	KASSERT(mutex_owned(&sme->sme_mtx));
    575 
    576 	error = workqueue_create(&sme->sme_wq, sme->sme_name,
    577 	    sme_events_worker, sme, PRI_NONE, IPL_SOFTCLOCK, WQ_MPSAFE);
    578 	if (error)
    579 		return error;
    580 
    581 	callout_init(&sme->sme_callout, CALLOUT_MPSAFE);
    582 	callout_setfunc(&sme->sme_callout, sme_events_check, sme);
    583 
    584 	mutex_enter(&sme->sme_work_mtx);
    585 	sme->sme_callout_state = SME_CALLOUT_READY;
    586 	sme_schedule_callout(sme);
    587 	mutex_exit(&sme->sme_work_mtx);
    588 
    589 	DPRINTF(("%s: events framework initialized for '%s'\n",
    590 	    __func__, sme->sme_name));
    591 
    592 	return error;
    593 }
    594 
    595 /*
    596  * sme_schedule_callout
    597  *
    598  *	(Re)-schedule the device's callout timer
    599  */
    600 void
    601 sme_schedule_callout(struct sysmon_envsys *sme)
    602 {
    603 	uint64_t timo;
    604 
    605 	KASSERT(sme != NULL);
    606 	KASSERT(mutex_owned(&sme->sme_work_mtx));
    607 
    608 	if (sme->sme_callout_state != SME_CALLOUT_READY)
    609 		return;
    610 
    611 	if (sme->sme_events_timeout)
    612 		timo = sme->sme_events_timeout * hz;
    613 	else
    614 		timo = SME_EVTIMO;
    615 
    616 	callout_schedule(&sme->sme_callout, timo);
    617 }
    618 
    619 /*
    620  * sme_events_halt_callout:
    621  *
    622  * 	+ Halt the callout of the event framework for this device.
    623  */
    624 void
    625 sme_events_halt_callout(struct sysmon_envsys *sme)
    626 {
    627 
    628 	KASSERT(mutex_owned(&sme->sme_mtx));
    629 	KASSERT(sme->sme_callout_state == SME_CALLOUT_READY);
    630 
    631 	/*
    632 	 * Set HALTED before callout_halt to ensure callout is not
    633 	 * scheduled again during callout_halt.  (callout_halt()
    634 	 * can potentially release the mutex, so an active callout
    635 	 * could reschedule itself if it grabs the mutex.)
    636 	 */
    637 
    638 	sme->sme_callout_state = SME_CALLOUT_HALTED;
    639 	callout_halt(&sme->sme_callout, &sme->sme_mtx);
    640 }
    641 
    642 /*
    643  * sme_events_destroy:
    644  *
    645  * 	+ Destroy the callout and the workqueue of the event framework
    646  *	  for this device.
    647  */
    648 void
    649 sme_events_destroy(struct sysmon_envsys *sme)
    650 {
    651 
    652 	KASSERT(!mutex_owned(&sme->sme_mtx));
    653 
    654 	if (sme->sme_callout_state == SME_CALLOUT_HALTED) {
    655 		callout_destroy(&sme->sme_callout);
    656 		sme->sme_callout_state = SME_CALLOUT_INVALID;
    657 		workqueue_destroy(sme->sme_wq);
    658 	}
    659 	KASSERT(sme->sme_callout_state == SME_CALLOUT_INVALID);
    660 
    661 	DPRINTF(("%s: events framework destroyed for '%s'\n",
    662 	    __func__, sme->sme_name));
    663 }
    664 
    665 /*
    666  * sysmon_envsys_update_limits
    667  *
    668  *	+ If a driver needs to update the limits that it is providing,
    669  *	  we need to update the dictionary data as well as the limits.
    670  *	  This only makes sense if the driver is capable of providing
    671  *	  its limits, and if there is a limits event-monitor.
    672  */
    673 int
    674 sysmon_envsys_update_limits(struct sysmon_envsys *sme, envsys_data_t *edata)
    675 {
    676 	int err;
    677 
    678 	sysmon_envsys_acquire(sme, false);
    679 	if (sme->sme_get_limits == NULL ||
    680 	    (edata->flags & ENVSYS_FMONLIMITS) == 0)
    681 		err = EINVAL;
    682 	else
    683 		err = sme_update_limits(sme, edata);
    684 	sysmon_envsys_release(sme, false);
    685 
    686 	return err;
    687 }
    688 
    689 /*
    690  * sme_update_limits
    691  *
    692  *	+ Internal version of sysmon_envsys_update_limits() to be used
    693  *	  when the device has already been sysmon_envsys_acquire()d.
    694  */
    695 
    696 int
    697 sme_update_limits(struct sysmon_envsys *sme, envsys_data_t *edata)
    698 {
    699 	prop_dictionary_t sdict = NULL;
    700 	prop_array_t array = NULL;
    701 	sysmon_envsys_lim_t lims;
    702 	sme_event_t *see;
    703 	uint32_t props = 0;
    704 
    705 	/* Find the dictionary for this sensor */
    706 	array = prop_dictionary_get(sme_propd, sme->sme_name);
    707 	if (array == NULL ||
    708 	    prop_object_type(array) != PROP_TYPE_ARRAY) {
    709 		DPRINTF(("%s: array device failed\n", __func__));
    710 		return EINVAL;
    711 	}
    712 
    713 	sdict = prop_array_get(array, edata->sensor);
    714 	if (sdict == NULL) {
    715 		return EINVAL;
    716 	}
    717 
    718 	/* Find the event definition to get its powertype */
    719 	LIST_FOREACH(see, &sme->sme_events_list, see_list) {
    720 		if (edata == see->see_edata &&
    721 		    see->see_type == PENVSYS_EVENT_LIMITS)
    722 			break;
    723 	}
    724 	if (see == NULL)
    725 		return EINVAL;
    726 
    727 	/* Update limit values from driver if possible */
    728 	if (sme->sme_get_limits != NULL)
    729 		(*sme->sme_get_limits)(sme, edata, &lims, &props);
    730 
    731 	/* Update event and dictionary */
    732 	sme_event_register(sdict, edata, sme, &lims, props,
    733 			   PENVSYS_EVENT_LIMITS, see->see_pes.pes_type);
    734 
    735 	return 0;
    736 }
    737 
    738 /*
    739  * sme_events_check:
    740  *
    741  * 	+ Passes the events to the workqueue thread and stops
    742  * 	  the callout if the 'low-power' condition is triggered.
    743  */
    744 void
    745 sme_events_check(void *arg)
    746 {
    747 	struct sysmon_envsys *sme = arg;
    748 	sme_event_t *see;
    749 
    750 	KASSERT(sme != NULL);
    751 
    752 	mutex_enter(&sme->sme_work_mtx);
    753 	if (sme->sme_busy > 0) {
    754 		log(LOG_WARNING, "%s: workqueue busy: updates stopped\n",
    755 		    sme->sme_name);
    756 		mutex_exit(&sme->sme_work_mtx);
    757 		return;
    758 	}
    759 	LIST_FOREACH(see, &sme->sme_events_list, see_list) {
    760 		workqueue_enqueue(sme->sme_wq, &see->see_wk, NULL);
    761 		see->see_edata->flags |= ENVSYS_FNEED_REFRESH;
    762 		sme->sme_busy++;
    763 	}
    764 	if (!sysmon_low_power)
    765 		sme_schedule_callout(sme);
    766 	mutex_exit(&sme->sme_work_mtx);
    767 }
    768 
    769 /*
    770  * sme_events_worker:
    771  *
    772  * 	+ workqueue thread that checks if there's a critical condition
    773  * 	  and sends an event if it was triggered.
    774  */
    775 void
    776 sme_events_worker(struct work *wk, void *arg)
    777 {
    778 	sme_event_t *see = (void *)wk;
    779 	struct sysmon_envsys *sme = see->see_sme;
    780 	envsys_data_t *edata = see->see_edata;
    781 
    782 	KASSERT(wk == &see->see_wk);
    783 	KASSERT(sme != NULL);
    784 	KASSERT(edata != NULL);
    785 
    786 	mutex_enter(&sme->sme_mtx);
    787 	see->see_flags |= SEE_EVENT_WORKING;
    788 	/*
    789 	 * sme_events_check marks the sensors to make us refresh them here.
    790 	 * sme_envsys_refresh_sensor will not call the driver if the driver
    791 	 * does its own setting of the sensor value.
    792 	 */
    793 	mutex_enter(&sme->sme_work_mtx);
    794 	if ((edata->flags & ENVSYS_FNEED_REFRESH) != 0) {
    795 		/* refresh sensor in device */
    796 		mutex_exit(&sme->sme_work_mtx);
    797 		sysmon_envsys_refresh_sensor(sme, edata);
    798 		mutex_enter(&sme->sme_work_mtx);
    799 		edata->flags &= ~ENVSYS_FNEED_REFRESH;
    800 	}
    801 	mutex_exit(&sme->sme_work_mtx);
    802 
    803 	DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d type=%d state=%d units=%d "
    804 	    "value_cur=%d upropset=0x%04x\n", __func__, sme->sme_name, edata->desc,
    805 	    edata->sensor, see->see_type, edata->state, edata->units,
    806 	    edata->value_cur, edata->upropset));
    807 
    808 	/* skip the event if current sensor is in invalid state */
    809 	if (edata->state == ENVSYS_SINVALID)
    810 		goto out;
    811 
    812 	/*
    813 	 * For range limits, if the driver claims responsibility for
    814 	 * limit/range checking, just user driver-supplied status.
    815 	 * Else calculate our own status.  Note that driver must
    816 	 * relinquish responsibility for ALL limits if there is even
    817 	 * one limit that it cannot handle!
    818 	 *
    819 	 * If this is a CAPACITY monitor, but the sensor's max_value
    820 	 * is not set, treat it as though the monitor does not exist.
    821 	 */
    822 	if ((see->see_type == PENVSYS_EVENT_LIMITS ||
    823 	     see->see_type == PENVSYS_EVENT_CAPACITY) &&
    824 	    (edata->upropset & PROP_DRIVER_LIMITS) == 0) {
    825 		if ((see->see_type == PENVSYS_EVENT_CAPACITY) &&
    826 		    (edata->value_max == 0))
    827 			edata->state = ENVSYS_SVALID;
    828 		else if ((edata->upropset & (PROP_CRITMIN | PROP_BATTCAP)) &&
    829 		    (edata->value_cur < edata->limits.sel_critmin))
    830 			edata->state = ENVSYS_SCRITUNDER;
    831 		else if ((edata->upropset & (PROP_WARNMIN | PROP_BATTWARN)) &&
    832 			 (edata->value_cur < edata->limits.sel_warnmin))
    833 			edata->state = ENVSYS_SWARNUNDER;
    834 		else if ((edata->upropset & (PROP_CRITMAX | PROP_BATTMAX)) &&
    835 			 (edata->value_cur > edata->limits.sel_critmax))
    836 			edata->state = ENVSYS_SCRITOVER;
    837 		else if ((edata->upropset & (PROP_WARNMAX | PROP_BATTHIGH)) &&
    838 			 (edata->value_cur > edata->limits.sel_warnmax))
    839 			edata->state = ENVSYS_SWARNOVER;
    840 		else
    841 			edata->state = ENVSYS_SVALID;
    842 	}
    843 	sme_deliver_event(see);
    844 
    845 out:
    846 	see->see_flags &= ~SEE_EVENT_WORKING;
    847 	cv_broadcast(&sme->sme_condvar);
    848 	mutex_enter(&sme->sme_work_mtx);
    849 	KASSERT(sme->sme_busy > 0);
    850 	sme->sme_busy--;
    851 	mutex_exit(&sme->sme_work_mtx);
    852 	mutex_exit(&sme->sme_mtx);
    853 }
    854 
    855 /*
    856  * sysmon_envsys_sensor_event
    857  *
    858  *	+ Find the monitor event of a particular type for a given sensor
    859  *	  on a device and deliver the event if one is required.  If
    860  *	  no event type is specified, deliver all events for the sensor.
    861  */
    862 void
    863 sysmon_envsys_sensor_event(struct sysmon_envsys *sme, envsys_data_t *edata,
    864 			   int ev_type)
    865 {
    866 	sme_event_t *see;
    867 
    868 	mutex_enter(&sme->sme_mtx);
    869 	LIST_FOREACH(see, &sme->sme_events_list, see_list) {
    870 		if (edata != see->see_edata)
    871 			continue;
    872 		if (ev_type == 0 ||
    873 		    ev_type == see->see_type) {
    874 			sme_deliver_event(see);
    875 			if (ev_type != 0)
    876 				break;
    877 		}
    878 	}
    879 	mutex_exit(&sme->sme_mtx);
    880 }
    881 
    882 /*
    883  * sme_deliver_event:
    884  *
    885  * 	+ If new sensor state requires it, send an event to powerd
    886  *
    887  *	  Must be called with the device's sysmon mutex held
    888  *		see->see_sme->sme_mtx
    889  */
    890 void
    891 sme_deliver_event(sme_event_t *see)
    892 {
    893 	envsys_data_t *edata = see->see_edata;
    894 	const struct sme_descr_entry *sdt = NULL;
    895 	const struct sme_sensor_event *sse = sme_sensor_event;
    896 	int i, state = 0;
    897 
    898 	switch (see->see_type) {
    899 	case PENVSYS_EVENT_LIMITS:
    900 	case PENVSYS_EVENT_CAPACITY:
    901 		/*
    902 		 * Send event if state has changed
    903 		 */
    904 		if (edata->state == see->see_evstate)
    905 			break;
    906 
    907 		for (i = 0; sse[i].state != -1; i++)
    908 			if (sse[i].state == edata->state)
    909 				break;
    910 
    911 		if (sse[i].state == -1)
    912 			break;
    913 
    914 		if (edata->state == ENVSYS_SVALID)
    915 			sysmon_penvsys_event(&see->see_pes,
    916 					     PENVSYS_EVENT_NORMAL);
    917 		else
    918 			sysmon_penvsys_event(&see->see_pes, sse[i].event);
    919 
    920 		see->see_evstate = edata->state;
    921 		DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d state=%d send_ev=%d\n",
    922 		    __func__, see->see_sme->sme_name, edata->desc,
    923 		    edata->sensor, edata->state,
    924 		    (edata->state == ENVSYS_SVALID) ? PENVSYS_EVENT_NORMAL :
    925 			sse[i].event));
    926 
    927 		break;
    928 
    929 	/*
    930 	 * Send PENVSYS_EVENT_CRITICAL event if:
    931 	 *	State has gone from non-CRITICAL to CRITICAL,
    932 	 *	State remains CRITICAL and value has changed, or
    933 	 *	State has returned from CRITICAL to non-CRITICAL
    934 	 */
    935 	case PENVSYS_EVENT_CRITICAL:
    936 		DPRINTF(("%s: CRITICAL: old/new state %d/%d, old/new value "
    937 		    "%d/%d\n", __func__, see->see_evstate, edata->state,
    938 		    see->see_evvalue, edata->value_cur));
    939 		if (edata->state == ENVSYS_SVALID &&
    940 		    see->see_evstate != ENVSYS_SVALID) {
    941 			sysmon_penvsys_event(&see->see_pes,
    942 					     PENVSYS_EVENT_NORMAL);
    943 			see->see_evstate = ENVSYS_SVALID;
    944 			break;
    945 		} else if (edata->state != ENVSYS_SCRITICAL)
    946 			break;
    947 		if (see->see_evstate != ENVSYS_SCRITICAL ||
    948 		    see->see_evvalue != edata->value_cur) {
    949 			sysmon_penvsys_event(&see->see_pes,
    950 					     PENVSYS_EVENT_CRITICAL);
    951 			see->see_evstate = ENVSYS_SCRITICAL;
    952 		}
    953 		see->see_evvalue = edata->value_cur;
    954 		break;
    955 
    956 	/*
    957 	 * if value_cur is not normal (battery) or online (drive),
    958 	 * send the event...
    959 	 */
    960 	case PENVSYS_EVENT_STATE_CHANGED:
    961 		/*
    962 		 * the state has not been changed, just ignore the event.
    963 		 */
    964 		if (edata->value_cur == see->see_evvalue)
    965 			break;
    966 
    967 		switch (edata->units) {
    968 		case ENVSYS_DRIVE:
    969 			sdt = sme_find_table_entry(SME_DESC_DRIVE_STATES,
    970 			    edata->value_cur);
    971 			state = ENVSYS_DRIVE_ONLINE;
    972 			break;
    973 		case ENVSYS_BATTERY_CAPACITY:
    974 			sdt = sme_find_table_entry(SME_DESC_BATTERY_CAPACITY,
    975 			    edata->value_cur);
    976 			state = ENVSYS_BATTERY_CAPACITY_NORMAL;
    977 			break;
    978 		case ENVSYS_INDICATOR:
    979 			sdt = sme_find_table_entry(SME_DESC_INDICATOR,
    980 			    edata->value_cur);
    981 			state = see->see_evvalue;	/* force state change */
    982 			break;
    983 		default:
    984 			panic("%s: bad units for PENVSYS_EVENT_STATE_CHANGED",
    985 			    __func__);
    986 		}
    987 
    988 		if (sdt->type == -1)
    989 			break;
    990 
    991 		/*
    992 		 * copy current state description.
    993 		 */
    994 		(void)strlcpy(see->see_pes.pes_statedesc, sdt->desc,
    995 		    sizeof(see->see_pes.pes_statedesc));
    996 
    997 		if (edata->value_cur == state)
    998 			/*
    999 			 * state returned to normal condition
   1000 			 */
   1001 			sysmon_penvsys_event(&see->see_pes,
   1002 					     PENVSYS_EVENT_NORMAL);
   1003 		else
   1004 			/*
   1005 			 * state changed to abnormal condition
   1006 			 */
   1007 			sysmon_penvsys_event(&see->see_pes, see->see_type);
   1008 
   1009 		see->see_evvalue = edata->value_cur;
   1010 
   1011 		/*
   1012 		 * There's no need to continue if it's a drive sensor.
   1013 		 */
   1014 		if (edata->units == ENVSYS_DRIVE)
   1015 			break;
   1016 
   1017 		/*
   1018 		 * Check if the system is running in low power and send the
   1019 		 * event to powerd (if running) or shutdown the system
   1020 		 * otherwise.
   1021 		 */
   1022 		if (!sysmon_low_power && sme_event_check_low_power()) {
   1023 			struct penvsys_state pes;
   1024 			struct sysmon_envsys *sme = see->see_sme;
   1025 
   1026 			/*
   1027 			 * Stop the callout and send the 'low-power' event.
   1028 			 */
   1029 			sysmon_low_power = true;
   1030 			KASSERT(mutex_owned(&sme->sme_mtx));
   1031 			KASSERT(sme->sme_callout_state == SME_CALLOUT_READY);
   1032 			callout_stop(&sme->sme_callout);
   1033 			sme->sme_callout_state = SME_CALLOUT_HALTED;
   1034 			pes.pes_type = PENVSYS_TYPE_BATTERY;
   1035 			sysmon_penvsys_event(&pes, PENVSYS_EVENT_LOW_POWER);
   1036 		}
   1037 		break;
   1038 	case PENVSYS_EVENT_NULL:
   1039 		break;
   1040 	default:
   1041 		panic("%s: invalid event type %d", __func__, see->see_type);
   1042 	}
   1043 }
   1044 
   1045 /*
   1046  * Returns true if the system is in low power state: an AC adapter
   1047  * is OFF and all batteries are in LOW/CRITICAL state.
   1048  */
   1049 static bool
   1050 sme_event_check_low_power(void)
   1051 {
   1052 	if (!sme_acadapter_check())
   1053 		return false;
   1054 
   1055 	return sme_battery_check();
   1056 }
   1057 
   1058 /*
   1059  * Called with the sysmon_envsys device mtx held through the
   1060  * workqueue thread.
   1061  */
   1062 static bool
   1063 sme_acadapter_check(void)
   1064 {
   1065 	struct sysmon_envsys *sme;
   1066 	envsys_data_t *edata;
   1067 	bool dev = false, sensor = false;
   1068 
   1069 	LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) {
   1070 		if (sme->sme_class == SME_CLASS_ACADAPTER) {
   1071 			dev = true;
   1072 			break;
   1073 		}
   1074 	}
   1075 
   1076 	/*
   1077 	 * No AC Adapter devices were found.
   1078 	 */
   1079 	if (!dev)
   1080 		return false;
   1081 
   1082 	/*
   1083 	 * Check if there's an AC adapter device connected.
   1084 	 */
   1085 	TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
   1086 		if (edata->units == ENVSYS_INDICATOR) {
   1087 			sensor = true;
   1088 			/* refresh current sensor */
   1089 			sysmon_envsys_refresh_sensor(sme, edata);
   1090 
   1091 			if (edata->value_cur)
   1092 				return false;
   1093 		}
   1094 	}
   1095 
   1096 	if (!sensor)
   1097 		return false;
   1098 
   1099 	/*
   1100 	 * AC adapter found and not connected.
   1101 	 */
   1102 	return true;
   1103 }
   1104 
   1105 /*
   1106  * Called with the sysmon_envsys device mtx held through the
   1107  * workqueue thread.
   1108  */
   1109 static bool
   1110 sme_battery_check(void)
   1111 {
   1112 	struct sysmon_envsys *sme;
   1113 	envsys_data_t *edata;
   1114 	int batteriesfound = 0;
   1115 	bool present, batterycap, batterycharge;
   1116 
   1117 	/*
   1118 	 * Check for battery devices and its state.
   1119 	 */
   1120 	LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) {
   1121 		if (sme->sme_class != SME_CLASS_BATTERY)
   1122 			continue;
   1123 
   1124 		present = true;
   1125 
   1126 		/*
   1127 		 * XXX
   1128 		 * this assumes that the first valid ENVSYS_INDICATOR is the
   1129 		 * presence indicator
   1130 		 */
   1131 		TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
   1132 			if ((edata->units == ENVSYS_INDICATOR) &&
   1133 			    (edata->state == ENVSYS_SVALID)) {
   1134 				present = edata->value_cur;
   1135 				break;
   1136 			}
   1137 		}
   1138 		if (!present)
   1139 			continue;
   1140 		/*
   1141 		 * We've found a battery device...
   1142 		 */
   1143 		batteriesfound++;
   1144 		batterycap = batterycharge = false;
   1145 		TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
   1146 			/* no need to even look at sensors that aren't valid */
   1147 			if (edata->state != ENVSYS_SVALID)
   1148 				continue;
   1149 			if (edata->units == ENVSYS_BATTERY_CAPACITY) {
   1150 				batterycap = true;
   1151 				if (!sme_battery_critical(edata))
   1152 					return false;
   1153 			} else if (edata->units == ENVSYS_BATTERY_CHARGE) {
   1154 				batterycharge = true;
   1155 				if (edata->value_cur)
   1156 					return false;
   1157 			}
   1158 		}
   1159 		if (!batterycap || !batterycharge)
   1160 			return false;
   1161 	}
   1162 
   1163 	if (!batteriesfound)
   1164 		return false;
   1165 
   1166 	/*
   1167 	 * All batteries in low/critical capacity and discharging.
   1168 	 */
   1169 	return true;
   1170 }
   1171 
   1172 static bool
   1173 sme_battery_critical(envsys_data_t *edata)
   1174 {
   1175 	if (edata->value_cur == ENVSYS_BATTERY_CAPACITY_CRITICAL ||
   1176 	    edata->value_cur == ENVSYS_BATTERY_CAPACITY_LOW)
   1177 		return true;
   1178 
   1179 	return false;
   1180 }
   1181