dockapps/wmpower/src/power_management/power_management.c
2014-10-05 19:18:49 +01:00

549 lines
15 KiB
C

/***************************************************************************
power_management.c - description
-------------------
begin : Feb 10 2003
copyright : (C) 2003-2005 by Noberasco Michele
e-mail : 2001s098@educ.disi.unige.it
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
* *
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "lib_utils.h"
#include "power_management.h"
#include "libacpi.h"
#include "libapm.h"
#include "toshiba_lib.h"
#include "dell_lib.h"
#include "compal_lib.h"
#include "cpufreq.h"
/* what kind of machine is running us? */
#define UNKNOWN 0
#define TOSHIBA 1
#define DELL 2
#define COMPAL 3
int machine = UNKNOWN;
int fast_charge_mode=0;
int use_toshiba_hardware=1;
int pm_type=PM_Error;
int ac_on_line;
int battery_percentage;
int battery_present;
int use_noflushd=1;
int use_cpufreq=1;
/* Battery to monitor */
int Battery;
int get_fan_status();
void get_temperature(int *temperature, int *temp_is_celsius);
int fast_battery_charge(int toggle);
int calculate_battery_time(int battery_percentage, int ac_on_line);
kernel_versions Get_Kernel_version(void);
int pm_support(int use_this_battery)
{
Battery = use_this_battery;
if (!use_noflushd) fprintf(stderr, "use of noflushd is disabled\n");
if (!use_cpufreq) fprintf(stderr, "CPU frequency scaling disabled\n");
/* What kernel version are we running in? */
kernel_version = Get_Kernel_version();
/* check for specific hardware */
while (1)
{
/* is this a compal laptop? */
if (machine_is_compal())
{
machine = COMPAL;
fprintf(stderr, "detected Compal laptop, %s\n", compal_model);
break;
}
/* is this a dell laptop? */
if (machine_is_dell())
{
machine = DELL;
fprintf(stderr, "detected DELL laptop\n");
break;
}
/* Is this a Toshiba Laptop? */
if (machine_is_toshiba(&use_toshiba_hardware))
{
machine = TOSHIBA;
fprintf(stderr, "detected TOSHIBA laptop, %s\n", toshiba_model);
if (!use_toshiba_hardware) fprintf(stderr, "direct access to TOSHIBA hardware disabled\n");
break;
}
break;
}
/* Does this system support CPU frequency scaling? */
if (use_cpufreq)
{
use_cpufreq = check_cpufreq();
if (use_cpufreq) fprintf(stderr, "CPU frequency scaling available\n");
else fprintf(stderr, "CPU frequency scaling NOT available\n");
/* Set default governors if we were supplied none */
if (!cpufreq_offline_governor)
cpufreq_offline_governor = CPUFREQ_GOV_ONDEMAND;
if (!cpufreq_online_governor)
cpufreq_online_governor = CPUFREQ_GOV_PERFORMANCE;
}
/* Is this an acpi system? */
if (check_acpi())
{
pm_type= PM_ACPI;
fprintf(stderr, "Detected ACPI subsystem\n");
return 1;
}
/* Is this an APM system? */
if (apm_exists())
{
pm_type= PM_APM;
fprintf(stderr, "detected APM subsystem\n");
if (Battery != 1)
{
fprintf(stderr, "You must use ACPI to monitor any battery\n");
fprintf(stderr, "other than the first one. Cannot continue.\n");
exit(1);
}
return 1;
}
fprintf(stderr, "No power management subsystem detected\n");
return 0;
}
void get_power_status(pm_status *power_status)
{
/* Is this an ACPI system? */
if (pm_type == PM_ACPI)
{
static ACPIinfo acpiinfo;
static ACADstate acadstate;
static ACPIstate acpistate;
read_acpi_info(&acpiinfo, Battery - 1);
read_acpi_state(&acpistate, &acpiinfo, Battery - 1);
read_acad_state(&acadstate);
/* Check if we are on ac power */
ac_on_line = power_status->ac_on_line = acadstate.state;
/* Check to see if we are charging. */
if (acpistate.state == CHARGING) power_status->battery_charging=1;
else power_status->battery_charging=0;
/* Check battery time and percentage */
power_status->battery_time = acpistate.rtime;
power_status->battery_percentage = acpistate.percentage;
if (power_status->battery_percentage > 100) power_status->battery_percentage = 100;
battery_percentage = power_status->battery_percentage;
/* Check if battery is plugged in */
battery_present = power_status->battery_present = acpistate.present;
/* Get temperature and fan status */
power_status->fan_status=get_fan_status();
get_temperature(&(power_status->temperature), &(power_status->temp_is_celsius));
if (fast_charge_mode && (power_status->battery_percentage == 100)) fast_battery_charge(0);
/* Let's see wether we failed to get battery time */
if (battery_present && (battery_percentage < 100) && (power_status->battery_time <= 0))
{
/* OK, we failed, we calculate the value ourselves */
power_status->battery_time = calculate_battery_time(battery_percentage, ac_on_line);
}
return;
}
/* Is this an APM system? */
if (pm_type == PM_APM)
{
struct_apm_data apm_data;
if (!apm_read(&apm_data))
{
fprintf(stderr, "Could not read APM info!\n");
return;
}
ac_on_line = power_status->ac_on_line = apm_data.ac_line_status;
if ( apm_data.battery_percentage == -1)
{
battery_present = power_status->battery_present = 0;
battery_percentage = power_status->battery_percentage = 0;
}
else
{
battery_present = power_status->battery_present = 1;
power_status->battery_percentage = apm_data.battery_percentage;
if (power_status->battery_percentage > 100) power_status->battery_percentage = 100;
battery_percentage = power_status->battery_percentage;
}
if ( (int)(apm_data.battery_status) == 3)
power_status->battery_charging=1;
else power_status->battery_charging=0;
power_status->battery_time = (apm_data.using_minutes) ? apm_data.battery_time : apm_data.battery_time / 60;
power_status->fan_status=get_fan_status();
get_temperature(&(power_status->temperature), &(power_status->temp_is_celsius));
if (fast_charge_mode && (power_status->battery_percentage == 100)) fast_battery_charge(0);
return;
}
}
int get_fan_status(void)
{
if (machine == COMPAL) return compal_get_fan_status();
if (machine == DELL) return dell_get_fan_status();
if (machine == TOSHIBA) return toshiba_get_fan_status(use_toshiba_hardware);
if (pm_type == PM_ACPI) return acpi_get_fan_status();
return PM_Error;
}
void get_temperature(int *temperature, int *temp_is_celsius)
{
/* for Compal laptops... */
if (machine == COMPAL)
{
int result = compal_get_temperature();
if (result == PM_Error)
{
(*temperature) = PM_Error;
(*temp_is_celsius) = PM_Error;
return;
}
(*temperature) = result;
(*temp_is_celsius) = 1;
return;
}
/* for Dell laptops... */
if (machine == DELL)
{
int result = dell_get_temperature();
if (result == PM_Error)
{
(*temperature) = PM_Error;
(*temp_is_celsius) = PM_Error;
return;
}
(*temperature) = result;
(*temp_is_celsius) = 1;
return;
}
/* No special hardware... */
if (pm_type == PM_ACPI)
{
acpi_get_temperature(temperature, temp_is_celsius);
return;
}
/* No ACPI */
(*temperature) = PM_Error;
(*temp_is_celsius) = PM_Error;
}
void internal_set_pm_features(int ac_status)
{
static int noflushd = -1;
if (fast_charge_mode) ac_status=0;
if (ac_status)
{ /* we are on AC power */
/* Stop noflushd damon (disable HD spindown) */
if (use_noflushd && noflushd)
{
system("/etc/init.d/noflushd stop >/dev/null 2>/dev/null");
noflushd = 0;
}
/* Set CPU governor to 'performance' (or whatever our master chose when online) */
if (use_cpufreq)
if (!cpufreq_set_governor(cpufreq_online_governor))
fprintf(stderr, "failed setting '%s' CPUfreq governor!\n", cpufreq_online_governor);
if (machine == COMPAL)
{
/* Set LCD to maximum brightness */
compal_set_lcd_brightness(COMPAL_LCD_MAX);
}
if (machine == TOSHIBA)
{
/* Set LCD to maximum brightness */
toshiba_set_lcd_brightness(TOSHIBA_LCD_MAX, use_toshiba_hardware);
/* Start fan */
if (use_toshiba_hardware && (pm_type != PM_ACPI)) toshiba_set_fan_status(1);
return;
}
if (pm_type == PM_ACPI)
{
/* Set generic ACPI features here... */
return;
}
}
else
{ /* we are on battery power */
if (use_noflushd && (!noflushd || noflushd == -1))
{
/* Start noflushd damon (enable HD spindown) */
system("/etc/init.d/noflushd start >/dev/null 2>/dev/null");
noflushd = 1;
}
/* Set CPU governor to 'ondemand' (or whatever our master chose when offline) */
if (use_cpufreq)
if (!cpufreq_set_governor(cpufreq_offline_governor))
fprintf(stderr, "failed setting '%s' CPUfreq governor!\n", cpufreq_offline_governor);
if (machine == COMPAL)
{
/* Set LCD to minimum brightness */
compal_set_lcd_brightness(COMPAL_LCD_MIN);
}
if (machine == TOSHIBA)
{
/* Set LCD to minimum brightness */
toshiba_set_lcd_brightness(TOSHIBA_LCD_MIN, use_toshiba_hardware);
/* Stop fan */
if (use_toshiba_hardware && (pm_type != PM_ACPI)) toshiba_set_fan_status(0);
return;
}
if (pm_type == PM_ACPI)
{
/* Set generic ACPI features here... */
return;
}
}
}
void set_pm_features(void)
{
static int old_value = -1;
if (ac_on_line != old_value)
{
internal_set_pm_features(ac_on_line);
old_value = ac_on_line;
}
}
int fast_battery_charge(int toggle)
{
if (toggle && !battery_present)
{
fprintf(stderr, "Fast charge mode without a battery? I think not ;-)\n");
return 0;
}
if (toggle && !ac_on_line)
{
fprintf(stderr, "Fast charge mode while on battery? I think not ;-)\n");
return 0;
}
if (toggle && (battery_percentage == 100))
{
fprintf(stderr, "Battery is already at maximum capacity: fast charging not enabled\n");
return 0;
}
if (!toggle && fast_charge_mode)
{
if (battery_percentage == 100) fprintf(stderr, "battery at maximum capacity: ");
fprintf(stderr, "disabling fast charge mode\n");
fast_charge_mode=0;
internal_set_pm_features(ac_on_line);
return 0;
}
if (toggle)
{
fprintf(stderr, "enabling fast charge mode\n");
internal_set_pm_features(0);
fast_charge_mode=1;
return 1;
}
return fast_charge_mode;
}
int get_fast_battery_charge_mode(void)
{
return fast_charge_mode;
}
void set_noflushd_use(int toggle)
{
use_noflushd = toggle;
}
void set_toshiba_hardware_use(int toggle)
{
use_toshiba_hardware = toggle;
}
void set_cpufreq_use(int toggle)
{
use_cpufreq = toggle;
}
void lcdBrightness_UpOneStep()
{
if (machine == COMPAL) Compal_lcdBrightness_UpOneStep();
if (machine == TOSHIBA) Toshiba_lcdBrightness_UpOneStep(use_toshiba_hardware);
}
void lcdBrightness_DownOneStep()
{
if (machine == COMPAL) Compal_lcdBrightness_DownOneStep();
if (machine == TOSHIBA) Toshiba_lcdBrightness_DownOneStep(use_toshiba_hardware);
}
kernel_versions Get_Kernel_version(void)
{
FILE *fp = fopen("/proc/version", "r");
char buf[512];
if (!fp) return IS_OTHER;
if (!fgets(buf, 512, fp))
{
fclose(fp);
return IS_OTHER;
}
fclose(fp);
if (strstr(buf, "2.6.")) return IS_2_6;
return IS_OTHER;
}
/* Calculate estimated battery time
* note that the result this function returns
* is fairly inaccurate, and will be used only
* if we fail to get the value trough ACPI, APM
* or whatever...
*/
int calculate_battery_time(int battery_percentage, int ac_on_line)
{
static int first_percentage = 0;
static int first_on_line = -1;
static time_t first_time = 0;
static time_t prev_time = 0;
time_t curr_time = time(NULL);
int battery_time;
int elapsed_time;
/* First time we are run, we cannot return anything meaningful;
* also, we have to reinitialize ourselves when our host switches
* to/from battery power. Plus, it is best to reinitialize our stuff
* when the time elapsed since our last update is significantly
* higher than our polling interval, because this likely means that
* the system has been suspended (we allow it some play -60 seconds-
* because -who knows- the system might be on really heavy load)...
*/
if (!first_time || (first_on_line != ac_on_line) || (first_time && (curr_time-prev_time>(waittime + 60))))
{
if (!first_time)
{
fprintf(stderr, "could not read battery time!\n");
fprintf(stderr, "I will calculate it directly, but this is FAR from accurate..\n");
}
else fprintf(stderr, "re-initializing battery time!\n");
first_percentage = battery_percentage;
first_on_line = ac_on_line;
first_time = curr_time;
prev_time = curr_time;
return 0;
}
prev_time = curr_time;
/* Seconds passed since we were initialized */
elapsed_time = curr_time - first_time;
if (!ac_on_line)
{ /* We calculate time left until battery drains out */
/* How much battery was drained in this time? */
int drained_battery = first_percentage - battery_percentage;
if (!drained_battery) return 0;
/* elapsed_time : drained_battery = ? time : remaining battery */
battery_time = battery_percentage * elapsed_time / drained_battery;
}
else
{ /* We calculate time left until battery is fully recharged */
/* How much battery was gained in this time? */
int gained_battery = battery_percentage - first_percentage;
/* How much battery still to be charged? */
int remaining_battery = 100 - battery_percentage;
if (!gained_battery ) return 0;
if (!remaining_battery) return 0;
/* elapsed_time : gained_battery = ? time : battery still to be charged */
battery_time = remaining_battery * elapsed_time / gained_battery;
}
/* the value is in seconds, but we need it in minutes */
battery_time = battery_time / 60;
/* Don't allow it to be higher than 99 hours and 59 minutes,
* otherwise our GUI will not be able to handle it
*/
if (battery_time > 5999) battery_time = 0;
return battery_time;
}