Logo Search packages:      
Sourcecode: pbbuttonsd version File versions  Download package

module_powersave.c

/* --------------------------------------------------------------------------
 * module_powersave.c
 * code for powermanagement module
 *
 * Copyright 2002 Matthias Grimm
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 * -------------------------------------------------------------------------*/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <utmp.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/kd.h>
#include "pbbinput.h"

#ifdef HAVE_INITREQ_H
#  include <initreq.h>
#endif

#include <pbb.h>

#include "gettext_macros.h"
#include "input_manager.h"
#include "config_manager.h"
#include "module_powersave.h"
#include "module_display.h"
#include "support.h"
#include "debug.h"


/* --- private module data structure of module powermanagement --- */
struct moddata_power {
      struct modflags_power flags;
      struct powerprofile onAC;
      struct powerprofile onBattery;
      struct powerprofile *activeProfile;
      int policy;        /* current power policy */
      int powersource;    /* REDUNDANT copy from module pmac */
      int timeremaining;   /* REDUNDANT copy from module pmac */
      int mode;             /* module mode: awake, sleep, dim. */
      int sleeptime;         /* counter */
      int sleepcountdown;     /* another counter */
      int warnlevel;           /* current battery warnlevel */
      struct timeval tv;       /* time of key press */
      int sleepkeydelaytime;    /* delay in ms */
      unsigned short keysleep;  /* keycode for sleepfunction */
      unsigned short modsleep;  /* modifier for sleepfunction */
      int BWL1;
      int BWL2;
      int BWL3;
      int emergency;       /* emergency action at low battery condition */
      int cpuload_min;      /* cpuload minimum level to lock sleep */
      int cpuload_period;    /* period of time for that cpu activity mut be below cpuload */
      int netload_min;        /* netload minimum level to lock sleep */
      int netload_period;      /* period of time for that net activity mut be below netload */
      char *netload_dev;        /* device to observe for netload sleeplock */
      char *script_pmcs;         /* command to call at certain event */
} modbase_power;

/* --- interface functions of module powermanagement --- */

/* Init function called by the program init procedure. Function is known by
   the prginitab. It does module data initialisation and registers the module
   at the needed input queues. */

int
power_init ()
{
      struct moddata_power *base = &modbase_power;
      static char scriptbuffer1[STDBUFFERLEN];
      static char scriptbuffer2[STDBUFFERLEN];
      int sid;

      bzero (base, sizeof (struct moddata_power));
      bzero (scriptbuffer1, STDBUFFERLEN);  /* no default for script_pmcs */
      sprintf (scriptbuffer2, DEFAULT_NETLOAD_DEV);
      base->script_pmcs  = scriptbuffer1;
      base->netload_dev  = scriptbuffer2;
      base->mode         = MODE_AWAKE;
      base->policy       = POLICY_PERFORMANCE;
      
      if ((sid = registerCfgSection ("MODULE POWERSAVE")) == 0) {
            print_msg (PBB_ERR, _("Can't register configuration section %s, out of memory."), "MODULE POWERSAVE");
            return E_NOMEM;
      } else {
            registerCfgOptionList (sid, "onAC_Policy", TAG_ONAC_POLICY, 0,
                        N_("'"POLICY_NONE_NAME"', '"POLICY_POWERSAVE_NAME"', '"POLICY_USER_NAME"' or '"POLICY_PERFORMANCE_NAME"'"),
                        POLICY_NONE_NAME, POLICY_POWERSAVE_NAME, POLICY_USER_NAME, POLICY_PERFORMANCE_NAME, NULL);
            registerCfgOptionList (sid, "onAC_TimerAction", TAG_ONAC_TIMERACTION, 0,
                        N_("'"ACTION_NONE_NAME"', '"ACTION_TORAM_NAME"', '"ACTION_TODISK_NAME"', '"ACTION_BLANK_NAME"' or '"ACTION_SHUTDOWN_NAME"'"),
                        ACTION_NONE_NAME, ACTION_TORAM_NAME, ACTION_TODISK_NAME, ACTION_BLANK_NAME, ACTION_SHUTDOWN_NAME, NULL);
            registerCfgOptionList (sid, "onAC_CoverAction",  TAG_ONAC_COVERACTION, 0,
                        N_("see TimerAction for possible values"),
                        ACTION_NONE_NAME, ACTION_TORAM_NAME, ACTION_TODISK_NAME, ACTION_BLANK_NAME, ACTION_SHUTDOWN_NAME, NULL);
            registerCfgOptionList (sid, "onAC_KeyAction", TAG_ONAC_KEYACTION, 0,
                        N_("Action (see TimerAction) for the sleepkey"),
                        ACTION_NONE_NAME, ACTION_TORAM_NAME, ACTION_TODISK_NAME, ACTION_BLANK_NAME, ACTION_SHUTDOWN_NAME, NULL);
            registerCfgOptionInt (sid, "onAC_SuspendTime", TAG_ONAC_TIMESUSPEND, 0,
                        NULL);
            registerCfgOptionInt (sid, "onAC_DimTime",     TAG_ONAC_TIMEDIM,     0,
                        NULL);

            registerCfgOptionList (sid, "onBattery_Policy", TAG_ONBATT_POLICY, 0,
                        N_("'"POLICY_NONE_NAME"', '"POLICY_POWERSAVE_NAME"', '"POLICY_USER_NAME"' or '"POLICY_PERFORMANCE_NAME"'"),
                        POLICY_NONE_NAME, POLICY_POWERSAVE_NAME, POLICY_USER_NAME, POLICY_PERFORMANCE_NAME, NULL);
            registerCfgOptionList (sid, "onBattery_TimerAction", TAG_ONBATT_TIMERACTION, 0,
                        N_("'"ACTION_NONE_NAME"', '"ACTION_TORAM_NAME"', '"ACTION_TODISK_NAME"', '"ACTION_BLANK_NAME"' or '"ACTION_SHUTDOWN_NAME"'"),
                        ACTION_NONE_NAME, ACTION_TORAM_NAME, ACTION_TODISK_NAME, ACTION_BLANK_NAME, ACTION_SHUTDOWN_NAME, NULL);
            registerCfgOptionList (sid, "onBattery_CoverAction",  TAG_ONBATT_COVERACTION, 0,
                        N_("see TimerAction for possible values"),
                        ACTION_NONE_NAME, ACTION_TORAM_NAME, ACTION_TODISK_NAME, ACTION_BLANK_NAME, ACTION_SHUTDOWN_NAME, NULL);
            registerCfgOptionList (sid, "onBattery_KeyAction", TAG_ONBATT_KEYACTION, 0,
                        N_("Action (see TimerAction) for the sleepkey"),
                        ACTION_NONE_NAME, ACTION_TORAM_NAME, ACTION_TODISK_NAME, ACTION_BLANK_NAME, ACTION_SHUTDOWN_NAME, NULL);
            registerCfgOptionInt (sid, "onBattery_SuspendTime", TAG_ONBATT_TIMESUSPEND, 0,
                        NULL);
            registerCfgOptionInt (sid, "onBattery_DimTime",     TAG_ONBATT_TIMEDIM,     0,
                        NULL);
            
            registerCfgOptionKey (sid, "SleepKey", TAG_SLEEPKEY, TAG_SLEEPMOD, 0,
                        NULL);
            registerCfgOptionInt (sid, "SleepKeyDelay", TAG_SLEEPKEYDELAY, 0,
                        N_("values > 0 may be dangerous, if the power key is used to trigger sleep"));
            registerCfgOptionInt (sid, "BWL_first", TAG_BWLFIRST, 0,
                        N_("first battery warnlevel, time in minutes"));
            registerCfgOptionInt (sid, "BWL_second", TAG_BWLSECOND, 0,
                        N_("second battery warnlevel, time in minutes"));
            registerCfgOptionInt (sid, "BWL_last", TAG_BWLLAST, 0,
                        N_("last battery warnlevel, time in minutes"));
            registerCfgOptionString (sid, "Script_PMCS", TAG_SCRIPTPMCS, 0,
                        NULL);
            registerCfgOptionList (sid, "EmergencyAction", TAG_EMERGENCYACTION, 0,
                        N_("action, if battery is critically low"),
                        EMA_SLEEP_NAME, EMA_SIGNAL_NAME, EMA_COMMAND_NAME, NULL);
            registerCfgOptionBool (sid, "HeartbeatBeep", TAG_HEARTBEATBEEP, 0,
                        N_("beep, if nothing else showed that the computer lives"));
            registerCfgOptionBool (sid, "CPULoad_sleeplock", TAG_CPULOADSLEEPLOCK, 0,
                        NULL);
            registerCfgOptionInt (sid, "CPULoad_min", TAG_CPULOADMIN, 0,
                        N_("value in percent"));
            registerCfgOptionInt (sid, "CPULoad_period", TAG_CPULOADPERIOD, 0,
                        N_("time in seconds"));
            registerCfgOptionBool (sid, "NETLoad_sleeplock", TAG_NETLOADSLEEPLOCK, 0,
                        NULL);
            registerCfgOptionInt (sid, "NETLoad_min", TAG_NETLOADMIN, 0,
                        N_("traffic in Bytes/s"));
            registerCfgOptionInt (sid, "NETLoad_period", TAG_NETLOADPERIOD, 0,
                        N_("time in seconds"));
            registerCfgOptionString (sid, "NETLoad_device", TAG_NETLOADDEV, 0,
                        NULL);
      }

      ipc_protect_tag (TAG_SCRIPTPMCS);  /* tags only for privileged use */

      register_function (QUERYQUEUE, power_query);
      register_function (CONFIGQUEUE, power_configure);
      return 0;
}

int
power_open (struct tagitem *taglist)
{
      struct moddata_power *base = &modbase_power;
      
      power_sync(); /* synchronise some data with module_pmac */
      
      /* default profile for running on AC power */
      base->onAC.policy         = POLICY_PERFORMANCE;
      base->onAC.timeraction      = base->flags.sleep_supported ? ACTION_TORAM : ACTION_BLANK;
      base->onAC.coveraction      = base->flags.sleep_supported ? ACTION_TORAM : ACTION_BLANK;
      base->onAC.keyaction        = base->flags.sleep_supported ? ACTION_TORAM : ACTION_BLANK;
      base->onAC.suspendtime      = AC_TIME_SLEEP;
      base->onAC.dimtime          = AC_TIME_DIM;
      
      /* default profile for running on battery */
      base->onBattery.policy      = POLICY_POWERSAVE;
      base->onBattery.timeraction = base->flags.sleep_supported ? ACTION_TORAM : ACTION_BLANK;
      base->onBattery.coveraction = base->flags.sleep_supported ? ACTION_TORAM : ACTION_BLANK;
      base->onBattery.keyaction   = base->flags.sleep_supported ? ACTION_TORAM : ACTION_BLANK;
      base->onBattery.suspendtime = BATTERY_TIME_SLEEP;
      base->onBattery.dimtime     = BATTERY_TIME_DIM;

      /* definitions for immediatly sleep function */
      base->keysleep              = KEY_POWER;
      base->modsleep              = MOD_NONE;
      base->sleepkeydelaytime     = 0;       /* time delay till reaction */
      base->sleeptime             = 0;      /* counter for time till sleep */
      base->sleepcountdown        = 60;    /* counter for last 5 seconds before sleep */

      /* definitions for battery supervising functions */
      base->BWL1                  = BAT_WARNLEVEL1;
      base->BWL2                  = BAT_WARNLEVEL2;
      base->BWL3                  = BAT_WARNLEVEL3;
      base->warnlevel             = 0;          /* current battery warnlevel */
      base->emergency             = base->flags.sleep_supported ? EMA_SLEEP : EMA_SIGNAL; /* default emergency action */
      base->flags.sigpwr_sent     = 0;  /* currently no sigpwr in progress */

      /* definitions for sleeplocks */
      base->flags.cpuload_enable  = 0;    /* disabled by default */
      base->flags.sleeplock_cpu   = 0;    /* lock open */
      base->cpuload_min           = 20;   /* lock closed if cpuload <20% */
      base->cpuload_period        = 60;   /*  for >60s, if enabled */
      base->flags.netload_enable  = 0;    /* disabled by default */
      base->flags.sleeplock_net   = 0;    /* lock open */
      base->netload_min           = 4096; /* lock closed if netload <4 KB/s */
      base->netload_period        = 60;   /*  for >60s, if enabled  */

      base->flags.heartbeat       = 0;  /* heartbeat beep disabled */
      base->flags.heartbeat_enable= 1;  /* heartbeat beep is possible by default */ 
      base->flags.coveropen       = 1;  /* cover open by default same as in laptop module */

      base->powersource = 2; /* HACK: The pmcs script sould be started at startup but
                          it will only start if the status has changed (policy
                          or powersource). We set an impossible value here and pbbuttonsd.c
                          will send a TAG_POWERCHANGE to correct it and start the script
                          Not elegant but works */
      
      register_function (KBDQUEUE, power_keyboard);
      register_function (MOUSEQUEUE, power_mouse);
      register_function (T100QUEUE, power_timer);
      register_function (T1000QUEUE, power_timer1000);
      return 0;
}

int
power_close ()
{
      return 0;
}

int
power_exit ()
{
      return 0;
}

void
power_mouse (struct tagitem *taglist)
{
      struct moddata_power *base = &modbase_power;

      if (base->flags.coveropen)
            power_awake ();   /* call only if cover is open */
}

void
power_keyboard (struct tagitem *taglist)
{
      struct moddata_power *base = &modbase_power;
      int code, value, mod;

      code = (int) tagfind (taglist, TAG_KEYCODE, 0);
      value = (int) tagfind (taglist, TAG_KEYREPEAT, 0);
      mod = (int) tagfind (taglist, TAG_MODIFIER, 0);

      if (value && base->flags.coveropen)
            if (value == 1) {
                  power_awake ();
                  base->flags.sleeptriggered = 0;
            }
            if ((code == base->keysleep) && (mod == base->modsleep))
                  if ((base->sleepkeydelaytime == 0) || (keydelayms(&base->tv, value, base->sleepkeydelaytime)))
                        if (base->flags.sleeptriggered == 0) {
                              base->flags.sleeptriggered = 1; /* sleep should be triggered only once */
                              power_suspend (base->activeProfile->keyaction);
                        }
}

/* This function will be called on every keyboard or mouse action. With
   other words the user is active if this function is called so sleeptimer
   is reset here. */

void
power_awake ()
{
      struct moddata_power *base = &modbase_power;

      if (base->mode != MODE_AWAKE) {
            process_queue_single (CONFIGQUEUE, TAG_BRIGHTNESSOP, OP_DIM_RECOVER);
            base->flags.heartbeat = 0; /* disable heartbeat beep */
            base->mode = MODE_AWAKE;
      }
      base->sleeptime = 0;
}

void
power_sync ()
{
      struct moddata_power *base = &modbase_power;
      struct tagitem args[4];

      taglist_init (args);
      taglist_add (args, TAG_SLEEPSUPPORTED, 0);
      taglist_add (args, TAG_POWERSOURCE, 0);
      taglist_add (args, TAG_TIMEREMAINING, -1);
      process_queue (QUERYQUEUE, args);
      base->flags.sleep_supported = tagfind (args, TAG_SLEEPSUPPORTED, 0);
      base->powersource = tagfind (args, TAG_POWERSOURCE, 0);
      base->timeremaining = tagfind (args, TAG_TIMEREMAINING, -1);
      base->activeProfile = base->powersource ? &base->onAC : &base->onBattery;
}

void
power_query (struct tagitem *taglist)
{
  power_handle_tags (MODE_QUERY, taglist);
}

void
power_configure (struct tagitem *taglist)
{
  power_handle_tags (MODE_CONFIG, taglist);
}

void
power_handle_tags (int cfgure, struct tagitem *taglist)
{
      struct moddata_power *base = &modbase_power;
      int err, val;

      while (taglist->tag != TAG_END) {
            switch (taglist->tag) {
            case TAG_REINIT:
                  /* set power policy again after resume from suspend
                   * to disk. */
                  power_setpolicy(taglist->data, base->powersource);
                  break;
            case TAG_SCRIPTPMCS:
                  if (cfgure) {
                        if ((check_script ((char *) taglist->data, "sss")) == 0) {
                              if ((err = copy_path ((char *) taglist->data, base->script_pmcs, TYPE_FILE, CPFLG_PBBONLY)))
                                    tagerror (taglist, err);
                        } else {
                              print_msg (PBB_ERR, _("Too many formatsigns. Max three %%s allowed in TAG_SCRIPTPMCS.\n"));
                              tagerror (taglist, E_FORMAT);
                        }
                  } else
                        taglist->data = (long) base->script_pmcs;
                  break;
                  
            /* Tags for power profiles */ 
            case TAG_ONAC_POLICY:
                  if (cfgure) {
                        if (taglist->data > POLICY_LAST)
                              tagerror(taglist, E_INVALID);
                        else {
                              base->onAC.policy = taglist->data;
                              if (&base->onAC == base->activeProfile
                                  && !base->flags.usersetpolicy)
                                    power_setpolicy(taglist->data, base->powersource);
                        }
                  } else
                        taglist->data = base->onAC.policy;
                  break;
            case TAG_ONAC_TIMERACTION:
                  if (cfgure) {
                        if (taglist->data > ACTION_LAST)
                              tagerror(taglist, E_INVALID);
                        else
                              base->onAC.timeraction = taglist->data;
                  } else
                        taglist->data = base->onAC.timeraction;
                  break;
            case TAG_ONAC_COVERACTION:
                  if (cfgure) {
                        if (taglist->data > ACTION_LAST)
                              tagerror(taglist, E_INVALID);
                        else
                              base->onAC.coveraction = taglist->data;
                  } else
                        taglist->data = base->onAC.coveraction;
                  break;
            case TAG_ONAC_KEYACTION:   /* sleepkey */
                  if (cfgure) {
                        if (taglist->data > ACTION_LAST)
                              tagerror(taglist, E_INVALID);
                        else
                              base->onAC.keyaction = taglist->data;
                  } else
                        taglist->data = base->onAC.keyaction;
                  break;      
            case TAG_ONAC_TIMESUSPEND:
                  if (cfgure) base->onAC.suspendtime = taglist->data;
                  else        taglist->data = base->onAC.suspendtime;
                  break;
            case TAG_ONAC_TIMEDIM:
                  if (cfgure) base->onAC.dimtime = taglist->data;
                  else        taglist->data = base->onAC.dimtime;
                  break;
      
            case TAG_ONBATT_POLICY: 
                  if (cfgure) {
                        if (taglist->data > POLICY_LAST)
                              tagerror(taglist, E_INVALID);
                        else {
                              base->onBattery.policy = taglist->data;
                              if (&base->onBattery == base->activeProfile
                                  && !base->flags.usersetpolicy)
                                    power_setpolicy(taglist->data, base->powersource);
                        }
                  } else      
                        taglist->data = base->onBattery.policy;
                  break;
            case TAG_ONBATT_TIMERACTION:
                  if (cfgure) {
                        if (taglist->data > ACTION_LAST)
                              tagerror(taglist, E_INVALID);
                        else
                              base->onBattery.timeraction = taglist->data;
                  } else
                        taglist->data = base->onBattery.timeraction;
                  break;
            case TAG_ONBATT_COVERACTION:
                  if (cfgure) {
                        if (taglist->data > ACTION_LAST)
                              tagerror(taglist, E_INVALID);
                        else
                              base->onBattery.coveraction = taglist->data;
                  } else
                        taglist->data = base->onBattery.coveraction;
                  break;
            case TAG_ONBATT_KEYACTION:   /* sleepkey */
                  if (cfgure) {
                        if (taglist->data > ACTION_LAST)
                              tagerror(taglist, E_INVALID);
                        else
                              base->onBattery.keyaction = taglist->data;
                  } else
                        taglist->data = base->onBattery.keyaction;
                  break;
            case TAG_ONBATT_TIMESUSPEND:
                  if (cfgure) base->onBattery.suspendtime = taglist->data;
                  else        taglist->data = base->onBattery.suspendtime;
                  break;
            case TAG_ONBATT_TIMEDIM:
                  if (cfgure) base->onBattery.dimtime = taglist->data;
                  else        taglist->data = base->onBattery.dimtime;
                  break;
            /* Tags for power profiles end */

            case TAG_SLEEPKEY:
                  if (cfgure) base->keysleep = taglist->data;
                  else        taglist->data = base->keysleep;
                  break;
            case TAG_SLEEPMOD:
                  if (cfgure) base->modsleep = taglist->data;
                  else        taglist->data = base->modsleep;
                  break;
            case TAG_SLEEPKEYDELAY:
                  if (cfgure) base->sleepkeydelaytime = taglist->data;
                  else        taglist->data = base->sleepkeydelaytime;
                  break;
            case TAG_BWLFIRST:
                  if (cfgure) base->BWL1 = taglist->data;
                  else        taglist->data = base->BWL1;
                  break;
            case TAG_BWLSECOND:
                  if (cfgure) base->BWL2 = taglist->data;
                  else        taglist->data = base->BWL2;
                  break;
            case TAG_BWLLAST:
                  if (cfgure) base->BWL3 = taglist->data;
                  else        taglist->data = base->BWL3;
                  break;
            case TAG_CURRENTBWL:
                  if (cfgure) tagerror (taglist, E_NOWRITE);
                  else        taglist->data = base->warnlevel;
                  break;
            case TAG_CPULOADSLEEPLOCK:
                  if (cfgure) {
                        base->flags.cpuload_enable = taglist->data;
                        base->flags.sleeplock_cpu = taglist->data;
                  } else
                        taglist->data = base->flags.cpuload_enable;
                  break;
            case TAG_CPULOADMIN:
                  if (cfgure) base->cpuload_min = taglist->data;
                  else        taglist->data = base->cpuload_min;
                  break;
            case TAG_CPULOADPERIOD:
                  if (cfgure) base->cpuload_period = taglist->data;
                  else        taglist->data = base->cpuload_period;
                  break;
            case TAG_NETLOADSLEEPLOCK:
                  if (cfgure) {
                        base->flags.netload_enable = taglist->data;
                        base->flags.sleeplock_net = taglist->data;
                  } else
                        taglist->data = base->flags.netload_enable;
                  break;
            case TAG_NETLOADMIN:
                  if (cfgure) base->netload_min = taglist->data;
                  else        taglist->data = base->netload_min;
                  break;
            case TAG_NETLOADPERIOD:
                  if (cfgure) base->netload_period = taglist->data;
                  else        taglist->data = base->netload_period;
                  break;
            case TAG_NETLOADDEV:
                  if (cfgure) strncpy (base->netload_dev, (char *) taglist->data, STDBUFFERLEN-1);
                  else        taglist->data = (long) base->netload_dev;
                  break;
            case TAG_EMERGENCYACTION:
                  if (cfgure) {
                        if (taglist->data > EMA_LAST)
                              tagerror(taglist, E_INVALID);
                        else if (taglist->data == EMA_SLEEP) {
                              if (base->flags.sleep_supported) /* sleep supported? */
                                    base->emergency = EMA_SLEEP; /* then set to EMA_SLEEP */
                              else
                                    tagerror (taglist, E_NOSUPPORT); /* otherwise send error */
                        } else
                              base->emergency = taglist->data;
                  } else
                        taglist->data = (long) base->emergency;
                  break;
            case TAG_HEARTBEATBEEP:
                  if (cfgure) base->flags.heartbeat_enable = taglist->data;
                  else        taglist->data = base->flags.heartbeat_enable;
                  break;
            case TAG_POWERCHANGED:  /* private tag */
                  if (cfgure) {
                        if (taglist->data)
                              base->activeProfile = &base->onAC;
                        else
                              base->activeProfile = &base->onBattery;
                        power_setpolicy(base->activeProfile->policy, taglist->data);
                        base->flags.usersetpolicy = 0;
                        
                        /* Due to changed timeout values of sleeping and
                         * dimming the sleeptime counter have to be adjusted
                         * to prevent unexpected jumps in behaviour. Depending
                         * on the current mode the sleeptime counter will be
                         * resetted to the begining of the current mode.
                         */
                        if (base->mode == MODE_AWAKE)
                              base->sleeptime = 0;
                        else if (base->mode == MODE_DIM)
                              base->sleeptime = base->activeProfile->dimtime;
                        power_check_battery (base->timeremaining, base->powersource);
                        
                        /* If the power source changed during the cover is closed
                         * the machine should follow the active profile.
                         */
                        if (!base->flags.coveropen)
                              power_suspend (base->activeProfile->coveraction);
                  }
                  break;
            case TAG_TIMECHANGED:  /* private tag */
                  if (cfgure) {
                        base->timeremaining = taglist->data;
                        power_check_battery (base->timeremaining, base->powersource);
                  }
                  break;
            case TAG_PREPAREFORSLEEP:  /* private tag */
                  /* PMCS-script will be called in power_suspend() so that it's nothing left to do here */
                  break;
            case TAG_WAKEUPFROMSLEEP:  /* private tag */
                  if (cfgure) {
                        power_awake ();
                        val = base->powersource;
                        power_sync ();  /* syncronise redundant data from module_pmac */
                        power_check_battery (-1, base->powersource);
                        call_script (base->script_pmcs, "resume", base->powersource ? "ac" : "battery", "ram");
                        /* if power source changed during sleep call PMCS script
                         * again to set power policy */
                        if (val != base->powersource)
                              power_setpolicy(base->activeProfile->policy, base->powersource);
                  }
                  break;
            case TAG_COVERSTATUS:  /* private tag */
                  if (cfgure) {
                        base->flags.coveropen = taglist->data & 1;
                        val = base->activeProfile->coveraction;
                        if (val == ACTION_BLANK || ((val == ACTION_TORAM) && !base->flags.sleep_supported))
                              call_script (base->script_pmcs, 
                                    base->flags.coveropen ? "cover-open" : "cover-close",
                                    base->powersource ? "ac" : "battery",
                                    base->flags.coveropen ? "open" : "close");
                        if (base->flags.coveropen) 
                              power_awake ();
                        else {
                              if (val != ACTION_BLANK)
                                    power_suspend (ACTION_BLANK); /* always blank screen if cover has been closed */
                              power_suspend (val);          /* then follow user's will */
                        }
                  }
                  break;
            case TAG_GOTOSLEEP:
                  if (cfgure) power_suspend (ACTION_TORAM);
                  else        tagerror (taglist, E_NOREAD);
                  break;
            case TAG_GOTOHIBERNATE:
                  if (cfgure) power_suspend (ACTION_TODISK);
                  else        tagerror (taglist, E_NOREAD);
                  break;
            case TAG_POLICY:
                  if (cfgure) {
                        if (taglist->data == POLICY_NONE || taglist->data > POLICY_LAST)
                              tagerror(taglist, E_INVALID);
                        else {
                              power_setpolicy(taglist->data, base->powersource);
                              base->flags.usersetpolicy = 1;
                        }
                  } else      
                        taglist->data = base->policy;
                  break;
            }
            taglist++;
      }
}

/* This routine will be called every 10ms */

void
power_timer (struct tagitem *taglist)
{
      struct moddata_power *base = &modbase_power;
      int n;

      base->sleeptime++;
      switch (base->mode) {
      case MODE_AWAKE:
            if ((base->activeProfile->dimtime) 
              && (base->sleeptime > base->activeProfile->dimtime)) { /* time to dim the display? */
                  process_queue_single (CONFIGQUEUE, TAG_BRIGHTNESSOP, OP_DIM_LIGHT); /* then dim */
                  base->mode = MODE_DIM;
            }
      case MODE_DIM:
            if ((base->activeProfile->suspendtime)
              && (base->sleeptime > base->activeProfile->suspendtime)
              && !base->flags.sleeplock_cpu
              && !base->flags.sleeplock_net) { /* time to sleep? */
                  base->mode = MODE_DOZY;
                  base->sleepcountdown = 60;
            }
            break;
      case MODE_DOZY:
            if (base->activeProfile->timeraction != ACTION_NONE) {
                  n = --base->sleepcountdown / 10;       /* time left in seconds */
                  if (n == 0)
                        power_suspend (base->activeProfile->timeraction);
                  else
                        singletag_to_clients(WARNING, TAG_SLEEPINSECONDS, n);
            }
      case MODE_SUSPEND:
            base->sleeptime--;
            break;
      }
}

void
power_timer1000 (struct tagitem *taglist)
{
      struct moddata_power *base = &modbase_power;
      static int cputimer = 0, nettimer = 0, beeptimer = 0;
      int cpuload = 0, netload = 0;

      if (base->flags.cpuload_enable) {
            cpuload = power_read_cpu ();
            if (cpuload < base->cpuload_min) {
                  if (base->flags.sleeplock_cpu) {   /* skip following if lock is already open */
                        cputimer++;
                        if (cputimer > base->cpuload_period)
                              base->flags.sleeplock_cpu = 0;   /* open lock */
                  }
            } else {
                  cputimer = 0;
                  base->flags.sleeplock_cpu = 1;   /* close lock */
            }
      } else
            cputimer = 0;

      if (base->flags.netload_enable) {
            netload = power_read_net (base->netload_dev);
            if (netload < base->netload_min) {
                  if (base->flags.sleeplock_net) {   /* skip following if lock is already open */
                        nettimer++;
                        if (nettimer > base->netload_period)
                              base->flags.sleeplock_net = 0;   /* open lock */
                  }
            } else {
                  nettimer = 0;
                  base->flags.sleeplock_net = 1;   /* close lock */
            }
      } else
            nettimer = 0;

      if (base->flags.heartbeat_enable) {  /* user wants heartbeat beep ? */
            if (base->flags.heartbeat) {
                  beeptimer++;
                  if (beeptimer > BEEPINTERVAL) {
                        power_beep();
                        beeptimer = 0;
                  }
            } else
                  beeptimer = 0;
      } else
            beeptimer = 0;

#if defined(DEBUG) && SLEEPLOCKS
      printf ("cpu: ");
      if (base->flags.cpuload_enable)
            printf ("%2d%% (lock %s)", cpuload, base->flags.sleeplock_cpu ? "closed" : "open  ");
      else
            printf ("disabled");

      printf (",  net: ");
      if (base->flags.netload_enable)
            printf ("%8d B/s (lock %s)", netload, base->flags.sleeplock_net ? "closed" : "open  ");
      else
            printf ("disabled");
      printf ("\r");
      fflush(stdout);
#endif
}

void
power_check_battery (int time, int psource)
{
      struct moddata_power *base = &modbase_power;
      struct tagitem taglist[3];

      int warnlevel[] = {1200, base->BWL1 + BAT_HYSTERESIS,
                             base->BWL1, base->BWL2 + BAT_HYSTERESIS,
                             base->BWL2, base->BWL3 + BAT_HYSTERESIS,
                             base->BWL3, 1,
                             1,          0};
      int n, wl;

      /* if pbbuttons was started with AC plugged in, timeleft would
       * remain at -1 and after AC pulled out the value wasn't changed
       * quick enough so that the warning level 4 was set due to
       * -1 / 60 = 0 -> wl = 4 -> shutdown. To prevent this the timeleft
       * value of -1 is filtered out here
       */

      if (psource || time == -1) { /* if battery is charging or time is invalid */
            wl = 0;                   /* we don't need to calculate warnlevels */
            if (base->warnlevel) {      /* but maybe have to inform clients */
                  taglist_init (taglist);
                  taglist_add (taglist, TAG_CURRENTBWL, wl);
                  distribute_to_clients(WARNING, taglist);
            }
      } else {
            time /= 60;           /* calculate minutes */
            wl = base->warnlevel;
            if (time >= 0) {
                  for (n=4; n >= 0; n--)
                        if (time < warnlevel[2*n])
                              if (time >= warnlevel[2*n+1]) {
                                    wl = n;
                                    break;
                              }

                  if (wl > base->warnlevel) {  /* warnlevel increased ? */
                        taglist_init (taglist);
                        taglist_add (taglist, TAG_CURRENTBWL, wl);
                        taglist_add (taglist, TAG_TIMEREMAINING, time);
                        distribute_to_clients(WARNING, taglist);
                        if (wl == 4) {             /* oh oh, battery is realy low */
                              switch (base->emergency) {
                              case EMA_SLEEP:
                                    process_queue_single (CONFIGQUEUE, TAG_REQUESTSLEEP, 0);
                                    break;
                              case EMA_SIGNAL:
                                    if ((send_sigpwr('F')) != 0) { /* send init a SIGPWR signal */
                                          if (base->flags.sleep_supported) { /* fallback to sleep if possible */
                                                base->emergency = EMA_SLEEP;
                                                process_queue_single (CONFIGQUEUE, TAG_REQUESTSLEEP, 0);
                                          } else {              /* otherwise use the last way out */
                                                base->emergency = EMA_COMMAND;
                                                call_script (base->script_pmcs, "emergency", base->powersource ? "ac" : "battery", "");
                                                sleep (10);
                                          }
                                    } else {
                                          base->flags.sigpwr_sent = 1;  /* sigpwr in progress */
                                          sleep(5);  /* give INIT a chance to react */
                                    }
                                    break;
                              case EMA_COMMAND:
                                    call_script (base->script_pmcs, "emergency", base->powersource ? "ac" : "battery", "");
                                    sleep(10);   /* give the script a chance to work */
                                    break;
                              }
                        }
                  } else if (wl < base->warnlevel && base->flags.sigpwr_sent) { /* battery has recovered */
                        send_sigpwr('O');             /* abort sigpwr action */
                        base->flags.sigpwr_sent = 0; /* sigpwr no longer in progress */
                  }
            }
      }
      base->warnlevel = wl;
}

int
power_shutdown_in_progress ()
{
      struct utmp *ut;
      int runlevel=-1;

      setutent();
      while ((ut = getutent()) != NULL) {
            if (ut->ut_type == RUN_LVL) {
                  runlevel = ut->ut_pid % 256;
                  break;
            }
      }
      endutent();

      if (runlevel == '0' || runlevel == '6')
            return 1; /* shutdown or reboot in progress */

      return 0;
}
                        
/* This function requests sleep mode and take user configurations
 * like enable_blank into account.
 */
void
power_suspend (int action)
{
      struct moddata_power *base = &modbase_power;

      if (action == ACTION_NONE)  /* no action, mode will not change */
            return;

      /* If a shutdown is in progress allow only ACTION_BLANK
       * and reject all other actions.
       */
    if (power_shutdown_in_progress() && action != ACTION_BLANK)
            return;
      
      base->mode = MODE_SUSPEND;

      switch (action) {
      case ACTION_TODISK:
            singletag_to_clients(WARNING, TAG_SLEEPINSECONDS, 0);
            call_script (base->script_pmcs, "suspend", base->powersource ? "ac" : "battery", "disk");
            break;
      case ACTION_TORAM:
            if (base->flags.sleep_supported) {
                  singletag_to_clients(WARNING, TAG_SLEEPINSECONDS, 0);
                  call_script (base->script_pmcs, "suspend", base->powersource ? "ac" : "battery", "ram");
                  process_queue_single (CONFIGQUEUE, TAG_REQUESTSLEEP, 0);
                  break;
            } /* else ACTION_BLANK */
      case ACTION_BLANK:
            process_queue_single (CONFIGQUEUE, TAG_BRIGHTNESSOP, OP_DIM_OFF);
            if (base->flags.heartbeat_enable) {  /* user wants heartbeat beep ? */
                  base->flags.heartbeat = 1; /* enable heartbeat beep */
                  power_beep();
            }
            break;
      case ACTION_SHUTDOWN:  /* action currently disabled */
            singletag_to_clients(WARNING, TAG_SLEEPINSECONDS, 0);
            call_script (base->script_pmcs, "shutdown", base->powersource ? "ac" : "battery", "");
            sleep (10);
            break;
      }
}

/* This function updates the power policy and the powersource
 * in the base data structure. If one of them changes the
 * pmcs script will be executed. The illegal POLICY_NONE will
 * be filtered.
 */
void
power_setpolicy (int policy, int powersource)
{
      struct moddata_power *base = &modbase_power;
      char *policies[] = {POLICY_NONE_NAME, POLICY_POWERSAVE_NAME, POLICY_USER_NAME, POLICY_PERFORMANCE_NAME};
      char *powersrc[] = {"battery", "ac"};
      int change = 0;

      if (policy != POLICY_NONE && base->policy != policy) {
            base->policy = policy;
            change = 1;
      }
      if (base->powersource != powersource) {
            base->powersource = powersource;
            change = 1;
      }
      if (change)
            call_script (base->script_pmcs, policies[base->policy], powersrc[base->powersource], "");
}

int
check_script (char *name, char *allowed)
{
  while (*name != 0) {
    if (*name++ == '%')
      if ((*allowed == 0) || (*name++ != *allowed++))
        return -1;
  }
  return 0;
}

int
send_sigpwr (char mode)
{
  FILE *fd;
  int err = -1;

#ifdef HAVE_INITREQ_H
  struct init_request initreq;

  initreq.magic     = INIT_MAGIC;
  initreq.cmd       = mode == 'F' ? INIT_CMD_POWERFAIL : INIT_CMD_POWEROK;
  initreq.sleeptime = 5;

  if ((fd = fopen(INIT_FIFO, "r+")) != NULL) {
    err = 0;
    if ((fwrite(&initreq, sizeof(initreq), 1, fd)) != 1)
      err = -1;
    fclose(fd);
  }
#endif

  if (err == -1) {
    if ((fd = fopen("/etc/powerstatus", "w")) != NULL) {
      err = 0;
      if ((fwrite(&mode, 1, 1, fd)) != 1)
        err = -1;
      fclose(fd);
    }
    err = kill(1, SIGPWR);  /* send SIGPWR to the INIT process */
  }
  return err;
}

/* This function reads /proc/stat and calculates the current cpuload.
    If an error occured, the return value would be '0', so that no program
    features espacially sleep will be blocked. */

int
power_read_cpu (void)
{
      char buffer[1024], *token;
      static ulong pvalue = 0, ptotal;
      ulong value = 0, total = 1, user, nice, sys, idle;
      FILE *fd;

      if ((fd = fopen ("/proc/stat","r"))) {
            while (fgets (buffer, sizeof (buffer), fd))
                  if ((token = strtok (buffer," \t\n"))) {
                        if (!strncmp ("cpu", token, 3)) {
                              sscanf(strtok (0, "\n"), "%lu %lu %lu %lu",
                                       &user, &nice, &sys, &idle);
                              break;
                        } else
                              strtok (0,"\n");
                  }
            fclose(fd);

            if (pvalue) {
                  value = user + sys + nice - pvalue;
                  total = user + sys + nice + idle - ptotal;
            }
            pvalue = user + sys + nice;
            ptotal = pvalue + idle;
            return (int) (value * 100 / total);
      }
      return 0;
}

/* This function reads /proc/net/dev and calculates the current trafic to and
    from net. Only the given network device will be observed and incomming
    and outgoing trafic would be combined to one single bytes/second value.
    If an error occured, the return value would be '0', so that no program
    features espacially sleep will be blocked. */

int
power_read_net (char *dev)
{
      static ulong ptrafic = 0;
      char buffer[1024], *token;
      ulong dummy, bytesread = 0, byteswrite = 0, trafic = 0;
      FILE *fd;

      if ((fd = fopen ("/proc/net/dev","r"))) {
            while (fgets (buffer, sizeof (buffer), fd))
                  if ((token = strtok (buffer,":\n"))) {
                        cleanup_buffer (token);
                        if (!strncmp (dev, token, strlen (dev))) {
                              sscanf(strtok (0, "\n"), "%lu %lu %lu %lu %lu %lu %lu %lu %lu",
                                       &bytesread, &dummy, &dummy, &dummy, &dummy,
                                     &dummy, &dummy, &dummy, &byteswrite);
                              break;
                        } else
                              strtok (0,"\n");
                  }
            fclose(fd);

            if (ptrafic)
                  trafic = bytesread + byteswrite - ptrafic;
            ptrafic = bytesread + byteswrite;
            return (int) trafic;
      }
      return 0;
}

void
power_beep()
{
      int arg, fd;
      unsigned int ms = 150;
      unsigned int freq = 5000;

      if ((fd = open("/dev/console", O_RDWR)) >= 0) {
            arg = (ms << 16) | freq;
            ioctl(fd, KDMKTONE, arg);
            usleep(ms*1000);
            close (fd);
      }
}

Generated by  Doxygen 1.6.0   Back to index