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