C++程序  |  273行  |  7.92 KB

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