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