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