The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/sysmon/sysmon_power.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.