1/* $NetBSD: sysmon_power.c,v 1.57 2015/12/14 01:08:47 pgoyette Exp $ */
2
3/*-
4 * Copyright (c) 2007 Juan Romero Pardines.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28/*
29 * Copyright (c) 2003 Wasabi Systems, Inc.
30 * All rights reserved.
31 *
32 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
33 *
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions
36 * are met:
37 * 1. Redistributions of source code must retain the above copyright
38 * notice, this list of conditions and the following disclaimer.
39 * 2. Redistributions in binary form must reproduce the above copyright
40 * notice, this list of conditions and the following disclaimer in the
41 * documentation and/or other materials provided with the distribution.
42 * 3. All advertising materials mentioning features or use of this software
43 * must display the following acknowledgement:
44 * This product includes software developed for the NetBSD Project by
45 * Wasabi Systems, Inc.
46 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
47 * or promote products derived from this software without specific prior
48 * written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
52 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
53 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
54 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
57 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
58 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
59 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
60 * POSSIBILITY OF SUCH DAMAGE.
61 */
62
63/*
64 * Power management framework for sysmon.
65 *
66 * We defer to a power management daemon running in userspace, since
67 * power management is largely a policy issue. This merely provides
68 * for power management event notification to that daemon.
69 */
70
71#include <sys/cdefs.h>
72__KERNEL_RCSID(0, "$NetBSD: sysmon_power.c,v 1.57 2015/12/14 01:08:47 pgoyette Exp $");
73
74#ifdef _KERNEL_OPT
75#include "opt_compat_netbsd.h"
76#endif
77
78#include <sys/param.h>
79#include <sys/reboot.h>
80#include <sys/systm.h>
81#include <sys/poll.h>
82#include <sys/select.h>
83#include <sys/vnode.h>
84#include <sys/condvar.h>
85#include <sys/mutex.h>
86#include <sys/kmem.h>
87#include <sys/proc.h>
88#include <sys/device.h>
89#include <sys/rndsource.h>
90#include <sys/module.h>
91#include <sys/once.h>
92
93#include <dev/sysmon/sysmonvar.h>
94#include <prop/proplib.h>
95
96MODULE(MODULE_CLASS_DRIVER, sysmon_power, "sysmon");
97
98/*
99 * Singly linked list for dictionaries to be stored/sent.
100 */
101struct power_event_dictionary {
102 SIMPLEQ_ENTRY(power_event_dictionary) pev_dict_head;
103 prop_dictionary_t dict;
104 int flags;
105};
106
107struct power_event_description {
108 int type;
109 const char *desc;
110};
111
112/*
113 * Available events for power switches.
114 */
115static const struct power_event_description pswitch_event_desc[] = {
116 { PSWITCH_EVENT_PRESSED, "pressed" },
117 { PSWITCH_EVENT_RELEASED, "released" },
118 { -1, NULL }
119};
120
121/*
122 * Available script names for power switches.
123 */
124static const struct power_event_description pswitch_type_desc[] = {
125 { PSWITCH_TYPE_POWER, "power_button" },
126 { PSWITCH_TYPE_SLEEP, "sleep_button" },
127 { PSWITCH_TYPE_LID, "lid_switch" },
128 { PSWITCH_TYPE_RESET, "reset_button" },
129 { PSWITCH_TYPE_ACADAPTER, "acadapter" },
130 { PSWITCH_TYPE_HOTKEY, "hotkey_button" },
131 { PSWITCH_TYPE_RADIO, "radio_button" },
132 { -1, NULL }
133};
134
135/*
136 * Available events for envsys(4).
137 */
138static const struct power_event_description penvsys_event_desc[] = {
139 { PENVSYS_EVENT_NORMAL, "normal" },
140 { PENVSYS_EVENT_CRITICAL, "critical" },
141 { PENVSYS_EVENT_CRITOVER, "critical-over" },
142 { PENVSYS_EVENT_CRITUNDER, "critical-under" },
143 { PENVSYS_EVENT_WARNOVER, "warning-over" },
144 { PENVSYS_EVENT_WARNUNDER, "warning-under" },
145 { PENVSYS_EVENT_BATT_CRIT, "critical-capacity" },
146 { PENVSYS_EVENT_BATT_WARN, "warning-capacity" },
147 { PENVSYS_EVENT_BATT_HIGH, "high-capacity" },
148 { PENVSYS_EVENT_BATT_MAX, "maximum-capacity" },
149 { PENVSYS_EVENT_STATE_CHANGED, "state-changed" },
150 { PENVSYS_EVENT_LOW_POWER, "low-power" },
151 { -1, NULL }
152};
153
154/*
155 * Available script names for envsys(4).
156 */
157static const struct power_event_description penvsys_type_desc[] = {
158 { PENVSYS_TYPE_BATTERY, "sensor_battery" },
159 { PENVSYS_TYPE_DRIVE, "sensor_drive" },
160 { PENVSYS_TYPE_FAN, "sensor_fan" },
161 { PENVSYS_TYPE_INDICATOR, "sensor_indicator" },
162 { PENVSYS_TYPE_POWER, "sensor_power" },
163 { PENVSYS_TYPE_RESISTANCE, "sensor_resistance" },
164 { PENVSYS_TYPE_TEMP, "sensor_temperature" },
165 { PENVSYS_TYPE_VOLTAGE, "sensor_voltage" },
166 { -1, NULL }
167};
168
169#define SYSMON_MAX_POWER_EVENTS 32
170#define SYSMON_POWER_DICTIONARY_BUSY 0x01
171#define SYSMON_POWER_DICTIONARY_READY 0x02
172
173static power_event_t sysmon_power_event_queue[SYSMON_MAX_POWER_EVENTS];
174static int sysmon_power_event_queue_head;
175static int sysmon_power_event_queue_tail;
176static int sysmon_power_event_queue_count;
177
178static krndsource_t sysmon_rndsource;
179
180static SIMPLEQ_HEAD(, power_event_dictionary) pev_dict_list =
181 SIMPLEQ_HEAD_INITIALIZER(pev_dict_list);
182
183static struct selinfo sysmon_power_event_queue_selinfo;
184static struct lwp *sysmon_power_daemon;
185
186static kmutex_t sysmon_power_event_queue_mtx;
187static kcondvar_t sysmon_power_event_queue_cv;
188
189static char sysmon_power_type[32];
190
191static int sysmon_power_make_dictionary(prop_dictionary_t, void *, int, int);
192static int sysmon_power_daemon_task(struct power_event_dictionary *,
193 void *, int);
194static void sysmon_power_destroy_dictionary(struct power_event_dictionary *);
195
196static struct sysmon_opvec sysmon_power_opvec = {
197 sysmonopen_power, sysmonclose_power, sysmonioctl_power,
198 sysmonread_power, sysmonpoll_power, sysmonkqfilter_power
199};
200
201#define SYSMON_NEXT_EVENT(x) (((x) + 1) % SYSMON_MAX_POWER_EVENTS)
202
203ONCE_DECL(once_power);
204
205static int
206power_preinit(void)
207{
208
209 mutex_init(&sysmon_power_event_queue_mtx, MUTEX_DEFAULT, IPL_NONE);
210 cv_init(&sysmon_power_event_queue_cv, "smpower");
211
212 return 0;
213}
214
215/*
216 * sysmon_power_init:
217 *
218 * Initializes the mutexes and condition variables in the
219 * boot process via module initialization process.
220 */
221int
222sysmon_power_init(void)
223{
224 int error;
225
226 (void)RUN_ONCE(&once_power, power_preinit);
227
228 selinit(&sysmon_power_event_queue_selinfo);
229
230 rnd_attach_source(&sysmon_rndsource, "system-power",
231 RND_TYPE_POWER, RND_FLAG_DEFAULT);
232
233 error = sysmon_attach_minor(SYSMON_MINOR_POWER, &sysmon_power_opvec);
234
235 return error;
236}
237
238int
239sysmon_power_fini(void)
240{
241 int error;
242
243 if (sysmon_power_daemon != NULL)
244 error = EBUSY;
245 else
246 error = sysmon_attach_minor(SYSMON_MINOR_POWER, NULL);
247
248 if (error == 0) {
249 rnd_detach_source(&sysmon_rndsource);
250 seldestroy(&sysmon_power_event_queue_selinfo);
251 cv_destroy(&sysmon_power_event_queue_cv);
252 mutex_destroy(&sysmon_power_event_queue_mtx);
253 }
254
255 return error;
256}
257
258/*
259 * sysmon_queue_power_event:
260 *
261 * Enqueue a power event for the power management daemon. Returns
262 * non-zero if we were able to enqueue a power event.
263 */
264static int
265sysmon_queue_power_event(power_event_t *pev)
266{
267 KASSERT(mutex_owned(&sysmon_power_event_queue_mtx));
268
269 if (sysmon_power_event_queue_count == SYSMON_MAX_POWER_EVENTS)
270 return 0;
271
272 sysmon_power_event_queue[sysmon_power_event_queue_head] = *pev;
273 sysmon_power_event_queue_head =
274 SYSMON_NEXT_EVENT(sysmon_power_event_queue_head);
275 sysmon_power_event_queue_count++;
276
277 return 1;
278}
279
280/*
281 * sysmon_get_power_event:
282 *
283 * Get a power event from the queue. Returns non-zero if there
284 * is an event available.
285 */
286static int
287sysmon_get_power_event(power_event_t *pev)
288{
289 KASSERT(mutex_owned(&sysmon_power_event_queue_mtx));
290
291 if (sysmon_power_event_queue_count == 0)
292 return 0;
293
294 *pev = sysmon_power_event_queue[sysmon_power_event_queue_tail];
295 sysmon_power_event_queue_tail =
296 SYSMON_NEXT_EVENT(sysmon_power_event_queue_tail);
297 sysmon_power_event_queue_count--;
298
299 return 1;
300}
301
302/*
303 * sysmon_power_event_queue_flush:
304 *
305 * Flush the event queue, and reset all state.
306 */
307static void
308sysmon_power_event_queue_flush(void)
309{
310 KASSERT(mutex_owned(&sysmon_power_event_queue_mtx));
311
312 sysmon_power_event_queue_head = 0;
313 sysmon_power_event_queue_tail = 0;
314 sysmon_power_event_queue_count = 0;
315}
316
317/*
318 * sysmon_power_daemon_task:
319 *
320 * Assign required power event members and sends a signal
321 * to the process to notify that an event was enqueued successfully.
322 */
323static int
324sysmon_power_daemon_task(struct power_event_dictionary *ped,
325 void *pev_data, int event)
326{
327 power_event_t pev;
328 int rv, error = 0;
329
330 if (!ped || !ped->dict || !pev_data)
331 return EINVAL;
332
333 mutex_enter(&sysmon_power_event_queue_mtx);
334
335 switch (event) {
336 /*
337 * Power switch events.
338 */
339 case PSWITCH_EVENT_PRESSED:
340 case PSWITCH_EVENT_RELEASED:
341 {
342
343 struct sysmon_pswitch *pswitch =
344 (struct sysmon_pswitch *)pev_data;
345
346 pev.pev_type = POWER_EVENT_SWITCH_STATE_CHANGE;
347#ifdef COMPAT_40
348 pev.pev_switch.psws_state = event;
349 pev.pev_switch.psws_type = pswitch->smpsw_type;
350
351 if (pswitch->smpsw_name) {
352 (void)strlcpy(pev.pev_switch.psws_name,
353 pswitch->smpsw_name,
354 sizeof(pev.pev_switch.psws_name));
355 }
356#endif
357 error = sysmon_power_make_dictionary(ped->dict,
358 pswitch,
359 event,
360 pev.pev_type);
361 if (error) {
362 mutex_exit(&sysmon_power_event_queue_mtx);
363 goto out;
364 }
365
366 break;
367 }
368
369 /*
370 * ENVSYS events.
371 */
372 case PENVSYS_EVENT_NORMAL:
373 case PENVSYS_EVENT_CRITICAL:
374 case PENVSYS_EVENT_CRITUNDER:
375 case PENVSYS_EVENT_CRITOVER:
376 case PENVSYS_EVENT_WARNUNDER:
377 case PENVSYS_EVENT_WARNOVER:
378 case PENVSYS_EVENT_BATT_CRIT:
379 case PENVSYS_EVENT_BATT_WARN:
380 case PENVSYS_EVENT_BATT_HIGH:
381 case PENVSYS_EVENT_BATT_MAX:
382 case PENVSYS_EVENT_STATE_CHANGED:
383 case PENVSYS_EVENT_LOW_POWER:
384 {
385 struct penvsys_state *penvsys =
386 (struct penvsys_state *)pev_data;
387
388 pev.pev_type = POWER_EVENT_ENVSYS_STATE_CHANGE;
389
390 error = sysmon_power_make_dictionary(ped->dict,
391 penvsys,
392 event,
393 pev.pev_type);
394 if (error) {
395 mutex_exit(&sysmon_power_event_queue_mtx);
396 goto out;
397 }
398
399 break;
400 }
401 default:
402 error = ENOTTY;
403 mutex_exit(&sysmon_power_event_queue_mtx);
404 goto out;
405 }
406
407 /*
408 * Enqueue the event.
409 */
410 rv = sysmon_queue_power_event(&pev);
411 if (rv == 0) {
412 printf("%s: WARNING: state change event %d lost; "
413 "queue full\n", __func__, pev.pev_type);
414 mutex_exit(&sysmon_power_event_queue_mtx);
415 error = EINVAL;
416 goto out;
417 } else {
418 /*
419 * Notify the daemon that an event is ready and its
420 * dictionary is ready to be fetched.
421 */
422 ped->flags |= SYSMON_POWER_DICTIONARY_READY;
423 SIMPLEQ_INSERT_TAIL(&pev_dict_list, ped, pev_dict_head);
424 cv_broadcast(&sysmon_power_event_queue_cv);
425 mutex_exit(&sysmon_power_event_queue_mtx);
426 selnotify(&sysmon_power_event_queue_selinfo, 0, 0);
427 }
428
429out:
430 return error;
431}
432
433/*
434 * sysmonopen_power:
435 *
436 * Open the system monitor device.
437 */
438int
439sysmonopen_power(dev_t dev, int flag, int mode, struct lwp *l)
440{
441 int error = 0;
442
443 mutex_enter(&sysmon_power_event_queue_mtx);
444 if (sysmon_power_daemon != NULL)
445 error = EBUSY;
446 else {
447 sysmon_power_daemon = l;
448 sysmon_power_event_queue_flush();
449 }
450 mutex_exit(&sysmon_power_event_queue_mtx);
451
452 return error;
453}
454
455/*
456 * sysmonclose_power:
457 *
458 * Close the system monitor device.
459 */
460int
461sysmonclose_power(dev_t dev, int flag, int mode, struct lwp *l)
462{
463 int count;
464
465 mutex_enter(&sysmon_power_event_queue_mtx);
466 count = sysmon_power_event_queue_count;
467 sysmon_power_daemon = NULL;
468 sysmon_power_event_queue_flush();
469 mutex_exit(&sysmon_power_event_queue_mtx);
470
471 if (count)
472 printf("WARNING: %d power event%s lost by exiting daemon\n",
473 count, count > 1 ? "s" : "");
474
475 return 0;
476}
477
478/*
479 * sysmonread_power:
480 *
481 * Read the system monitor device.
482 */
483int
484sysmonread_power(dev_t dev, struct uio *uio, int flags)
485{
486 power_event_t pev;
487 int rv;
488
489 /* We only allow one event to be read at a time. */
490 if (uio->uio_resid != POWER_EVENT_MSG_SIZE)
491 return EINVAL;
492
493 mutex_enter(&sysmon_power_event_queue_mtx);
494 for (;;) {
495 if (sysmon_get_power_event(&pev)) {
496 rv = uiomove(&pev, POWER_EVENT_MSG_SIZE, uio);
497 break;
498 }
499
500 if (flags & IO_NDELAY) {
501 rv = EWOULDBLOCK;
502 break;
503 }
504
505 cv_wait(&sysmon_power_event_queue_cv,
506 &sysmon_power_event_queue_mtx);
507 }
508 mutex_exit(&sysmon_power_event_queue_mtx);
509
510 return rv;
511}
512
513/*
514 * sysmonpoll_power:
515 *
516 * Poll the system monitor device.
517 */
518int
519sysmonpoll_power(dev_t dev, int events, struct lwp *l)
520{
521 int revents;
522
523 revents = events & (POLLOUT | POLLWRNORM);
524
525 /* Attempt to save some work. */
526 if ((events & (POLLIN | POLLRDNORM)) == 0)
527 return revents;
528
529 mutex_enter(&sysmon_power_event_queue_mtx);
530 if (sysmon_power_event_queue_count)
531 revents |= events & (POLLIN | POLLRDNORM);
532 else
533 selrecord(l, &sysmon_power_event_queue_selinfo);
534 mutex_exit(&sysmon_power_event_queue_mtx);
535
536 return revents;
537}
538
539static void
540filt_sysmon_power_rdetach(struct knote *kn)
541{
542
543 mutex_enter(&sysmon_power_event_queue_mtx);
544 SLIST_REMOVE(&sysmon_power_event_queue_selinfo.sel_klist,
545 kn, knote, kn_selnext);
546 mutex_exit(&sysmon_power_event_queue_mtx);
547}
548
549static int
550filt_sysmon_power_read(struct knote *kn, long hint)
551{
552
553 mutex_enter(&sysmon_power_event_queue_mtx);
554 kn->kn_data = sysmon_power_event_queue_count;
555 mutex_exit(&sysmon_power_event_queue_mtx);
556
557 return kn->kn_data > 0;
558}
559
560static const struct filterops sysmon_power_read_filtops =
561 { 1, NULL, filt_sysmon_power_rdetach, filt_sysmon_power_read };
562
563static const struct filterops sysmon_power_write_filtops =
564 { 1, NULL, filt_sysmon_power_rdetach, filt_seltrue };
565
566/*
567 * sysmonkqfilter_power:
568 *
569 * Kqueue filter for the system monitor device.
570 */
571int
572sysmonkqfilter_power(dev_t dev, struct knote *kn)
573{
574 struct klist *klist;
575
576 switch (kn->kn_filter) {
577 case EVFILT_READ:
578 klist = &sysmon_power_event_queue_selinfo.sel_klist;
579 kn->kn_fop = &sysmon_power_read_filtops;
580 break;
581
582 case EVFILT_WRITE:
583 klist = &sysmon_power_event_queue_selinfo.sel_klist;
584 kn->kn_fop = &sysmon_power_write_filtops;
585 break;
586
587 default:
588 return EINVAL;
589 }
590
591 mutex_enter(&sysmon_power_event_queue_mtx);
592 SLIST_INSERT_HEAD(klist, kn, kn_selnext);
593 mutex_exit(&sysmon_power_event_queue_mtx);
594
595 return 0;
596}
597
598/*
599 * sysmonioctl_power:
600 *
601 * Perform a power management control request.
602 */
603int
604sysmonioctl_power(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
605{
606 int error = 0;
607
608 switch (cmd) {
609 case POWER_IOC_GET_TYPE:
610 case POWER_IOC_GET_TYPE_WITH_LOSSAGE:
611 {
612 struct power_type *power_type = (void *) data;
613
614 (void)strlcpy(power_type->power_type,
615 sysmon_power_type,
616 sizeof(power_type->power_type));
617 break;
618 }
619 case POWER_EVENT_RECVDICT:
620 {
621 struct plistref *plist = (struct plistref *)data;
622 struct power_event_dictionary *ped;
623
624 /*
625 * Get the first dictionary enqueued and mark it
626 * as busy.
627 */
628 mutex_enter(&sysmon_power_event_queue_mtx);
629 ped = SIMPLEQ_FIRST(&pev_dict_list);
630 if (!ped || !ped->dict) {
631 mutex_exit(&sysmon_power_event_queue_mtx);
632 error = ENOTSUP;
633 break;
634 }
635
636 if ((ped->flags & SYSMON_POWER_DICTIONARY_READY) == 0) {
637 mutex_exit(&sysmon_power_event_queue_mtx);
638 error = EINVAL;
639 break;
640 }
641
642 if (ped->flags & SYSMON_POWER_DICTIONARY_BUSY) {
643 mutex_exit(&sysmon_power_event_queue_mtx);
644 error = EBUSY;
645 break;
646 }
647
648 ped->flags |= SYSMON_POWER_DICTIONARY_BUSY;
649 mutex_exit(&sysmon_power_event_queue_mtx);
650
651 /*
652 * Send it now.
653 */
654 error = prop_dictionary_copyout_ioctl(plist,
655 cmd,
656 ped->dict);
657
658 /*
659 * Remove the dictionary now that we don't need it.
660 */
661 mutex_enter(&sysmon_power_event_queue_mtx);
662 ped->flags &= ~SYSMON_POWER_DICTIONARY_BUSY;
663 ped->flags &= ~SYSMON_POWER_DICTIONARY_READY;
664 SIMPLEQ_REMOVE_HEAD(&pev_dict_list, pev_dict_head);
665 mutex_exit(&sysmon_power_event_queue_mtx);
666 sysmon_power_destroy_dictionary(ped);
667
668 break;
669 }
670 default:
671 error = ENOTTY;
672 }
673
674 return error;
675}
676
677/*
678 * sysmon_power_make_dictionary:
679 *
680 * Adds the properties for an event in a dictionary.
681 */
682int
683sysmon_power_make_dictionary(prop_dictionary_t dict, void *power_data,
684 int event, int type)
685{
686 int i;
687
688 KASSERT(mutex_owned(&sysmon_power_event_queue_mtx));
689
690 switch (type) {
691 /*
692 * create the dictionary for a power switch event.
693 */
694 case POWER_EVENT_SWITCH_STATE_CHANGE:
695 {
696 const struct power_event_description *peevent =
697 pswitch_event_desc;
698 const struct power_event_description *petype =
699 pswitch_type_desc;
700 struct sysmon_pswitch *smpsw =
701 (struct sysmon_pswitch *)power_data;
702 const char *pwrtype = "pswitch";
703
704#define SETPROP(key, str) \
705do { \
706 if ((str) != NULL && !prop_dictionary_set_cstring(dict, \
707 (key), \
708 (str))) { \
709 printf("%s: failed to set %s\n", __func__, (str)); \
710 return EINVAL; \
711 } \
712} while (/* CONSTCOND */ 0)
713
714
715 SETPROP("driver-name", smpsw->smpsw_name);
716
717 for (i = 0; peevent[i].type != -1; i++)
718 if (peevent[i].type == event)
719 break;
720
721 SETPROP("powerd-event-name", peevent[i].desc);
722
723 for (i = 0; petype[i].type != -1; i++)
724 if (petype[i].type == smpsw->smpsw_type)
725 break;
726
727 SETPROP("powerd-script-name", petype[i].desc);
728 SETPROP("power-type", pwrtype);
729 break;
730 }
731 /*
732 * create a dictionary for power envsys event.
733 */
734 case POWER_EVENT_ENVSYS_STATE_CHANGE:
735 {
736 const struct power_event_description *peevent =
737 penvsys_event_desc;
738 const struct power_event_description *petype =
739 penvsys_type_desc;
740 struct penvsys_state *pes =
741 (struct penvsys_state *)power_data;
742 const char *pwrtype = "envsys";
743
744 SETPROP("driver-name", pes->pes_dvname);
745 SETPROP("sensor-name", pes->pes_sensname);
746 SETPROP("state-description", pes->pes_statedesc);
747
748 for (i = 0; peevent[i].type != -1; i++)
749 if (peevent[i].type == event)
750 break;
751
752 SETPROP("powerd-event-name", peevent[i].desc);
753
754 for (i = 0; petype[i].type != -1; i++)
755 if (petype[i].type == pes->pes_type)
756 break;
757
758 SETPROP("powerd-script-name", petype[i].desc);
759 SETPROP("power-type", pwrtype);
760 break;
761 }
762 default:
763 return ENOTSUP;
764 }
765
766 return 0;
767}
768
769/*
770 * sysmon_power_destroy_dictionary:
771 *
772 * Destroys a power_event_dictionary object and all its
773 * properties in the dictionary.
774 */
775static void
776sysmon_power_destroy_dictionary(struct power_event_dictionary *ped)
777{
778 prop_object_iterator_t iter;
779 prop_object_t obj;
780
781 KASSERT(ped != NULL);
782 KASSERT((ped->flags & SYSMON_POWER_DICTIONARY_BUSY) == 0);
783
784 iter = prop_dictionary_iterator(ped->dict);
785 if (iter == NULL)
786 return;
787
788 while ((obj = prop_object_iterator_next(iter)) != NULL) {
789 prop_dictionary_remove(ped->dict,
790 prop_dictionary_keysym_cstring_nocopy(obj));
791 prop_object_iterator_reset(iter);
792 }
793
794 prop_object_iterator_release(iter);
795 prop_object_release(ped->dict);
796
797 kmem_free(ped, sizeof(*ped));
798}
799
800/*
801 * sysmon_power_settype:
802 *
803 * Sets the back-end power management type. This information can
804 * be used by the power management daemon.
805 */
806void
807sysmon_power_settype(const char *type)
808{
809
810 /*
811 * Don't bother locking this; it's going to be set
812 * during autoconfiguration, and then only read from
813 * then on.
814 */
815 (void)strlcpy(sysmon_power_type, type, sizeof(sysmon_power_type));
816}
817
818#define PENVSYS_SHOWSTATE(str) \
819 do { \
820 printf("%s: %s limit on '%s'\n", \
821 pes->pes_dvname, (str), pes->pes_sensname); \
822 } while (/* CONSTCOND */ 0)
823
824/*
825 * sysmon_penvsys_event:
826 *
827 * Puts an event onto the sysmon power queue and sends the
828 * appropriate event if the daemon is running, otherwise a
829 * message is shown.
830 */
831void
832sysmon_penvsys_event(struct penvsys_state *pes, int event)
833{
834 struct power_event_dictionary *ped;
835 const char *mystr = NULL;
836
837 KASSERT(pes != NULL);
838
839 rnd_add_uint32(&sysmon_rndsource, pes->pes_type);
840
841 if (sysmon_power_daemon != NULL) {
842 /*
843 * Create a dictionary for the new event.
844 */
845 ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP);
846 if (!ped)
847 return;
848 ped->dict = prop_dictionary_create();
849
850 if (sysmon_power_daemon_task(ped, pes, event) == 0)
851 return;
852 /* We failed */
853 prop_object_release(ped->dict);
854 kmem_free(ped, sizeof(*ped));
855 }
856
857 switch (pes->pes_type) {
858 case PENVSYS_TYPE_BATTERY:
859 switch (event) {
860 case PENVSYS_EVENT_LOW_POWER:
861 printf("sysmon: LOW POWER! SHUTTING DOWN.\n");
862 cpu_reboot(RB_POWERDOWN, NULL);
863 break;
864 case PENVSYS_EVENT_STATE_CHANGED:
865 printf("%s: state changed on '%s' to '%s'\n",
866 pes->pes_dvname, pes->pes_sensname,
867 pes->pes_statedesc);
868 break;
869 case PENVSYS_EVENT_BATT_CRIT:
870 mystr = "critical capacity";
871 PENVSYS_SHOWSTATE(mystr);
872 break;
873 case PENVSYS_EVENT_BATT_WARN:
874 mystr = "warning capacity";
875 PENVSYS_SHOWSTATE(mystr);
876 break;
877 case PENVSYS_EVENT_BATT_HIGH:
878 mystr = "high capacity";
879 PENVSYS_SHOWSTATE(mystr);
880 break;
881 case PENVSYS_EVENT_BATT_MAX:
882 mystr = "maximum capacity";
883 PENVSYS_SHOWSTATE(mystr);
884 break;
885 case PENVSYS_EVENT_NORMAL:
886 printf("%s: normal capacity on '%s'\n",
887 pes->pes_dvname, pes->pes_sensname);
888 break;
889 }
890 break;
891 case PENVSYS_TYPE_FAN:
892 case PENVSYS_TYPE_INDICATOR:
893 case PENVSYS_TYPE_TEMP:
894 case PENVSYS_TYPE_POWER:
895 case PENVSYS_TYPE_RESISTANCE:
896 case PENVSYS_TYPE_VOLTAGE:
897 switch (event) {
898 case PENVSYS_EVENT_CRITICAL:
899 mystr = "critical";
900 PENVSYS_SHOWSTATE(mystr);
901 break;
902 case PENVSYS_EVENT_CRITOVER:
903 mystr = "critical over";
904 PENVSYS_SHOWSTATE(mystr);
905 break;
906 case PENVSYS_EVENT_CRITUNDER:
907 mystr = "critical under";
908 PENVSYS_SHOWSTATE(mystr);
909 break;
910 case PENVSYS_EVENT_WARNOVER:
911 mystr = "warning over";
912 PENVSYS_SHOWSTATE(mystr);
913 break;
914 case PENVSYS_EVENT_WARNUNDER:
915 mystr = "warning under";
916 PENVSYS_SHOWSTATE(mystr);
917 break;
918 case PENVSYS_EVENT_NORMAL:
919 printf("%s: normal state on '%s'\n",
920 pes->pes_dvname, pes->pes_sensname);
921 break;
922 default:
923 printf("%s: unknown event\n", __func__);
924 }
925 break;
926 case PENVSYS_TYPE_DRIVE:
927 switch (event) {
928 case PENVSYS_EVENT_STATE_CHANGED:
929 printf("%s: state changed on '%s' to '%s'\n",
930 pes->pes_dvname, pes->pes_sensname,
931 pes->pes_statedesc);
932 break;
933 case PENVSYS_EVENT_NORMAL:
934 printf("%s: normal state on '%s' (%s)\n",
935 pes->pes_dvname, pes->pes_sensname,
936 pes->pes_statedesc);
937 break;
938 }
939 break;
940 default:
941 printf("%s: unknown power type\n", __func__);
942 break;
943 }
944}
945
946/*
947 * sysmon_pswitch_register:
948 *
949 * Register a power switch device.
950 */
951int
952sysmon_pswitch_register(struct sysmon_pswitch *smpsw)
953{
954 (void)RUN_ONCE(&once_power, power_preinit);
955
956 return 0;
957}
958
959/*
960 * sysmon_pswitch_unregister:
961 *
962 * Unregister a power switch device.
963 */
964void
965sysmon_pswitch_unregister(struct sysmon_pswitch *smpsw)
966{
967 /* nada */
968}
969
970/*
971 * sysmon_pswitch_event:
972 *
973 * Register an event on a power switch device.
974 */
975void
976sysmon_pswitch_event(struct sysmon_pswitch *smpsw, int event)
977{
978 struct power_event_dictionary *ped = NULL;
979
980 KASSERT(smpsw != NULL);
981
982 /*
983 * For pnp specific events, we don't care if the power daemon
984 * is running or not
985 */
986 if (smpsw->smpsw_type == PSWITCH_TYPE_LID) {
987 switch (event) {
988 case PSWITCH_EVENT_PRESSED:
989 pmf_event_inject(NULL, PMFE_CHASSIS_LID_CLOSE);
990 break;
991 case PSWITCH_EVENT_RELEASED:
992 pmf_event_inject(NULL, PMFE_CHASSIS_LID_OPEN);
993 break;
994 default:
995 break;
996 }
997 }
998
999 if (sysmon_power_daemon != NULL) {
1000 /*
1001 * Create a new dictionary for the event.
1002 */
1003 ped = kmem_zalloc(sizeof(*ped), KM_NOSLEEP);
1004 if (!ped)
1005 return;
1006 ped->dict = prop_dictionary_create();
1007
1008 if (sysmon_power_daemon_task(ped, smpsw, event) == 0)
1009 return;
1010 /* We failed */
1011 prop_object_release(ped->dict);
1012 kmem_free(ped, sizeof(*ped));
1013 }
1014
1015 switch (smpsw->smpsw_type) {
1016 case PSWITCH_TYPE_POWER:
1017 if (event != PSWITCH_EVENT_PRESSED) {
1018 /* just ignore it */
1019 return;
1020 }
1021
1022 /*
1023 * Attempt a somewhat graceful shutdown of the system,
1024 * as if the user has issued a reboot(2) call with
1025 * RB_POWERDOWN.
1026 */
1027 printf("%s: power button pressed, shutting down!\n",
1028 smpsw->smpsw_name);
1029 cpu_reboot(RB_POWERDOWN, NULL);
1030 break;
1031
1032 case PSWITCH_TYPE_RESET:
1033 if (event != PSWITCH_EVENT_PRESSED) {
1034 /* just ignore it */
1035 return;
1036 }
1037
1038 /*
1039 * Attempt a somewhat graceful reboot of the system,
1040 * as if the user had issued a reboot(2) call.
1041 */
1042 printf("%s: reset button pressed, rebooting!\n",
1043 smpsw->smpsw_name);
1044 cpu_reboot(0, NULL);
1045 break;
1046
1047 case PSWITCH_TYPE_SLEEP:
1048 if (event != PSWITCH_EVENT_PRESSED) {
1049 /* just ignore it */
1050 return;
1051 }
1052
1053 /*
1054 * Try to enter a "sleep" state.
1055 */
1056 /* XXX */
1057 printf("%s: sleep button pressed.\n", smpsw->smpsw_name);
1058 break;
1059
1060 case PSWITCH_TYPE_HOTKEY:
1061 /*
1062 * Eat up the event, there's nothing we can do
1063 */
1064 break;
1065
1066 case PSWITCH_TYPE_LID:
1067 switch (event) {
1068 case PSWITCH_EVENT_PRESSED:
1069 /*
1070 * Try to enter a "standby" state.
1071 */
1072 /* XXX */
1073 printf("%s: lid closed.\n", smpsw->smpsw_name);
1074 break;
1075
1076 case PSWITCH_EVENT_RELEASED:
1077 /*
1078 * Come out of "standby" state.
1079 */
1080 /* XXX */
1081 printf("%s: lid opened.\n", smpsw->smpsw_name);
1082 break;
1083
1084 default:
1085 printf("%s: unknown lid switch event: %d\n",
1086 smpsw->smpsw_name, event);
1087 }
1088 break;
1089
1090 case PSWITCH_TYPE_ACADAPTER:
1091 switch (event) {
1092 case PSWITCH_EVENT_PRESSED:
1093 /*
1094 * Come out of power-save state.
1095 */
1096 aprint_normal("%s: AC adapter online.\n",
1097 smpsw->smpsw_name);
1098 break;
1099
1100 case PSWITCH_EVENT_RELEASED:
1101 /*
1102 * Try to enter a power-save state.
1103 */
1104 aprint_normal("%s: AC adapter offline.\n",
1105 smpsw->smpsw_name);
1106 break;
1107 }
1108 break;
1109
1110 }
1111}
1112
1113static
1114int
1115sysmon_power_modcmd(modcmd_t cmd, void *arg)
1116{
1117 int ret;
1118
1119 switch (cmd) {
1120 case MODULE_CMD_INIT:
1121 ret = sysmon_power_init();
1122 break;
1123
1124 case MODULE_CMD_FINI:
1125 ret = sysmon_power_fini();
1126 break;
1127
1128 case MODULE_CMD_STAT:
1129 default:
1130 ret = ENOTTY;
1131 }
1132
1133 return ret;
1134}
1135
1136