Home | History | Annotate | Line # | Download | only in sysmon
sysmon_power.c revision 1.49
      1 /*	$NetBSD: sysmon_power.c,v 1.48 2014/11/21 23:28:57 joerg Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2007 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  * Copyright (c) 2003 Wasabi Systems, Inc.
     30  * All rights reserved.
     31  *
     32  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
     33  *
     34  * Redistribution and use in source and binary forms, with or without
     35  * modification, are permitted provided that the following conditions
     36  * are met:
     37  * 1. Redistributions of source code must retain the above copyright
     38  *    notice, this list of conditions and the following disclaimer.
     39  * 2. Redistributions in binary form must reproduce the above copyright
     40  *    notice, this list of conditions and the following disclaimer in the
     41  *    documentation and/or other materials provided with the distribution.
     42  * 3. All advertising materials mentioning features or use of this software
     43  *    must display the following acknowledgement:
     44  *	This product includes software developed for the NetBSD Project by
     45  *	Wasabi Systems, Inc.
     46  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
     47  *    or promote products derived from this software without specific prior
     48  *    written permission.
     49  *
     50  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
     51  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     52  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     53  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
     54  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     55  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     56  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     57  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     58  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     59  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     60  * POSSIBILITY OF SUCH DAMAGE.
     61  */
     62 
     63 /*
     64  * Power management framework for sysmon.
     65  *
     66  * We defer to a power management daemon running in userspace, since
     67  * power management is largely a policy issue.  This merely provides
     68  * for power management event notification to that daemon.
     69  */
     70 
     71 #include <sys/cdefs.h>
     72 __KERNEL_RCSID(0, "$NetBSD: sysmon_power.c,v 1.48 2014/11/21 23:28:57 joerg Exp $");
     73 
     74 #include "opt_compat_netbsd.h"
     75 #include <sys/param.h>
     76 #include <sys/reboot.h>
     77 #include <sys/systm.h>
     78 #include <sys/poll.h>
     79 #include <sys/select.h>
     80 #include <sys/vnode.h>
     81 #include <sys/condvar.h>
     82 #include <sys/mutex.h>
     83 #include <sys/kmem.h>
     84 #include <sys/proc.h>
     85 #include <sys/device.h>
     86 #include <sys/rnd.h>
     87 
     88 #include <dev/sysmon/sysmonvar.h>
     89 #include <prop/proplib.h>
     90 
     91 /*
     92  * Singly linked list for dictionaries to be stored/sent.
     93  */
     94 struct power_event_dictionary {
     95 	SIMPLEQ_ENTRY(power_event_dictionary) pev_dict_head;
     96 	prop_dictionary_t dict;
     97 	int flags;
     98 };
     99 
    100 struct power_event_description {
    101 	int type;
    102 	const char *desc;
    103 };
    104 
    105 /*
    106  * Available events for power switches.
    107  */
    108 static const struct power_event_description pswitch_event_desc[] = {
    109 	{ PSWITCH_EVENT_PRESSED, 	"pressed" },
    110 	{ PSWITCH_EVENT_RELEASED,	"released" },
    111 	{ -1, NULL }
    112 };
    113 
    114 /*
    115  * Available script names for power switches.
    116  */
    117 static const struct power_event_description pswitch_type_desc[] = {
    118 	{ PSWITCH_TYPE_POWER, 		"power_button" },
    119 	{ PSWITCH_TYPE_SLEEP, 		"sleep_button" },
    120 	{ PSWITCH_TYPE_LID, 		"lid_switch" },
    121 	{ PSWITCH_TYPE_RESET, 		"reset_button" },
    122 	{ PSWITCH_TYPE_ACADAPTER,	"acadapter" },
    123 	{ PSWITCH_TYPE_HOTKEY,		"hotkey_button" },
    124 	{ PSWITCH_TYPE_RADIO,		"radio_button" },
    125 	{ -1, NULL }
    126 };
    127 
    128 /*
    129  * Available events for envsys(4).
    130  */
    131 static const struct power_event_description penvsys_event_desc[] = {
    132 	{ PENVSYS_EVENT_NORMAL, 	"normal" },
    133 	{ PENVSYS_EVENT_CRITICAL,	"critical" },
    134 	{ PENVSYS_EVENT_CRITOVER,	"critical-over" },
    135 	{ PENVSYS_EVENT_CRITUNDER,	"critical-under" },
    136 	{ PENVSYS_EVENT_WARNOVER,	"warning-over" },
    137 	{ PENVSYS_EVENT_WARNUNDER,	"warning-under" },
    138 	{ PENVSYS_EVENT_BATT_CRIT,	"critical-capacity" },
    139 	{ PENVSYS_EVENT_BATT_WARN,	"warning-capacity" },
    140 	{ PENVSYS_EVENT_BATT_HIGH,	"high-capacity" },
    141 	{ PENVSYS_EVENT_BATT_MAX,	"maximum-capacity" },
    142 	{ PENVSYS_EVENT_STATE_CHANGED,	"state-changed" },
    143 	{ PENVSYS_EVENT_LOW_POWER,	"low-power" },
    144 	{ -1, NULL }
    145 };
    146 
    147 /*
    148  * Available script names for envsys(4).
    149  */
    150 static const struct power_event_description penvsys_type_desc[] = {
    151 	{ PENVSYS_TYPE_BATTERY,		"sensor_battery" },
    152 	{ PENVSYS_TYPE_DRIVE,		"sensor_drive" },
    153 	{ PENVSYS_TYPE_FAN,		"sensor_fan" },
    154 	{ PENVSYS_TYPE_INDICATOR,	"sensor_indicator" },
    155 	{ PENVSYS_TYPE_POWER,		"sensor_power" },
    156 	{ PENVSYS_TYPE_RESISTANCE,	"sensor_resistance" },
    157 	{ PENVSYS_TYPE_TEMP,		"sensor_temperature" },
    158 	{ PENVSYS_TYPE_VOLTAGE,		"sensor_voltage" },
    159 	{ -1, NULL }
    160 };
    161 
    162 #define SYSMON_MAX_POWER_EVENTS		32
    163 #define SYSMON_POWER_DICTIONARY_BUSY	0x01
    164 #define SYSMON_POWER_DICTIONARY_READY	0x02
    165 
    166 static power_event_t sysmon_power_event_queue[SYSMON_MAX_POWER_EVENTS];
    167 static int sysmon_power_event_queue_head;
    168 static int sysmon_power_event_queue_tail;
    169 static int sysmon_power_event_queue_count;
    170 
    171 static krndsource_t sysmon_rndsource;
    172 
    173 static SIMPLEQ_HEAD(, power_event_dictionary) pev_dict_list =
    174     SIMPLEQ_HEAD_INITIALIZER(pev_dict_list);
    175 
    176 static struct selinfo sysmon_power_event_queue_selinfo;
    177 static struct lwp *sysmon_power_daemon;
    178 
    179 static kmutex_t sysmon_power_event_queue_mtx;
    180 static kcondvar_t sysmon_power_event_queue_cv;
    181 
    182 static char sysmon_power_type[32];
    183 
    184 static int sysmon_power_make_dictionary(prop_dictionary_t, void *, int, int);
    185 static int sysmon_power_daemon_task(struct power_event_dictionary *,
    186 				    void *, int);
    187 static void sysmon_power_destroy_dictionary(struct power_event_dictionary *);
    188 
    189 #define	SYSMON_NEXT_EVENT(x)		(((x) + 1) % SYSMON_MAX_POWER_EVENTS)
    190 
    191 /*
    192  * sysmon_power_init:
    193  *
    194  * 	Initializes the mutexes and condition variables in the
    195  * 	boot process via init_main.c.
    196  */
    197 void
    198 sysmon_power_init(void)
    199 {
    200 	mutex_init(&sysmon_power_event_queue_mtx, MUTEX_DEFAULT, IPL_NONE);
    201 	cv_init(&sysmon_power_event_queue_cv, "smpower");
    202 	selinit(&sysmon_power_event_queue_selinfo);
    203 
    204 	rnd_attach_source(&sysmon_rndsource, "system-power",
    205 			  RND_TYPE_POWER, RND_FLAG_DEFAULT);
    206 
    207 }
    208 
    209 /*
    210  * sysmon_queue_power_event:
    211  *
    212  *	Enqueue a power event for the power management daemon.  Returns
    213  *	non-zero if we were able to enqueue a power event.
    214  */
    215 static int
    216 sysmon_queue_power_event(power_event_t *pev)
    217 {
    218 	KASSERT(mutex_owned(&sysmon_power_event_queue_mtx));
    219 
    220 	if (sysmon_power_event_queue_count == SYSMON_MAX_POWER_EVENTS)
    221 		return 0;
    222 
    223 	sysmon_power_event_queue[sysmon_power_event_queue_head] = *pev;
    224 	sysmon_power_event_queue_head =
    225 	    SYSMON_NEXT_EVENT(sysmon_power_event_queue_head);
    226 	sysmon_power_event_queue_count++;
    227 
    228 	return 1;
    229 }
    230 
    231 /*
    232  * sysmon_get_power_event:
    233  *
    234  *	Get a power event from the queue.  Returns non-zero if there
    235  *	is an event available.
    236  */
    237 static int
    238 sysmon_get_power_event(power_event_t *pev)
    239 {
    240 	KASSERT(mutex_owned(&sysmon_power_event_queue_mtx));
    241 
    242 	if (sysmon_power_event_queue_count == 0)
    243 		return 0;
    244 
    245 	*pev = sysmon_power_event_queue[sysmon_power_event_queue_tail];
    246 	sysmon_power_event_queue_tail =
    247 	    SYSMON_NEXT_EVENT(sysmon_power_event_queue_tail);
    248 	sysmon_power_event_queue_count--;
    249 
    250 	return 1;
    251 }
    252 
    253 /*
    254  * sysmon_power_event_queue_flush:
    255  *
    256  *	Flush the event queue, and reset all state.
    257  */
    258 static void
    259 sysmon_power_event_queue_flush(void)
    260 {
    261 	KASSERT(mutex_owned(&sysmon_power_event_queue_mtx));
    262 
    263 	sysmon_power_event_queue_head = 0;
    264 	sysmon_power_event_queue_tail = 0;
    265 	sysmon_power_event_queue_count = 0;
    266 }
    267 
    268 /*
    269  * sysmon_power_daemon_task:
    270  *
    271  *	Assign required power event members and sends a signal
    272  *	to the process to notify that an event was enqueued successfully.
    273  */
    274 static int
    275 sysmon_power_daemon_task(struct power_event_dictionary *ped,
    276 			 void *pev_data, int event)
    277 {
    278 	power_event_t pev;
    279 	int rv, error = 0;
    280 
    281 	if (!ped || !ped->dict || !pev_data)
    282 		return EINVAL;
    283 
    284 	mutex_enter(&sysmon_power_event_queue_mtx);
    285 
    286 	switch (event) {
    287 	/*
    288 	 * Power switch events.
    289 	 */
    290 	case PSWITCH_EVENT_PRESSED:
    291 	case PSWITCH_EVENT_RELEASED:
    292 	    {
    293 
    294 		struct sysmon_pswitch *pswitch =
    295 		    (struct sysmon_pswitch *)pev_data;
    296 
    297 		pev.pev_type = POWER_EVENT_SWITCH_STATE_CHANGE;
    298 #ifdef COMPAT_40
    299 		pev.pev_switch.psws_state = event;
    300 		pev.pev_switch.psws_type = pswitch->smpsw_type;
    301 
    302 		if (pswitch->smpsw_name) {
    303 			(void)strlcpy(pev.pev_switch.psws_name,
    304 			          pswitch->smpsw_name,
    305 			          sizeof(pev.pev_switch.psws_name));
    306 		}
    307 #endif
    308 		error = sysmon_power_make_dictionary(ped->dict,
    309 						     pswitch,
    310 						     event,
    311 						     pev.pev_type);
    312 		if (error) {
    313 			mutex_exit(&sysmon_power_event_queue_mtx);
    314 			goto out;
    315 		}
    316 
    317 		break;
    318 	    }
    319 
    320 	/*
    321 	 * ENVSYS events.
    322 	 */
    323 	case PENVSYS_EVENT_NORMAL:
    324 	case PENVSYS_EVENT_CRITICAL:
    325 	case PENVSYS_EVENT_CRITUNDER:
    326 	case PENVSYS_EVENT_CRITOVER:
    327 	case PENVSYS_EVENT_WARNUNDER:
    328 	case PENVSYS_EVENT_WARNOVER:
    329 	case PENVSYS_EVENT_BATT_CRIT:
    330 	case PENVSYS_EVENT_BATT_WARN:
    331 	case PENVSYS_EVENT_BATT_HIGH:
    332 	case PENVSYS_EVENT_BATT_MAX:
    333 	case PENVSYS_EVENT_STATE_CHANGED:
    334 	case PENVSYS_EVENT_LOW_POWER:
    335 	    {
    336 		struct penvsys_state *penvsys =
    337 		    (struct penvsys_state *)pev_data;
    338 
    339 		pev.pev_type = POWER_EVENT_ENVSYS_STATE_CHANGE;
    340 
    341 		error = sysmon_power_make_dictionary(ped->dict,
    342 						     penvsys,
    343 						     event,
    344 						     pev.pev_type);
    345 		if (error) {
    346 			mutex_exit(&sysmon_power_event_queue_mtx);
    347 			goto out;
    348 		}
    349 
    350 		break;
    351 	    }
    352 	default:
    353 		error = ENOTTY;
    354 		mutex_exit(&sysmon_power_event_queue_mtx);
    355 		goto out;
    356 	}
    357 
    358 	/*
    359 	 * Enqueue the event.
    360 	 */
    361 	rv = sysmon_queue_power_event(&pev);
    362 	if (rv == 0) {
    363 		printf("%s: WARNING: state change event %d lost; "
    364 		    "queue full\n", __func__, pev.pev_type);
    365 		mutex_exit(&sysmon_power_event_queue_mtx);
    366 		error = EINVAL;
    367 		goto out;
    368 	} else {
    369 		/*
    370 		 * Notify the daemon that an event is ready and its
    371 		 * dictionary is ready to be fetched.
    372 		 */
    373 		ped->flags |= SYSMON_POWER_DICTIONARY_READY;
    374 		SIMPLEQ_INSERT_TAIL(&pev_dict_list, ped, pev_dict_head);
    375 		cv_broadcast(&sysmon_power_event_queue_cv);
    376 		mutex_exit(&sysmon_power_event_queue_mtx);
    377 		selnotify(&sysmon_power_event_queue_selinfo, 0, 0);
    378 	}
    379 
    380 out:
    381 	return error;
    382 }
    383 
    384 /*
    385  * sysmonopen_power:
    386  *
    387  *	Open the system monitor device.
    388  */
    389 int
    390 sysmonopen_power(dev_t dev, int flag, int mode, struct lwp *l)
    391 {
    392 	int error = 0;
    393 
    394 	mutex_enter(&sysmon_power_event_queue_mtx);
    395 	if (sysmon_power_daemon != NULL)
    396 		error = EBUSY;
    397 	else {
    398 		sysmon_power_daemon = l;
    399 		sysmon_power_event_queue_flush();
    400 	}
    401 	mutex_exit(&sysmon_power_event_queue_mtx);
    402 
    403 	return error;
    404 }
    405 
    406 /*
    407  * sysmonclose_power:
    408  *
    409  *	Close the system monitor device.
    410  */
    411 int
    412 sysmonclose_power(dev_t dev, int flag, int mode, struct lwp *l)
    413 {
    414 	int count;
    415 
    416 	mutex_enter(&sysmon_power_event_queue_mtx);
    417 	count = sysmon_power_event_queue_count;
    418 	sysmon_power_daemon = NULL;
    419 	sysmon_power_event_queue_flush();
    420 	mutex_exit(&sysmon_power_event_queue_mtx);
    421 
    422 	if (count)
    423 		printf("WARNING: %d power event%s lost by exiting daemon\n",
    424 		    count, count > 1 ? "s" : "");
    425 
    426 	return 0;
    427 }
    428 
    429 /*
    430  * sysmonread_power:
    431  *
    432  *	Read the system monitor device.
    433  */
    434 int
    435 sysmonread_power(dev_t dev, struct uio *uio, int flags)
    436 {
    437 	power_event_t pev;
    438 	int rv;
    439 
    440 	/* We only allow one event to be read at a time. */
    441 	if (uio->uio_resid != POWER_EVENT_MSG_SIZE)
    442 		return EINVAL;
    443 
    444 	mutex_enter(&sysmon_power_event_queue_mtx);
    445 	for (;;) {
    446 		if (sysmon_get_power_event(&pev)) {
    447 			rv =  uiomove(&pev, POWER_EVENT_MSG_SIZE, uio);
    448 			break;
    449 		}
    450 
    451 		if (flags & IO_NDELAY) {
    452 			rv = EWOULDBLOCK;
    453 			break;
    454 		}
    455 
    456 		cv_wait(&sysmon_power_event_queue_cv,
    457 			&sysmon_power_event_queue_mtx);
    458 	}
    459 	mutex_exit(&sysmon_power_event_queue_mtx);
    460 
    461 	return rv;
    462 }
    463 
    464 /*
    465  * sysmonpoll_power:
    466  *
    467  *	Poll the system monitor device.
    468  */
    469 int
    470 sysmonpoll_power(dev_t dev, int events, struct lwp *l)
    471 {
    472 	int revents;
    473 
    474 	revents = events & (POLLOUT | POLLWRNORM);
    475 
    476 	/* Attempt to save some work. */
    477 	if ((events & (POLLIN | POLLRDNORM)) == 0)
    478 		return revents;
    479 
    480 	mutex_enter(&sysmon_power_event_queue_mtx);
    481 	if (sysmon_power_event_queue_count)
    482 		revents |= events & (POLLIN | POLLRDNORM);
    483 	else
    484 		selrecord(l, &sysmon_power_event_queue_selinfo);
    485 	mutex_exit(&sysmon_power_event_queue_mtx);
    486 
    487 	return revents;
    488 }
    489 
    490 static void
    491 filt_sysmon_power_rdetach(struct knote *kn)
    492 {
    493 
    494 	mutex_enter(&sysmon_power_event_queue_mtx);
    495 	SLIST_REMOVE(&sysmon_power_event_queue_selinfo.sel_klist,
    496 	    kn, knote, kn_selnext);
    497 	mutex_exit(&sysmon_power_event_queue_mtx);
    498 }
    499 
    500 static int
    501 filt_sysmon_power_read(struct knote *kn, long hint)
    502 {
    503 
    504 	mutex_enter(&sysmon_power_event_queue_mtx);
    505 	kn->kn_data = sysmon_power_event_queue_count;
    506 	mutex_exit(&sysmon_power_event_queue_mtx);
    507 
    508 	return kn->kn_data > 0;
    509 }
    510 
    511 static const struct filterops sysmon_power_read_filtops =
    512     { 1, NULL, filt_sysmon_power_rdetach, filt_sysmon_power_read };
    513 
    514 static const struct filterops sysmon_power_write_filtops =
    515     { 1, NULL, filt_sysmon_power_rdetach, filt_seltrue };
    516 
    517 /*
    518  * sysmonkqfilter_power:
    519  *
    520  *	Kqueue filter for the system monitor device.
    521  */
    522 int
    523 sysmonkqfilter_power(dev_t dev, struct knote *kn)
    524 {
    525 	struct klist *klist;
    526 
    527 	switch (kn->kn_filter) {
    528 	case EVFILT_READ:
    529 		klist = &sysmon_power_event_queue_selinfo.sel_klist;
    530 		kn->kn_fop = &sysmon_power_read_filtops;
    531 		break;
    532 
    533 	case EVFILT_WRITE:
    534 		klist = &sysmon_power_event_queue_selinfo.sel_klist;
    535 		kn->kn_fop = &sysmon_power_write_filtops;
    536 		break;
    537 
    538 	default:
    539 		return EINVAL;
    540 	}
    541 
    542 	mutex_enter(&sysmon_power_event_queue_mtx);
    543 	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
    544 	mutex_exit(&sysmon_power_event_queue_mtx);
    545 
    546 	return 0;
    547 }
    548 
    549 /*
    550  * sysmonioctl_power:
    551  *
    552  *	Perform a power management control request.
    553  */
    554 int
    555 sysmonioctl_power(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
    556 {
    557 	int error = 0;
    558 
    559 	switch (cmd) {
    560 	case POWER_IOC_GET_TYPE:
    561 	case POWER_IOC_GET_TYPE_WITH_LOSSAGE:
    562 	    {
    563 		struct power_type *power_type = (void *) data;
    564 
    565 		(void)strlcpy(power_type->power_type,
    566 			      sysmon_power_type,
    567 			      sizeof(power_type->power_type));
    568 		break;
    569 	    }
    570 	case POWER_EVENT_RECVDICT:
    571 	    {
    572 		struct plistref *plist = (struct plistref *)data;
    573 		struct power_event_dictionary *ped;
    574 
    575 		/*
    576 		 * Get the first dictionary enqueued and mark it
    577 		 * as busy.
    578 		 */
    579 		mutex_enter(&sysmon_power_event_queue_mtx);
    580 		ped = SIMPLEQ_FIRST(&pev_dict_list);
    581 		if (!ped || !ped->dict) {
    582 			mutex_exit(&sysmon_power_event_queue_mtx);
    583 			error = ENOTSUP;
    584 			break;
    585 		}
    586 
    587 		if ((ped->flags & SYSMON_POWER_DICTIONARY_READY) == 0) {
    588 			mutex_exit(&sysmon_power_event_queue_mtx);
    589 			error = EINVAL;
    590 			break;
    591 		}
    592 
    593 		if (ped->flags & SYSMON_POWER_DICTIONARY_BUSY) {
    594 			mutex_exit(&sysmon_power_event_queue_mtx);
    595 			error = EBUSY;
    596 			break;
    597 		}
    598 
    599 		ped->flags |= SYSMON_POWER_DICTIONARY_BUSY;
    600 		mutex_exit(&sysmon_power_event_queue_mtx);
    601 
    602 		/*
    603 		 * Send it now.
    604 		 */
    605 		error = prop_dictionary_copyout_ioctl(plist,
    606 						      cmd,
    607 						      ped->dict);
    608 
    609 		/*
    610 		 * Remove the dictionary now that we don't need it.
    611 		 */
    612 		mutex_enter(&sysmon_power_event_queue_mtx);
    613 		ped->flags &= ~SYSMON_POWER_DICTIONARY_BUSY;
    614 		ped->flags &= ~SYSMON_POWER_DICTIONARY_READY;
    615 		SIMPLEQ_REMOVE_HEAD(&pev_dict_list, pev_dict_head);
    616 		mutex_exit(&sysmon_power_event_queue_mtx);
    617 		sysmon_power_destroy_dictionary(ped);
    618 
    619 		break;
    620 	    }
    621 	default:
    622 		error = ENOTTY;
    623 	}
    624 
    625 	return error;
    626 }
    627 
    628 /*
    629  * sysmon_power_make_dictionary:
    630  *
    631  * 	Adds the properties for an event in a dictionary.
    632  */
    633 int
    634 sysmon_power_make_dictionary(prop_dictionary_t dict, void *power_data,
    635 			     int event, int type)
    636 {
    637 	int i;
    638 
    639 	KASSERT(mutex_owned(&sysmon_power_event_queue_mtx));
    640 
    641 	switch (type) {
    642 	/*
    643 	 * create the dictionary for a power switch event.
    644 	 */
    645 	case POWER_EVENT_SWITCH_STATE_CHANGE:
    646 	    {
    647 		const struct power_event_description *peevent =
    648 		    pswitch_event_desc;
    649 		const struct power_event_description *petype =
    650 		    pswitch_type_desc;
    651 		struct sysmon_pswitch *smpsw =
    652 		    (struct sysmon_pswitch *)power_data;
    653 		const char *pwrtype = "pswitch";
    654 
    655 #define SETPROP(key, str)						\
    656 do {									\
    657 	if ((str) != NULL && !prop_dictionary_set_cstring(dict,		\
    658 						  (key),		\
    659 						  (str))) {		\
    660 		printf("%s: failed to set %s\n", __func__, (str));	\
    661 		return EINVAL;						\
    662 	}								\
    663 } while (/* CONSTCOND */ 0)
    664 
    665 
    666 		SETPROP("driver-name", smpsw->smpsw_name);
    667 
    668 		for (i = 0; peevent[i].type != -1; i++)
    669 			if (peevent[i].type == event)
    670 				break;
    671 
    672 		SETPROP("powerd-event-name", peevent[i].desc);
    673 
    674 		for (i = 0; petype[i].type != -1; i++)
    675 			if (petype[i].type == smpsw->smpsw_type)
    676 				break;
    677 
    678 		SETPROP("powerd-script-name", petype[i].desc);
    679 		SETPROP("power-type", pwrtype);
    680 		break;
    681 	    }
    682 	/*
    683 	 * create a dictionary for power envsys event.
    684 	 */
    685 	case POWER_EVENT_ENVSYS_STATE_CHANGE:
    686 	    {
    687 		const struct power_event_description *peevent =
    688 			penvsys_event_desc;
    689 		const struct power_event_description *petype =
    690 			penvsys_type_desc;
    691 		struct penvsys_state *pes =
    692 		    (struct penvsys_state *)power_data;
    693 		const char *pwrtype = "envsys";
    694 
    695 		SETPROP("driver-name", pes->pes_dvname);
    696 		SETPROP("sensor-name", pes->pes_sensname);
    697 		SETPROP("state-description", pes->pes_statedesc);
    698 
    699 		for (i = 0; peevent[i].type != -1; i++)
    700 			if (peevent[i].type == event)
    701 				break;
    702 
    703 		SETPROP("powerd-event-name", peevent[i].desc);
    704 
    705 		for (i = 0; petype[i].type != -1; i++)
    706 			if (petype[i].type == pes->pes_type)
    707 				break;
    708 
    709 		SETPROP("powerd-script-name", petype[i].desc);
    710 		SETPROP("power-type", pwrtype);
    711 		break;
    712 	    }
    713 	default:
    714 		return ENOTSUP;
    715 	}
    716 
    717 	return 0;
    718 }
    719 
    720 /*
    721  * sysmon_power_destroy_dictionary:
    722  *
    723  * 	Destroys a power_event_dictionary object and all its
    724  * 	properties in the dictionary.
    725  */
    726 static void
    727 sysmon_power_destroy_dictionary(struct power_event_dictionary *ped)
    728 {
    729 	prop_object_iterator_t iter;
    730 	prop_object_t obj;
    731 
    732 	KASSERT(ped != NULL);
    733 	KASSERT((ped->flags & SYSMON_POWER_DICTIONARY_BUSY) == 0);
    734 
    735 	iter = prop_dictionary_iterator(ped->dict);
    736 	if (iter == NULL)
    737 		return;
    738 
    739 	while ((obj = prop_object_iterator_next(iter)) != NULL) {
    740 		prop_dictionary_remove(ped->dict,
    741 		    prop_dictionary_keysym_cstring_nocopy(obj));
    742 		prop_object_iterator_reset(iter);
    743 	}
    744 
    745 	prop_object_iterator_release(iter);
    746 	prop_object_release(ped->dict);
    747 
    748 	kmem_free(ped, sizeof(*ped));
    749 }
    750 
    751 /*
    752  * sysmon_power_settype:
    753  *
    754  *	Sets the back-end power management type.  This information can
    755  *	be used by the power management daemon.
    756  */
    757 void
    758 sysmon_power_settype(const char *type)
    759 {
    760 
    761 	/*
    762 	 * Don't bother locking this; it's going to be set
    763 	 * during autoconfiguration, and then only read from
    764 	 * then on.
    765 	 */
    766 	(void)strlcpy(sysmon_power_type, type, sizeof(sysmon_power_type));
    767 }
    768 
    769 #define PENVSYS_SHOWSTATE(str)						\
    770 	do {								\
    771 		printf("%s: %s limit on '%s'\n",			\
    772 		    pes->pes_dvname, (str), pes->pes_sensname);		\
    773 	} while (/* CONSTCOND */ 0)
    774 
    775 /*
    776  * sysmon_penvsys_event:
    777  *
    778  * 	Puts an event onto the sysmon power queue and sends the
    779  * 	appropriate event if the daemon is running, otherwise a
    780  * 	message is shown.
    781  */
    782 void
    783 sysmon_penvsys_event(struct penvsys_state *pes, int event)
    784 {
    785 	struct power_event_dictionary *ped;
    786 	const char *mystr = NULL;
    787 
    788 	KASSERT(pes != NULL);
    789 
    790 	rnd_add_uint32(&sysmon_rndsource, pes->pes_type);
    791 
    792 	if (sysmon_power_daemon != NULL) {
    793 		/*
    794 		 * Create a dictionary for the new event.
    795 		 */
    796 		ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP);
    797 		if (!ped)
    798 			return;
    799 		ped->dict = prop_dictionary_create();
    800 
    801 		if (sysmon_power_daemon_task(ped, pes, event) == 0)
    802 			return;
    803 	}
    804 
    805 	switch (pes->pes_type) {
    806 	case PENVSYS_TYPE_BATTERY:
    807 		switch (event) {
    808 		case PENVSYS_EVENT_LOW_POWER:
    809 			printf("sysmon: LOW POWER! SHUTTING DOWN.\n");
    810 			cpu_reboot(RB_POWERDOWN, NULL);
    811 			break;
    812 		case PENVSYS_EVENT_STATE_CHANGED:
    813 			printf("%s: state changed on '%s' to '%s'\n",
    814 			    pes->pes_dvname, pes->pes_sensname,
    815 			    pes->pes_statedesc);
    816 			break;
    817 		case PENVSYS_EVENT_BATT_CRIT:
    818 			mystr = "critical capacity";
    819 			PENVSYS_SHOWSTATE(mystr);
    820 			break;
    821 		case PENVSYS_EVENT_BATT_WARN:
    822 			mystr = "warning capacity";
    823 			PENVSYS_SHOWSTATE(mystr);
    824 			break;
    825 		case PENVSYS_EVENT_BATT_HIGH:
    826 			mystr = "high capacity";
    827 			PENVSYS_SHOWSTATE(mystr);
    828 			break;
    829 		case PENVSYS_EVENT_BATT_MAX:
    830 			mystr = "maximum capacity";
    831 			PENVSYS_SHOWSTATE(mystr);
    832 			break;
    833 		case PENVSYS_EVENT_NORMAL:
    834 			printf("%s: normal capacity on '%s'\n",
    835 			    pes->pes_dvname, pes->pes_sensname);
    836 			break;
    837 		}
    838 		break;
    839 	case PENVSYS_TYPE_FAN:
    840 	case PENVSYS_TYPE_INDICATOR:
    841 	case PENVSYS_TYPE_TEMP:
    842 	case PENVSYS_TYPE_POWER:
    843 	case PENVSYS_TYPE_RESISTANCE:
    844 	case PENVSYS_TYPE_VOLTAGE:
    845 		switch (event) {
    846 		case PENVSYS_EVENT_CRITICAL:
    847 			mystr = "critical";
    848 			PENVSYS_SHOWSTATE(mystr);
    849 			break;
    850 		case PENVSYS_EVENT_CRITOVER:
    851 			mystr = "critical over";
    852 			PENVSYS_SHOWSTATE(mystr);
    853 			break;
    854 		case PENVSYS_EVENT_CRITUNDER:
    855 			mystr = "critical under";
    856 			PENVSYS_SHOWSTATE(mystr);
    857 			break;
    858 		case PENVSYS_EVENT_WARNOVER:
    859 			mystr = "warning over";
    860 			PENVSYS_SHOWSTATE(mystr);
    861 			break;
    862 		case PENVSYS_EVENT_WARNUNDER:
    863 			mystr = "warning under";
    864 			PENVSYS_SHOWSTATE(mystr);
    865 			break;
    866 		case PENVSYS_EVENT_NORMAL:
    867 			printf("%s: normal state on '%s'\n",
    868 			    pes->pes_dvname, pes->pes_sensname);
    869 			break;
    870 		default:
    871 			printf("%s: unknown event\n", __func__);
    872 		}
    873 		break;
    874 	case PENVSYS_TYPE_DRIVE:
    875 		switch (event) {
    876 		case PENVSYS_EVENT_STATE_CHANGED:
    877 			printf("%s: state changed on '%s' to '%s'\n",
    878 			    pes->pes_dvname, pes->pes_sensname,
    879 			    pes->pes_statedesc);
    880 			break;
    881 		case PENVSYS_EVENT_NORMAL:
    882 			printf("%s: normal state on '%s' (%s)\n",
    883 			    pes->pes_dvname, pes->pes_sensname,
    884 			    pes->pes_statedesc);
    885 			break;
    886 		}
    887 		break;
    888 	default:
    889 		printf("%s: unknown power type\n", __func__);
    890 		break;
    891 	}
    892 }
    893 
    894 /*
    895  * sysmon_pswitch_register:
    896  *
    897  *	Register a power switch device.
    898  */
    899 int
    900 sysmon_pswitch_register(struct sysmon_pswitch *smpsw)
    901 {
    902 	/* nada */
    903 	return 0;
    904 }
    905 
    906 /*
    907  * sysmon_pswitch_unregister:
    908  *
    909  *	Unregister a power switch device.
    910  */
    911 void
    912 sysmon_pswitch_unregister(struct sysmon_pswitch *smpsw)
    913 {
    914 	/* nada */
    915 }
    916 
    917 /*
    918  * sysmon_pswitch_event:
    919  *
    920  *	Register an event on a power switch device.
    921  */
    922 void
    923 sysmon_pswitch_event(struct sysmon_pswitch *smpsw, int event)
    924 {
    925 	struct power_event_dictionary *ped = NULL;
    926 
    927 	KASSERT(smpsw != NULL);
    928 
    929 	/*
    930 	 * For pnp specific events, we don't care if the power daemon
    931 	 * is running or not
    932 	 */
    933 	if (smpsw->smpsw_type == PSWITCH_TYPE_LID) {
    934 		switch (event) {
    935 		case PSWITCH_EVENT_PRESSED:
    936 			pmf_event_inject(NULL, PMFE_CHASSIS_LID_CLOSE);
    937 			break;
    938 		case PSWITCH_EVENT_RELEASED:
    939 			pmf_event_inject(NULL, PMFE_CHASSIS_LID_OPEN);
    940 			break;
    941 		default:
    942 			break;
    943 		}
    944 	}
    945 
    946 	if (sysmon_power_daemon != NULL) {
    947 		/*
    948 		 * Create a new dictionary for the event.
    949 		 */
    950 		ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP);
    951 		if (!ped)
    952 			return;
    953 		ped->dict = prop_dictionary_create();
    954 
    955 		if (sysmon_power_daemon_task(ped, smpsw, event) == 0)
    956 			return;
    957 	}
    958 
    959 	switch (smpsw->smpsw_type) {
    960 	case PSWITCH_TYPE_POWER:
    961 		if (event != PSWITCH_EVENT_PRESSED) {
    962 			/* just ignore it */
    963 			return;
    964 		}
    965 
    966 		/*
    967 		 * Attempt a somewhat graceful shutdown of the system,
    968 		 * as if the user has issued a reboot(2) call with
    969 		 * RB_POWERDOWN.
    970 		 */
    971 		printf("%s: power button pressed, shutting down!\n",
    972 		    smpsw->smpsw_name);
    973 		cpu_reboot(RB_POWERDOWN, NULL);
    974 		break;
    975 
    976 	case PSWITCH_TYPE_RESET:
    977 		if (event != PSWITCH_EVENT_PRESSED) {
    978 			/* just ignore it */
    979 			return;
    980 		}
    981 
    982 		/*
    983 		 * Attempt a somewhat graceful reboot of the system,
    984 		 * as if the user had issued a reboot(2) call.
    985 		 */
    986 		printf("%s: reset button pressed, rebooting!\n",
    987 		    smpsw->smpsw_name);
    988 		cpu_reboot(0, NULL);
    989 		break;
    990 
    991 	case PSWITCH_TYPE_SLEEP:
    992 		if (event != PSWITCH_EVENT_PRESSED) {
    993 			/* just ignore it */
    994 			return;
    995 		}
    996 
    997 		/*
    998 		 * Try to enter a "sleep" state.
    999 		 */
   1000 		/* XXX */
   1001 		printf("%s: sleep button pressed.\n", smpsw->smpsw_name);
   1002 		break;
   1003 
   1004 	case PSWITCH_TYPE_HOTKEY:
   1005 		/*
   1006 		 * Eat up the event, there's nothing we can do
   1007 		 */
   1008 		break;
   1009 
   1010 	case PSWITCH_TYPE_LID:
   1011 		switch (event) {
   1012 		case PSWITCH_EVENT_PRESSED:
   1013 			/*
   1014 			 * Try to enter a "standby" state.
   1015 			 */
   1016 			/* XXX */
   1017 			printf("%s: lid closed.\n", smpsw->smpsw_name);
   1018 			break;
   1019 
   1020 		case PSWITCH_EVENT_RELEASED:
   1021 			/*
   1022 			 * Come out of "standby" state.
   1023 			 */
   1024 			/* XXX */
   1025 			printf("%s: lid opened.\n", smpsw->smpsw_name);
   1026 			break;
   1027 
   1028 		default:
   1029 			printf("%s: unknown lid switch event: %d\n",
   1030 			    smpsw->smpsw_name, event);
   1031 		}
   1032 		break;
   1033 
   1034 	case PSWITCH_TYPE_ACADAPTER:
   1035 		switch (event) {
   1036 		case PSWITCH_EVENT_PRESSED:
   1037 			/*
   1038 			 * Come out of power-save state.
   1039 			 */
   1040 			aprint_normal("%s: AC adapter online.\n",
   1041 			    smpsw->smpsw_name);
   1042 			break;
   1043 
   1044 		case PSWITCH_EVENT_RELEASED:
   1045 			/*
   1046 			 * Try to enter a power-save state.
   1047 			 */
   1048 			aprint_normal("%s: AC adapter offline.\n",
   1049 			    smpsw->smpsw_name);
   1050 			break;
   1051 		}
   1052 		break;
   1053 
   1054 	}
   1055 }
   1056