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