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