/*************************************************************************** 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 #include #include #include #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; }