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