/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "healthd-flounder" #include <healthd/healthd.h> #include <sys/stat.h> #include <fcntl.h> #include <cutils/klog.h> #include <sys/types.h> #include <sys/sysinfo.h> /* Nominal voltage for ENERGY_COUNTER computation */ #define VOLTAGE_NOMINAL 3.7 #define POWER_SUPPLY_SUBSYSTEM "power_supply" #define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM #define MAX17050_PATH POWER_SUPPLY_SYSFS_PATH "/battery" #define CHARGE_COUNTER_EXT_PATH MAX17050_PATH "/charge_counter_ext" #define PALMAS_VOLTAGE_MONITOR_PATH POWER_SUPPLY_SYSFS_PATH "/palmas_voltage_monitor" #define VOLTAGE_MONITOR_PATH PALMAS_VOLTAGE_MONITOR_PATH "/device/voltage_monitor" #define BATTERY_FULL 100 #define BATTERY_LOW 15 #define BATTERY_CRITICAL_LOW_MV (3000) #define BATTERY_DEAD_MV (2800) #define NORMAL_MAX_SOC_DEC (2) #define CRITICAL_LOW_FORCE_SOC_DROP (6) #define UPDATE_PERIOD_MINIMUM_S (55) #define BATTERY_OVERTEMP_THRESHOLD (550) #define BATTERY_UNDERTEMP_THRESHOLD (0) using namespace android; static bool first_update_done; static int lasttime_soc; static unsigned int flounder_monitor_voltage; static long last_update_time; static bool force_decrease; static int read_sysfs(const char *path, char *buf, size_t size) { char *cp = NULL; int fd = open(path, O_RDONLY, 0); if (fd == -1) { KLOG_ERROR(LOG_TAG, "Could not open '%s'\n", path); return -1; } ssize_t count = TEMP_FAILURE_RETRY(read(fd, buf, size)); if (count > 0) cp = (char *)memrchr(buf, '\n', count); if (cp) *cp = '\0'; else buf[0] = '\0'; close(fd); return count; } static void write_sysfs(const char *path, char *s) { char buf[80]; int len; int fd = open(path, O_WRONLY); if (fd < 0) { strerror_r(errno, buf, sizeof(buf)); KLOG_ERROR(LOG_TAG, "Error opening %s: %s\n", path, buf); return; } len = write(fd, s, strlen(s)); if (len < 0) { strerror_r(errno, buf, sizeof(buf)); KLOG_ERROR(LOG_TAG, "Error writing to %s: %s\n", path, buf); } close(fd); } static int64_t get_int64_field(const char *path) { const int SIZE = 21; char buf[SIZE]; int64_t value = 0; if (read_sysfs(path, buf, SIZE) > 0) { value = strtoll(buf, NULL, 0); } return value; } static void flounder_status_check(struct BatteryProperties *props) { if (props->batteryStatus == BATTERY_STATUS_UNKNOWN) props->batteryStatus = BATTERY_STATUS_DISCHARGING; else if (props->batteryStatus == BATTERY_STATUS_FULL && props->batteryLevel < BATTERY_FULL) props->batteryStatus = BATTERY_STATUS_CHARGING; } static void flounder_health_check(struct BatteryProperties *props) { if (props->batteryLevel >= BATTERY_FULL) props->batteryHealth = BATTERY_HEALTH_GOOD; else if (props->batteryLevel < BATTERY_LOW) props->batteryHealth = BATTERY_HEALTH_DEAD; else props->batteryHealth = BATTERY_HEALTH_GOOD; if (props->batteryHealth == BATTERY_HEALTH_GOOD){ if (props->batteryTemperature > BATTERY_OVERTEMP_THRESHOLD) props->batteryHealth = BATTERY_HEALTH_OVERHEAT; else if(props->batteryTemperature < BATTERY_UNDERTEMP_THRESHOLD) props->batteryHealth = BATTERY_HEALTH_COLD; } } static void flounder_voltage_monitor_check(struct BatteryProperties *props) { unsigned int monitor_voltage = 0; int vcell_mv; char voltage[10]; if (props->batteryStatus != BATTERY_STATUS_CHARGING && props->batteryStatus != BATTERY_STATUS_FULL && props->batteryLevel > 0) { vcell_mv = props->batteryVoltage; if (vcell_mv > BATTERY_CRITICAL_LOW_MV) monitor_voltage = BATTERY_CRITICAL_LOW_MV; else if (vcell_mv > BATTERY_DEAD_MV) monitor_voltage = BATTERY_DEAD_MV; } if (monitor_voltage != flounder_monitor_voltage) { snprintf(voltage, sizeof(voltage), "%d", monitor_voltage); write_sysfs(VOLTAGE_MONITOR_PATH, voltage); flounder_monitor_voltage = monitor_voltage; } } static void flounder_soc_adjust(struct BatteryProperties *props) { int soc_decrease; int soc, vcell_mv; struct sysinfo info; long uptime = 0; int ret; ret = sysinfo(&info); if (ret) { KLOG_ERROR(LOG_TAG, "Fail to get sysinfo!!\n"); uptime = last_update_time; } else uptime = info.uptime; if (!first_update_done) { if (props->batteryLevel >= BATTERY_FULL) { props->batteryLevel = BATTERY_FULL - 1; lasttime_soc = BATTERY_FULL - 1; } else { lasttime_soc = props->batteryLevel; } last_update_time = uptime; first_update_done = true; } if (props->batteryStatus == BATTERY_STATUS_FULL) soc = BATTERY_FULL; else if (props->batteryLevel >= BATTERY_FULL && lasttime_soc < BATTERY_FULL) soc = BATTERY_FULL - 1; else soc = props->batteryLevel; if (props->batteryLevel > BATTERY_FULL) props->batteryLevel = BATTERY_FULL; else if (props->batteryLevel < 0) props->batteryLevel = 0; vcell_mv = props->batteryVoltage; if (props->batteryStatus == BATTERY_STATUS_DISCHARGING || props->batteryStatus == BATTERY_STATUS_NOT_CHARGING || props->batteryStatus == BATTERY_STATUS_UNKNOWN) { if (vcell_mv >= BATTERY_CRITICAL_LOW_MV) { force_decrease = false; soc_decrease = lasttime_soc - soc; if (soc_decrease < 0) { soc = lasttime_soc; goto done; } if (uptime > last_update_time && uptime - last_update_time <= UPDATE_PERIOD_MINIMUM_S) { soc = lasttime_soc; goto done; } if (soc_decrease < 0) soc_decrease = 0; else if (soc_decrease > NORMAL_MAX_SOC_DEC) soc_decrease = NORMAL_MAX_SOC_DEC; soc = lasttime_soc - soc_decrease; } else if (vcell_mv < BATTERY_DEAD_MV) { soc = 0; } else { if (force_decrease && uptime > last_update_time && uptime - last_update_time <= UPDATE_PERIOD_MINIMUM_S) { soc = lasttime_soc; goto done; } soc_decrease = CRITICAL_LOW_FORCE_SOC_DROP; if (lasttime_soc <= soc_decrease) soc = 0; else soc = lasttime_soc - soc_decrease; force_decrease = true; } } else { force_decrease = false; if (soc > lasttime_soc) soc = lasttime_soc + 1; } last_update_time = uptime; done: props->batteryLevel = lasttime_soc = soc; } static void flounder_bat_monitor(struct BatteryProperties *props) { flounder_soc_adjust(props); flounder_health_check(props); flounder_status_check(props); flounder_voltage_monitor_check(props); } int healthd_board_battery_update(struct BatteryProperties *props) { flounder_bat_monitor(props); // return 0 to log periodic polled battery status to kernel log return 0; } static int flounder_energy_counter(int64_t *energy) { *energy = get_int64_field(CHARGE_COUNTER_EXT_PATH) * VOLTAGE_NOMINAL; return 0; } void healthd_board_init(struct healthd_config *config) { config->energyCounter = flounder_energy_counter; }