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