Home | History | Annotate | Line # | Download | only in sysmon
sysmon_envsys_events.c revision 1.83
      1 /* $NetBSD: sysmon_envsys_events.c,v 1.83 2010/02/14 23:30:52 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.83 2010/02/14 23:30:52 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 	{ -1, 					-1 }
     67 };
     68 
     69 static bool sysmon_low_power;
     70 
     71 #define SME_EVTIMO	(SME_EVENTS_DEFTIMEOUT * hz)
     72 
     73 static bool sme_event_check_low_power(void);
     74 static bool sme_battery_check(void);
     75 static bool sme_battery_critical(envsys_data_t *);
     76 static bool sme_acadapter_check(void);
     77 
     78 /*
     79  * sme_event_register:
     80  *
     81  * 	+ Registers a new sysmon envsys event or updates any event
     82  * 	  already in the queue.
     83  */
     84 int
     85 sme_event_register(prop_dictionary_t sdict, envsys_data_t *edata,
     86 		   struct sysmon_envsys *sme, sysmon_envsys_lim_t *lims,
     87 		   uint32_t props, int crittype, int powertype)
     88 {
     89 	sme_event_t *see = NULL, *osee = NULL;
     90 	prop_object_t obj;
     91 	int error = 0;
     92 	const char *objkey;
     93 
     94 	KASSERT(sdict != NULL);
     95 	KASSERT(edata != NULL);
     96 	KASSERT(sme != NULL);
     97 	KASSERT(lims != NULL);
     98 
     99 	/*
    100 	 * Some validation first for limit-checking events
    101 	 *
    102 	 * Capacity limits are permitted only if the sensor has the
    103 	 * ENVSYS_FPERCENT flag set.
    104 	 * Value limits are permitted only if the ENVSYS_FPERCENT
    105 	 * flag is not set and the units is not ENVSYS_INDICATOR.
    106 	 */
    107 
    108 	DPRINTF(("%s: units %d props 0x%04x edata-flags 0x%04x\n",
    109 		__func__, edata->units, props, edata->flags));
    110 
    111 	if ((props & PROP_VAL_LIMITS) &&
    112 	    ((edata->flags & ENVSYS_FPERCENT) ||
    113 	     (edata->units == ENVSYS_INDICATOR)))
    114 		return ENOTSUP;
    115 	if ((props & PROP_CAP_LIMITS) &&
    116 	    !(edata->flags & ENVSYS_FPERCENT))
    117 		return ENOTSUP;
    118 
    119 	/*
    120 	 * check if the event is already on the list and return
    121 	 * EEXIST if value provided hasn't been changed.
    122 	 */
    123 	mutex_enter(&sme->sme_mtx);
    124 	LIST_FOREACH(osee, &sme->sme_events_list, see_list) {
    125 		if (strcmp(edata->desc, osee->see_pes.pes_sensname) != 0)
    126 			continue;
    127 		if (crittype != osee->see_type)
    128 			continue;
    129 
    130 		/*
    131 		 * We found an existing event for this sensor.  Make
    132 		 * sure it references the correct edata
    133 		 */
    134 		KASSERT(edata == osee->see_edata);
    135 
    136 		DPRINTF(("%s: dev %s sensor %s: event type %d exists\n",
    137 		    __func__, sme->sme_name, edata->desc, crittype));
    138 
    139 		see = osee;
    140 		if (props & PROP_CRITMAX) {
    141 			if (lims->sel_critmax == edata->limits.sel_critmax) {
    142 				DPRINTF(("%s: type=%d (critmax exists)\n",
    143 				    __func__, crittype));
    144 				error = EEXIST;
    145 				props &= ~PROP_CRITMAX;
    146 			}
    147 		}
    148 		if (props & PROP_WARNMAX) {
    149 			if (lims->sel_warnmax == edata->limits.sel_warnmax) {
    150 				DPRINTF(("%s: warnmax exists\n", __func__));
    151 				error = EEXIST;
    152 				props &= ~PROP_WARNMAX;
    153 			}
    154 		}
    155 		if (props & (PROP_WARNMIN | PROP_BATTWARN)) {
    156 			if (lims->sel_warnmin == edata->limits.sel_warnmin) {
    157 				DPRINTF(("%s: warnmin exists\n", __func__));
    158 				error = EEXIST;
    159 				props &= ~(PROP_WARNMIN | PROP_BATTWARN);
    160 			}
    161 		}
    162 		if (props & (PROP_CRITMIN | PROP_BATTCAP)) {
    163 			if (lims->sel_critmin == edata->limits.sel_critmin) {
    164 				DPRINTF(("%s: critmin exists\n", __func__));
    165 				error = EEXIST;
    166 				props &= ~(PROP_CRITMIN | PROP_BATTCAP);
    167 			}
    168 		}
    169 		break;
    170 	}
    171 	if (see == NULL) {
    172 		/*
    173 		 * New event requested - allocate a sysmon_envsys event.
    174 		 */
    175 		see = kmem_zalloc(sizeof(*see), KM_SLEEP);
    176 		if (see == NULL)
    177 			return ENOMEM;
    178 
    179 		DPRINTF(("%s: dev %s sensor %s: new event\n",
    180 		    __func__, sme->sme_name, edata->desc));
    181 
    182 		see->see_type = crittype;
    183 		see->see_sme = sme;
    184 		see->see_edata = edata;
    185 
    186 		/* Initialize sensor type and previously-sent state */
    187 
    188 		see->see_pes.pes_type = powertype;
    189 
    190 		switch (crittype) {
    191 		case PENVSYS_EVENT_LIMITS:
    192 			see->see_evsent = ENVSYS_SVALID;
    193 			break;
    194 		case PENVSYS_EVENT_CAPACITY:
    195 			see->see_evsent = ENVSYS_BATTERY_CAPACITY_NORMAL;
    196 			break;
    197 		case PENVSYS_EVENT_STATE_CHANGED:
    198 			if (edata->units == ENVSYS_BATTERY_CAPACITY)
    199 				see->see_evsent = ENVSYS_BATTERY_CAPACITY_NORMAL;
    200 			else if (edata->units == ENVSYS_DRIVE)
    201 				see->see_evsent = ENVSYS_DRIVE_EMPTY;
    202 			else
    203 				panic("%s: bad units for "
    204 				      "PENVSYS_EVENT_STATE_CHANGED", __func__);
    205 			break;
    206 		case PENVSYS_EVENT_CRITICAL:
    207 		default:
    208 			see->see_evsent = 0;
    209 			break;
    210 		}
    211 
    212 		(void)strlcpy(see->see_pes.pes_dvname, sme->sme_name,
    213 		    sizeof(see->see_pes.pes_dvname));
    214 		(void)strlcpy(see->see_pes.pes_sensname, edata->desc,
    215 		    sizeof(see->see_pes.pes_sensname));
    216 	}
    217 
    218 	/*
    219 	 * Limit operation requested.
    220 	 */
    221 #define	LIMIT_OP(k, l, p)						\
    222 	if (props & p) {						\
    223 		objkey = k;						\
    224 		obj = prop_dictionary_get(sdict, objkey);		\
    225 		if (obj != NULL &&					\
    226 		    prop_object_type(obj) != PROP_TYPE_NUMBER) {	\
    227 			DPRINTF(("%s: (%s) %s object no TYPE_NUMBER\n",	\
    228 			    __func__, sme->sme_name, objkey));		\
    229 			error = ENOTSUP;				\
    230 		} else {						\
    231 			edata->limits.l = lims->l;			\
    232 			error = sme_sensor_upint32(sdict, objkey,lims->l); \
    233 			DPRINTF(("%s: (%s) event [sensor=%s type=%d] "	\
    234 			    "(%s updated)\n", __func__, sme->sme_name,	\
    235 			    edata->desc, crittype, objkey));		\
    236 		}							\
    237 		if (error && error != EEXIST)				\
    238 			goto out;					\
    239 		edata->upropset |= p;					\
    240 	}
    241 
    242 	/* Value-based limits */
    243 	LIMIT_OP("critical-max", sel_critmax, PROP_CRITMAX);
    244 	LIMIT_OP("warning-max",  sel_warnmax, PROP_WARNMAX);
    245 	LIMIT_OP("warning-min",  sel_warnmin, PROP_WARNMIN);
    246 	LIMIT_OP("critical-min", sel_critmin, PROP_CRITMIN);
    247 
    248 	/* %Capacity-based limits */
    249 	LIMIT_OP("warning-capacity",  sel_warnmin,  PROP_BATTWARN);
    250 	LIMIT_OP("critical-capacity", sel_critmin,  PROP_BATTCAP);
    251 
    252 #undef LIMIT_OP
    253 
    254 	if (props & PROP_DRIVER_LIMITS)
    255 		edata->upropset |= PROP_DRIVER_LIMITS;
    256 	else
    257 		edata->upropset &= ~PROP_DRIVER_LIMITS;
    258 
    259 	DPRINTF(("%s: (%s) event registered (sensor=%s snum=%d type=%d "
    260 	    "critmin=%" PRIu32 " warnmin=%" PRIu32 " warnmax=%" PRIu32
    261 	    " critmax=%" PRIu32 " props 0x%04x)\n", __func__,
    262 	    see->see_sme->sme_name, see->see_pes.pes_sensname,
    263 	    edata->sensor, see->see_type, edata->limits.sel_critmin,
    264 	    edata->limits.sel_warnmin, edata->limits.sel_warnmax,
    265 	    edata->limits.sel_critmax, edata->upropset));
    266 	/*
    267 	 * Initialize the events framework if it wasn't initialized before.
    268 	 */
    269 	if ((sme->sme_flags & SME_CALLOUT_INITIALIZED) == 0)
    270 		error = sme_events_init(sme);
    271 
    272 	/*
    273 	 * If driver requested notification, advise it of new
    274 	 * limit values
    275 	 */
    276 	if (sme->sme_set_limits)
    277 		(*sme->sme_set_limits)(sme, edata, &(edata->limits),
    278 					&(edata->upropset));
    279 
    280 out:
    281 	if ((error == 0 || error == EEXIST) && osee == NULL)
    282 		LIST_INSERT_HEAD(&sme->sme_events_list, see, see_list);
    283 
    284 	mutex_exit(&sme->sme_mtx);
    285 
    286 	return error;
    287 }
    288 
    289 /*
    290  * sme_event_unregister_all:
    291  *
    292  * 	+ Unregisters all events associated with a sysmon envsys device.
    293  */
    294 void
    295 sme_event_unregister_all(struct sysmon_envsys *sme)
    296 {
    297 	sme_event_t *see;
    298 	int evcounter = 0;
    299 
    300 	KASSERT(sme != NULL);
    301 
    302 	mutex_enter(&sme->sme_mtx);
    303 	LIST_FOREACH(see, &sme->sme_events_list, see_list) {
    304 		while (see->see_flags & SEE_EVENT_WORKING)
    305 			cv_wait(&sme->sme_condvar, &sme->sme_mtx);
    306 
    307 		if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0)
    308 			evcounter++;
    309 	}
    310 
    311 	DPRINTF(("%s: total events %d (%s)\n", __func__,
    312 	    evcounter, sme->sme_name));
    313 
    314 	while ((see = LIST_FIRST(&sme->sme_events_list))) {
    315 		if (evcounter == 0)
    316 			break;
    317 
    318 		if (strcmp(see->see_pes.pes_dvname, sme->sme_name) == 0) {
    319 			LIST_REMOVE(see, see_list);
    320 			DPRINTF(("%s: event %s %d removed (%s)\n", __func__,
    321 			    see->see_pes.pes_sensname, see->see_type,
    322 			    sme->sme_name));
    323 			kmem_free(see, sizeof(*see));
    324 			evcounter--;
    325 		}
    326 	}
    327 
    328 	if (LIST_EMPTY(&sme->sme_events_list))
    329 		if (sme->sme_flags & SME_CALLOUT_INITIALIZED)
    330 			sme_events_destroy(sme);
    331 	mutex_exit(&sme->sme_mtx);
    332 }
    333 
    334 /*
    335  * sme_event_unregister:
    336  *
    337  * 	+ Unregisters an event from the specified sysmon envsys device.
    338  */
    339 int
    340 sme_event_unregister(struct sysmon_envsys *sme, const char *sensor, int type)
    341 {
    342 	sme_event_t *see;
    343 	bool found = false;
    344 
    345 	KASSERT(sensor != NULL);
    346 
    347 	mutex_enter(&sme->sme_mtx);
    348 	LIST_FOREACH(see, &sme->sme_events_list, see_list) {
    349 		if (strcmp(see->see_pes.pes_sensname, sensor) == 0) {
    350 			if (see->see_type == type) {
    351 				found = true;
    352 				break;
    353 			}
    354 		}
    355 	}
    356 
    357 	if (!found) {
    358 		mutex_exit(&sme->sme_mtx);
    359 		return EINVAL;
    360 	}
    361 
    362 	/*
    363 	 * Wait for the event to finish its work, remove from the list
    364 	 * and release resouces.
    365 	 */
    366 	while (see->see_flags & SEE_EVENT_WORKING)
    367 		cv_wait(&sme->sme_condvar, &sme->sme_mtx);
    368 
    369 	DPRINTF(("%s: removed dev=%s sensor=%s type=%d\n",
    370 	    __func__, see->see_pes.pes_dvname, sensor, type));
    371 	LIST_REMOVE(see, see_list);
    372 	/*
    373 	 * So the events list is empty, we'll do the following:
    374 	 *
    375 	 * 	- stop and destroy the callout.
    376 	 * 	- destroy the workqueue.
    377 	 */
    378 	if (LIST_EMPTY(&sme->sme_events_list))
    379 		sme_events_destroy(sme);
    380 	mutex_exit(&sme->sme_mtx);
    381 
    382 	kmem_free(see, sizeof(*see));
    383 	return 0;
    384 }
    385 
    386 /*
    387  * sme_event_drvadd:
    388  *
    389  * 	+ Registers a new event for a device that had enabled any of
    390  * 	  the monitoring flags in the driver.
    391  */
    392 void
    393 sme_event_drvadd(void *arg)
    394 {
    395 	sme_event_drv_t *sed_t = arg;
    396 	sysmon_envsys_lim_t lims;
    397 	uint32_t props;
    398 	int error = 0;
    399 
    400 	KASSERT(sed_t != NULL);
    401 
    402 #define SEE_REGEVENT(a, b, c)						\
    403 do {									\
    404 	if (sed_t->sed_edata->flags & (a)) {				\
    405 		char str[ENVSYS_DESCLEN] = "monitoring-state-";		\
    406 									\
    407 		error = sme_event_register(sed_t->sed_sdict,		\
    408 				      sed_t->sed_edata,			\
    409 				      sed_t->sed_sme,			\
    410 				      &lims, props,			\
    411 				      (b),				\
    412 				      sed_t->sed_powertype);		\
    413 		if (error && error != EEXIST)				\
    414 			printf("%s: failed to add event! "		\
    415 			    "error=%d sensor=%s event=%s\n",		\
    416 			    __func__, error,				\
    417 			    sed_t->sed_edata->desc, (c));		\
    418 		else {							\
    419 			(void)strlcat(str, (c), sizeof(str));		\
    420 			prop_dictionary_set_bool(sed_t->sed_sdict,	\
    421 						 str,			\
    422 						 true);			\
    423 		}							\
    424 	}								\
    425 } while (/* CONSTCOND */ 0)
    426 
    427 	/*
    428 	 * If driver provides a method to retrieve its internal limit
    429 	 * values, call it and use those returned values as initial
    430 	 * limits for event monitoring.
    431 	 */
    432 	props = 0;
    433 	if (sed_t->sed_edata->flags & ENVSYS_FMONLIMITS)
    434 		if (sed_t->sed_sme->sme_get_limits)
    435 			(*sed_t->sed_sme->sme_get_limits)(sed_t->sed_sme,
    436 							  sed_t->sed_edata,
    437 							  &lims, &props);
    438 	/*
    439 	 * If no values returned, don't create the event monitor at
    440 	 * this time.  We'll get another chance later when the user
    441 	 * provides us with limits.
    442 	 */
    443 	if (props == 0)
    444 		sed_t->sed_edata->flags &= ~ENVSYS_FMONLIMITS;
    445 
    446 	/*
    447 	 * If driver doesn't provide a way to "absorb" user-specified
    448 	 * limit values, we must monitor all limits ourselves
    449 	 */
    450 	else if (sed_t->sed_sme->sme_set_limits == NULL)
    451 		props &= ~PROP_DRIVER_LIMITS;
    452 
    453 	/* Register the events that were specified */
    454 
    455 	SEE_REGEVENT(ENVSYS_FMONCRITICAL,
    456 		     PENVSYS_EVENT_CRITICAL,
    457 		     "critical");
    458 
    459 	SEE_REGEVENT(ENVSYS_FMONSTCHANGED,
    460 		     PENVSYS_EVENT_STATE_CHANGED,
    461 		     "state-changed");
    462 
    463 	SEE_REGEVENT(ENVSYS_FMONLIMITS,
    464 		     PENVSYS_EVENT_LIMITS,
    465 		     "hw-range-limits");
    466 
    467 	/*
    468 	 * we are done, free memory now.
    469 	 */
    470 	kmem_free(sed_t, sizeof(*sed_t));
    471 }
    472 
    473 /*
    474  * sme_events_init:
    475  *
    476  * 	+ Initialize the events framework for this device.
    477  */
    478 int
    479 sme_events_init(struct sysmon_envsys *sme)
    480 {
    481 	int error = 0;
    482 	uint64_t timo;
    483 
    484 	KASSERT(sme != NULL);
    485 	KASSERT(mutex_owned(&sme->sme_mtx));
    486 
    487 	if (sme->sme_events_timeout)
    488 		timo = sme->sme_events_timeout * hz;
    489 	else
    490 		timo = SME_EVTIMO;
    491 
    492 	error = workqueue_create(&sme->sme_wq, sme->sme_name,
    493 	    sme_events_worker, sme, PRI_NONE, IPL_SOFTCLOCK, WQ_MPSAFE);
    494 	if (error)
    495 		return error;
    496 
    497 	mutex_init(&sme->sme_callout_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
    498 	callout_init(&sme->sme_callout, CALLOUT_MPSAFE);
    499 	callout_setfunc(&sme->sme_callout, sme_events_check, sme);
    500 	callout_schedule(&sme->sme_callout, timo);
    501 	sme->sme_flags |= SME_CALLOUT_INITIALIZED;
    502 	DPRINTF(("%s: events framework initialized for '%s'\n",
    503 	    __func__, sme->sme_name));
    504 
    505 	return error;
    506 }
    507 
    508 /*
    509  * sme_events_destroy:
    510  *
    511  * 	+ Destroys the event framework for this device: callout
    512  * 	  stopped, workqueue destroyed and callout mutex destroyed.
    513  */
    514 void
    515 sme_events_destroy(struct sysmon_envsys *sme)
    516 {
    517 	KASSERT(mutex_owned(&sme->sme_mtx));
    518 
    519 	callout_stop(&sme->sme_callout);
    520 	workqueue_destroy(sme->sme_wq);
    521 	mutex_destroy(&sme->sme_callout_mtx);
    522 	callout_destroy(&sme->sme_callout);
    523 	sme->sme_flags &= ~SME_CALLOUT_INITIALIZED;
    524 	DPRINTF(("%s: events framework destroyed for '%s'\n",
    525 	    __func__, sme->sme_name));
    526 }
    527 
    528 /*
    529  * sme_events_check:
    530  *
    531  * 	+ Passes the events to the workqueue thread and stops
    532  * 	  the callout if the 'low-power' condition is triggered.
    533  */
    534 void
    535 sme_events_check(void *arg)
    536 {
    537 	struct sysmon_envsys *sme = arg;
    538 	sme_event_t *see;
    539 	uint64_t timo;
    540 
    541 	KASSERT(sme != NULL);
    542 
    543 	mutex_enter(&sme->sme_callout_mtx);
    544 	LIST_FOREACH(see, &sme->sme_events_list, see_list) {
    545 		workqueue_enqueue(sme->sme_wq, &see->see_wk, NULL);
    546 		see->see_edata->flags |= ENVSYS_FNEED_REFRESH;
    547 	}
    548 	if (sme->sme_events_timeout)
    549 		timo = sme->sme_events_timeout * hz;
    550 	else
    551 		timo = SME_EVTIMO;
    552 	if (!sysmon_low_power)
    553 		callout_schedule(&sme->sme_callout, timo);
    554 	mutex_exit(&sme->sme_callout_mtx);
    555 }
    556 
    557 /*
    558  * sme_events_worker:
    559  *
    560  * 	+ workqueue thread that checks if there's a critical condition
    561  * 	  and sends an event if it was triggered.
    562  */
    563 void
    564 sme_events_worker(struct work *wk, void *arg)
    565 {
    566 	const struct sme_description_table *sdt = NULL;
    567 	const struct sme_sensor_event *sse = sme_sensor_event;
    568 	sme_event_t *see = (void *)wk;
    569 	struct sysmon_envsys *sme = see->see_sme;
    570 	envsys_data_t *edata = see->see_edata;
    571 	int i, state = 0;
    572 
    573 	KASSERT(wk == &see->see_wk);
    574 	KASSERT(sme != NULL || edata != NULL);
    575 
    576 	mutex_enter(&sme->sme_mtx);
    577 	if ((see->see_flags & SEE_EVENT_WORKING) == 0)
    578 		see->see_flags |= SEE_EVENT_WORKING;
    579 	/*
    580 	 * sme_events_check marks the sensors to make us refresh them here.
    581 	 * Don't refresh if the driver uses its own method for refreshing.
    582 	 */
    583 	if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0) {
    584 		if ((edata->flags & ENVSYS_FNEED_REFRESH) != 0) {
    585 			/* refresh sensor in device */
    586 			(*sme->sme_refresh)(sme, edata);
    587 			edata->flags &= ~ENVSYS_FNEED_REFRESH;
    588 		}
    589 	}
    590 
    591 	DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d type=%d state=%d units=%d "
    592 	    "value_cur=%d upropset=%d\n", __func__, sme->sme_name, edata->desc,
    593 	    edata->sensor, see->see_type, edata->state, edata->units,
    594 	    edata->value_cur, edata->upropset));
    595 
    596 	/* skip the event if current sensor is in invalid state */
    597 	if (edata->state == ENVSYS_SINVALID)
    598 		goto out;
    599 
    600 	switch (see->see_type) {
    601 	/*
    602 	 * For range limits, if the driver claims responsibility for
    603 	 * limit/range checking, just user driver-supplied status.
    604 	 * Else calculate our own status.  Note that driver must
    605 	 * relinquish responsibility for ALL limits if there is even
    606 	 * one limit that it cannot handle!
    607 	 */
    608 	case PENVSYS_EVENT_LIMITS:
    609 	case PENVSYS_EVENT_CAPACITY:
    610 #define	__EXCEED_LIM(valid, lim, rel) \
    611 		((edata->upropset & (valid)) && \
    612 		 (edata->value_cur rel (edata->limits.lim)))
    613 
    614 		if ((edata->upropset & PROP_DRIVER_LIMITS) == 0) {
    615 			if __EXCEED_LIM(PROP_CRITMIN | PROP_BATTCAP,
    616 					sel_critmin, <)
    617 				edata->state = ENVSYS_SCRITUNDER;
    618 			else if __EXCEED_LIM(PROP_WARNMIN | PROP_BATTWARN,
    619 					sel_warnmin, <)
    620 				edata->state = ENVSYS_SWARNUNDER;
    621 			else if __EXCEED_LIM(PROP_CRITMAX, sel_critmax, >)
    622 				edata->state = ENVSYS_SCRITOVER;
    623 			else if __EXCEED_LIM(PROP_WARNMAX, sel_warnmax, >)
    624 				edata->state = ENVSYS_SWARNOVER;
    625 			else
    626 				edata->state = ENVSYS_SVALID;
    627 		}
    628 #undef	__EXCEED_LIM
    629 
    630 		/*
    631 		 * Send event if state has changed
    632 		 */
    633 		if (edata->state == see->see_evsent)
    634 			break;
    635 
    636 		for (i = 0; sse[i].state != -1; i++)
    637 			if (sse[i].state == edata->state)
    638 				break;
    639 
    640 		if (sse[i].state == -1)
    641 			break;
    642 
    643 		if (edata->state == ENVSYS_SVALID)
    644 			sysmon_penvsys_event(&see->see_pes,
    645 					     PENVSYS_EVENT_NORMAL);
    646 		else
    647 			sysmon_penvsys_event(&see->see_pes, sse[i].event);
    648 
    649 		see->see_evsent = edata->state;
    650 		DPRINTFOBJ(("%s: (%s) desc=%s sensor=%d state=%d send_ev=%d\n",
    651 		    __func__, sme->sme_name, edata->desc, edata->sensor,
    652 		    edata->state,
    653 		    (edata->state == ENVSYS_SVALID) ? PENVSYS_EVENT_NORMAL :
    654 			sse[i].event));
    655 
    656 		break;
    657 
    658 	/*
    659 	 * Send PENVSYS_EVENT_CRITICAL event if:
    660 	 *	State has gone from non-CRITICAL to CRITICAL,
    661 	 *	State remains CRITICAL and value has changed, or
    662 	 *	State has returned from CRITICAL to non-CRITICAL
    663 	 */
    664 	case PENVSYS_EVENT_CRITICAL:
    665 		if (edata->state == ENVSYS_SVALID &&
    666 		    see->see_evsent != 0) {
    667 			sysmon_penvsys_event(&see->see_pes,
    668 					     PENVSYS_EVENT_NORMAL);
    669 			see->see_evsent = 0;
    670 		} else if (edata->state == ENVSYS_SCRITICAL &&
    671 		    see->see_evsent != edata->value_cur) {
    672 			sysmon_penvsys_event(&see->see_pes,
    673 					     PENVSYS_EVENT_CRITICAL);
    674 			see->see_evsent = edata->value_cur;
    675 		}
    676 		break;
    677 
    678 	/*
    679 	 * if value_cur is not normal (battery) or online (drive),
    680 	 * send the event...
    681 	 */
    682 	case PENVSYS_EVENT_STATE_CHANGED:
    683 		/*
    684 		 * the state has not been changed, just ignore the event.
    685 		 */
    686 		if (edata->value_cur == see->see_evsent)
    687 			break;
    688 
    689 		switch (edata->units) {
    690 		case ENVSYS_DRIVE:
    691 			sdt = sme_get_description_table(SME_DESC_DRIVE_STATES);
    692 			state = ENVSYS_DRIVE_ONLINE;
    693 			break;
    694 		case ENVSYS_BATTERY_CAPACITY:
    695 			sdt = sme_get_description_table(
    696 			    SME_DESC_BATTERY_CAPACITY);
    697 			state = ENVSYS_BATTERY_CAPACITY_NORMAL;
    698 			break;
    699 		default:
    700 			panic("%s: bad units for PENVSYS_EVENT_STATE_CHANGED",
    701 			    __func__);
    702 		}
    703 
    704 		for (i = 0; sdt[i].type != -1; i++)
    705 			if (sdt[i].type == edata->value_cur)
    706 				break;
    707 
    708 		if (sdt[i].type == -1)
    709 			break;
    710 
    711 		/*
    712 		 * copy current state description.
    713 		 */
    714 		(void)strlcpy(see->see_pes.pes_statedesc, sdt[i].desc,
    715 		    sizeof(see->see_pes.pes_statedesc));
    716 
    717 		if (edata->value_cur == state)
    718 			/*
    719 			 * state returned to normal condition
    720 			 */
    721 			sysmon_penvsys_event(&see->see_pes,
    722 					     PENVSYS_EVENT_NORMAL);
    723 		else
    724 			/*
    725 			 * state changed to abnormal condition
    726 			 */
    727 			sysmon_penvsys_event(&see->see_pes, see->see_type);
    728 
    729 		see->see_evsent = edata->value_cur;
    730 
    731 		/*
    732 		 * There's no need to continue if it's a drive sensor.
    733 		 */
    734 		if (edata->units == ENVSYS_DRIVE)
    735 			break;
    736 
    737 		/*
    738 		 * Check if the system is running in low power and send the
    739 		 * event to powerd (if running) or shutdown the system
    740 		 * otherwise.
    741 		 */
    742 		if (!sysmon_low_power && sme_event_check_low_power()) {
    743 			struct penvsys_state pes;
    744 
    745 			/*
    746 			 * Stop the callout and send the 'low-power' event.
    747 			 */
    748 			sysmon_low_power = true;
    749 			callout_stop(&sme->sme_callout);
    750 			pes.pes_type = PENVSYS_TYPE_BATTERY;
    751 			sysmon_penvsys_event(&pes, PENVSYS_EVENT_LOW_POWER);
    752 		}
    753 		break;
    754 	default:
    755 		panic("%s: invalid event type %d", __func__, see->see_type);
    756 	}
    757 
    758 out:
    759 	see->see_flags &= ~SEE_EVENT_WORKING;
    760 	cv_broadcast(&sme->sme_condvar);
    761 	mutex_exit(&sme->sme_mtx);
    762 }
    763 
    764 /*
    765  * Returns true if the system is in low power state: an AC adapter
    766  * is OFF and all batteries are in LOW/CRITICAL state.
    767  */
    768 static bool
    769 sme_event_check_low_power(void)
    770 {
    771 	if (!sme_acadapter_check())
    772 		return false;
    773 
    774 	return sme_battery_check();
    775 }
    776 
    777 /*
    778  * Called with the sysmon_envsys device mtx held through the
    779  * workqueue thread.
    780  */
    781 static bool
    782 sme_acadapter_check(void)
    783 {
    784 	struct sysmon_envsys *sme;
    785 	envsys_data_t *edata;
    786 	bool dev = false, sensor = false;
    787 
    788 	LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) {
    789 		if (sme->sme_class == SME_CLASS_ACADAPTER) {
    790 			dev = true;
    791 			break;
    792 		}
    793 	}
    794 
    795 	/*
    796 	 * No AC Adapter devices were found.
    797 	 */
    798 	if (!dev)
    799 		return false;
    800 
    801 	/*
    802 	 * Check if there's an AC adapter device connected.
    803 	 */
    804 	TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
    805 		if (edata->units == ENVSYS_INDICATOR) {
    806 			sensor = true;
    807 			/* refresh current sensor */
    808 			if ((sme->sme_flags & SME_DISABLE_REFRESH) == 0)
    809 				(*sme->sme_refresh)(sme, edata);
    810 			if (edata->value_cur)
    811 				return false;
    812 		}
    813 	}
    814 
    815 	if (!sensor)
    816 		return false;
    817 
    818 	/*
    819 	 * AC adapter found and not connected.
    820 	 */
    821 	return true;
    822 }
    823 
    824 /*
    825  * Called with the sysmon_envsys device mtx held through the
    826  * workqueue thread.
    827  */
    828 static bool
    829 sme_battery_check(void)
    830 {
    831 	struct sysmon_envsys *sme;
    832 	envsys_data_t *edata;
    833 	int batteriesfound = 0;
    834 	bool present, batterycap, batterycharge;
    835 
    836 	/*
    837 	 * Check for battery devices and its state.
    838 	 */
    839 	LIST_FOREACH(sme, &sysmon_envsys_list, sme_list) {
    840 		if (sme->sme_class != SME_CLASS_BATTERY)
    841 			continue;
    842 
    843 		present = true;
    844 		TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
    845 			if (edata->units == ENVSYS_INDICATOR &&
    846 			    !edata->value_cur) {
    847 				present = false;
    848 				break;
    849 			}
    850 		}
    851 		if (!present)
    852 			continue;
    853 		/*
    854 		 * We've found a battery device...
    855 		 */
    856 		batteriesfound++;
    857 		batterycap = batterycharge = false;
    858 		TAILQ_FOREACH(edata, &sme->sme_sensors_list, sensors_head) {
    859 			if (edata->units == ENVSYS_BATTERY_CAPACITY) {
    860 				batterycap = true;
    861 				if (!sme_battery_critical(edata))
    862 					return false;
    863 			} else if (edata->units == ENVSYS_BATTERY_CHARGE) {
    864 				batterycharge = true;
    865 				if (edata->value_cur)
    866 					return false;
    867 			}
    868 		}
    869 		if (!batterycap || !batterycharge)
    870 			return false;
    871 	}
    872 
    873 	if (!batteriesfound)
    874 		return false;
    875 
    876 	/*
    877 	 * All batteries in low/critical capacity and discharging.
    878 	 */
    879 	return true;
    880 }
    881 
    882 static bool
    883 sme_battery_critical(envsys_data_t *edata)
    884 {
    885 	if (edata->value_cur == ENVSYS_BATTERY_CAPACITY_CRITICAL ||
    886 	    edata->value_cur == ENVSYS_BATTERY_CAPACITY_LOW)
    887 		return true;
    888 
    889 	return false;
    890 }
    891