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.8 2003/07/14 15:47:28 lukem Exp $   */
    2 
    3 /*
    4  * Copyright (c) 2003 Wasabi Systems, Inc.
    5  * All rights reserved.
    6  *
    7  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  * 3. All advertising materials mentioning features or use of this software
   18  *    must display the following acknowledgement:
   19  *      This product includes software developed for the NetBSD Project by
   20  *      Wasabi Systems, Inc.
   21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
   22  *    or promote products derived from this software without specific prior
   23  *    written permission.
   24  *
   25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
   26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
   29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   35  * POSSIBILITY OF SUCH DAMAGE.
   36  */
   37 
   38 /*
   39  * Power management framework for sysmon.
   40  *
   41  * We defer to a power management daemon running in userspace, since
   42  * power management is largely a policy issue.  This merely provides
   43  * for power management event notification to that daemon.
   44  */
   45 
   46 #include <sys/cdefs.h>
   47 __KERNEL_RCSID(0, "$NetBSD: sysmon_power.c,v 1.8 2003/07/14 15:47:28 lukem Exp $");
   48 
   49 #include <sys/param.h>
   50 #include <sys/reboot.h>
   51 #include <sys/systm.h>
   52 #include <sys/poll.h>
   53 #include <sys/select.h>
   54 #include <sys/vnode.h>
   55 
   56 #include <dev/sysmon/sysmonvar.h>
   57 
   58 static LIST_HEAD(, sysmon_pswitch) sysmon_pswitch_list =
   59     LIST_HEAD_INITIALIZER(sysmon_pswitch_list);
   60 static struct simplelock sysmon_pswitch_list_slock =
   61     SIMPLELOCK_INITIALIZER;
   62 
   63 static struct proc *sysmon_power_daemon;
   64 
   65 #define SYSMON_MAX_POWER_EVENTS         32
   66 
   67 static struct simplelock sysmon_power_event_queue_slock =
   68     SIMPLELOCK_INITIALIZER;
   69 static power_event_t sysmon_power_event_queue[SYSMON_MAX_POWER_EVENTS];
   70 static int sysmon_power_event_queue_head;
   71 static int sysmon_power_event_queue_tail;
   72 static int sysmon_power_event_queue_count;
   73 static int sysmon_power_event_queue_flags;
   74 static struct selinfo sysmon_power_event_queue_selinfo;
   75 
   76 static char sysmon_power_type[32];
   77 
   78 #define PEVQ_F_WAITING          0x01    /* daemon waiting for event */
   79 
   80 #define SYSMON_NEXT_EVENT(x)            (((x) + 1) / SYSMON_MAX_POWER_EVENTS)
   81 
   82 /*
   83  * sysmon_queue_power_event:
   84  *
   85  *      Enqueue a power event for the power mangement daemon.  Returns
   86  *      non-zero if we were able to enqueue a power event.
   87  */
   88 static int
   89 sysmon_queue_power_event(power_event_t *pev)
   90 {
   91 
   92         LOCK_ASSERT(simple_lock_held(&sysmon_power_event_queue_slock));
   93 
   94         if (sysmon_power_event_queue_count == SYSMON_MAX_POWER_EVENTS)
   95                 return (0);
   96 
   97         sysmon_power_event_queue[sysmon_power_event_queue_head] = *pev;
   98         sysmon_power_event_queue_head =
   99             SYSMON_NEXT_EVENT(sysmon_power_event_queue_head);
  100         sysmon_power_event_queue_count++;
  101 
  102         return (1);
  103 }
  104 
  105 /*
  106  * sysmon_get_power_event:
  107  *
  108  *      Get a power event from the queue.  Returns non-zero if there
  109  *      is an event available.
  110  */
  111 static int
  112 sysmon_get_power_event(power_event_t *pev)
  113 {
  114 
  115         LOCK_ASSERT(simple_lock_held(&sysmon_power_event_queue_slock));
  116 
  117         if (sysmon_power_event_queue_count == 0)
  118                 return (0);
  119 
  120         *pev = sysmon_power_event_queue[sysmon_power_event_queue_tail];
  121         sysmon_power_event_queue_tail =
  122             SYSMON_NEXT_EVENT(sysmon_power_event_queue_tail);
  123         sysmon_power_event_queue_count--;
  124 
  125         return (1);
  126 }
  127 
  128 /*
  129  * sysmon_power_event_queue_flush:
  130  *
  131  *      Flush the event queue, and reset all state.
  132  */
  133 static void
  134 sysmon_power_event_queue_flush(void)
  135 {
  136 
  137         sysmon_power_event_queue_head = 0;
  138         sysmon_power_event_queue_tail = 0;
  139         sysmon_power_event_queue_count = 0;
  140         sysmon_power_event_queue_flags = 0;
  141 }
  142 
  143 /*
  144  * sysmonopen_power:
  145  *
  146  *      Open the system monitor device.
  147  */
  148 int
  149 sysmonopen_power(dev_t dev, int flag, int mode, struct proc *p)
  150 {
  151         int error = 0;
  152 
  153         simple_lock(&sysmon_power_event_queue_slock);
  154         if (sysmon_power_daemon != NULL)
  155                 error = EBUSY;
  156         else {
  157                 sysmon_power_daemon = p;
  158                 sysmon_power_event_queue_flush();
  159         }
  160         simple_unlock(&sysmon_power_event_queue_slock);
  161 
  162         return (error);
  163 }
  164 
  165 /*
  166  * sysmonclose_power:
  167  *
  168  *      Close the system monitor device.
  169  */
  170 int
  171 sysmonclose_power(dev_t dev, int flag, int mode, struct proc *p)
  172 {
  173         int count;
  174 
  175         simple_lock(&sysmon_power_event_queue_slock);
  176         count = sysmon_power_event_queue_count;
  177         sysmon_power_daemon = NULL;
  178         sysmon_power_event_queue_flush();
  179         simple_unlock(&sysmon_power_event_queue_slock);
  180 
  181         if (count)
  182                 printf("WARNING: %d power events lost by exiting daemon\n",
  183                     count);
  184 
  185         return (0);
  186 }
  187 
  188 /*
  189  * sysmonread_power:
  190  *
  191  *      Read the system monitor device.
  192  */
  193 int
  194 sysmonread_power(dev_t dev, struct uio *uio, int flags)
  195 {
  196         power_event_t pev;
  197         int error;
  198 
  199         /* We only allow one event to be read at a time. */
  200         if (uio->uio_resid != POWER_EVENT_MSG_SIZE)
  201                 return (EINVAL);
  202 
  203         simple_lock(&sysmon_power_event_queue_slock);
  204  again:
  205         if (sysmon_get_power_event(&pev)) {
  206                 simple_unlock(&sysmon_power_event_queue_slock);
  207                 return (uiomove(&pev, POWER_EVENT_MSG_SIZE, uio));
  208         }
  209 
  210         if (flags & IO_NDELAY) {
  211                 simple_unlock(&sysmon_power_event_queue_slock);
  212                 return (EWOULDBLOCK);
  213         }
  214 
  215         sysmon_power_event_queue_flags |= PEVQ_F_WAITING;
  216         error = ltsleep(&sysmon_power_event_queue_count,
  217             PRIBIO|PCATCH, "smpower", 0, &sysmon_power_event_queue_slock);
  218         if (error) {
  219                 simple_unlock(&sysmon_power_event_queue_slock);
  220                 return (error);
  221         }
  222         goto again;
  223 }
  224 
  225 /*
  226  * sysmonpoll_power:
  227  *
  228  *      Poll the system monitor device.
  229  */
  230 int
  231 sysmonpoll_power(dev_t dev, int events, struct proc *p)
  232 {
  233         int revents;
  234 
  235         revents = events & (POLLOUT | POLLWRNORM);
  236 
  237         /* Attempt to save some work. */
  238         if ((events & (POLLIN | POLLRDNORM)) == 0)
  239                 return (revents);
  240 
  241         simple_lock(&sysmon_power_event_queue_slock);
  242         if (sysmon_power_event_queue_count)
  243                 revents |= events & (POLLIN | POLLRDNORM);
  244         else
  245                 selrecord(p, &sysmon_power_event_queue_selinfo);
  246         simple_unlock(&sysmon_power_event_queue_slock);
  247 
  248         return (revents);
  249 }
  250 
  251 static void
  252 filt_sysmon_power_rdetach(struct knote *kn)
  253 {
  254 
  255         simple_lock(&sysmon_power_event_queue_slock);
  256         SLIST_REMOVE(&sysmon_power_event_queue_selinfo.sel_klist,
  257             kn, knote, kn_selnext);
  258         simple_unlock(&sysmon_power_event_queue_slock);
  259 }
  260 
  261 static int
  262 filt_sysmon_power_read(struct knote *kn, long hint)
  263 {
  264 
  265         simple_lock(&sysmon_power_event_queue_slock);
  266         kn->kn_data = sysmon_power_event_queue_count;
  267         simple_unlock(&sysmon_power_event_queue_slock);
  268 
  269         return (kn->kn_data > 0);
  270 }
  271 
  272 static const struct filterops sysmon_power_read_filtops =
  273     { 1, NULL, filt_sysmon_power_rdetach, filt_sysmon_power_read };
  274 
  275 static const struct filterops sysmon_power_write_filtops =
  276     { 1, NULL, filt_sysmon_power_rdetach, filt_seltrue };
  277 
  278 /*
  279  * sysmonkqfilter_power:
  280  *
  281  *      Kqueue filter for the system monitor device.
  282  */
  283 int
  284 sysmonkqfilter_power(dev_t dev, struct knote *kn)
  285 {
  286         struct klist *klist;
  287 
  288         switch (kn->kn_filter) {
  289         case EVFILT_READ:
  290                 klist = &sysmon_power_event_queue_selinfo.sel_klist;
  291                 kn->kn_fop = &sysmon_power_read_filtops;
  292                 break;
  293 
  294         case EVFILT_WRITE:
  295                 klist = &sysmon_power_event_queue_selinfo.sel_klist;
  296                 kn->kn_fop = &sysmon_power_write_filtops;
  297                 break;
  298 
  299         default:
  300                 return (1);
  301         }
  302 
  303         simple_lock(&sysmon_power_event_queue_slock);
  304         SLIST_INSERT_HEAD(klist, kn, kn_selnext);
  305         simple_unlock(&sysmon_power_event_queue_slock);
  306 
  307         return (0);
  308 }
  309 
  310 /*
  311  * sysmonioctl_power:
  312  *
  313  *      Perform a power managmenet control request.
  314  */
  315 int
  316 sysmonioctl_power(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
  317 {
  318         int error = 0;
  319 
  320         switch (cmd) {
  321         case POWER_IOC_GET_TYPE:
  322             {
  323                 struct power_type *power_type = (void *) data;
  324 
  325                 strcpy(power_type->power_type, sysmon_power_type);
  326                 break;
  327             }
  328         default:
  329                 error = ENOTTY;
  330         }
  331 
  332         return (error);
  333 }
  334 
  335 /*
  336  * sysmon_power_settype:
  337  *
  338  *      Sets the back-end power management type.  This information can
  339  *      be used by the power management daemon.
  340  */
  341 void
  342 sysmon_power_settype(const char *type)
  343 {
  344 
  345         /*
  346          * Don't bother locking this; it's going to be set
  347          * during autoconfiguration, and then only read from
  348          * then on.
  349          */
  350         strcpy(sysmon_power_type, type);
  351 }
  352 
  353 /*
  354  * sysmon_pswitch_register:
  355  *
  356  *      Register a power switch device.
  357  */
  358 int
  359 sysmon_pswitch_register(struct sysmon_pswitch *smpsw)
  360 {
  361 
  362         simple_lock(&sysmon_pswitch_list_slock);
  363         LIST_INSERT_HEAD(&sysmon_pswitch_list, smpsw, smpsw_list);
  364         simple_unlock(&sysmon_pswitch_list_slock);
  365 
  366         return (0);
  367 }
  368 
  369 /*
  370  * sysmon_pswitch_unregister:
  371  *
  372  *      Unregister a power switch device.
  373  */
  374 void
  375 sysmon_pswitch_unregister(struct sysmon_pswitch *smpsw)
  376 {
  377 
  378         simple_lock(&sysmon_pswitch_list_slock);
  379         LIST_REMOVE(smpsw, smpsw_list);
  380         simple_unlock(&sysmon_pswitch_list_slock);
  381 }
  382 
  383 /*
  384  * sysmon_pswitch_event:
  385  *
  386  *      Register an event on a power switch device.
  387  */
  388 void
  389 sysmon_pswitch_event(struct sysmon_pswitch *smpsw, int event)
  390 {
  391 
  392         /*
  393          * If a power management daemon is connected, then simply
  394          * deliver the event to them.  If not, we need to try to
  395          * do something reasonable ourselves.
  396          */
  397         simple_lock(&sysmon_power_event_queue_slock);
  398         if (sysmon_power_daemon != NULL) {
  399                 power_event_t pev;
  400                 int rv;
  401 
  402                 pev.pev_type = POWER_EVENT_SWITCH_STATE_CHANGE;
  403                 pev.pev_switch.psws_state = event;
  404                 pev.pev_switch.psws_type = smpsw->smpsw_type;
  405                 strcpy(pev.pev_switch.psws_name, smpsw->smpsw_name);
  406 
  407                 rv = sysmon_queue_power_event(&pev);
  408                 if (rv == 0) {
  409                         simple_unlock(&sysmon_power_event_queue_slock);
  410                         printf("%s: WARNING: state change event %d lost; "
  411                             "queue full\n", smpsw->smpsw_name,
  412                             pev.pev_type);
  413                         return;
  414                 } else {
  415                         if (sysmon_power_event_queue_flags & PEVQ_F_WAITING) {
  416                                 sysmon_power_event_queue_flags &= ~PEVQ_F_WAITING;
  417                                 simple_unlock(&sysmon_power_event_queue_slock);
  418                                 wakeup(&sysmon_power_event_queue_count);
  419                         } else {
  420                                 simple_unlock(&sysmon_power_event_queue_slock);
  421                         }
  422                         selnotify(&sysmon_power_event_queue_selinfo, 0);
  423                         return;
  424                 }
  425         }
  426         simple_unlock(&sysmon_power_event_queue_slock);
  427 
  428         switch (smpsw->smpsw_type) {
  429         case PSWITCH_TYPE_POWER:
  430                 if (event != PSWITCH_EVENT_PRESSED) {
  431                         /* just ignore it */
  432                         return;
  433                 }
  434 
  435                 /*
  436                  * Attempt a somewhat graceful shutdown of the system,
  437                  * as if the user has issued a reboot(2) call with
  438                  * RB_POWERDOWN.
  439                  */
  440                 printf("%s: power button pressed, shutting down!\n",
  441                     smpsw->smpsw_name);
  442                 cpu_reboot(RB_POWERDOWN, NULL);
  443                 break;
  444 
  445         case PSWITCH_TYPE_RESET:
  446                 if (event != PSWITCH_EVENT_PRESSED) {
  447                         /* just ignore it */
  448                         return;
  449                 }
  450 
  451                 /*
  452                  * Attempt a somewhat graceful reboot of the system,
  453                  * as if the user had issued a reboot(2) call.
  454                  */
  455                 printf("%s: reset button pressed, rebooting!\n",
  456                     smpsw->smpsw_name);
  457                 cpu_reboot(0, NULL);
  458                 break;
  459 
  460         case PSWITCH_TYPE_SLEEP:
  461                 if (event != PSWITCH_EVENT_PRESSED) {
  462                         /* just ignore it */
  463                         return;
  464                 }
  465 
  466                 /*
  467                  * Try to enter a "sleep" state.
  468                  */
  469                 /* XXX */
  470                 printf("%s: sleep button pressed.\n", smpsw->smpsw_name);
  471                 break;
  472 
  473         case PSWITCH_TYPE_LID:
  474                 switch (event) {
  475                 case PSWITCH_EVENT_PRESSED:
  476                         /*
  477                          * Try to enter a "standby" state.
  478                          */
  479                         /* XXX */
  480                         printf("%s: lid closed.\n", smpsw->smpsw_name);
  481                         break;
  482 
  483                 case PSWITCH_EVENT_RELEASED:
  484                         /*
  485                          * Come out of "standby" state.
  486                          */
  487                         /* XXX */
  488                         printf("%s: lid opened.\n", smpsw->smpsw_name);
  489                         break;
  490 
  491                 default:
  492                         printf("%s: unknown lid switch event: %d\n",
  493                             smpsw->smpsw_name, event);
  494                 }
  495                 break;
  496 
  497         default:
  498                 printf("%s: sysmon_pswitch_event can't handle me.\n",
  499                     smpsw->smpsw_name);
  500         }
  501 }

Cache object: 2d00f741f1c38c0fbbb85b648bc51ac0


[ 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.