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