Home | History | Annotate | Line # | Download | only in sysmon
sysmon_envsys_events.c revision 1.85
      1 /* $NetBSD: sysmon_envsys_events.c,v 1.85 2010/02/18 12:30:53 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.85 2010/02/18 12:30:53 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 	const struct sme_description_table *sdt = NULL;
    582 	const struct sme_sensor_event *sse = sme_sensor_event;
    583 	sme_event_t *see = (void *)wk;
    584 	struct sysmon_envsys *sme = see->see_sme;
    585 	envsys_data_t *edata = see->see_edata;
    586 	int i, state = 0;
    587 
    588 	KASSERT(wk == &see->see_wk);
    589 	KASSERT(sme != NULL || edata != NULL);
    590 
    591 	mutex_enter(&sme->sme_mtx);
    592 	if ((see->see_flags & SEE_EVENT_WORKING) == 0)
    593 		see->see_flags |= SEE_EVENT_WORKING;
    594 	/*
    595 	 * sme_events_check marks the sensors to make us refresh them here.
    596 	 * Don't refresh if the driver uses its own method for refreshing.
    597 	 */
    598 	if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) {
    599 		if ((edata->flags & ENVSYS_FNEED_REFRESH) != 0) {
    600 			/* refresh sensor in device */
    601 			(*sme->sme_refresh)(sme, edata);
    602 			edata->flags &= ~ENVSYS_FNEED_REFRESH;
    603 		}
    604 	}
    605 
    606 	DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d type=%d state=%d units=%d "
    607 	    "value_cur=%d upropset=%d\n", __func__, sme->sme_name, edata->desc,
    608 	    edata->sensor, see->see_type, edata->state, edata->units,
    609 	    edata->value_cur, edata->upropset));
    610 
    611 	/* skip the event if current sensor is in invalid state */
    612 	if (edata->state == ENVSYS_SINVALID)
    613 		goto out;
    614 
    615 	switch (see->see_type) {
    616 	/*
    617 	 * For range limits, if the driver claims responsibility for
    618 	 * limit/range checking, just user driver-supplied status.
    619 	 * Else calculate our own status.  Note that driver must
    620 	 * relinquish responsibility for ALL limits if there is even
    621 	 * one limit that it cannot handle!
    622 	 */
    623 	case PENVSYS_EVENT_LIMITS:
    624 	case PENVSYS_EVENT_CAPACITY:
    625 #define	__EXCEED_LIM(valid, lim, rel) \
    626 		((edata->upropset & (valid)) && \
    627 		 (edata->value_cur rel (edata->limits.lim)))
    628 
    629 		if ((edata->upropset & PROP_DRIVER_LIMITS) == 0) {
    630 			if __EXCEED_LIM(PROP_CRITMIN | PROP_BATTCAP,
    631 					sel_critmin, <)
    632 				edata->state = ENVSYS_SCRITUNDER;
    633 			else if __EXCEED_LIM(PROP_WARNMIN | PROP_BATTWARN,
    634 					sel_warnmin, <)
    635 				edata->state = ENVSYS_SWARNUNDER;
    636 			else if __EXCEED_LIM(PROP_CRITMAX | PROP_BATTMAX,
    637 					sel_critmax, >)
    638 				edata->state = ENVSYS_SCRITOVER;
    639 			else if __EXCEED_LIM(PROP_WARNMAX | PROP_BATTHIGH,
    640 					sel_warnmax, >)
    641 				edata->state = ENVSYS_SWARNOVER;
    642 			else
    643 				edata->state = ENVSYS_SVALID;
    644 		}
    645 #undef	__EXCEED_LIM
    646 
    647 		/*
    648 		 * Send event if state has changed
    649 		 */
    650 		if (edata->state == see->see_evsent)
    651 			break;
    652 
    653 		for (i = 0; sse[i].state != -1; i++)
    654 			if (sse[i].state == edata->state)
    655 				break;
    656 
    657 		if (sse[i].state == -1)
    658 			break;
    659 
    660 		if (edata->state == ENVSYS_SVALID)
    661 			sysmon_penvsys_event(&see->see_pes,
    662 					     PENVSYS_EVENT_NORMAL);
    663 		else
    664 			sysmon_penvsys_event(&see->see_pes, sse[i].event);
    665 
    666 		see->see_evsent = edata->state;
    667 		DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d state=%d send_ev=%d\n",
    668 		    __func__, sme->sme_name, edata->desc, edata->sensor,
    669 		    edata->state,
    670 		    (edata->state == ENVSYS_SVALID) ? PENVSYS_EVENT_NORMAL :
    671 			sse[i].event));
    672 
    673 		break;
    674 
    675 	/*
    676 	 * Send PENVSYS_EVENT_CRITICAL event if:
    677 	 *	State has gone from non-CRITICAL to CRITICAL,
    678 	 *	State remains CRITICAL and value has changed, or
    679 	 *	State has returned from CRITICAL to non-CRITICAL
    680 	 */
    681 	case PENVSYS_EVENT_CRITICAL:
    682 		if (edata->state == ENVSYS_SVALID &&
    683 		    see->see_evsent != 0) {
    684 			sysmon_penvsys_event(&see->see_pes,
    685 					     PENVSYS_EVENT_NORMAL);
    686 			see->see_evsent = 0;
    687 		} else if (edata->state == ENVSYS_SCRITICAL &&
    688 		    see->see_evsent != edata->value_cur) {
    689 			sysmon_penvsys_event(&see->see_pes,
    690 					     PENVSYS_EVENT_CRITICAL);
    691 			see->see_evsent = edata->value_cur;
    692 		}
    693 		break;
    694 
    695 	/*
    696 	 * if value_cur is not normal (battery) or online (drive),
    697 	 * send the event...
    698 	 */
    699 	case PENVSYS_EVENT_STATE_CHANGED:
    700 		/*
    701 		 * the state has not been changed, just ignore the event.
    702 		 */
    703 		if (edata->value_cur == see->see_evsent)
    704 			break;
    705 
    706 		switch (edata->units) {
    707 		case ENVSYS_DRIVE:
    708 			sdt = sme_get_description_table(SME_DESC_DRIVE_STATES);
    709 			state = ENVSYS_DRIVE_ONLINE;
    710 			break;
    711 		case ENVSYS_BATTERY_CAPACITY:
    712 			sdt = sme_get_description_table(
    713 			    SME_DESC_BATTERY_CAPACITY);
    714 			state = ENVSYS_BATTERY_CAPACITY_NORMAL;
    715 			break;
    716 		default:
    717 			panic("%s: bad units for PENVSYS_EVENT_STATE_CHANGED",
    718 			    __func__);
    719 		}
    720 
    721 		for (i = 0; sdt[i].type != -1; i++)
    722 			if (sdt[i].type == edata->value_cur)
    723 				break;
    724 
    725 		if (sdt[i].type == -1)
    726 			break;
    727 
    728 		/*
    729 		 * copy current state description.
    730 		 */
    731 		(void)strlcpy(see->see_pes.pes_statedesc, sdt[i].desc,
    732 		    sizeof(see->see_pes.pes_statedesc));
    733 
    734 		if (edata->value_cur == state)
    735 			/*
    736 			 * state returned to normal condition
    737 			 */
    738 			sysmon_penvsys_event(&see->see_pes,
    739 					     PENVSYS_EVENT_NORMAL);
    740 		else
    741 			/*
    742 			 * state changed to abnormal condition
    743 			 */
    744 			sysmon_penvsys_event(&see->see_pes, see->see_type);
    745 
    746 		see->see_evsent = edata->value_cur;
    747 
    748 		/*
    749 		 * There's no need to continue if it's a drive sensor.
    750 		 */
    751 		if (edata->units == ENVSYS_DRIVE)
    752 			break;
    753 
    754 		/*
    755 		 * Check if the system is running in low power and send the
    756 		 * event to powerd (if running) or shutdown the system
    757 		 * otherwise.
    758 		 */
    759 		if (!sysmon_low_power && sme_event_check_low_power()) {
    760 			struct penvsys_state pes;
    761 
    762 			/*
    763 			 * Stop the callout and send the 'low-power' event.
    764 			 */
    765 			sysmon_low_power = true;
    766 			callout_stop(&sme->sme_callout);
    767 			pes.pes_type = PENVSYS_TYPE_BATTERY;
    768 			sysmon_penvsys_event(&pes, PENVSYS_EVENT_LOW_POWER);
    769 		}
    770 		break;
    771 	default:
    772 		panic("%s: invalid event type %d", __func__, see->see_type);
    773 	}
    774 
    775 out:
    776 	see->see_flags &= ~SEE_EVENT_WORKING;
    777 	cv_broadcast(&sme->sme_condvar);
    778 	mutex_exit(&sme->sme_mtx);
    779 }
    780 
    781 /*
    782  * Returns true if the system is in low power state: an AC adapter
    783  * is OFF and all batteries are in LOW/CRITICAL state.
    784  */
    785 static bool
    786 sme_event_check_low_power(void)
    787 {
    788 	if (!sme_acadapter_check())
    789 		return false;
    790 
    791 	return sme_battery_check();
    792 }
    793 
    794 /*
    795  * Called with the sysmon_envsys device mtx held through the
    796  * workqueue thread.
    797  */
    798 static bool
    799 sme_acadapter_check(void)
    800 {
    801 	struct sysmon_envsys *sme;
    802 	envsys_data_t *edata;
    803 	bool dev = false, sensor = false;
    804 
    805 	LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) {
    806 		if (sme->sme_class == SME_CLASS_ACADAPTER) {
    807 			dev = true;
    808 			break;
    809 		}
    810 	}
    811 
    812 	/*
    813 	 * No AC Adapter devices were found.
    814 	 */
    815 	if (!dev)
    816 		return false;
    817 
    818 	/*
    819 	 * Check if there's an AC adapter device connected.
    820 	 */
    821 	TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
    822 		if (edata->units == ENVSYS_INDICATOR) {
    823 			sensor = true;
    824 			/* refresh current sensor */
    825 			if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0)
    826 				(*sme->sme_refresh)(sme, edata);
    827 			if (edata->value_cur)
    828 				return false;
    829 		}
    830 	}
    831 
    832 	if (!sensor)
    833 		return false;
    834 
    835 	/*
    836 	 * AC adapter found and not connected.
    837 	 */
    838 	return true;
    839 }
    840 
    841 /*
    842  * Called with the sysmon_envsys device mtx held through the
    843  * workqueue thread.
    844  */
    845 static bool
    846 sme_battery_check(void)
    847 {
    848 	struct sysmon_envsys *sme;
    849 	envsys_data_t *edata;
    850 	int batteriesfound = 0;
    851 	bool present, batterycap, batterycharge;
    852 
    853 	/*
    854 	 * Check for battery devices and its state.
    855 	 */
    856 	LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) {
    857 		if (sme->sme_class != SME_CLASS_BATTERY)
    858 			continue;
    859 
    860 		present = true;
    861 		TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
    862 			if (edata->units == ENVSYS_INDICATOR &&
    863 			    !edata->value_cur) {
    864 				present = false;
    865 				break;
    866 			}
    867 		}
    868 		if (!present)
    869 			continue;
    870 		/*
    871 		 * We've found a battery device...
    872 		 */
    873 		batteriesfound++;
    874 		batterycap = batterycharge = false;
    875 		TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
    876 			if (edata->units == ENVSYS_BATTERY_CAPACITY) {
    877 				batterycap = true;
    878 				if (!sme_battery_critical(edata))
    879 					return false;
    880 			} else if (edata->units == ENVSYS_BATTERY_CHARGE) {
    881 				batterycharge = true;
    882 				if (edata->value_cur)
    883 					return false;
    884 			}
    885 		}
    886 		if (!batterycap || !batterycharge)
    887 			return false;
    888 	}
    889 
    890 	if (!batteriesfound)
    891 		return false;
    892 
    893 	/*
    894 	 * All batteries in low/critical capacity and discharging.
    895 	 */
    896 	return true;
    897 }
    898 
    899 static bool
    900 sme_battery_critical(envsys_data_t *edata)
    901 {
    902 	if (edata->value_cur == ENVSYS_BATTERY_CAPACITY_CRITICAL ||
    903 	    edata->value_cur == ENVSYS_BATTERY_CAPACITY_LOW)
    904 		return true;
    905 
    906 	return false;
    907 }
    908