xref: /netbsd-src/sys/dev/sysmon/sysmon_power.c (revision 54853d3dca98132886bdeab790dd5c95437cf1d2)
1*54853d3dSriastradh /*	$NetBSD: sysmon_power.c,v 1.69 2021/12/31 11:05:41 riastradh Exp $	*/
2bf4558f8Sxtraeme 
3bf4558f8Sxtraeme /*-
44814127dSxtraeme  * Copyright (c) 2007 Juan Romero Pardines.
5bf4558f8Sxtraeme  * All rights reserved.
6bf4558f8Sxtraeme  *
7bf4558f8Sxtraeme  * Redistribution and use in source and binary forms, with or without
8bf4558f8Sxtraeme  * modification, are permitted provided that the following conditions
9bf4558f8Sxtraeme  * are met:
10bf4558f8Sxtraeme  * 1. Redistributions of source code must retain the above copyright
11bf4558f8Sxtraeme  *    notice, this list of conditions and the following disclaimer.
12bf4558f8Sxtraeme  * 2. Redistributions in binary form must reproduce the above copyright
13bf4558f8Sxtraeme  *    notice, this list of conditions and the following disclaimer in the
14bf4558f8Sxtraeme  *    documentation and/or other materials provided with the distribution.
15bf4558f8Sxtraeme  *
164814127dSxtraeme  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
174814127dSxtraeme  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
184814127dSxtraeme  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
194814127dSxtraeme  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
204814127dSxtraeme  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
214814127dSxtraeme  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
224814127dSxtraeme  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
234814127dSxtraeme  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
244814127dSxtraeme  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
254814127dSxtraeme  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26bf4558f8Sxtraeme  */
27b0f79c65Sthorpej 
28b0f79c65Sthorpej /*
29b0f79c65Sthorpej  * Copyright (c) 2003 Wasabi Systems, Inc.
30b0f79c65Sthorpej  * All rights reserved.
31b0f79c65Sthorpej  *
32b0f79c65Sthorpej  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
33b0f79c65Sthorpej  *
34b0f79c65Sthorpej  * Redistribution and use in source and binary forms, with or without
35b0f79c65Sthorpej  * modification, are permitted provided that the following conditions
36b0f79c65Sthorpej  * are met:
37b0f79c65Sthorpej  * 1. Redistributions of source code must retain the above copyright
38b0f79c65Sthorpej  *    notice, this list of conditions and the following disclaimer.
39b0f79c65Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
40b0f79c65Sthorpej  *    notice, this list of conditions and the following disclaimer in the
41b0f79c65Sthorpej  *    documentation and/or other materials provided with the distribution.
42b0f79c65Sthorpej  * 3. All advertising materials mentioning features or use of this software
43b0f79c65Sthorpej  *    must display the following acknowledgement:
44b0f79c65Sthorpej  *	This product includes software developed for the NetBSD Project by
45b0f79c65Sthorpej  *	Wasabi Systems, Inc.
46b0f79c65Sthorpej  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
47b0f79c65Sthorpej  *    or promote products derived from this software without specific prior
48b0f79c65Sthorpej  *    written permission.
49b0f79c65Sthorpej  *
50b0f79c65Sthorpej  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
51b0f79c65Sthorpej  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
52b0f79c65Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
53b0f79c65Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
54b0f79c65Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55b0f79c65Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56b0f79c65Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
57b0f79c65Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
58b0f79c65Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
59b0f79c65Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
60b0f79c65Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
61b0f79c65Sthorpej  */
62b0f79c65Sthorpej 
63b0f79c65Sthorpej /*
64b0f79c65Sthorpej  * Power management framework for sysmon.
65b0f79c65Sthorpej  *
66b0f79c65Sthorpej  * We defer to a power management daemon running in userspace, since
67b0f79c65Sthorpej  * power management is largely a policy issue.  This merely provides
68b0f79c65Sthorpej  * for power management event notification to that daemon.
69b0f79c65Sthorpej  */
70b0f79c65Sthorpej 
71365cbd94Slukem #include <sys/cdefs.h>
72*54853d3dSriastradh __KERNEL_RCSID(0, "$NetBSD: sysmon_power.c,v 1.69 2021/12/31 11:05:41 riastradh Exp $");
73365cbd94Slukem 
74d8e04c90Spooka #ifdef _KERNEL_OPT
75bf4558f8Sxtraeme #include "opt_compat_netbsd.h"
768061eedeSpgoyette #endif
778061eedeSpgoyette 
78b0f79c65Sthorpej #include <sys/param.h>
79b0f79c65Sthorpej #include <sys/reboot.h>
80b0f79c65Sthorpej #include <sys/systm.h>
815a20f4beSthorpej #include <sys/poll.h>
825a20f4beSthorpej #include <sys/select.h>
835a20f4beSthorpej #include <sys/vnode.h>
84bf4558f8Sxtraeme #include <sys/condvar.h>
85bf4558f8Sxtraeme #include <sys/mutex.h>
864814127dSxtraeme #include <sys/kmem.h>
873ea42591Sad #include <sys/proc.h>
8814587719Smartin #include <sys/device.h>
89233f556cSriastradh #include <sys/rndsource.h>
908061eedeSpgoyette #include <sys/module.h>
91eb659e13Spgoyette #include <sys/once.h>
92d91f98a8Spgoyette #include <sys/compat_stub.h>
93b0f79c65Sthorpej 
94b0f79c65Sthorpej #include <dev/sysmon/sysmonvar.h>
95c514daadSpavel #include <prop/proplib.h>
96b0f79c65Sthorpej 
97142f2b40Spgoyette MODULE(MODULE_CLASS_DRIVER, sysmon_power, "sysmon");
988061eedeSpgoyette 
994814127dSxtraeme /*
1004814127dSxtraeme  * Singly linked list for dictionaries to be stored/sent.
1014814127dSxtraeme  */
1024814127dSxtraeme struct power_event_dictionary {
103ce87c7efSjakllsch 	SIMPLEQ_ENTRY(power_event_dictionary) pev_dict_head;
1044814127dSxtraeme 	prop_dictionary_t dict;
1054814127dSxtraeme 	int flags;
1064814127dSxtraeme };
107bf4558f8Sxtraeme 
108bf4558f8Sxtraeme struct power_event_description {
109bf4558f8Sxtraeme 	int type;
110bf4558f8Sxtraeme 	const char *desc;
111bf4558f8Sxtraeme };
112bf4558f8Sxtraeme 
113bf4558f8Sxtraeme /*
114bf4558f8Sxtraeme  * Available events for power switches.
115bf4558f8Sxtraeme  */
116bf4558f8Sxtraeme static const struct power_event_description pswitch_event_desc[] = {
117bf4558f8Sxtraeme 	{ PSWITCH_EVENT_PRESSED, 	"pressed" },
118bf4558f8Sxtraeme 	{ PSWITCH_EVENT_RELEASED,	"released" },
119bf4558f8Sxtraeme 	{ -1, NULL }
120bf4558f8Sxtraeme };
121bf4558f8Sxtraeme 
122bf4558f8Sxtraeme /*
123bf4558f8Sxtraeme  * Available script names for power switches.
124bf4558f8Sxtraeme  */
125bf4558f8Sxtraeme static const struct power_event_description pswitch_type_desc[] = {
126bf4558f8Sxtraeme 	{ PSWITCH_TYPE_POWER, 		"power_button" },
127bf4558f8Sxtraeme 	{ PSWITCH_TYPE_SLEEP, 		"sleep_button" },
128555fcb56Sxtraeme 	{ PSWITCH_TYPE_LID, 		"lid_switch" },
129bf4558f8Sxtraeme 	{ PSWITCH_TYPE_RESET, 		"reset_button" },
130bf4558f8Sxtraeme 	{ PSWITCH_TYPE_ACADAPTER,	"acadapter" },
131ebad5777Sjmcneill 	{ PSWITCH_TYPE_HOTKEY,		"hotkey_button" },
13287fe8c7aSbouyer 	{ PSWITCH_TYPE_RADIO,		"radio_button" },
133bf4558f8Sxtraeme 	{ -1, NULL }
134bf4558f8Sxtraeme };
135bf4558f8Sxtraeme 
136bf4558f8Sxtraeme /*
137bf4558f8Sxtraeme  * Available events for envsys(4).
138bf4558f8Sxtraeme  */
139bf4558f8Sxtraeme static const struct power_event_description penvsys_event_desc[] = {
140bf4558f8Sxtraeme 	{ PENVSYS_EVENT_NORMAL, 	"normal" },
141bf4558f8Sxtraeme 	{ PENVSYS_EVENT_CRITICAL,	"critical" },
142bf4558f8Sxtraeme 	{ PENVSYS_EVENT_CRITOVER,	"critical-over" },
143bf4558f8Sxtraeme 	{ PENVSYS_EVENT_CRITUNDER,	"critical-under" },
144bf4558f8Sxtraeme 	{ PENVSYS_EVENT_WARNOVER,	"warning-over" },
145bf4558f8Sxtraeme 	{ PENVSYS_EVENT_WARNUNDER,	"warning-under" },
146f7ad7038Spgoyette 	{ PENVSYS_EVENT_BATT_CRIT,	"critical-capacity" },
147f7ad7038Spgoyette 	{ PENVSYS_EVENT_BATT_WARN,	"warning-capacity" },
1488844275bSpgoyette 	{ PENVSYS_EVENT_BATT_HIGH,	"high-capacity" },
1498844275bSpgoyette 	{ PENVSYS_EVENT_BATT_MAX,	"maximum-capacity" },
1505b53183eSxtraeme 	{ PENVSYS_EVENT_STATE_CHANGED,	"state-changed" },
1517ade6d03Sxtraeme 	{ PENVSYS_EVENT_LOW_POWER,	"low-power" },
152bf4558f8Sxtraeme 	{ -1, NULL }
153bf4558f8Sxtraeme };
154bf4558f8Sxtraeme 
155bf4558f8Sxtraeme /*
156bf4558f8Sxtraeme  * Available script names for envsys(4).
157bf4558f8Sxtraeme  */
158bf4558f8Sxtraeme static const struct power_event_description penvsys_type_desc[] = {
159bf4558f8Sxtraeme 	{ PENVSYS_TYPE_BATTERY,		"sensor_battery" },
160bf4558f8Sxtraeme 	{ PENVSYS_TYPE_DRIVE,		"sensor_drive" },
161bf4558f8Sxtraeme 	{ PENVSYS_TYPE_FAN,		"sensor_fan" },
1626a9b5725Sxtraeme 	{ PENVSYS_TYPE_INDICATOR,	"sensor_indicator" },
163bf4558f8Sxtraeme 	{ PENVSYS_TYPE_POWER,		"sensor_power" },
164bf4558f8Sxtraeme 	{ PENVSYS_TYPE_RESISTANCE,	"sensor_resistance" },
165bf4558f8Sxtraeme 	{ PENVSYS_TYPE_TEMP,		"sensor_temperature" },
166bf4558f8Sxtraeme 	{ PENVSYS_TYPE_VOLTAGE,		"sensor_voltage" },
167bf4558f8Sxtraeme 	{ -1, NULL }
168bf4558f8Sxtraeme };
169b0f79c65Sthorpej 
1705a20f4beSthorpej #define SYSMON_MAX_POWER_EVENTS		32
1714814127dSxtraeme #define SYSMON_POWER_DICTIONARY_BUSY	0x01
17291551751Sxtraeme #define SYSMON_POWER_DICTIONARY_READY	0x02
1735a20f4beSthorpej 
1745a20f4beSthorpej static power_event_t sysmon_power_event_queue[SYSMON_MAX_POWER_EVENTS];
1755a20f4beSthorpej static int sysmon_power_event_queue_head;
1765a20f4beSthorpej static int sysmon_power_event_queue_tail;
1775a20f4beSthorpej static int sysmon_power_event_queue_count;
1784814127dSxtraeme 
1797b0b7dedStls static krndsource_t sysmon_rndsource;
1807b0b7dedStls 
181ce87c7efSjakllsch static SIMPLEQ_HEAD(, power_event_dictionary) pev_dict_list =
182ce87c7efSjakllsch     SIMPLEQ_HEAD_INITIALIZER(pev_dict_list);
1834814127dSxtraeme 
1845a20f4beSthorpej static struct selinfo sysmon_power_event_queue_selinfo;
1854814127dSxtraeme static struct lwp *sysmon_power_daemon;
1864814127dSxtraeme 
1874814127dSxtraeme static kmutex_t sysmon_power_event_queue_mtx;
1884814127dSxtraeme static kcondvar_t sysmon_power_event_queue_cv;
1895a20f4beSthorpej 
1905a20f4beSthorpej static char sysmon_power_type[32];
1915a20f4beSthorpej 
1924814127dSxtraeme static int sysmon_power_make_dictionary(prop_dictionary_t, void *, int, int);
1934814127dSxtraeme static int sysmon_power_daemon_task(struct power_event_dictionary *,
1944814127dSxtraeme 				    void *, int);
1954814127dSxtraeme static void sysmon_power_destroy_dictionary(struct power_event_dictionary *);
1965a20f4beSthorpej 
1978061eedeSpgoyette static struct sysmon_opvec sysmon_power_opvec = {
1988061eedeSpgoyette 	sysmonopen_power, sysmonclose_power, sysmonioctl_power,
1998061eedeSpgoyette 	sysmonread_power, sysmonpoll_power, sysmonkqfilter_power
2008061eedeSpgoyette };
2018061eedeSpgoyette 
202a88dd66aSfreza #define	SYSMON_NEXT_EVENT(x)		(((x) + 1) % SYSMON_MAX_POWER_EVENTS)
2035a20f4beSthorpej 
204eb659e13Spgoyette ONCE_DECL(once_power);
205eb659e13Spgoyette 
206eb659e13Spgoyette static int
power_preinit(void)207eb659e13Spgoyette power_preinit(void)
208eb659e13Spgoyette {
209eb659e13Spgoyette 
210eb659e13Spgoyette 	mutex_init(&sysmon_power_event_queue_mtx, MUTEX_DEFAULT, IPL_NONE);
211eb659e13Spgoyette 	cv_init(&sysmon_power_event_queue_cv, "smpower");
212eb659e13Spgoyette 
213eb659e13Spgoyette 	return 0;
214eb659e13Spgoyette }
215eb659e13Spgoyette 
2165a20f4beSthorpej /*
217bf4558f8Sxtraeme  * sysmon_power_init:
218bf4558f8Sxtraeme  *
219bf4558f8Sxtraeme  * 	Initializes the mutexes and condition variables in the
2208061eedeSpgoyette  * 	boot process via module initialization process.
221bf4558f8Sxtraeme  */
2228061eedeSpgoyette int
sysmon_power_init(void)223bf4558f8Sxtraeme sysmon_power_init(void)
224bf4558f8Sxtraeme {
2258061eedeSpgoyette 	int error;
2268061eedeSpgoyette 
227eb659e13Spgoyette 	(void)RUN_ONCE(&once_power, power_preinit);
228eb659e13Spgoyette 
229c6186facSrmind 	selinit(&sysmon_power_event_queue_selinfo);
2307b0b7dedStls 
2317b0b7dedStls 	rnd_attach_source(&sysmon_rndsource, "system-power",
232ea6af427Stls 			  RND_TYPE_POWER, RND_FLAG_DEFAULT);
2337b0b7dedStls 
2348061eedeSpgoyette 	error = sysmon_attach_minor(SYSMON_MINOR_POWER, &sysmon_power_opvec);
2358061eedeSpgoyette 
2368061eedeSpgoyette 	return error;
2378061eedeSpgoyette }
2388061eedeSpgoyette 
2398061eedeSpgoyette int
sysmon_power_fini(void)2408061eedeSpgoyette sysmon_power_fini(void)
2418061eedeSpgoyette {
2428061eedeSpgoyette 	int error;
2438061eedeSpgoyette 
2448061eedeSpgoyette 	if (sysmon_power_daemon != NULL)
2458061eedeSpgoyette 		error = EBUSY;
2468061eedeSpgoyette 	else
2478061eedeSpgoyette 		error = sysmon_attach_minor(SYSMON_MINOR_POWER, NULL);
2488061eedeSpgoyette 
2498061eedeSpgoyette 	if (error == 0) {
2508061eedeSpgoyette 		rnd_detach_source(&sysmon_rndsource);
2518061eedeSpgoyette 		seldestroy(&sysmon_power_event_queue_selinfo);
2528061eedeSpgoyette 		cv_destroy(&sysmon_power_event_queue_cv);
2538061eedeSpgoyette 		mutex_destroy(&sysmon_power_event_queue_mtx);
2548061eedeSpgoyette 	}
2558061eedeSpgoyette 
2568061eedeSpgoyette 	return error;
257bf4558f8Sxtraeme }
258bf4558f8Sxtraeme 
259bf4558f8Sxtraeme /*
2605a20f4beSthorpej  * sysmon_queue_power_event:
2615a20f4beSthorpej  *
26277c09939Sjruoho  *	Enqueue a power event for the power management daemon.  Returns
2635a20f4beSthorpej  *	non-zero if we were able to enqueue a power event.
2645a20f4beSthorpej  */
2655a20f4beSthorpej static int
sysmon_queue_power_event(power_event_t * pev)2665a20f4beSthorpej sysmon_queue_power_event(power_event_t *pev)
2675a20f4beSthorpej {
268dee3b63cSxtraeme 	KASSERT(mutex_owned(&sysmon_power_event_queue_mtx));
2695a20f4beSthorpej 
2705a20f4beSthorpej 	if (sysmon_power_event_queue_count == SYSMON_MAX_POWER_EVENTS)
271bf4558f8Sxtraeme 		return 0;
2725a20f4beSthorpej 
2735a20f4beSthorpej 	sysmon_power_event_queue[sysmon_power_event_queue_head] = *pev;
2745a20f4beSthorpej 	sysmon_power_event_queue_head =
2755a20f4beSthorpej 	    SYSMON_NEXT_EVENT(sysmon_power_event_queue_head);
2765a20f4beSthorpej 	sysmon_power_event_queue_count++;
2775a20f4beSthorpej 
278bf4558f8Sxtraeme 	return 1;
2795a20f4beSthorpej }
2805a20f4beSthorpej 
2815a20f4beSthorpej /*
2825a20f4beSthorpej  * sysmon_get_power_event:
2835a20f4beSthorpej  *
2845a20f4beSthorpej  *	Get a power event from the queue.  Returns non-zero if there
2855a20f4beSthorpej  *	is an event available.
2865a20f4beSthorpej  */
2875a20f4beSthorpej static int
sysmon_get_power_event(power_event_t * pev)2885a20f4beSthorpej sysmon_get_power_event(power_event_t *pev)
2895a20f4beSthorpej {
290dee3b63cSxtraeme 	KASSERT(mutex_owned(&sysmon_power_event_queue_mtx));
291dee3b63cSxtraeme 
2925a20f4beSthorpej 	if (sysmon_power_event_queue_count == 0)
293bf4558f8Sxtraeme 		return 0;
2945a20f4beSthorpej 
2955a20f4beSthorpej 	*pev = sysmon_power_event_queue[sysmon_power_event_queue_tail];
2965a20f4beSthorpej 	sysmon_power_event_queue_tail =
2975a20f4beSthorpej 	    SYSMON_NEXT_EVENT(sysmon_power_event_queue_tail);
2985a20f4beSthorpej 	sysmon_power_event_queue_count--;
2995a20f4beSthorpej 
300bf4558f8Sxtraeme 	return 1;
3015a20f4beSthorpej }
3025a20f4beSthorpej 
3035a20f4beSthorpej /*
3045a20f4beSthorpej  * sysmon_power_event_queue_flush:
3055a20f4beSthorpej  *
3065a20f4beSthorpej  *	Flush the event queue, and reset all state.
3075a20f4beSthorpej  */
3085a20f4beSthorpej static void
sysmon_power_event_queue_flush(void)3095a20f4beSthorpej sysmon_power_event_queue_flush(void)
3105a20f4beSthorpej {
311dee3b63cSxtraeme 	KASSERT(mutex_owned(&sysmon_power_event_queue_mtx));
312dee3b63cSxtraeme 
3135a20f4beSthorpej 	sysmon_power_event_queue_head = 0;
3145a20f4beSthorpej 	sysmon_power_event_queue_tail = 0;
3155a20f4beSthorpej 	sysmon_power_event_queue_count = 0;
316bf4558f8Sxtraeme }
317bf4558f8Sxtraeme 
318bf4558f8Sxtraeme /*
319bf4558f8Sxtraeme  * sysmon_power_daemon_task:
320bf4558f8Sxtraeme  *
321bf4558f8Sxtraeme  *	Assign required power event members and sends a signal
322ce87c7efSjakllsch  *	to the process to notify that an event was enqueued successfully.
323bf4558f8Sxtraeme  */
324bf4558f8Sxtraeme static int
sysmon_power_daemon_task(struct power_event_dictionary * ped,void * pev_data,int event)3254814127dSxtraeme sysmon_power_daemon_task(struct power_event_dictionary *ped,
3264814127dSxtraeme 			 void *pev_data, int event)
327bf4558f8Sxtraeme {
328bf4558f8Sxtraeme 	power_event_t pev;
329bf4558f8Sxtraeme 	int rv, error = 0;
330bf4558f8Sxtraeme 
33191551751Sxtraeme 	if (!ped || !ped->dict || !pev_data)
33207f9bca5Sxtraeme 		return EINVAL;
33307f9bca5Sxtraeme 
334b7c81d4eSmaxv 	memset(&pev, 0, sizeof(pev));
335b7c81d4eSmaxv 
3364814127dSxtraeme 	mutex_enter(&sysmon_power_event_queue_mtx);
3374814127dSxtraeme 
338bf4558f8Sxtraeme 	switch (event) {
3394814127dSxtraeme 	/*
3404814127dSxtraeme 	 * Power switch events.
3414814127dSxtraeme 	 */
342bf4558f8Sxtraeme 	case PSWITCH_EVENT_PRESSED:
343bf4558f8Sxtraeme 	case PSWITCH_EVENT_RELEASED:
344bf4558f8Sxtraeme 	    {
345bf4558f8Sxtraeme 
346bf4558f8Sxtraeme 		struct sysmon_pswitch *pswitch =
347bf4558f8Sxtraeme 		    (struct sysmon_pswitch *)pev_data;
348bf4558f8Sxtraeme 
349bf4558f8Sxtraeme 		pev.pev_type = POWER_EVENT_SWITCH_STATE_CHANGE;
350bf4558f8Sxtraeme 
3518c2f80f1Spgoyette 		MODULE_HOOK_CALL_VOID(compat_sysmon_power_40_hook,
352d91f98a8Spgoyette 		    (&pev, pswitch, event), __nothing);
353d91f98a8Spgoyette 
3544814127dSxtraeme 		error = sysmon_power_make_dictionary(ped->dict,
3554814127dSxtraeme 						     pswitch,
356bf4558f8Sxtraeme 						     event,
357bf4558f8Sxtraeme 						     pev.pev_type);
3584814127dSxtraeme 		if (error) {
3594814127dSxtraeme 			mutex_exit(&sysmon_power_event_queue_mtx);
360bf4558f8Sxtraeme 			goto out;
3614814127dSxtraeme 		}
362bf4558f8Sxtraeme 
363bf4558f8Sxtraeme 		break;
364bf4558f8Sxtraeme 	    }
365bf4558f8Sxtraeme 
3664814127dSxtraeme 	/*
3674814127dSxtraeme 	 * ENVSYS events.
3684814127dSxtraeme 	 */
369bf4558f8Sxtraeme 	case PENVSYS_EVENT_NORMAL:
370bf4558f8Sxtraeme 	case PENVSYS_EVENT_CRITICAL:
371bf4558f8Sxtraeme 	case PENVSYS_EVENT_CRITUNDER:
372bf4558f8Sxtraeme 	case PENVSYS_EVENT_CRITOVER:
373bf4558f8Sxtraeme 	case PENVSYS_EVENT_WARNUNDER:
374bf4558f8Sxtraeme 	case PENVSYS_EVENT_WARNOVER:
375f7ad7038Spgoyette 	case PENVSYS_EVENT_BATT_CRIT:
376f7ad7038Spgoyette 	case PENVSYS_EVENT_BATT_WARN:
3778844275bSpgoyette 	case PENVSYS_EVENT_BATT_HIGH:
3788844275bSpgoyette 	case PENVSYS_EVENT_BATT_MAX:
3795b53183eSxtraeme 	case PENVSYS_EVENT_STATE_CHANGED:
3807ade6d03Sxtraeme 	case PENVSYS_EVENT_LOW_POWER:
381bf4558f8Sxtraeme 	    {
382bf4558f8Sxtraeme 		struct penvsys_state *penvsys =
383bf4558f8Sxtraeme 		    (struct penvsys_state *)pev_data;
384bf4558f8Sxtraeme 
385bf4558f8Sxtraeme 		pev.pev_type = POWER_EVENT_ENVSYS_STATE_CHANGE;
386bf4558f8Sxtraeme 
3874814127dSxtraeme 		error = sysmon_power_make_dictionary(ped->dict,
3884814127dSxtraeme 						     penvsys,
389bf4558f8Sxtraeme 						     event,
390bf4558f8Sxtraeme 						     pev.pev_type);
3914814127dSxtraeme 		if (error) {
3924814127dSxtraeme 			mutex_exit(&sysmon_power_event_queue_mtx);
393bf4558f8Sxtraeme 			goto out;
3944814127dSxtraeme 		}
395bf4558f8Sxtraeme 
396bf4558f8Sxtraeme 		break;
397bf4558f8Sxtraeme 	    }
398bf4558f8Sxtraeme 	default:
399bf4558f8Sxtraeme 		error = ENOTTY;
4004814127dSxtraeme 		mutex_exit(&sysmon_power_event_queue_mtx);
401bf4558f8Sxtraeme 		goto out;
402bf4558f8Sxtraeme 	}
403bf4558f8Sxtraeme 
4044814127dSxtraeme 	/*
4054814127dSxtraeme 	 * Enqueue the event.
4064814127dSxtraeme 	 */
407bf4558f8Sxtraeme 	rv = sysmon_queue_power_event(&pev);
408bf4558f8Sxtraeme 	if (rv == 0) {
409bf4558f8Sxtraeme 		printf("%s: WARNING: state change event %d lost; "
410bf4558f8Sxtraeme 		    "queue full\n", __func__, pev.pev_type);
4114814127dSxtraeme 		mutex_exit(&sysmon_power_event_queue_mtx);
412bf4558f8Sxtraeme 		error = EINVAL;
413bf4558f8Sxtraeme 		goto out;
414bf4558f8Sxtraeme 	} else {
4154814127dSxtraeme 		/*
41691551751Sxtraeme 		 * Notify the daemon that an event is ready and its
41791551751Sxtraeme 		 * dictionary is ready to be fetched.
4184814127dSxtraeme 		 */
41991551751Sxtraeme 		ped->flags |= SYSMON_POWER_DICTIONARY_READY;
420ce87c7efSjakllsch 		SIMPLEQ_INSERT_TAIL(&pev_dict_list, ped, pev_dict_head);
421bf4558f8Sxtraeme 		cv_broadcast(&sysmon_power_event_queue_cv);
4220c8ebd67Sthorpej 		selnotify(&sysmon_power_event_queue_selinfo,
4230c8ebd67Sthorpej 		    POLLIN | POLLRDNORM, NOTE_SUBMIT);
424bf4558f8Sxtraeme 		mutex_exit(&sysmon_power_event_queue_mtx);
425bf4558f8Sxtraeme 	}
426bf4558f8Sxtraeme 
427bf4558f8Sxtraeme out:
428bf4558f8Sxtraeme 	return error;
4295a20f4beSthorpej }
4305a20f4beSthorpej 
4315a20f4beSthorpej /*
4325a20f4beSthorpej  * sysmonopen_power:
4335a20f4beSthorpej  *
4345a20f4beSthorpej  *	Open the system monitor device.
4355a20f4beSthorpej  */
4365a20f4beSthorpej int
sysmonopen_power(dev_t dev,int flag,int mode,struct lwp * l)437bf4558f8Sxtraeme sysmonopen_power(dev_t dev, int flag, int mode, struct lwp *l)
4385a20f4beSthorpej {
4395a20f4beSthorpej 	int error = 0;
4405a20f4beSthorpej 
441bf4558f8Sxtraeme 	mutex_enter(&sysmon_power_event_queue_mtx);
4425a20f4beSthorpej 	if (sysmon_power_daemon != NULL)
4435a20f4beSthorpej 		error = EBUSY;
4445a20f4beSthorpej 	else {
4453ea42591Sad 		sysmon_power_daemon = l;
4465a20f4beSthorpej 		sysmon_power_event_queue_flush();
4475a20f4beSthorpej 	}
448bf4558f8Sxtraeme 	mutex_exit(&sysmon_power_event_queue_mtx);
4495a20f4beSthorpej 
450bf4558f8Sxtraeme 	return error;
4515a20f4beSthorpej }
4525a20f4beSthorpej 
4535a20f4beSthorpej /*
4545a20f4beSthorpej  * sysmonclose_power:
4555a20f4beSthorpej  *
4565a20f4beSthorpej  *	Close the system monitor device.
4575a20f4beSthorpej  */
4585a20f4beSthorpej int
sysmonclose_power(dev_t dev,int flag,int mode,struct lwp * l)459bf4558f8Sxtraeme sysmonclose_power(dev_t dev, int flag, int mode, struct lwp *l)
4605a20f4beSthorpej {
4615a20f4beSthorpej 	int count;
4625a20f4beSthorpej 
463bf4558f8Sxtraeme 	mutex_enter(&sysmon_power_event_queue_mtx);
4645a20f4beSthorpej 	count = sysmon_power_event_queue_count;
4655a20f4beSthorpej 	sysmon_power_daemon = NULL;
4665a20f4beSthorpej 	sysmon_power_event_queue_flush();
467bf4558f8Sxtraeme 	mutex_exit(&sysmon_power_event_queue_mtx);
4685a20f4beSthorpej 
4695a20f4beSthorpej 	if (count)
470bf4558f8Sxtraeme 		printf("WARNING: %d power event%s lost by exiting daemon\n",
471bf4558f8Sxtraeme 		    count, count > 1 ? "s" : "");
4725a20f4beSthorpej 
473bf4558f8Sxtraeme 	return 0;
4745a20f4beSthorpej }
4755a20f4beSthorpej 
4765a20f4beSthorpej /*
4775a20f4beSthorpej  * sysmonread_power:
4785a20f4beSthorpej  *
4795a20f4beSthorpej  *	Read the system monitor device.
4805a20f4beSthorpej  */
4815a20f4beSthorpej int
sysmonread_power(dev_t dev,struct uio * uio,int flags)482168cd830Schristos sysmonread_power(dev_t dev, struct uio *uio, int flags)
4835a20f4beSthorpej {
4845a20f4beSthorpej 	power_event_t pev;
4852b28c4e8Sgmcgarry 	int rv;
4865a20f4beSthorpej 
4875a20f4beSthorpej 	/* We only allow one event to be read at a time. */
4885a20f4beSthorpej 	if (uio->uio_resid != POWER_EVENT_MSG_SIZE)
489bf4558f8Sxtraeme 		return EINVAL;
4905a20f4beSthorpej 
491bf4558f8Sxtraeme 	mutex_enter(&sysmon_power_event_queue_mtx);
492bf4558f8Sxtraeme 	for (;;) {
4935a20f4beSthorpej 		if (sysmon_get_power_event(&pev)) {
4942b28c4e8Sgmcgarry 			rv =  uiomove(&pev, POWER_EVENT_MSG_SIZE, uio);
4952b28c4e8Sgmcgarry 			break;
4965a20f4beSthorpej 		}
4975a20f4beSthorpej 
4985a20f4beSthorpej 		if (flags & IO_NDELAY) {
4992b28c4e8Sgmcgarry 			rv = EWOULDBLOCK;
5002b28c4e8Sgmcgarry 			break;
5015a20f4beSthorpej 		}
5025a20f4beSthorpej 
503bf4558f8Sxtraeme 		cv_wait(&sysmon_power_event_queue_cv,
504bf4558f8Sxtraeme 			&sysmon_power_event_queue_mtx);
5055a20f4beSthorpej 	}
506bf4558f8Sxtraeme 	mutex_exit(&sysmon_power_event_queue_mtx);
5072b28c4e8Sgmcgarry 
5082b28c4e8Sgmcgarry 	return rv;
5095a20f4beSthorpej }
5105a20f4beSthorpej 
5115a20f4beSthorpej /*
5125a20f4beSthorpej  * sysmonpoll_power:
5135a20f4beSthorpej  *
5145a20f4beSthorpej  *	Poll the system monitor device.
5155a20f4beSthorpej  */
5165a20f4beSthorpej int
sysmonpoll_power(dev_t dev,int events,struct lwp * l)517168cd830Schristos sysmonpoll_power(dev_t dev, int events, struct lwp *l)
5185a20f4beSthorpej {
5195a20f4beSthorpej 	int revents;
5205a20f4beSthorpej 
5215a20f4beSthorpej 	revents = events & (POLLOUT | POLLWRNORM);
5225a20f4beSthorpej 
5235a20f4beSthorpej 	/* Attempt to save some work. */
5245a20f4beSthorpej 	if ((events & (POLLIN | POLLRDNORM)) == 0)
525bf4558f8Sxtraeme 		return revents;
5265a20f4beSthorpej 
527bf4558f8Sxtraeme 	mutex_enter(&sysmon_power_event_queue_mtx);
5285a20f4beSthorpej 	if (sysmon_power_event_queue_count)
5295a20f4beSthorpej 		revents |= events & (POLLIN | POLLRDNORM);
5305a20f4beSthorpej 	else
53195e1ffb1Schristos 		selrecord(l, &sysmon_power_event_queue_selinfo);
532bf4558f8Sxtraeme 	mutex_exit(&sysmon_power_event_queue_mtx);
5335a20f4beSthorpej 
534bf4558f8Sxtraeme 	return revents;
5355a20f4beSthorpej }
5365a20f4beSthorpej 
5375a20f4beSthorpej static void
filt_sysmon_power_rdetach(struct knote * kn)5385a20f4beSthorpej filt_sysmon_power_rdetach(struct knote *kn)
5395a20f4beSthorpej {
5405a20f4beSthorpej 
541bf4558f8Sxtraeme 	mutex_enter(&sysmon_power_event_queue_mtx);
542954344f4Sthorpej 	selremove_knote(&sysmon_power_event_queue_selinfo, kn);
543bf4558f8Sxtraeme 	mutex_exit(&sysmon_power_event_queue_mtx);
5445a20f4beSthorpej }
5455a20f4beSthorpej 
5465a20f4beSthorpej static int
filt_sysmon_power_read(struct knote * kn,long hint)547168cd830Schristos filt_sysmon_power_read(struct knote *kn, long hint)
5485a20f4beSthorpej {
5495a20f4beSthorpej 
5500c8ebd67Sthorpej 	if (hint & NOTE_SUBMIT) {
5510c8ebd67Sthorpej 		KASSERT(mutex_owned(&sysmon_power_event_queue_mtx));
5520c8ebd67Sthorpej 	} else {
553bf4558f8Sxtraeme 		mutex_enter(&sysmon_power_event_queue_mtx);
5540c8ebd67Sthorpej 	}
5550c8ebd67Sthorpej 
5565a20f4beSthorpej 	kn->kn_data = sysmon_power_event_queue_count;
5570c8ebd67Sthorpej 
5580c8ebd67Sthorpej 	if ((hint & NOTE_SUBMIT) == 0) {
559bf4558f8Sxtraeme 		mutex_exit(&sysmon_power_event_queue_mtx);
5600c8ebd67Sthorpej 	}
5615a20f4beSthorpej 
562bf4558f8Sxtraeme 	return kn->kn_data > 0;
5635a20f4beSthorpej }
5645a20f4beSthorpej 
56518b796d4Smaya static const struct filterops sysmon_power_read_filtops = {
5660c8ebd67Sthorpej 	.f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
56718b796d4Smaya 	.f_attach = NULL,
56818b796d4Smaya 	.f_detach = filt_sysmon_power_rdetach,
56918b796d4Smaya 	.f_event = filt_sysmon_power_read,
57018b796d4Smaya };
5715a20f4beSthorpej 
5725a20f4beSthorpej /*
5735a20f4beSthorpej  * sysmonkqfilter_power:
5745a20f4beSthorpej  *
5755a20f4beSthorpej  *	Kqueue filter for the system monitor device.
5765a20f4beSthorpej  */
5775a20f4beSthorpej int
sysmonkqfilter_power(dev_t dev,struct knote * kn)578168cd830Schristos sysmonkqfilter_power(dev_t dev, struct knote *kn)
5795a20f4beSthorpej {
5805a20f4beSthorpej 
5815a20f4beSthorpej 	switch (kn->kn_filter) {
5825a20f4beSthorpej 	case EVFILT_READ:
5835a20f4beSthorpej 		kn->kn_fop = &sysmon_power_read_filtops;
5840c8ebd67Sthorpej 		mutex_enter(&sysmon_power_event_queue_mtx);
5850c8ebd67Sthorpej 		selrecord_knote(&sysmon_power_event_queue_selinfo, kn);
5860c8ebd67Sthorpej 		mutex_exit(&sysmon_power_event_queue_mtx);
5875a20f4beSthorpej 		break;
5885a20f4beSthorpej 
5895a20f4beSthorpej 	case EVFILT_WRITE:
5900c8ebd67Sthorpej 		kn->kn_fop = &seltrue_filtops;
5915a20f4beSthorpej 		break;
5925a20f4beSthorpej 
5935a20f4beSthorpej 	default:
5944e38160dSpooka 		return EINVAL;
5955a20f4beSthorpej 	}
5965a20f4beSthorpej 
597bf4558f8Sxtraeme 	return 0;
5985a20f4beSthorpej }
5995a20f4beSthorpej 
6005a20f4beSthorpej /*
6015a20f4beSthorpej  * sysmonioctl_power:
6025a20f4beSthorpej  *
60377c09939Sjruoho  *	Perform a power management control request.
6045a20f4beSthorpej  */
6055a20f4beSthorpej int
sysmonioctl_power(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)606bf4558f8Sxtraeme sysmonioctl_power(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
6075a20f4beSthorpej {
6085a20f4beSthorpej 	int error = 0;
6095a20f4beSthorpej 
6105a20f4beSthorpej 	switch (cmd) {
6115a20f4beSthorpej 	case POWER_IOC_GET_TYPE:
612d50bb58aSjakllsch 	case POWER_IOC_GET_TYPE_WITH_LOSSAGE:
6135a20f4beSthorpej 	    {
6145a20f4beSthorpej 		struct power_type *power_type = (void *) data;
6155a20f4beSthorpej 
616bf4558f8Sxtraeme 		(void)strlcpy(power_type->power_type,
617bf4558f8Sxtraeme 			      sysmon_power_type,
618bf4558f8Sxtraeme 			      sizeof(power_type->power_type));
619bf4558f8Sxtraeme 		break;
620bf4558f8Sxtraeme 	    }
621bf4558f8Sxtraeme 	case POWER_EVENT_RECVDICT:
622bf4558f8Sxtraeme 	    {
623bf4558f8Sxtraeme 		struct plistref *plist = (struct plistref *)data;
6244814127dSxtraeme 		struct power_event_dictionary *ped;
625bf4558f8Sxtraeme 
6264814127dSxtraeme 		/*
6274814127dSxtraeme 		 * Get the first dictionary enqueued and mark it
6284814127dSxtraeme 		 * as busy.
6294814127dSxtraeme 		 */
6304814127dSxtraeme 		mutex_enter(&sysmon_power_event_queue_mtx);
631ce87c7efSjakllsch 		ped = SIMPLEQ_FIRST(&pev_dict_list);
63291551751Sxtraeme 		if (!ped || !ped->dict) {
6334814127dSxtraeme 			mutex_exit(&sysmon_power_event_queue_mtx);
634bf4558f8Sxtraeme 			error = ENOTSUP;
635bf4558f8Sxtraeme 			break;
636bf4558f8Sxtraeme 		}
637bf4558f8Sxtraeme 
63891551751Sxtraeme 		if ((ped->flags & SYSMON_POWER_DICTIONARY_READY) == 0) {
63991551751Sxtraeme 			mutex_exit(&sysmon_power_event_queue_mtx);
64091551751Sxtraeme 			error = EINVAL;
64191551751Sxtraeme 			break;
64291551751Sxtraeme 		}
64391551751Sxtraeme 
6444814127dSxtraeme 		if (ped->flags & SYSMON_POWER_DICTIONARY_BUSY) {
6454814127dSxtraeme 			mutex_exit(&sysmon_power_event_queue_mtx);
6464814127dSxtraeme 			error = EBUSY;
6474814127dSxtraeme 			break;
6484814127dSxtraeme 		}
6494814127dSxtraeme 
6504814127dSxtraeme 		ped->flags |= SYSMON_POWER_DICTIONARY_BUSY;
6514814127dSxtraeme 		mutex_exit(&sysmon_power_event_queue_mtx);
6524814127dSxtraeme 
6534814127dSxtraeme 		/*
6544814127dSxtraeme 		 * Send it now.
6554814127dSxtraeme 		 */
656bf4558f8Sxtraeme 		error = prop_dictionary_copyout_ioctl(plist,
657bf4558f8Sxtraeme 						      cmd,
6584814127dSxtraeme 						      ped->dict);
6594814127dSxtraeme 
6602e97fc93Sxtraeme 		/*
6614814127dSxtraeme 		 * Remove the dictionary now that we don't need it.
6622e97fc93Sxtraeme 		 */
6634814127dSxtraeme 		mutex_enter(&sysmon_power_event_queue_mtx);
6644814127dSxtraeme 		ped->flags &= ~SYSMON_POWER_DICTIONARY_BUSY;
66591551751Sxtraeme 		ped->flags &= ~SYSMON_POWER_DICTIONARY_READY;
666ce87c7efSjakllsch 		SIMPLEQ_REMOVE_HEAD(&pev_dict_list, pev_dict_head);
6674814127dSxtraeme 		mutex_exit(&sysmon_power_event_queue_mtx);
6684814127dSxtraeme 		sysmon_power_destroy_dictionary(ped);
6694814127dSxtraeme 
6705a20f4beSthorpej 		break;
6715a20f4beSthorpej 	    }
6725a20f4beSthorpej 	default:
6735a20f4beSthorpej 		error = ENOTTY;
6745a20f4beSthorpej 	}
6755a20f4beSthorpej 
676bf4558f8Sxtraeme 	return error;
677bf4558f8Sxtraeme }
678bf4558f8Sxtraeme 
679bf4558f8Sxtraeme /*
680bf4558f8Sxtraeme  * sysmon_power_make_dictionary:
681bf4558f8Sxtraeme  *
6824814127dSxtraeme  * 	Adds the properties for an event in a dictionary.
683bf4558f8Sxtraeme  */
684bf4558f8Sxtraeme int
sysmon_power_make_dictionary(prop_dictionary_t dict,void * power_data,int event,int type)6854814127dSxtraeme sysmon_power_make_dictionary(prop_dictionary_t dict, void *power_data,
6864814127dSxtraeme 			     int event, int type)
687bf4558f8Sxtraeme {
6884814127dSxtraeme 	int i;
689bf4558f8Sxtraeme 
6904814127dSxtraeme 	KASSERT(mutex_owned(&sysmon_power_event_queue_mtx));
691bf4558f8Sxtraeme 
692bf4558f8Sxtraeme 	switch (type) {
693bf4558f8Sxtraeme 	/*
694bf4558f8Sxtraeme 	 * create the dictionary for a power switch event.
695bf4558f8Sxtraeme 	 */
696bf4558f8Sxtraeme 	case POWER_EVENT_SWITCH_STATE_CHANGE:
697bf4558f8Sxtraeme 	    {
698bf4558f8Sxtraeme 		const struct power_event_description *peevent =
699bf4558f8Sxtraeme 		    pswitch_event_desc;
700bf4558f8Sxtraeme 		const struct power_event_description *petype =
701bf4558f8Sxtraeme 		    pswitch_type_desc;
702bf4558f8Sxtraeme 		struct sysmon_pswitch *smpsw =
703bf4558f8Sxtraeme 		    (struct sysmon_pswitch *)power_data;
704bf4558f8Sxtraeme 		const char *pwrtype = "pswitch";
705bf4558f8Sxtraeme 
706bf4558f8Sxtraeme #define SETPROP(key, str)						\
707bf4558f8Sxtraeme do {									\
708814a7798Sthorpej 	if ((str) != NULL && !prop_dictionary_set_string(dict,		\
709bf4558f8Sxtraeme 						  (key),		\
710bf4558f8Sxtraeme 						  (str))) {		\
711bf4558f8Sxtraeme 		printf("%s: failed to set %s\n", __func__, (str));	\
712bf4558f8Sxtraeme 		return EINVAL;						\
713bf4558f8Sxtraeme 	}								\
714bf4558f8Sxtraeme } while (/* CONSTCOND */ 0)
715bf4558f8Sxtraeme 
7167ade6d03Sxtraeme 
717bf4558f8Sxtraeme 		SETPROP("driver-name", smpsw->smpsw_name);
718bf4558f8Sxtraeme 
719bf4558f8Sxtraeme 		for (i = 0; peevent[i].type != -1; i++)
720bf4558f8Sxtraeme 			if (peevent[i].type == event)
721bf4558f8Sxtraeme 				break;
722bf4558f8Sxtraeme 
723bf4558f8Sxtraeme 		SETPROP("powerd-event-name", peevent[i].desc);
724bf4558f8Sxtraeme 
725bf4558f8Sxtraeme 		for (i = 0; petype[i].type != -1; i++)
726bf4558f8Sxtraeme 			if (petype[i].type == smpsw->smpsw_type)
727bf4558f8Sxtraeme 				break;
728bf4558f8Sxtraeme 
729bf4558f8Sxtraeme 		SETPROP("powerd-script-name", petype[i].desc);
730bf4558f8Sxtraeme 		SETPROP("power-type", pwrtype);
731bf4558f8Sxtraeme 		break;
732bf4558f8Sxtraeme 	    }
733bf4558f8Sxtraeme 	/*
734bf4558f8Sxtraeme 	 * create a dictionary for power envsys event.
735bf4558f8Sxtraeme 	 */
736bf4558f8Sxtraeme 	case POWER_EVENT_ENVSYS_STATE_CHANGE:
737bf4558f8Sxtraeme 	    {
738bf4558f8Sxtraeme 		const struct power_event_description *peevent =
739bf4558f8Sxtraeme 			penvsys_event_desc;
740bf4558f8Sxtraeme 		const struct power_event_description *petype =
741bf4558f8Sxtraeme 			penvsys_type_desc;
742bf4558f8Sxtraeme 		struct penvsys_state *pes =
743bf4558f8Sxtraeme 		    (struct penvsys_state *)power_data;
744bf4558f8Sxtraeme 		const char *pwrtype = "envsys";
745bf4558f8Sxtraeme 
746bf4558f8Sxtraeme 		SETPROP("driver-name", pes->pes_dvname);
747bf4558f8Sxtraeme 		SETPROP("sensor-name", pes->pes_sensname);
7485b53183eSxtraeme 		SETPROP("state-description", pes->pes_statedesc);
749bf4558f8Sxtraeme 
750bf4558f8Sxtraeme 		for (i = 0; peevent[i].type != -1; i++)
751bf4558f8Sxtraeme 			if (peevent[i].type == event)
752bf4558f8Sxtraeme 				break;
753bf4558f8Sxtraeme 
754bf4558f8Sxtraeme 		SETPROP("powerd-event-name", peevent[i].desc);
755bf4558f8Sxtraeme 
756bf4558f8Sxtraeme 		for (i = 0; petype[i].type != -1; i++)
757bf4558f8Sxtraeme 			if (petype[i].type == pes->pes_type)
758bf4558f8Sxtraeme 				break;
759bf4558f8Sxtraeme 
760bf4558f8Sxtraeme 		SETPROP("powerd-script-name", petype[i].desc);
761bf4558f8Sxtraeme 		SETPROP("power-type", pwrtype);
762bf4558f8Sxtraeme 		break;
763bf4558f8Sxtraeme 	    }
764bf4558f8Sxtraeme 	default:
765bf4558f8Sxtraeme 		return ENOTSUP;
766bf4558f8Sxtraeme 	}
767bf4558f8Sxtraeme 
768bf4558f8Sxtraeme 	return 0;
7695a20f4beSthorpej }
7705a20f4beSthorpej 
7715a20f4beSthorpej /*
7722e97fc93Sxtraeme  * sysmon_power_destroy_dictionary:
7732e97fc93Sxtraeme  *
7744814127dSxtraeme  * 	Destroys a power_event_dictionary object and all its
7754814127dSxtraeme  * 	properties in the dictionary.
7762e97fc93Sxtraeme  */
7772e97fc93Sxtraeme static void
sysmon_power_destroy_dictionary(struct power_event_dictionary * ped)7784814127dSxtraeme sysmon_power_destroy_dictionary(struct power_event_dictionary *ped)
7792e97fc93Sxtraeme {
7802e97fc93Sxtraeme 	prop_object_iterator_t iter;
7812e97fc93Sxtraeme 	prop_object_t obj;
7822e97fc93Sxtraeme 
7834814127dSxtraeme 	KASSERT(ped != NULL);
7844814127dSxtraeme 	KASSERT((ped->flags & SYSMON_POWER_DICTIONARY_BUSY) == 0);
7854814127dSxtraeme 
7864814127dSxtraeme 	iter = prop_dictionary_iterator(ped->dict);
7872e97fc93Sxtraeme 	if (iter == NULL)
7882e97fc93Sxtraeme 		return;
7892e97fc93Sxtraeme 
7902e97fc93Sxtraeme 	while ((obj = prop_object_iterator_next(iter)) != NULL) {
7914814127dSxtraeme 		prop_dictionary_remove(ped->dict,
792a2b798fdSthorpej 		    prop_dictionary_keysym_value(obj));
7932e97fc93Sxtraeme 		prop_object_iterator_reset(iter);
7942e97fc93Sxtraeme 	}
7952e97fc93Sxtraeme 
7962e97fc93Sxtraeme 	prop_object_iterator_release(iter);
7974814127dSxtraeme 	prop_object_release(ped->dict);
7984814127dSxtraeme 
7994814127dSxtraeme 	kmem_free(ped, sizeof(*ped));
8002e97fc93Sxtraeme }
8012e97fc93Sxtraeme 
8022e97fc93Sxtraeme /*
8035a20f4beSthorpej  * sysmon_power_settype:
8045a20f4beSthorpej  *
8055a20f4beSthorpej  *	Sets the back-end power management type.  This information can
8065a20f4beSthorpej  *	be used by the power management daemon.
8075a20f4beSthorpej  */
8085a20f4beSthorpej void
sysmon_power_settype(const char * type)8095a20f4beSthorpej sysmon_power_settype(const char *type)
8105a20f4beSthorpej {
8115a20f4beSthorpej 
8125a20f4beSthorpej 	/*
8135a20f4beSthorpej 	 * Don't bother locking this; it's going to be set
8145a20f4beSthorpej 	 * during autoconfiguration, and then only read from
8155a20f4beSthorpej 	 * then on.
8165a20f4beSthorpej 	 */
817bf4558f8Sxtraeme 	(void)strlcpy(sysmon_power_type, type, sizeof(sysmon_power_type));
818bf4558f8Sxtraeme }
819bf4558f8Sxtraeme 
820bf4558f8Sxtraeme #define PENVSYS_SHOWSTATE(str)						\
821bf4558f8Sxtraeme 	do {								\
822bf4558f8Sxtraeme 		printf("%s: %s limit on '%s'\n",			\
823bf4558f8Sxtraeme 		    pes->pes_dvname, (str), pes->pes_sensname);		\
824bf4558f8Sxtraeme 	} while (/* CONSTCOND */ 0)
825bf4558f8Sxtraeme 
826bf4558f8Sxtraeme /*
827bf4558f8Sxtraeme  * sysmon_penvsys_event:
828bf4558f8Sxtraeme  *
829bf4558f8Sxtraeme  * 	Puts an event onto the sysmon power queue and sends the
830ce87c7efSjakllsch  * 	appropriate event if the daemon is running, otherwise a
8314814127dSxtraeme  * 	message is shown.
832bf4558f8Sxtraeme  */
833bf4558f8Sxtraeme void
sysmon_penvsys_event(struct penvsys_state * pes,int event)834bf4558f8Sxtraeme sysmon_penvsys_event(struct penvsys_state *pes, int event)
835bf4558f8Sxtraeme {
8364814127dSxtraeme 	struct power_event_dictionary *ped;
837bf4558f8Sxtraeme 	const char *mystr = NULL;
838bf4558f8Sxtraeme 
83907f9bca5Sxtraeme 	KASSERT(pes != NULL);
84007f9bca5Sxtraeme 
8417b0b7dedStls 	rnd_add_uint32(&sysmon_rndsource, pes->pes_type);
8427b0b7dedStls 
8434814127dSxtraeme 	if (sysmon_power_daemon != NULL) {
8444814127dSxtraeme 		/*
8454814127dSxtraeme 		 * Create a dictionary for the new event.
8464814127dSxtraeme 		 */
84750509ea8Sxtraeme 		ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP);
84850509ea8Sxtraeme 		if (!ped)
84950509ea8Sxtraeme 			return;
8504814127dSxtraeme 		ped->dict = prop_dictionary_create();
8514814127dSxtraeme 
8524814127dSxtraeme 		if (sysmon_power_daemon_task(ped, pes, event) == 0)
853bf4558f8Sxtraeme 			return;
854a71dc1a6Schristos 		/* We failed */
855a71dc1a6Schristos 		prop_object_release(ped->dict);
85601d53ea7Sriastradh 		kmem_free(ped, sizeof(*ped));
8574814127dSxtraeme 	}
858bf4558f8Sxtraeme 
859bf4558f8Sxtraeme 	switch (pes->pes_type) {
860bf4558f8Sxtraeme 	case PENVSYS_TYPE_BATTERY:
861bf4558f8Sxtraeme 		switch (event) {
8627ade6d03Sxtraeme 		case PENVSYS_EVENT_LOW_POWER:
8637ade6d03Sxtraeme 			printf("sysmon: LOW POWER! SHUTTING DOWN.\n");
864599c2405Sthorpej 			kern_reboot(RB_POWERDOWN, NULL);
8657ade6d03Sxtraeme 			break;
8665b53183eSxtraeme 		case PENVSYS_EVENT_STATE_CHANGED:
8675b53183eSxtraeme 			printf("%s: state changed on '%s' to '%s'\n",
8685b53183eSxtraeme 			    pes->pes_dvname, pes->pes_sensname,
8695b53183eSxtraeme 			    pes->pes_statedesc);
870bf4558f8Sxtraeme 			break;
871f7ad7038Spgoyette 		case PENVSYS_EVENT_BATT_CRIT:
872bf4558f8Sxtraeme 			mystr = "critical capacity";
873bf4558f8Sxtraeme 			PENVSYS_SHOWSTATE(mystr);
874bf4558f8Sxtraeme 			break;
875f7ad7038Spgoyette 		case PENVSYS_EVENT_BATT_WARN:
8766d65ba83Spgoyette 			mystr = "warning capacity";
8776d65ba83Spgoyette 			PENVSYS_SHOWSTATE(mystr);
8786d65ba83Spgoyette 			break;
8798844275bSpgoyette 		case PENVSYS_EVENT_BATT_HIGH:
8808844275bSpgoyette 			mystr = "high capacity";
8818844275bSpgoyette 			PENVSYS_SHOWSTATE(mystr);
8828844275bSpgoyette 			break;
8838844275bSpgoyette 		case PENVSYS_EVENT_BATT_MAX:
8848844275bSpgoyette 			mystr = "maximum capacity";
8858844275bSpgoyette 			PENVSYS_SHOWSTATE(mystr);
8868844275bSpgoyette 			break;
887bf4558f8Sxtraeme 		case PENVSYS_EVENT_NORMAL:
8885b53183eSxtraeme 			printf("%s: normal capacity on '%s'\n",
889bf4558f8Sxtraeme 			    pes->pes_dvname, pes->pes_sensname);
890bf4558f8Sxtraeme 			break;
891bf4558f8Sxtraeme 		}
892bf4558f8Sxtraeme 		break;
8936a9b5725Sxtraeme 	case PENVSYS_TYPE_FAN:
8946a9b5725Sxtraeme 	case PENVSYS_TYPE_INDICATOR:
895bf4558f8Sxtraeme 	case PENVSYS_TYPE_TEMP:
896bf4558f8Sxtraeme 	case PENVSYS_TYPE_POWER:
897bf4558f8Sxtraeme 	case PENVSYS_TYPE_RESISTANCE:
898bf4558f8Sxtraeme 	case PENVSYS_TYPE_VOLTAGE:
899bf4558f8Sxtraeme 		switch (event) {
900bf4558f8Sxtraeme 		case PENVSYS_EVENT_CRITICAL:
901bf4558f8Sxtraeme 			mystr = "critical";
902bf4558f8Sxtraeme 			PENVSYS_SHOWSTATE(mystr);
903bf4558f8Sxtraeme 			break;
904bf4558f8Sxtraeme 		case PENVSYS_EVENT_CRITOVER:
905bf4558f8Sxtraeme 			mystr = "critical over";
906bf4558f8Sxtraeme 			PENVSYS_SHOWSTATE(mystr);
907bf4558f8Sxtraeme 			break;
908bf4558f8Sxtraeme 		case PENVSYS_EVENT_CRITUNDER:
909bf4558f8Sxtraeme 			mystr = "critical under";
910bf4558f8Sxtraeme 			PENVSYS_SHOWSTATE(mystr);
911bf4558f8Sxtraeme 			break;
912bf4558f8Sxtraeme 		case PENVSYS_EVENT_WARNOVER:
913bf4558f8Sxtraeme 			mystr = "warning over";
914bf4558f8Sxtraeme 			PENVSYS_SHOWSTATE(mystr);
915bf4558f8Sxtraeme 			break;
916bf4558f8Sxtraeme 		case PENVSYS_EVENT_WARNUNDER:
917bf4558f8Sxtraeme 			mystr = "warning under";
918bf4558f8Sxtraeme 			PENVSYS_SHOWSTATE(mystr);
919bf4558f8Sxtraeme 			break;
920bf4558f8Sxtraeme 		case PENVSYS_EVENT_NORMAL:
921bf4558f8Sxtraeme 			printf("%s: normal state on '%s'\n",
922bf4558f8Sxtraeme 			    pes->pes_dvname, pes->pes_sensname);
923bf4558f8Sxtraeme 			break;
924bf4558f8Sxtraeme 		default:
925bf4558f8Sxtraeme 			printf("%s: unknown event\n", __func__);
926bf4558f8Sxtraeme 		}
927bf4558f8Sxtraeme 		break;
928bf4558f8Sxtraeme 	case PENVSYS_TYPE_DRIVE:
929bf4558f8Sxtraeme 		switch (event) {
9305b53183eSxtraeme 		case PENVSYS_EVENT_STATE_CHANGED:
931bf4558f8Sxtraeme 			printf("%s: state changed on '%s' to '%s'\n",
932bf4558f8Sxtraeme 			    pes->pes_dvname, pes->pes_sensname,
933bf4558f8Sxtraeme 			    pes->pes_statedesc);
9344089d906Sxtraeme 			break;
935bf4558f8Sxtraeme 		case PENVSYS_EVENT_NORMAL:
936bf4558f8Sxtraeme 			printf("%s: normal state on '%s' (%s)\n",
937bf4558f8Sxtraeme 			    pes->pes_dvname, pes->pes_sensname,
938bf4558f8Sxtraeme 			    pes->pes_statedesc);
939bf4558f8Sxtraeme 			break;
940bf4558f8Sxtraeme 		}
941bf4558f8Sxtraeme 		break;
942bf4558f8Sxtraeme 	default:
943bf4558f8Sxtraeme 		printf("%s: unknown power type\n", __func__);
944bf4558f8Sxtraeme 		break;
945bf4558f8Sxtraeme 	}
9465a20f4beSthorpej }
9475a20f4beSthorpej 
948b0f79c65Sthorpej /*
949b0f79c65Sthorpej  * sysmon_pswitch_register:
950b0f79c65Sthorpej  *
951b0f79c65Sthorpej  *	Register a power switch device.
952b0f79c65Sthorpej  */
953b0f79c65Sthorpej int
sysmon_pswitch_register(struct sysmon_pswitch * smpsw)954b0f79c65Sthorpej sysmon_pswitch_register(struct sysmon_pswitch *smpsw)
955b0f79c65Sthorpej {
956eb659e13Spgoyette 	(void)RUN_ONCE(&once_power, power_preinit);
957eb659e13Spgoyette 
958bf4558f8Sxtraeme 	return 0;
959b0f79c65Sthorpej }
960b0f79c65Sthorpej 
961b0f79c65Sthorpej /*
962b0f79c65Sthorpej  * sysmon_pswitch_unregister:
963b0f79c65Sthorpej  *
964b0f79c65Sthorpej  *	Unregister a power switch device.
965b0f79c65Sthorpej  */
966b0f79c65Sthorpej void
sysmon_pswitch_unregister(struct sysmon_pswitch * smpsw)967b0f79c65Sthorpej sysmon_pswitch_unregister(struct sysmon_pswitch *smpsw)
968b0f79c65Sthorpej {
969bf4558f8Sxtraeme 	/* nada */
970b0f79c65Sthorpej }
971b0f79c65Sthorpej 
972b0f79c65Sthorpej /*
973b0f79c65Sthorpej  * sysmon_pswitch_event:
974b0f79c65Sthorpej  *
975b0f79c65Sthorpej  *	Register an event on a power switch device.
976b0f79c65Sthorpej  */
977b0f79c65Sthorpej void
sysmon_pswitch_event(struct sysmon_pswitch * smpsw,int event)978b0f79c65Sthorpej sysmon_pswitch_event(struct sysmon_pswitch *smpsw, int event)
979b0f79c65Sthorpej {
9804814127dSxtraeme 	struct power_event_dictionary *ped = NULL;
98107f9bca5Sxtraeme 
98207f9bca5Sxtraeme 	KASSERT(smpsw != NULL);
98307f9bca5Sxtraeme 
9844c1d81b2Sjmcneill 	/*
9854c1d81b2Sjmcneill 	 * For pnp specific events, we don't care if the power daemon
9864c1d81b2Sjmcneill 	 * is running or not
9874c1d81b2Sjmcneill 	 */
9884c1d81b2Sjmcneill 	if (smpsw->smpsw_type == PSWITCH_TYPE_LID) {
9894c1d81b2Sjmcneill 		switch (event) {
9904c1d81b2Sjmcneill 		case PSWITCH_EVENT_PRESSED:
9914c1d81b2Sjmcneill 			pmf_event_inject(NULL, PMFE_CHASSIS_LID_CLOSE);
9924c1d81b2Sjmcneill 			break;
9934c1d81b2Sjmcneill 		case PSWITCH_EVENT_RELEASED:
9944c1d81b2Sjmcneill 			pmf_event_inject(NULL, PMFE_CHASSIS_LID_OPEN);
9954c1d81b2Sjmcneill 			break;
9964c1d81b2Sjmcneill 		default:
9974c1d81b2Sjmcneill 			break;
9984c1d81b2Sjmcneill 		}
9994c1d81b2Sjmcneill 	}
10004c1d81b2Sjmcneill 
10014814127dSxtraeme 	if (sysmon_power_daemon != NULL) {
10024814127dSxtraeme 		/*
10034814127dSxtraeme 		 * Create a new dictionary for the event.
10044814127dSxtraeme 		 */
100550509ea8Sxtraeme 		ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP);
100650509ea8Sxtraeme 		if (!ped)
100750509ea8Sxtraeme 			return;
10084814127dSxtraeme 		ped->dict = prop_dictionary_create();
10094814127dSxtraeme 
10104814127dSxtraeme 		if (sysmon_power_daemon_task(ped, smpsw, event) == 0)
1011b0f79c65Sthorpej 			return;
101260286af6Schristos 		/* We failed */
101360286af6Schristos 		prop_object_release(ped->dict);
101401d53ea7Sriastradh 		kmem_free(ped, sizeof(*ped));
10154814127dSxtraeme 	}
1016b0f79c65Sthorpej 
1017b0f79c65Sthorpej 	switch (smpsw->smpsw_type) {
10185a20f4beSthorpej 	case PSWITCH_TYPE_POWER:
10195a20f4beSthorpej 		if (event != PSWITCH_EVENT_PRESSED) {
1020b0f79c65Sthorpej 			/* just ignore it */
1021b0f79c65Sthorpej 			return;
1022b0f79c65Sthorpej 		}
1023b0f79c65Sthorpej 
1024b0f79c65Sthorpej 		/*
1025b0f79c65Sthorpej 		 * Attempt a somewhat graceful shutdown of the system,
1026b0f79c65Sthorpej 		 * as if the user has issued a reboot(2) call with
1027b0f79c65Sthorpej 		 * RB_POWERDOWN.
1028b0f79c65Sthorpej 		 */
1029b0f79c65Sthorpej 		printf("%s: power button pressed, shutting down!\n",
1030b0f79c65Sthorpej 		    smpsw->smpsw_name);
1031599c2405Sthorpej 		kern_reboot(RB_POWERDOWN, NULL);
1032b0f79c65Sthorpej 		break;
1033b0f79c65Sthorpej 
10344bbda47bSthorpej 	case PSWITCH_TYPE_RESET:
10354bbda47bSthorpej 		if (event != PSWITCH_EVENT_PRESSED) {
10364bbda47bSthorpej 			/* just ignore it */
10374bbda47bSthorpej 			return;
10384bbda47bSthorpej 		}
10394bbda47bSthorpej 
10404bbda47bSthorpej 		/*
10414bbda47bSthorpej 		 * Attempt a somewhat graceful reboot of the system,
10424bbda47bSthorpej 		 * as if the user had issued a reboot(2) call.
10434bbda47bSthorpej 		 */
10444bbda47bSthorpej 		printf("%s: reset button pressed, rebooting!\n",
10454bbda47bSthorpej 		    smpsw->smpsw_name);
1046599c2405Sthorpej 		kern_reboot(0, NULL);
10474bbda47bSthorpej 		break;
10484bbda47bSthorpej 
10495a20f4beSthorpej 	case PSWITCH_TYPE_SLEEP:
10505a20f4beSthorpej 		if (event != PSWITCH_EVENT_PRESSED) {
1051b0f79c65Sthorpej 			/* just ignore it */
1052b0f79c65Sthorpej 			return;
1053b0f79c65Sthorpej 		}
1054b0f79c65Sthorpej 
1055b0f79c65Sthorpej 		/*
1056b0f79c65Sthorpej 		 * Try to enter a "sleep" state.
1057b0f79c65Sthorpej 		 */
1058b0f79c65Sthorpej 		/* XXX */
1059b0f79c65Sthorpej 		printf("%s: sleep button pressed.\n", smpsw->smpsw_name);
1060b0f79c65Sthorpej 		break;
1061b0f79c65Sthorpej 
1062ebad5777Sjmcneill 	case PSWITCH_TYPE_HOTKEY:
1063ebad5777Sjmcneill 		/*
1064ebad5777Sjmcneill 		 * Eat up the event, there's nothing we can do
1065ebad5777Sjmcneill 		 */
1066ebad5777Sjmcneill 		break;
1067ebad5777Sjmcneill 
10685a20f4beSthorpej 	case PSWITCH_TYPE_LID:
1069b0f79c65Sthorpej 		switch (event) {
10705a20f4beSthorpej 		case PSWITCH_EVENT_PRESSED:
1071b0f79c65Sthorpej 			/*
1072b0f79c65Sthorpej 			 * Try to enter a "standby" state.
1073b0f79c65Sthorpej 			 */
1074b0f79c65Sthorpej 			/* XXX */
1075b0f79c65Sthorpej 			printf("%s: lid closed.\n", smpsw->smpsw_name);
1076b0f79c65Sthorpej 			break;
1077b0f79c65Sthorpej 
10785a20f4beSthorpej 		case PSWITCH_EVENT_RELEASED:
1079b0f79c65Sthorpej 			/*
1080b0f79c65Sthorpej 			 * Come out of "standby" state.
1081b0f79c65Sthorpej 			 */
1082b0f79c65Sthorpej 			/* XXX */
1083b0f79c65Sthorpej 			printf("%s: lid opened.\n", smpsw->smpsw_name);
1084b0f79c65Sthorpej 			break;
1085b0f79c65Sthorpej 
1086b0f79c65Sthorpej 		default:
1087b0f79c65Sthorpej 			printf("%s: unknown lid switch event: %d\n",
1088b0f79c65Sthorpej 			    smpsw->smpsw_name, event);
1089b0f79c65Sthorpej 		}
1090b0f79c65Sthorpej 		break;
1091b0f79c65Sthorpej 
10921bc204dbSkochi 	case PSWITCH_TYPE_ACADAPTER:
10931bc204dbSkochi 		switch (event) {
10941bc204dbSkochi 		case PSWITCH_EVENT_PRESSED:
10951bc204dbSkochi 			/*
1096c271a0deScube 			 * Come out of power-save state.
10971bc204dbSkochi 			 */
10980b590545Sjmcneill 			aprint_normal("%s: AC adapter online.\n",
10990b590545Sjmcneill 			    smpsw->smpsw_name);
11001bc204dbSkochi 			break;
11011bc204dbSkochi 
11021bc204dbSkochi 		case PSWITCH_EVENT_RELEASED:
11031bc204dbSkochi 			/*
1104c271a0deScube 			 * Try to enter a power-save state.
11051bc204dbSkochi 			 */
11060b590545Sjmcneill 			aprint_normal("%s: AC adapter offline.\n",
11070b590545Sjmcneill 			    smpsw->smpsw_name);
11081bc204dbSkochi 			break;
11091bc204dbSkochi 		}
11101bc204dbSkochi 		break;
11111bc204dbSkochi 
1112b0f79c65Sthorpej 	}
1113b0f79c65Sthorpej }
11148061eedeSpgoyette 
1115*54853d3dSriastradh static int
sysmon_power_modcmd(modcmd_t cmd,void * arg)11168061eedeSpgoyette sysmon_power_modcmd(modcmd_t cmd, void *arg)
11178061eedeSpgoyette {
11188061eedeSpgoyette 	int ret;
11198061eedeSpgoyette 
11208061eedeSpgoyette 	switch (cmd) {
11218061eedeSpgoyette 	case MODULE_CMD_INIT:
11228061eedeSpgoyette 		ret = sysmon_power_init();
11238061eedeSpgoyette 		break;
11248061eedeSpgoyette 	case MODULE_CMD_FINI:
11258061eedeSpgoyette 		ret = sysmon_power_fini();
11268061eedeSpgoyette 		break;
11278061eedeSpgoyette 	case MODULE_CMD_STAT:
11288061eedeSpgoyette 	default:
11298061eedeSpgoyette 		ret = ENOTTY;
11308061eedeSpgoyette 	}
11318061eedeSpgoyette 
11328061eedeSpgoyette 	return ret;
11338061eedeSpgoyette }
1134