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