C++程序  |  287行  |  10.42 KB

/*
 * Copyright (C) 2017 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.
 */

#include <cctype>
#include <cerrno>
#include <cinttypes>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <tuple>
#include <unordered_map>
#include <vector>

#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/stringprintf.h>

#include "sensors.h"
#include "thermal-helper.h"

namespace android {
namespace hardware {
namespace thermal {
namespace V1_1 {
namespace implementation {

constexpr const char kThermalSensorsRoot[] = "/sys/class/thermal";
constexpr char kThermalZoneDirSuffix[] = "thermal_zone";
constexpr char kSensorTypeFileSuffix[] = "type";
constexpr char kTemperatureFileSuffix[] = "temp";
// This is a golden set of thermal sensor names, their types, and their
// multiplier. Used when we read in sensor values. The tuple value stored is
// formatted as such:
// <temperature type, multiplier value for reading temp>
const std::unordered_map<std::string, std::tuple<TemperatureType, float>>
kValidThermalSensorsMap = {
    {"tsens_tz_sensor1", {TemperatureType::CPU, 0.1}},   // CPU0
    {"tsens_tz_sensor2", {TemperatureType::CPU, 0.1}},   // CPU1
    {"tsens_tz_sensor4", {TemperatureType::CPU, 0.1}},   // CPU2
    {"tsens_tz_sensor3", {TemperatureType::CPU, 0.1}},   // CPU3
    {"tsens_tz_sensor7", {TemperatureType::CPU, 0.1}},   // CPU4
    {"tsens_tz_sensor8", {TemperatureType::CPU, 0.1}},   // CPU5
    {"tsens_tz_sensor9", {TemperatureType::CPU, 0.1}},   // CPU6
    {"tsens_tz_sensor10", {TemperatureType::CPU, 0.1}},  // CPU7
    // GPU thermal sensor.
    {"tsens_tz_sensor13", {TemperatureType::GPU, 0.1}},
    // Battery thermal sensor.
    {"battery", {TemperatureType::BATTERY, 0.001}},
    // Skin thermal sensors. We use back_therm for walleye. For taimen we use
    // bd_therm and bd_therm2.
    {"back_therm", {TemperatureType::SKIN, 1.}},
    {"bd_therm", {TemperatureType::SKIN, 1.}},
    {"bd_therm2", {TemperatureType::SKIN, 1.}},
    // USBC thermal sensor.
    {"usb_port_temp", {TemperatureType::UNKNOWN, 0.1}},
};

namespace {

using ::android::hardware::thermal::V1_0::TemperatureType;

static std::string gSkinSensorType;
static unsigned int gSkinThrottlingThreshold;
static unsigned int gSkinShutdownThreshold;
static unsigned int gVrThrottledBelowMin;
Sensors gSensors;

// A map containing hardcoded thresholds per sensor type.  Its not const
// because initThermal() will modify the skin sensor thresholds depending on the
// hardware type. The tuple is formatted as follows:
// <throttling threshold, shutdown threshold, vr threshold>
std::unordered_map<TemperatureType, std::tuple<float, float, float>>
gSensorTypeToThresholdsMap = {
    {TemperatureType::CPU, {kCpuThrottlingThreshold, kCpuShutdownThreshold,
                             kCpuThrottlingThreshold}},
    {TemperatureType::GPU, {NAN, NAN, NAN}},
    {TemperatureType::BATTERY, {NAN, kBatteryShutdownThreshold, NAN}},
    {TemperatureType::SKIN, {NAN, NAN, NAN}},
    {TemperatureType::UNKNOWN, {NAN, NAN, NAN}}
};

bool initializeSensors() {
    auto thermal_zone_dir = std::unique_ptr<DIR, int (*)(DIR*)>(
        opendir(kThermalSensorsRoot), closedir);
    struct dirent* dp;
    size_t num_thermal_zones = 0;
    while ((dp = readdir(thermal_zone_dir.get())) != nullptr) {
        std::string dir_name(dp->d_name);
        if (dir_name.find(kThermalZoneDirSuffix) != std::string::npos) {
            ++num_thermal_zones;
        }
    }

    for (size_t sensor_zone_num = 0; sensor_zone_num < num_thermal_zones;
            ++sensor_zone_num) {
        std::string path = android::base::StringPrintf("%s/%s%zu",
                                                       kThermalSensorsRoot,
                                                       kThermalZoneDirSuffix,
                                                       sensor_zone_num);
        std::string sensor_name;
        if (android::base::ReadFileToString(
                path + "/" + kSensorTypeFileSuffix, &sensor_name)) {
            sensor_name = android::base::Trim(sensor_name);
            if (kValidThermalSensorsMap.find(sensor_name) !=
                kValidThermalSensorsMap.end()) {
                  TemperatureType type = std::get<0>(
                      kValidThermalSensorsMap.at(sensor_name));
                  auto thresholds = gSensorTypeToThresholdsMap.at(type);
                  if (!gSensors.addSensor(
                          sensor_name, path + "/" + kTemperatureFileSuffix,
                          std::get<0>(thresholds), std::get<1>(thresholds),
                          std::get<2>(thresholds), type)) {
                        LOG(ERROR) << "Could not add " << sensor_name
                                   << "to sensors map";
                  }
            }
        }
    }
    return (gSensors.getNumSensors() == kTemperatureNum);
}

}  // namespace

/**
 * Initialization constants based on platform
 *
 * @return true on success or false on error.
 */
bool initThermal() {
    std::string hardware = android::base::GetProperty("ro.hardware", "");
    if (hardware == "walleye") {
        LOG(ERROR) << "Initialization on Walleye";
        gSkinThrottlingThreshold = kWalleyeSkinThrottlingThreshold;
        gSkinShutdownThreshold = kWalleyeSkinShutdownThreshold;
        gVrThrottledBelowMin = kWalleyeVrThrottledBelowMin;
    } else if (hardware == "taimen") {
        std::string rev = android::base::GetProperty("ro.revision", "");
        if (rev == "rev_a" || rev == "rev_b") {
            LOG(ERROR) << "Initialization on Taimen pre revision C";
            gSkinThrottlingThreshold = kTaimenRabSkinThrottlingThreshold;
            gSkinShutdownThreshold = kTaimenRabSkinShutdownThreshold;
            gVrThrottledBelowMin = kTaimenRabVrThrottledBelowMin;
        } else {
            LOG(ERROR) << "Initialization on Taimen revision C and later";
            gSkinThrottlingThreshold = kTaimenRcSkinThrottlingThreshold;
            gSkinShutdownThreshold = kTaimenRcSkinShutdownThreshold;
            gVrThrottledBelowMin = kTaimenRcVrThrottledBelowMin;
        }
    } else {
        LOG(ERROR) << "Unsupported hardware: " << hardware;
        return false;
    }
    gSensorTypeToThresholdsMap[TemperatureType::SKIN] =
        std::make_tuple(gSkinThrottlingThreshold, gSkinShutdownThreshold,
                        gVrThrottledBelowMin);
    return initializeSensors();
}

ssize_t fillTemperatures(hidl_vec<Temperature>* temperatures) {
    temperatures->resize(gSensors.getNumSensors());
    ssize_t current_index = 0;
    for (const auto& name_type_mult_pair : kValidThermalSensorsMap) {
        Temperature temp;
        if (gSensors.readTemperature(name_type_mult_pair.first,
                                     std::get<1>(name_type_mult_pair.second),
                                     &temp)) {
            (*temperatures)[current_index] = temp;
            ++current_index;
        }
    }
    return current_index;
}

ssize_t fillCpuUsages(hidl_vec<CpuUsage> *cpuUsages) {
    int vals, cpu_num, online;
    ssize_t read;
    uint64_t user, nice, system, idle, active, total;
    char *line = NULL;
    size_t len = 0;
    size_t size = 0;
    char file_name[PATH_MAX];
    FILE *file;
    FILE *cpu_file;

    if (cpuUsages == NULL || cpuUsages->size() < kCpuNum ) {
        LOG(ERROR) << "fillCpuUsages: incorrect buffer";
        return -EINVAL;
    }

    file = fopen(kCpuUsageFile, "r");
    if (file == NULL) {
        PLOG(ERROR) << "fillCpuUsages: failed to open file (" << kCpuUsageFile << ")";
        return -errno;
    }

    while ((read = getline(&line, &len, file)) != -1) {
        // Skip non "cpu[0-9]" lines.
        if (strnlen(line, read) < 4 || strncmp(line, "cpu", 3) != 0 || !isdigit(line[3])) {
            free(line);
            line = NULL;
            len = 0;
            continue;
        }

        vals = sscanf(line, "cpu%d %" SCNu64 " %" SCNu64 " %" SCNu64 " %" SCNu64, &cpu_num, &user,
                &nice, &system, &idle);

        free(line);
        line = NULL;
        len = 0;

        if (vals != 5 || size == kCpuNum) {
            if (vals != 5) {
                PLOG(ERROR) << "fillCpuUsages: failed to read CPU information from file ("
                            << kCpuUsageFile << ")";
            } else {
                PLOG(ERROR) << "fillCpuUsages: file has incorrect format ("
                            << kCpuUsageFile << ")";
            }
            fclose(file);
            return errno ? -errno : -EIO;
        }

        active = user + nice + system;
        total = active + idle;

        // Read online CPU information.
        snprintf(file_name, PATH_MAX, kCpuOnlineFileFormat, cpu_num);
        cpu_file = fopen(file_name, "r");
        online = 0;
        if (cpu_file == NULL) {
            PLOG(ERROR) << "fillCpuUsages: failed to open file (" << file_name << ")";
            fclose(file);
            return -errno;
        }
        if (1 != fscanf(cpu_file, "%d", &online)) {
            PLOG(ERROR) << "fillCpuUsages: failed to read CPU online information from file ("
                        << file_name << ")";
            fclose(file);
            fclose(cpu_file);
            return errno ? -errno : -EIO;
        }
        fclose(cpu_file);

        (*cpuUsages)[size].name = kCpuLabel[size];
        (*cpuUsages)[size].active = active;
        (*cpuUsages)[size].total = total;
        (*cpuUsages)[size].isOnline = static_cast<bool>(online);

        LOG(DEBUG) << "fillCpuUsages: "<< kCpuLabel[size] << ": "
                   << active << " " << total << " " <<  online;
        size++;
    }
    fclose(file);

    if (size != kCpuNum) {
        PLOG(ERROR) << "fillCpuUsages: file has incorrect format (" << kCpuUsageFile << ")";
        return -EIO;
    }
    return kCpuNum;
}

std::string getTargetSkinSensorType() {
    return gSkinSensorType;
}

}  // namespace implementation
}  // namespace V1_1
}  // namespace thermal
}  // namespace hardware
}  // namespace android