549 lines
15 KiB
C
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;
|
|
}
|