Home | History | Annotate | Line # | Download | only in sysmon
sysmon_envsys_events.c revision 1.87
      1 /* $NetBSD: sysmon_envsys_events.c,v 1.87 2010/03/19 02:19:13 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.87 2010/03/19 02:19:13 pgoyette Exp $");
     34 
     35 #include <sys/param.h>
     36 #include <sys/types.h>
     37 #include <sys/conf.h>
     38 #include <sys/errno.h>
     39 #include <sys/kernel.h>
     40 #include <sys/systm.h>
     41 #include <sys/proc.h>
     42 #include <sys/mutex.h>
     43 #include <sys/kmem.h>
     44 #include <sys/callout.h>
     45 
     46 /* #define ENVSYS_DEBUG */
     47 /* #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 edata-flags 0x%04x\n",
    117 		__func__, edata->units, props, edata->flags));
    118 
    119 	if (props && edata->units == ENVSYS_INDICATOR)
    120 		return ENOTSUP;
    121 
    122 	if ((props & PROP_CAP_LIMITS) &&
    123 	    ((edata->value_max == 0) ||
    124 	     !(edata->flags & ENVSYS_FPERCENT) ||
    125 	     (props & PROP_VAL_LIMITS) ||
    126 	     (edata->upropset & PROP_VAL_LIMITS)))
    127 		return ENOTSUP;
    128 
    129 	if ((props & PROP_VAL_LIMITS) && (edata->upropset & PROP_CAP_LIMITS))
    130 		return ENOTSUP;
    131 
    132 	/*
    133 	 * check if the event is already on the list and return
    134 	 * EEXIST if value provided hasn't been changed.
    135 	 */
    136 	mutex_enter(&sme->sme_mtx);
    137 	LIST_FOREACH(osee, &sme->sme_events_list, see_list) {
    138 		if (strcmp(edata->desc, osee->see_pes.pes_sensname) != 0)
    139 			continue;
    140 		if (crittype != osee->see_type)
    141 			continue;
    142 
    143 		/*
    144 		 * We found an existing event for this sensor.  Make
    145 		 * sure it references the correct edata
    146 		 */
    147 		KASSERT(edata == osee->see_edata);
    148 
    149 		DPRINTF(("%s: dev %s sensor %s: event type %d exists\n",
    150 		    __func__, sme->sme_name, edata->desc, crittype));
    151 
    152 		see = osee;
    153 		if (props & (PROP_CRITMAX | PROP_BATTMAX)) {
    154 			if (lims->sel_critmax == edata->limits.sel_critmax) {
    155 				DPRINTF(("%s: type=%d (critmax exists)\n",
    156 				    __func__, crittype));
    157 				error = EEXIST;
    158 				props &= ~(PROP_CRITMAX | PROP_BATTMAX);
    159 			}
    160 		}
    161 		if (props & (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 & (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 & (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 no values returned, don't create the event monitor at
    455 	 * this time.  We'll get another chance later when the user
    456 	 * provides us with limits.
    457 	 */
    458 	if (props == 0)
    459 		sed_t->sed_edata->flags &= ~ENVSYS_FMONLIMITS;
    460 
    461 	/*
    462 	 * If driver doesn't provide a way to "absorb" user-specified
    463 	 * limit values, we must monitor all limits ourselves
    464 	 */
    465 	else if (sed_t->sed_sme->sme_set_limits == NULL)
    466 		props &= ~PROP_DRIVER_LIMITS;
    467 
    468 	/* Register the events that were specified */
    469 
    470 	SEE_REGEVENT(ENVSYS_FMONCRITICAL,
    471 		     PENVSYS_EVENT_CRITICAL,
    472 		     "critical");
    473 
    474 	SEE_REGEVENT(ENVSYS_FMONSTCHANGED,
    475 		     PENVSYS_EVENT_STATE_CHANGED,
    476 		     "state-changed");
    477 
    478 	SEE_REGEVENT(ENVSYS_FMONLIMITS,
    479 		     PENVSYS_EVENT_LIMITS,
    480 		     "hw-range-limits");
    481 
    482 	/*
    483 	 * we are done, free memory now.
    484 	 */
    485 	kmem_free(sed_t, sizeof(*sed_t));
    486 }
    487 
    488 /*
    489  * sme_events_init:
    490  *
    491  * 	+ Initialize the events framework for this device.
    492  */
    493 int
    494 sme_events_init(struct sysmon_envsys *sme)
    495 {
    496 	int error = 0;
    497 	uint64_t timo;
    498 
    499 	KASSERT(sme != NULL);
    500 	KASSERT(mutex_owned(&sme->sme_mtx));
    501 
    502 	if (sme->sme_events_timeout)
    503 		timo = sme->sme_events_timeout * hz;
    504 	else
    505 		timo = SME_EVTIMO;
    506 
    507 	error = workqueue_create(&sme->sme_wq, sme->sme_name,
    508 	    sme_events_worker, sme, PRI_NONE, IPL_SOFTCLOCK, WQ_MPSAFE);
    509 	if (error)
    510 		return error;
    511 
    512 	mutex_init(&sme->sme_callout_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
    513 	callout_init(&sme->sme_callout, CALLOUT_MPSAFE);
    514 	callout_setfunc(&sme->sme_callout, sme_events_check, sme);
    515 	callout_schedule(&sme->sme_callout, timo);
    516 	sme->sme_flags |= SME_CALLOUT_INITIALIZED;
    517 	DPRINTF(("%s: events framework initialized for '%s'\n",
    518 	    __func__, sme->sme_name));
    519 
    520 	return error;
    521 }
    522 
    523 /*
    524  * sme_events_destroy:
    525  *
    526  * 	+ Destroys the event framework for this device: callout
    527  * 	  stopped, workqueue destroyed and callout mutex destroyed.
    528  */
    529 void
    530 sme_events_destroy(struct sysmon_envsys *sme)
    531 {
    532 	KASSERT(mutex_owned(&sme->sme_mtx));
    533 
    534 	callout_stop(&sme->sme_callout);
    535 	workqueue_destroy(sme->sme_wq);
    536 	mutex_destroy(&sme->sme_callout_mtx);
    537 	callout_destroy(&sme->sme_callout);
    538 	sme->sme_flags &= ~SME_CALLOUT_INITIALIZED;
    539 	DPRINTF(("%s: events framework destroyed for '%s'\n",
    540 	    __func__, sme->sme_name));
    541 }
    542 
    543 /*
    544  * sme_events_check:
    545  *
    546  * 	+ Passes the events to the workqueue thread and stops
    547  * 	  the callout if the 'low-power' condition is triggered.
    548  */
    549 void
    550 sme_events_check(void *arg)
    551 {
    552 	struct sysmon_envsys *sme = arg;
    553 	sme_event_t *see;
    554 	uint64_t timo;
    555 
    556 	KASSERT(sme != NULL);
    557 
    558 	mutex_enter(&sme->sme_callout_mtx);
    559 	LIST_FOREACH(see, &sme->sme_events_list, see_list) {
    560 		workqueue_enqueue(sme->sme_wq, &see->see_wk, NULL);
    561 		see->see_edata->flags |= ENVSYS_FNEED_REFRESH;
    562 	}
    563 	if (sme->sme_events_timeout)
    564 		timo = sme->sme_events_timeout * hz;
    565 	else
    566 		timo = SME_EVTIMO;
    567 	if (!sysmon_low_power)
    568 		callout_schedule(&sme->sme_callout, timo);
    569 	mutex_exit(&sme->sme_callout_mtx);
    570 }
    571 
    572 /*
    573  * sme_events_worker:
    574  *
    575  * 	+ workqueue thread that checks if there's a critical condition
    576  * 	  and sends an event if it was triggered.
    577  */
    578 void
    579 sme_events_worker(struct work *wk, void *arg)
    580 {
    581 	sme_event_t *see = (void *)wk;
    582 	struct sysmon_envsys *sme = see->see_sme;
    583 	envsys_data_t *edata = see->see_edata;
    584 
    585 	KASSERT(wk == &see->see_wk);
    586 	KASSERT(sme != NULL || edata != NULL);
    587 
    588 	mutex_enter(&sme->sme_mtx);
    589 	see->see_flags |= SEE_EVENT_WORKING;
    590 	/*
    591 	 * sme_events_check marks the sensors to make us refresh them here.
    592 	 * Don't refresh if the driver uses its own method for refreshing.
    593 	 */
    594 	if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) {
    595 		if ((edata->flags & ENVSYS_FNEED_REFRESH) != 0) {
    596 			/* refresh sensor in device */
    597 			(*sme->sme_refresh)(sme, edata);
    598 			edata->flags &= ~ENVSYS_FNEED_REFRESH;
    599 		}
    600 	}
    601 
    602 	DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d type=%d state=%d units=%d "
    603 	    "value_cur=%d upropset=%d\n", __func__, sme->sme_name, edata->desc,
    604 	    edata->sensor, see->see_type, edata->state, edata->units,
    605 	    edata->value_cur, edata->upropset));
    606 
    607 	/* skip the event if current sensor is in invalid state */
    608 	if (edata->state == ENVSYS_SINVALID)
    609 		goto out;
    610 
    611 	/*
    612 	 * For range limits, if the driver claims responsibility for
    613 	 * limit/range checking, just user driver-supplied status.
    614 	 * Else calculate our own status.  Note that driver must
    615 	 * relinquish responsibility for ALL limits if there is even
    616 	 * one limit that it cannot handle!
    617 	 */
    618 	if ((see->see_type == PENVSYS_EVENT_LIMITS ||
    619 	     see->see_type == PENVSYS_EVENT_CAPACITY) &&
    620 	    (edata->upropset & PROP_DRIVER_LIMITS) == 0) {
    621 		if ((edata->upropset & (PROP_CRITMIN | PROP_BATTCAP)) &&
    622 		    (edata->value_cur < edata->limits.sel_critmin))
    623 			edata->state = ENVSYS_SCRITUNDER;
    624 		else if ((edata->upropset & (PROP_WARNMIN | PROP_BATTWARN)) &&
    625 			 (edata->value_cur < edata->limits.sel_warnmin))
    626 			edata->state = ENVSYS_SWARNUNDER;
    627 		else if ((edata->upropset & (PROP_CRITMAX | PROP_BATTMAX)) &&
    628 			 (edata->value_cur > edata->limits.sel_critmax))
    629 			edata->state = ENVSYS_SCRITOVER;
    630 		else if ((edata->upropset & (PROP_WARNMAX | PROP_BATTHIGH)) &&
    631 			 (edata->value_cur > edata->limits.sel_warnmax))
    632 			edata->state = ENVSYS_SWARNOVER;
    633 		else
    634 			edata->state = ENVSYS_SVALID;
    635 	}
    636 	sme_deliver_event(see);
    637 
    638 out:
    639 	see->see_flags &= ~SEE_EVENT_WORKING;
    640 	cv_broadcast(&sme->sme_condvar);
    641 	mutex_exit(&sme->sme_mtx);
    642 }
    643 
    644 /*
    645  * sysmon_envsys_sensor_event
    646  *
    647  *	+ Find the monitor event of a particular type for a given sensor
    648  *	  on a device and deliver the event if one is required.
    649  */
    650 void
    651 sysmon_envsys_sensor_event(struct sysmon_envsys *sme, envsys_data_t *edata,
    652 			   int ev_type)
    653 {
    654 	sme_event_t *see;
    655 
    656 	mutex_enter(&sme->sme_mtx);
    657 	LIST_FOREACH(see, &sme->sme_events_list, see_list) {
    658 		if (edata != see->see_edata ||
    659 		    see->see_type != ev_type)
    660 			continue;
    661 		sme_deliver_event(see);
    662 		break;
    663 	}
    664 	mutex_exit(&sme->sme_mtx);
    665 }
    666 
    667 /*
    668  * sme_deliver_event:
    669  *
    670  * 	+ If new sensor state requires it, send an event to powerd
    671  *
    672  *	  Must be called with the device's sysmon mutex held
    673  *		see->see_sme->sme_mtx
    674  */
    675 void
    676 sme_deliver_event(sme_event_t *see)
    677 {
    678 	envsys_data_t *edata = see->see_edata;
    679 	const struct sme_description_table *sdt = NULL;
    680 	const struct sme_sensor_event *sse = sme_sensor_event;
    681 	int i, state = 0;
    682 
    683 	switch (see->see_type) {
    684 	case PENVSYS_EVENT_LIMITS:
    685 	case PENVSYS_EVENT_CAPACITY:
    686 		/*
    687 		 * Send event if state has changed
    688 		 */
    689 		if (edata->state == see->see_evsent)
    690 			break;
    691 
    692 		for (i = 0; sse[i].state != -1; i++)
    693 			if (sse[i].state == edata->state)
    694 				break;
    695 
    696 		if (sse[i].state == -1)
    697 			break;
    698 
    699 		if (edata->state == ENVSYS_SVALID)
    700 			sysmon_penvsys_event(&see->see_pes,
    701 					     PENVSYS_EVENT_NORMAL);
    702 		else
    703 			sysmon_penvsys_event(&see->see_pes, sse[i].event);
    704 
    705 		see->see_evsent = edata->state;
    706 		DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d state=%d send_ev=%d\n",
    707 		    __func__, sme->sme_name, edata->desc, edata->sensor,
    708 		    edata->state,
    709 		    (edata->state == ENVSYS_SVALID) ? PENVSYS_EVENT_NORMAL :
    710 			sse[i].event));
    711 
    712 		break;
    713 
    714 	/*
    715 	 * Send PENVSYS_EVENT_CRITICAL event if:
    716 	 *	State has gone from non-CRITICAL to CRITICAL,
    717 	 *	State remains CRITICAL and value has changed, or
    718 	 *	State has returned from CRITICAL to non-CRITICAL
    719 	 */
    720 	case PENVSYS_EVENT_CRITICAL:
    721 		if (edata->state == ENVSYS_SVALID &&
    722 		    see->see_evsent != 0) {
    723 			sysmon_penvsys_event(&see->see_pes,
    724 					     PENVSYS_EVENT_NORMAL);
    725 			see->see_evsent = 0;
    726 		} else if (edata->state == ENVSYS_SCRITICAL &&
    727 		    see->see_evsent != edata->value_cur) {
    728 			sysmon_penvsys_event(&see->see_pes,
    729 					     PENVSYS_EVENT_CRITICAL);
    730 			see->see_evsent = edata->value_cur;
    731 		}
    732 		break;
    733 
    734 	/*
    735 	 * if value_cur is not normal (battery) or online (drive),
    736 	 * send the event...
    737 	 */
    738 	case PENVSYS_EVENT_STATE_CHANGED:
    739 		/*
    740 		 * the state has not been changed, just ignore the event.
    741 		 */
    742 		if (edata->value_cur == see->see_evsent)
    743 			break;
    744 
    745 		switch (edata->units) {
    746 		case ENVSYS_DRIVE:
    747 			sdt = sme_get_description_table(SME_DESC_DRIVE_STATES);
    748 			state = ENVSYS_DRIVE_ONLINE;
    749 			break;
    750 		case ENVSYS_BATTERY_CAPACITY:
    751 			sdt = sme_get_description_table(
    752 			    SME_DESC_BATTERY_CAPACITY);
    753 			state = ENVSYS_BATTERY_CAPACITY_NORMAL;
    754 			break;
    755 		default:
    756 			panic("%s: bad units for PENVSYS_EVENT_STATE_CHANGED",
    757 			    __func__);
    758 		}
    759 
    760 		for (i = 0; sdt[i].type != -1; i++)
    761 			if (sdt[i].type == edata->value_cur)
    762 				break;
    763 
    764 		if (sdt[i].type == -1)
    765 			break;
    766 
    767 		/*
    768 		 * copy current state description.
    769 		 */
    770 		(void)strlcpy(see->see_pes.pes_statedesc, sdt[i].desc,
    771 		    sizeof(see->see_pes.pes_statedesc));
    772 
    773 		if (edata->value_cur == state)
    774 			/*
    775 			 * state returned to normal condition
    776 			 */
    777 			sysmon_penvsys_event(&see->see_pes,
    778 					     PENVSYS_EVENT_NORMAL);
    779 		else
    780 			/*
    781 			 * state changed to abnormal condition
    782 			 */
    783 			sysmon_penvsys_event(&see->see_pes, see->see_type);
    784 
    785 		see->see_evsent = edata->value_cur;
    786 
    787 		/*
    788 		 * There's no need to continue if it's a drive sensor.
    789 		 */
    790 		if (edata->units == ENVSYS_DRIVE)
    791 			break;
    792 
    793 		/*
    794 		 * Check if the system is running in low power and send the
    795 		 * event to powerd (if running) or shutdown the system
    796 		 * otherwise.
    797 		 */
    798 		if (!sysmon_low_power && sme_event_check_low_power()) {
    799 			struct penvsys_state pes;
    800 
    801 			/*
    802 			 * Stop the callout and send the 'low-power' event.
    803 			 */
    804 			sysmon_low_power = true;
    805 			callout_stop(&see->see_sme->sme_callout);
    806 			pes.pes_type = PENVSYS_TYPE_BATTERY;
    807 			sysmon_penvsys_event(&pes, PENVSYS_EVENT_LOW_POWER);
    808 		}
    809 		break;
    810 	default:
    811 		panic("%s: invalid event type %d", __func__, see->see_type);
    812 	}
    813 }
    814 
    815 /*
    816  * Returns true if the system is in low power state: an AC adapter
    817  * is OFF and all batteries are in LOW/CRITICAL state.
    818  */
    819 static bool
    820 sme_event_check_low_power(void)
    821 {
    822 	if (!sme_acadapter_check())
    823 		return false;
    824 
    825 	return sme_battery_check();
    826 }
    827 
    828 /*
    829  * Called with the sysmon_envsys device mtx held through the
    830  * workqueue thread.
    831  */
    832 static bool
    833 sme_acadapter_check(void)
    834 {
    835 	struct sysmon_envsys *sme;
    836 	envsys_data_t *edata;
    837 	bool dev = false, sensor = false;
    838 
    839 	LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) {
    840 		if (sme->sme_class == SME_CLASS_ACADAPTER) {
    841 			dev = true;
    842 			break;
    843 		}
    844 	}
    845 
    846 	/*
    847 	 * No AC Adapter devices were found.
    848 	 */
    849 	if (!dev)
    850 		return false;
    851 
    852 	/*
    853 	 * Check if there's an AC adapter device connected.
    854 	 */
    855 	TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
    856 		if (edata->units == ENVSYS_INDICATOR) {
    857 			sensor = true;
    858 			/* refresh current sensor */
    859 			if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0)
    860 				(*sme->sme_refresh)(sme, edata);
    861 			if (edata->value_cur)
    862 				return false;
    863 		}
    864 	}
    865 
    866 	if (!sensor)
    867 		return false;
    868 
    869 	/*
    870 	 * AC adapter found and not connected.
    871 	 */
    872 	return true;
    873 }
    874 
    875 /*
    876  * Called with the sysmon_envsys device mtx held through the
    877  * workqueue thread.
    878  */
    879 static bool
    880 sme_battery_check(void)
    881 {
    882 	struct sysmon_envsys *sme;
    883 	envsys_data_t *edata;
    884 	int batteriesfound = 0;
    885 	bool present, batterycap, batterycharge;
    886 
    887 	/*
    888 	 * Check for battery devices and its state.
    889 	 */
    890 	LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) {
    891 		if (sme->sme_class != SME_CLASS_BATTERY)
    892 			continue;
    893 
    894 		present = true;
    895 		TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
    896 			if (edata->units == ENVSYS_INDICATOR &&
    897 			    !edata->value_cur) {
    898 				present = false;
    899 				break;
    900 			}
    901 		}
    902 		if (!present)
    903 			continue;
    904 		/*
    905 		 * We've found a battery device...
    906 		 */
    907 		batteriesfound++;
    908 		batterycap = batterycharge = false;
    909 		TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
    910 			if (edata->units == ENVSYS_BATTERY_CAPACITY) {
    911 				batterycap = true;
    912 				if (!sme_battery_critical(edata))
    913 					return false;
    914 			} else if (edata->units == ENVSYS_BATTERY_CHARGE) {
    915 				batterycharge = true;
    916 				if (edata->value_cur)
    917 					return false;
    918 			}
    919 		}
    920 		if (!batterycap || !batterycharge)
    921 			return false;
    922 	}
    923 
    924 	if (!batteriesfound)
    925 		return false;
    926 
    927 	/*
    928 	 * All batteries in low/critical capacity and discharging.
    929 	 */
    930 	return true;
    931 }
    932 
    933 static bool
    934 sme_battery_critical(envsys_data_t *edata)
    935 {
    936 	if (edata->value_cur == ENVSYS_BATTERY_CAPACITY_CRITICAL ||
    937 	    edata->value_cur == ENVSYS_BATTERY_CAPACITY_LOW)
    938 		return true;
    939 
    940 	return false;
    941 }
    942