xref: /netbsd-src/sys/dev/sysmon/sysmon_power.c (revision 6dffe8d42bd46273f674d7ab834e7be9b1af990e)
1 /*	$NetBSD: sysmon_power.c,v 1.41 2009/06/08 00:55:35 pgoyette 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.41 2009/06/08 00:55:35 pgoyette 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 	    {
550 		struct power_type *power_type = (void *) data;
551 
552 		(void)strlcpy(power_type->power_type,
553 			      sysmon_power_type,
554 			      sizeof(power_type->power_type));
555 		break;
556 	    }
557 	case POWER_EVENT_RECVDICT:
558 	    {
559 		struct plistref *plist = (struct plistref *)data;
560 		struct power_event_dictionary *ped;
561 
562 		/*
563 		 * Get the first dictionary enqueued and mark it
564 		 * as busy.
565 		 */
566 		mutex_enter(&sysmon_power_event_queue_mtx);
567 		ped = SLIST_FIRST(&pev_dict_list);
568 		if (!ped || !ped->dict) {
569 			mutex_exit(&sysmon_power_event_queue_mtx);
570 			error = ENOTSUP;
571 			break;
572 		}
573 
574 		if ((ped->flags & SYSMON_POWER_DICTIONARY_READY) == 0) {
575 			mutex_exit(&sysmon_power_event_queue_mtx);
576 			error = EINVAL;
577 			break;
578 		}
579 
580 		if (ped->flags & SYSMON_POWER_DICTIONARY_BUSY) {
581 			mutex_exit(&sysmon_power_event_queue_mtx);
582 			error = EBUSY;
583 			break;
584 		}
585 
586 		ped->flags |= SYSMON_POWER_DICTIONARY_BUSY;
587 		mutex_exit(&sysmon_power_event_queue_mtx);
588 
589 		/*
590 		 * Send it now.
591 		 */
592 		error = prop_dictionary_copyout_ioctl(plist,
593 						      cmd,
594 						      ped->dict);
595 
596 		/*
597 		 * Remove the dictionary now that we don't need it.
598 		 */
599 		mutex_enter(&sysmon_power_event_queue_mtx);
600 		ped->flags &= ~SYSMON_POWER_DICTIONARY_BUSY;
601 		ped->flags &= ~SYSMON_POWER_DICTIONARY_READY;
602 		SLIST_REMOVE_HEAD(&pev_dict_list, pev_dict_head);
603 		mutex_exit(&sysmon_power_event_queue_mtx);
604 		sysmon_power_destroy_dictionary(ped);
605 
606 		break;
607 	    }
608 	default:
609 		error = ENOTTY;
610 	}
611 
612 	return error;
613 }
614 
615 /*
616  * sysmon_power_make_dictionary:
617  *
618  * 	Adds the properties for an event in a dictionary.
619  */
620 int
621 sysmon_power_make_dictionary(prop_dictionary_t dict, void *power_data,
622 			     int event, int type)
623 {
624 	int i;
625 
626 	KASSERT(mutex_owned(&sysmon_power_event_queue_mtx));
627 
628 	switch (type) {
629 	/*
630 	 * create the dictionary for a power switch event.
631 	 */
632 	case POWER_EVENT_SWITCH_STATE_CHANGE:
633 	    {
634 		const struct power_event_description *peevent =
635 		    pswitch_event_desc;
636 		const struct power_event_description *petype =
637 		    pswitch_type_desc;
638 		struct sysmon_pswitch *smpsw =
639 		    (struct sysmon_pswitch *)power_data;
640 		const char *pwrtype = "pswitch";
641 
642 #define SETPROP(key, str)						\
643 do {									\
644 	if ((str) && !prop_dictionary_set_cstring(dict,			\
645 						  (key),		\
646 						  (str))) {		\
647 		printf("%s: failed to set %s\n", __func__, (str));	\
648 		return EINVAL;						\
649 	}								\
650 } while (/* CONSTCOND */ 0)
651 
652 
653 		SETPROP("driver-name", smpsw->smpsw_name);
654 
655 		for (i = 0; peevent[i].type != -1; i++)
656 			if (peevent[i].type == event)
657 				break;
658 
659 		SETPROP("powerd-event-name", peevent[i].desc);
660 
661 		for (i = 0; petype[i].type != -1; i++)
662 			if (petype[i].type == smpsw->smpsw_type)
663 				break;
664 
665 		SETPROP("powerd-script-name", petype[i].desc);
666 		SETPROP("power-type", pwrtype);
667 		break;
668 	    }
669 	/*
670 	 * create a dictionary for power envsys event.
671 	 */
672 	case POWER_EVENT_ENVSYS_STATE_CHANGE:
673 	    {
674 		const struct power_event_description *peevent =
675 			penvsys_event_desc;
676 		const struct power_event_description *petype =
677 			penvsys_type_desc;
678 		struct penvsys_state *pes =
679 		    (struct penvsys_state *)power_data;
680 		const char *pwrtype = "envsys";
681 
682 		SETPROP("driver-name", pes->pes_dvname);
683 		SETPROP("sensor-name", pes->pes_sensname);
684 		SETPROP("state-description", pes->pes_statedesc);
685 
686 		for (i = 0; peevent[i].type != -1; i++)
687 			if (peevent[i].type == event)
688 				break;
689 
690 		SETPROP("powerd-event-name", peevent[i].desc);
691 
692 		for (i = 0; petype[i].type != -1; i++)
693 			if (petype[i].type == pes->pes_type)
694 				break;
695 
696 		SETPROP("powerd-script-name", petype[i].desc);
697 		SETPROP("power-type", pwrtype);
698 		break;
699 	    }
700 	default:
701 		return ENOTSUP;
702 	}
703 
704 	return 0;
705 }
706 
707 /*
708  * sysmon_power_destroy_dictionary:
709  *
710  * 	Destroys a power_event_dictionary object and all its
711  * 	properties in the dictionary.
712  */
713 static void
714 sysmon_power_destroy_dictionary(struct power_event_dictionary *ped)
715 {
716 	prop_object_iterator_t iter;
717 	prop_object_t obj;
718 
719 	KASSERT(ped != NULL);
720 	KASSERT((ped->flags & SYSMON_POWER_DICTIONARY_BUSY) == 0);
721 
722 	iter = prop_dictionary_iterator(ped->dict);
723 	if (iter == NULL)
724 		return;
725 
726 	while ((obj = prop_object_iterator_next(iter)) != NULL) {
727 		prop_dictionary_remove(ped->dict,
728 		    prop_dictionary_keysym_cstring_nocopy(obj));
729 		prop_object_iterator_reset(iter);
730 	}
731 
732 	prop_object_iterator_release(iter);
733 	prop_object_release(ped->dict);
734 
735 	kmem_free(ped, sizeof(*ped));
736 }
737 
738 /*
739  * sysmon_power_settype:
740  *
741  *	Sets the back-end power management type.  This information can
742  *	be used by the power management daemon.
743  */
744 void
745 sysmon_power_settype(const char *type)
746 {
747 
748 	/*
749 	 * Don't bother locking this; it's going to be set
750 	 * during autoconfiguration, and then only read from
751 	 * then on.
752 	 */
753 	(void)strlcpy(sysmon_power_type, type, sizeof(sysmon_power_type));
754 }
755 
756 #define PENVSYS_SHOWSTATE(str)						\
757 	do {								\
758 		printf("%s: %s limit on '%s'\n",			\
759 		    pes->pes_dvname, (str), pes->pes_sensname);		\
760 	} while (/* CONSTCOND */ 0)
761 
762 /*
763  * sysmon_penvsys_event:
764  *
765  * 	Puts an event onto the sysmon power queue and sends the
766  * 	appropiate event if the daemon is running, otherwise a
767  * 	message is shown.
768  */
769 void
770 sysmon_penvsys_event(struct penvsys_state *pes, int event)
771 {
772 	struct power_event_dictionary *ped;
773 	const char *mystr = NULL;
774 
775 	KASSERT(pes != NULL);
776 
777 	if (sysmon_power_daemon != NULL) {
778 		/*
779 		 * Create a dictionary for the new event.
780 		 */
781 		ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP);
782 		if (!ped)
783 			return;
784 		ped->dict = prop_dictionary_create();
785 
786 		if (sysmon_power_daemon_task(ped, pes, event) == 0)
787 			return;
788 	}
789 
790 	switch (pes->pes_type) {
791 	case PENVSYS_TYPE_BATTERY:
792 		switch (event) {
793 		case PENVSYS_EVENT_LOW_POWER:
794 			printf("sysmon: LOW POWER! SHUTTING DOWN.\n");
795 			cpu_reboot(RB_POWERDOWN, NULL);
796 			break;
797 		case PENVSYS_EVENT_STATE_CHANGED:
798 			printf("%s: state changed on '%s' to '%s'\n",
799 			    pes->pes_dvname, pes->pes_sensname,
800 			    pes->pes_statedesc);
801 			break;
802 		case PENVSYS_EVENT_BATT_CRIT:
803 			mystr = "critical capacity";
804 			PENVSYS_SHOWSTATE(mystr);
805 			break;
806 		case PENVSYS_EVENT_BATT_WARN:
807 			mystr = "warning capacity";
808 			PENVSYS_SHOWSTATE(mystr);
809 			break;
810 		case PENVSYS_EVENT_NORMAL:
811 			printf("%s: normal capacity on '%s'\n",
812 			    pes->pes_dvname, pes->pes_sensname);
813 			break;
814 		}
815 		break;
816 	case PENVSYS_TYPE_FAN:
817 	case PENVSYS_TYPE_INDICATOR:
818 	case PENVSYS_TYPE_TEMP:
819 	case PENVSYS_TYPE_POWER:
820 	case PENVSYS_TYPE_RESISTANCE:
821 	case PENVSYS_TYPE_VOLTAGE:
822 		switch (event) {
823 		case PENVSYS_EVENT_CRITICAL:
824 			mystr = "critical";
825 			PENVSYS_SHOWSTATE(mystr);
826 			break;
827 		case PENVSYS_EVENT_CRITOVER:
828 			mystr = "critical over";
829 			PENVSYS_SHOWSTATE(mystr);
830 			break;
831 		case PENVSYS_EVENT_CRITUNDER:
832 			mystr = "critical under";
833 			PENVSYS_SHOWSTATE(mystr);
834 			break;
835 		case PENVSYS_EVENT_WARNOVER:
836 			mystr = "warning over";
837 			PENVSYS_SHOWSTATE(mystr);
838 			break;
839 		case PENVSYS_EVENT_WARNUNDER:
840 			mystr = "warning under";
841 			PENVSYS_SHOWSTATE(mystr);
842 			break;
843 		case PENVSYS_EVENT_NORMAL:
844 			printf("%s: normal state on '%s'\n",
845 			    pes->pes_dvname, pes->pes_sensname);
846 			break;
847 		default:
848 			printf("%s: unknown event\n", __func__);
849 		}
850 		break;
851 	case PENVSYS_TYPE_DRIVE:
852 		switch (event) {
853 		case PENVSYS_EVENT_STATE_CHANGED:
854 			printf("%s: state changed on '%s' to '%s'\n",
855 			    pes->pes_dvname, pes->pes_sensname,
856 			    pes->pes_statedesc);
857 			break;
858 		case PENVSYS_EVENT_NORMAL:
859 			printf("%s: normal state on '%s' (%s)\n",
860 			    pes->pes_dvname, pes->pes_sensname,
861 			    pes->pes_statedesc);
862 			break;
863 		}
864 		break;
865 	default:
866 		printf("%s: unknown power type\n", __func__);
867 		break;
868 	}
869 }
870 
871 /*
872  * sysmon_pswitch_register:
873  *
874  *	Register a power switch device.
875  */
876 int
877 sysmon_pswitch_register(struct sysmon_pswitch *smpsw)
878 {
879 	/* nada */
880 	return 0;
881 }
882 
883 /*
884  * sysmon_pswitch_unregister:
885  *
886  *	Unregister a power switch device.
887  */
888 void
889 sysmon_pswitch_unregister(struct sysmon_pswitch *smpsw)
890 {
891 	/* nada */
892 }
893 
894 /*
895  * sysmon_pswitch_event:
896  *
897  *	Register an event on a power switch device.
898  */
899 void
900 sysmon_pswitch_event(struct sysmon_pswitch *smpsw, int event)
901 {
902 	struct power_event_dictionary *ped = NULL;
903 
904 	KASSERT(smpsw != NULL);
905 
906 	/*
907 	 * For pnp specific events, we don't care if the power daemon
908 	 * is running or not
909 	 */
910 	if (smpsw->smpsw_type == PSWITCH_TYPE_LID) {
911 		switch (event) {
912 		case PSWITCH_EVENT_PRESSED:
913 			pmf_event_inject(NULL, PMFE_CHASSIS_LID_CLOSE);
914 			break;
915 		case PSWITCH_EVENT_RELEASED:
916 			pmf_event_inject(NULL, PMFE_CHASSIS_LID_OPEN);
917 			break;
918 		default:
919 			break;
920 		}
921 	}
922 
923 	if (sysmon_power_daemon != NULL) {
924 		/*
925 		 * Create a new dictionary for the event.
926 		 */
927 		ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP);
928 		if (!ped)
929 			return;
930 		ped->dict = prop_dictionary_create();
931 
932 		if (sysmon_power_daemon_task(ped, smpsw, event) == 0)
933 			return;
934 	}
935 
936 	switch (smpsw->smpsw_type) {
937 	case PSWITCH_TYPE_POWER:
938 		if (event != PSWITCH_EVENT_PRESSED) {
939 			/* just ignore it */
940 			return;
941 		}
942 
943 		/*
944 		 * Attempt a somewhat graceful shutdown of the system,
945 		 * as if the user has issued a reboot(2) call with
946 		 * RB_POWERDOWN.
947 		 */
948 		printf("%s: power button pressed, shutting down!\n",
949 		    smpsw->smpsw_name);
950 		cpu_reboot(RB_POWERDOWN, NULL);
951 		break;
952 
953 	case PSWITCH_TYPE_RESET:
954 		if (event != PSWITCH_EVENT_PRESSED) {
955 			/* just ignore it */
956 			return;
957 		}
958 
959 		/*
960 		 * Attempt a somewhat graceful reboot of the system,
961 		 * as if the user had issued a reboot(2) call.
962 		 */
963 		printf("%s: reset button pressed, rebooting!\n",
964 		    smpsw->smpsw_name);
965 		cpu_reboot(0, NULL);
966 		break;
967 
968 	case PSWITCH_TYPE_SLEEP:
969 		if (event != PSWITCH_EVENT_PRESSED) {
970 			/* just ignore it */
971 			return;
972 		}
973 
974 		/*
975 		 * Try to enter a "sleep" state.
976 		 */
977 		/* XXX */
978 		printf("%s: sleep button pressed.\n", smpsw->smpsw_name);
979 		break;
980 
981 	case PSWITCH_TYPE_HOTKEY:
982 		/*
983 		 * Eat up the event, there's nothing we can do
984 		 */
985 		break;
986 
987 	case PSWITCH_TYPE_LID:
988 		switch (event) {
989 		case PSWITCH_EVENT_PRESSED:
990 			/*
991 			 * Try to enter a "standby" state.
992 			 */
993 			/* XXX */
994 			printf("%s: lid closed.\n", smpsw->smpsw_name);
995 			break;
996 
997 		case PSWITCH_EVENT_RELEASED:
998 			/*
999 			 * Come out of "standby" state.
1000 			 */
1001 			/* XXX */
1002 			printf("%s: lid opened.\n", smpsw->smpsw_name);
1003 			break;
1004 
1005 		default:
1006 			printf("%s: unknown lid switch event: %d\n",
1007 			    smpsw->smpsw_name, event);
1008 		}
1009 		break;
1010 
1011 	case PSWITCH_TYPE_ACADAPTER:
1012 		switch (event) {
1013 		case PSWITCH_EVENT_PRESSED:
1014 			/*
1015 			 * Come out of power-save state.
1016 			 */
1017 			aprint_normal("%s: AC adapter online.\n",
1018 			    smpsw->smpsw_name);
1019 			break;
1020 
1021 		case PSWITCH_EVENT_RELEASED:
1022 			/*
1023 			 * Try to enter a power-save state.
1024 			 */
1025 			aprint_normal("%s: AC adapter offline.\n",
1026 			    smpsw->smpsw_name);
1027 			break;
1028 		}
1029 		break;
1030 
1031 	}
1032 }
1033