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