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