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