Home | History | Annotate | Download | only in health
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "healthd-flounder"
     18 #include <healthd/healthd.h>
     19 #include <sys/stat.h>
     20 #include <fcntl.h>
     21 #include <cutils/klog.h>
     22 #include <sys/types.h>
     23 #include <sys/sysinfo.h>
     24 
     25 /* Nominal voltage for ENERGY_COUNTER computation */
     26 #define VOLTAGE_NOMINAL 3.7
     27 
     28 #define POWER_SUPPLY_SUBSYSTEM "power_supply"
     29 #define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
     30 
     31 #define MAX17050_PATH POWER_SUPPLY_SYSFS_PATH "/battery"
     32 #define CHARGE_COUNTER_EXT_PATH MAX17050_PATH "/charge_counter_ext"
     33 
     34 #define PALMAS_VOLTAGE_MONITOR_PATH POWER_SUPPLY_SYSFS_PATH "/palmas_voltage_monitor"
     35 #define VOLTAGE_MONITOR_PATH PALMAS_VOLTAGE_MONITOR_PATH "/device/voltage_monitor"
     36 
     37 #define BATTERY_FULL 100
     38 #define BATTERY_LOW 15
     39 #define BATTERY_CRITICAL_LOW_MV (3000)
     40 #define BATTERY_DEAD_MV (2800)
     41 #define NORMAL_MAX_SOC_DEC (2)
     42 #define CRITICAL_LOW_FORCE_SOC_DROP (6)
     43 #define UPDATE_PERIOD_MINIMUM_S (55)
     44 #define BATTERY_OVERTEMP_THRESHOLD (550)
     45 #define BATTERY_UNDERTEMP_THRESHOLD (0)
     46 
     47 using namespace android;
     48 static bool first_update_done;
     49 static int lasttime_soc;
     50 static unsigned int flounder_monitor_voltage;
     51 static long last_update_time;
     52 static bool force_decrease;
     53 
     54 static int read_sysfs(const char *path, char *buf, size_t size) {
     55     char *cp = NULL;
     56 
     57     int fd = open(path, O_RDONLY, 0);
     58     if (fd == -1) {
     59         KLOG_ERROR(LOG_TAG, "Could not open '%s'\n", path);
     60         return -1;
     61     }
     62 
     63     ssize_t count = TEMP_FAILURE_RETRY(read(fd, buf, size));
     64     if (count > 0)
     65         cp = (char *)memrchr(buf, '\n', count);
     66 
     67     if (cp)
     68         *cp = '\0';
     69     else
     70         buf[0] = '\0';
     71 
     72     close(fd);
     73     return count;
     74 }
     75 
     76 static void write_sysfs(const char *path, char *s)
     77 {
     78     char buf[80];
     79     int len;
     80     int fd = open(path, O_WRONLY);
     81 
     82     if (fd < 0) {
     83         strerror_r(errno, buf, sizeof(buf));
     84         KLOG_ERROR(LOG_TAG, "Error opening %s: %s\n", path, buf);
     85         return;
     86     }
     87 
     88     len = write(fd, s, strlen(s));
     89     if (len < 0) {
     90         strerror_r(errno, buf, sizeof(buf));
     91         KLOG_ERROR(LOG_TAG, "Error writing to %s: %s\n", path, buf);
     92     }
     93 
     94     close(fd);
     95 }
     96 
     97 static int64_t get_int64_field(const char *path) {
     98     const int SIZE = 21;
     99     char buf[SIZE];
    100 
    101     int64_t value = 0;
    102     if (read_sysfs(path, buf, SIZE) > 0) {
    103         value = strtoll(buf, NULL, 0);
    104     }
    105     return value;
    106 }
    107 
    108 static void flounder_status_check(struct BatteryProperties *props)
    109 {
    110     if (props->batteryStatus == BATTERY_STATUS_UNKNOWN)
    111         props->batteryStatus = BATTERY_STATUS_DISCHARGING;
    112     else if (props->batteryStatus == BATTERY_STATUS_FULL &&
    113         props->batteryLevel < BATTERY_FULL)
    114         props->batteryStatus = BATTERY_STATUS_CHARGING;
    115 }
    116 
    117 static void flounder_health_check(struct BatteryProperties *props)
    118 {
    119     if (props->batteryLevel >= BATTERY_FULL)
    120         props->batteryHealth = BATTERY_HEALTH_GOOD;
    121     else if (props->batteryLevel < BATTERY_LOW)
    122         props->batteryHealth = BATTERY_HEALTH_DEAD;
    123     else
    124         props->batteryHealth = BATTERY_HEALTH_GOOD;
    125 
    126     if (props->batteryHealth == BATTERY_HEALTH_GOOD){
    127         if (props->batteryTemperature > BATTERY_OVERTEMP_THRESHOLD)
    128             props->batteryHealth = BATTERY_HEALTH_OVERHEAT;
    129         else if(props->batteryTemperature < BATTERY_UNDERTEMP_THRESHOLD)
    130             props->batteryHealth = BATTERY_HEALTH_COLD;
    131     }
    132 }
    133 
    134 static void flounder_voltage_monitor_check(struct BatteryProperties *props)
    135 {
    136     unsigned int monitor_voltage = 0;
    137     int vcell_mv;
    138     char voltage[10];
    139 
    140     if (props->batteryStatus != BATTERY_STATUS_CHARGING &&
    141         props->batteryStatus != BATTERY_STATUS_FULL && props->batteryLevel > 0) {
    142         vcell_mv = props->batteryVoltage;
    143         if (vcell_mv > BATTERY_CRITICAL_LOW_MV)
    144             monitor_voltage = BATTERY_CRITICAL_LOW_MV;
    145         else if (vcell_mv > BATTERY_DEAD_MV)
    146             monitor_voltage = BATTERY_DEAD_MV;
    147     }
    148 
    149     if (monitor_voltage != flounder_monitor_voltage) {
    150         snprintf(voltage, sizeof(voltage), "%d", monitor_voltage);
    151         write_sysfs(VOLTAGE_MONITOR_PATH, voltage);
    152         flounder_monitor_voltage = monitor_voltage;
    153     }
    154 }
    155 
    156 static void flounder_soc_adjust(struct BatteryProperties *props)
    157 {
    158     int soc_decrease;
    159     int soc, vcell_mv;
    160     struct sysinfo info;
    161     long uptime = 0;
    162     int ret;
    163 
    164     ret = sysinfo(&info);
    165     if (ret) {
    166        KLOG_ERROR(LOG_TAG, "Fail to get sysinfo!!\n");
    167        uptime = last_update_time;
    168     } else
    169        uptime = info.uptime;
    170 
    171     if (!first_update_done) {
    172         if (props->batteryLevel >= BATTERY_FULL) {
    173             props->batteryLevel = BATTERY_FULL - 1;
    174             lasttime_soc = BATTERY_FULL - 1;
    175         } else {
    176             lasttime_soc = props->batteryLevel;
    177         }
    178         last_update_time = uptime;
    179         first_update_done = true;
    180     }
    181 
    182     if (props->batteryStatus == BATTERY_STATUS_FULL)
    183         soc = BATTERY_FULL;
    184     else if (props->batteryLevel >= BATTERY_FULL &&
    185              lasttime_soc < BATTERY_FULL)
    186         soc = BATTERY_FULL - 1;
    187     else
    188         soc = props->batteryLevel;
    189 
    190     if (props->batteryLevel > BATTERY_FULL)
    191         props->batteryLevel = BATTERY_FULL;
    192     else if (props->batteryLevel < 0)
    193         props->batteryLevel = 0;
    194 
    195     vcell_mv = props->batteryVoltage;
    196     if (props->batteryStatus == BATTERY_STATUS_DISCHARGING ||
    197         props->batteryStatus == BATTERY_STATUS_NOT_CHARGING ||
    198         props->batteryStatus == BATTERY_STATUS_UNKNOWN) {
    199         if (vcell_mv >= BATTERY_CRITICAL_LOW_MV) {
    200             force_decrease = false;
    201             soc_decrease = lasttime_soc - soc;
    202             if (soc_decrease < 0) {
    203                 soc = lasttime_soc;
    204                 goto done;
    205             }
    206 
    207             if (uptime > last_update_time &&
    208                 uptime - last_update_time <= UPDATE_PERIOD_MINIMUM_S) {
    209                 soc = lasttime_soc;
    210                 goto done;
    211             }
    212 
    213             if (soc_decrease < 0)
    214                 soc_decrease = 0;
    215             else if (soc_decrease > NORMAL_MAX_SOC_DEC)
    216                 soc_decrease = NORMAL_MAX_SOC_DEC;
    217 
    218             soc = lasttime_soc - soc_decrease;
    219         } else if (vcell_mv < BATTERY_DEAD_MV) {
    220             soc = 0;
    221         } else {
    222             if (force_decrease &&
    223                 uptime > last_update_time &&
    224                 uptime - last_update_time <= UPDATE_PERIOD_MINIMUM_S) {
    225                 soc = lasttime_soc;
    226                 goto done;
    227             }
    228 
    229             soc_decrease = CRITICAL_LOW_FORCE_SOC_DROP;
    230             if (lasttime_soc <= soc_decrease)
    231                soc = 0;
    232             else
    233                 soc = lasttime_soc - soc_decrease;
    234             force_decrease = true;
    235         }
    236     } else {
    237         force_decrease = false;
    238         if (soc > lasttime_soc)
    239             soc = lasttime_soc + 1;
    240     }
    241     last_update_time = uptime;
    242 done:
    243     props->batteryLevel = lasttime_soc = soc;
    244 }
    245 
    246 static void flounder_bat_monitor(struct BatteryProperties *props)
    247 {
    248     flounder_soc_adjust(props);
    249     flounder_health_check(props);
    250     flounder_status_check(props);
    251     flounder_voltage_monitor_check(props);
    252 }
    253 
    254 int healthd_board_battery_update(struct BatteryProperties *props)
    255 {
    256 
    257     flounder_bat_monitor(props);
    258 
    259     // return 0 to log periodic polled battery status to kernel log
    260     return 0;
    261 }
    262 
    263 static int flounder_energy_counter(int64_t *energy)
    264 {
    265     *energy = get_int64_field(CHARGE_COUNTER_EXT_PATH) * VOLTAGE_NOMINAL;
    266     return 0;
    267 }
    268 
    269 void healthd_board_init(struct healthd_config *config)
    270 {
    271     config->energyCounter = flounder_energy_counter;
    272 }
    273