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