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