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