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
power_preinit(void)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
sysmon_power_init(void)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
sysmon_power_fini(void)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
sysmon_queue_power_event(power_event_t * pev)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
sysmon_get_power_event(power_event_t * pev)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
sysmon_power_event_queue_flush(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
sysmon_power_daemon_task(struct power_event_dictionary * ped,void * pev_data,int event)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
sysmonopen_power(dev_t dev,int flag,int mode,struct lwp * l)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
sysmonclose_power(dev_t dev,int flag,int mode,struct lwp * l)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
sysmonread_power(dev_t dev,struct uio * uio,int flags)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
sysmonpoll_power(dev_t dev,int events,struct lwp * l)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
filt_sysmon_power_rdetach(struct knote * kn)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
filt_sysmon_power_read(struct knote * kn,long hint)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
sysmonkqfilter_power(dev_t dev,struct knote * kn)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
sysmonioctl_power(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)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
sysmon_power_make_dictionary(prop_dictionary_t dict,void * power_data,int event,int type)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
sysmon_power_destroy_dictionary(struct power_event_dictionary * ped)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
sysmon_power_settype(const char * type)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
sysmon_penvsys_event(struct penvsys_state * pes,int event)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
sysmon_pswitch_register(struct sysmon_pswitch * smpsw)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
sysmon_pswitch_unregister(struct sysmon_pswitch * smpsw)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
sysmon_pswitch_event(struct sysmon_pswitch * smpsw,int event)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
sysmon_power_modcmd(modcmd_t cmd,void * arg)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 }
1134