blob: 6f238dedfc4043e05511c82b7c643c795dee0afe [file] [log] [blame]
// Copyright 2015 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "thermald/config_parser.h"
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "thermald/configuration.h"
#include "thermald/thermal_zone.h"
using std::make_pair;
using std::string;
using std::unique_ptr;
namespace thermald {
static const int kNoThreshold = -1;
Configuration *ConfigParser::ParseFile(
const base::FilePath &config_file) {
string data;
if (!base::ReadFileToString(config_file, &data)) {
return nullptr;
}
base::Optional<base::Value> root_node(
base::JSONReader::Read(data, base::JSON_ALLOW_TRAILING_COMMAS));
if (!root_node) {
LOG(ERROR) << "Configuration error: Configuration file is invalid";
return nullptr;
}
ConfigParser parser;
return parser.ParseConfiguration(root_node.value());
}
Configuration *ConfigParser::ParseConfiguration(const base::Value &root_node) {
if (!root_node.is_dict()) {
LOG(ERROR) << "Configuration error: Configuration file is invalid";
return nullptr;
}
unique_ptr<Configuration> cfg(new Configuration);
cfg_ = cfg.get();
const base::Value *sensors = root_node.FindListKey("sensors");
if (!sensors) {
LOG(ERROR) << "Configuration error: Sensor configuration missing";
return nullptr;
}
if (!ProcessSensorConfig(*sensors)) {
return nullptr;
}
const base::Value *thermal_zones = root_node.FindListKey("thermal_zones");
if (!thermal_zones) {
LOG(ERROR) << "Configuration error: Thermal zone configuration missing";
return nullptr;
}
if (!ProcessThermalZoneConfig(*thermal_zones)) {
return nullptr;
}
// If uma_metric_prefix section is not present in thermal configuration,
// then default UMA metric prefix will be "Platform".
cfg_->uma_metric_prefix = "Platform";
const base::Value *uma_metric_prefix =
root_node.FindListKey("uma_metric_prefix");
if (uma_metric_prefix) {
if (!ProcessUmaMetricPrefixConfig(*uma_metric_prefix)) {
return nullptr;
}
}
return cfg.release();
}
bool ConfigParser::ProcessOneThermalState(ThermalZoneCfg *zone_cfg,
const base::Value &state_node,
int index_state) {
unique_ptr<ThermalStateCfg> state_cfg(new ThermalStateCfg);
const auto &state_list = state_node.GetList();
if ((state_list.size() != 3) || !state_list[0].is_int() ||
!state_list[1].is_list() || !state_list[2].is_dict()) {
LOG(ERROR) << "Configuration error: State " << index_state
<< " in thermal zone '" << zone_cfg->name << "' is invalid";
return false;
}
state_cfg->id = state_list[0].GetInt();
const auto &threshold_list = state_list[1].GetList();
const size_t num_thresholds = threshold_list.size();
if (((index_state == 0) && (num_thresholds != 0)) ||
((index_state != 0) && (num_thresholds != zone_cfg->sensors.size()))) {
LOG(ERROR) << "Configuration error: Threshold configuration of state "
<< state_cfg->id << " in thermal zone '" << zone_cfg->name
<< "' is invalid";
return false;
}
if (index_state != 0) {
size_t i = 0;
for (const auto &thresholds_cfg : threshold_list) {
if (!thresholds_cfg.is_list()) {
LOG(ERROR) << "Configuration error: Threshold configuration of state "
<< state_cfg->id << " in thermal zone '" << zone_cfg->name
<< "' is invalid";
return false;
}
const auto &thresholds_cfg_list = thresholds_cfg.GetList();
if ((thresholds_cfg_list.size() != 2) ||
!thresholds_cfg_list[0].is_int() ||
!thresholds_cfg_list[1].is_int()) {
LOG(ERROR) << "Configuration error: Threshold configuration of state "
<< state_cfg->id << " in thermal zone '" << zone_cfg->name
<< "' is invalid";
return false;
}
ThermalPointThresholds thresholds;
thresholds.activation = thresholds_cfg_list[0].GetInt();
thresholds.bottom = thresholds_cfg_list[1].GetInt();
if ((thresholds.activation != kNoThreshold) &&
(thresholds.bottom != kNoThreshold)) {
thresholds.activation *= 1000;
thresholds.bottom *= 1000;
if ((thresholds.activation < thresholds_prev_state_[i].activation) ||
(thresholds.bottom < thresholds_prev_state_[i].bottom)) {
LOG(ERROR) << "Configuration error: Configuration of state "
<< state_cfg->id << " in thermal zone '"
<< zone_cfg->name << "' is invalid. Thermal states must be"
<< " ordered by ascending temperature";
return false;
}
thresholds_prev_state_[i] = thresholds;
} else {
// Sensor doesn't have thresholds in this thermal state.
thresholds.activation = kTempInfinite;
thresholds.bottom = kTemp0K;
}
if (thresholds.bottom >= thresholds.activation) {
LOG(ERROR) << "Configuration error: Threshold configuration of state "
<< state_cfg->id << " in thermal zone '" << zone_cfg->name
<< "' is invalid. Bottom treshold must be lower than"
<< " activation threshold";
return false;
}
state_cfg->thresholds.insert(make_pair(zone_cfg->sensors[i], thresholds));
i++;
}
} else {
// Set dummy thresholds for first state.
thresholds_prev_state_.clear();
for (const string &sensor : zone_cfg->sensors) {
ThermalPointThresholds thresholds;
thresholds.activation = kTemp0K;
thresholds.bottom = kTemp0K;
state_cfg->thresholds.insert(make_pair(sensor, thresholds));
thresholds_prev_state_.push_back(thresholds);
}
}
for (const auto &kv : state_list[2].DictItems()) {
auto value = kv.second.GetIfInt();
if (!value.has_value()) {
LOG(ERROR) << "Configuration error: Thermal output configuration "
<< "of state " << state_cfg->id << " in thermal zone '"
<< zone_cfg->name << "' is invalid";
return false;
}
ThermalStateOutput output = {kv.first, *value};
state_cfg->outputs.push_back(output);
}
zone_cfg->states.push_back(std::move(state_cfg));
return true;
}
bool ConfigParser::ProcessOneThermalZone(const base::Value &zone_node,
int index_zone) {
unique_ptr<ThermalZoneCfg> zone_cfg(new ThermalZoneCfg);
const auto &zone_list = zone_node.GetList();
if ((zone_list.size() != 3) || !zone_list[0].is_string() ||
!zone_list[1].is_list() || !zone_list[2].is_list()) {
LOG(ERROR) << "Configuration error: Configuration of thermal zone "
<< index_zone << " is invalid";
return false;
}
zone_cfg->name = zone_list[0].GetString();
const auto &sensors = zone_list[1].GetList();
const auto &thermal_states = zone_list[2].GetList();
const size_t num_sensors = sensors.size();
if (num_sensors == 0) {
LOG(ERROR) << "Configuration error: Thermal zone '" << zone_cfg->name
<< "' has no sensors";
return false;
}
for (const auto &sensor : sensors) {
if (!sensor.is_string()) {
LOG(ERROR) << "Configuration error: Sensor configuration of << "
<< "thermal zone '" << zone_cfg->name << "' is invalid";
return false;
}
const string& sensor_name = sensor.GetString();
if (cfg_->sensors.find(sensor_name) == cfg_->sensors.end()) {
LOG(ERROR) << "Configuration error: Thermal zone '" << zone_cfg->name
<< "' uses unknown sensor '" << sensor_name << "'";
return false;
}
zone_cfg->sensors.push_back(sensor_name);
}
const size_t num_states = thermal_states.size();
if (num_states < 2) {
LOG(ERROR) << "Configuration error: Not enough thermal states in "
<< "thermal zone '" << zone_cfg->name << "'";
return false;
}
size_t i = 0;
for (const auto &state : thermal_states) {
if (!state.is_list()) {
LOG(ERROR) << "Configuration error: State " << i << " in thermal zone '"
<< zone_cfg->name << "' is invalid";
return false;
}
if (!ProcessOneThermalState(zone_cfg.get(), state, i)) {
return false;
}
i++;
}
cfg_->zones.insert(make_pair(zone_cfg->name, std::move(zone_cfg)));
return true;
}
bool ConfigParser::ProcessThermalZoneConfig(const base::Value &thermal_zones) {
size_t i = 0;
for (const auto &zone_node : thermal_zones.GetList()) {
if (!zone_node.is_list()) {
LOG(ERROR) << "Configuration error: Configuration of thermal zone "
<< i << " is invalid";
return false;
}
if (!ProcessOneThermalZone(zone_node, i)) {
return false;
}
i++;
}
return true;
}
bool ConfigParser::ProcessOneSensor(const base::Value &sensor_node,
int index_sensor) {
unique_ptr<SensorCfg> sensor_cfg(new SensorCfg);
const auto &sensor_node_list = sensor_node.GetList();
if ((sensor_node_list.size() != 4) || !sensor_node_list[0].is_string() ||
!sensor_node_list[1].is_string() || !sensor_node_list[2].is_list() ||
!sensor_node_list[3].is_int()) {
LOG(ERROR) << "Configuration error: Configuration of sensor "
<< index_sensor << " is invalid";
return false;
}
sensor_cfg->name = sensor_node_list[0].GetString();
const string& sensor_type = sensor_node_list[1].GetString();
const auto &params = sensor_node_list[2].GetList();
sensor_cfg->sampling_period = sensor_node_list[3].GetInt();
if (cfg_->sensors.find(sensor_cfg->name) != cfg_->sensors.end()) {
LOG(ERROR) << "Configuration error: Sensor '" << sensor_cfg->name
<< "' is defined more than once";
return false;
}
if (sensor_type == "ath10k") {
if ((params.size() != 1) || !params[0].is_string()) {
LOG(ERROR) << "Configuration error: Sensor '" << sensor_cfg->name
<< "' has invalid parameters";
return false;
}
const string& wlan_interface = params[0].GetString();
base::strlcpy(sensor_cfg->ath10k_sensor.wlan_interface,
wlan_interface.c_str(),
sizeof(sensor_cfg->ath10k_sensor.wlan_interface));
sensor_cfg->type = kAth10kSensor;
} else if (sensor_type == "hwmon") {
if ((params.size() != 2) || !params[0].is_int() || !params[1].is_int()) {
LOG(ERROR) << "Configuration error: Sensor '" << sensor_cfg->name
<< "' has invalid parameters";
return false;
}
sensor_cfg->hwmon_sensor.chip_id = params[0].GetInt();
sensor_cfg->hwmon_sensor.sensor_id = params[1].GetInt();
sensor_cfg->type = kHwmonSensor;
} else if (sensor_type == "thermal_zone") {
if ((params.size() != 1) || !params[0].is_int()) {
LOG(ERROR) << "Configuration error: Sensor '" << sensor_cfg->name
<< "' has invalid parameters";
return false;
}
sensor_cfg->thermal_zone_sensor.zone_id = params[0].GetInt();
sensor_cfg->type = kThermalZoneSensor;
} else if (sensor_type == "iio") {
if ((params.size() != 2) || !params[0].is_string() ||
!params[1].is_string()) {
LOG(ERROR) << "Configuration error: Sensor '" << sensor_cfg->name
<< "' has invalid parameters";
return false;
}
base::strlcpy(sensor_cfg->iio_sensor.iio_device_name,
params[0].GetString().c_str(),
sizeof(sensor_cfg->iio_sensor.iio_device_name));
base::strlcpy(sensor_cfg->iio_sensor.iio_sensor_name,
params[1].GetString().c_str(),
sizeof(sensor_cfg->iio_sensor.iio_sensor_name));
sensor_cfg->type = kIioSensor;
} else if (sensor_type == "fake") {
if ((params.size() != 1) || !params[0].is_string()) {
LOG(ERROR) << "Configuration error: Sensor '" << sensor_cfg->name
<< "' has invalid parameters";
return false;
}
// This might cause a memory leak, but as this is a fake sensor it is
// preferrable over reserving memory for the path in SensorCfg
sensor_cfg->fake_sensor.fake_data_file =
strdup(params[0].GetString().c_str());
sensor_cfg->type = kFakeSensor;
} else {
LOG(ERROR) << "Configuration error: Invalid sensor type '"
<< sensor_type << "'";
return false;
}
cfg_->sensors.insert(make_pair(sensor_cfg->name, std::move(sensor_cfg)));
return true;
}
bool ConfigParser::ProcessSensorConfig(const base::Value &sensors) {
size_t i = 0;
for (const auto &sensor_node : sensors.GetList()) {
if (!sensor_node.is_list()) {
LOG(ERROR) << "Configuration error: Configuration of sensor "
<< i << " is invalid";
return false;
}
if (!ProcessOneSensor(sensor_node, i)) {
return false;
}
i++;
}
return true;
}
bool ConfigParser::ProcessUmaMetricPrefixConfig(
const base::Value &uma_metric_prefix) {
const auto &prefix_list = uma_metric_prefix.GetList();
if (prefix_list.size() != 1) {
LOG(ERROR) << "Configuration error: Configuration of uma_metric_prefix "
<< "is invalid";
return false;
}
if (!prefix_list[0].is_list()) {
LOG(ERROR) << "Configuration error: uma_metric_prefix has"
<< " invalid parameters";
return false;
}
const auto &params = prefix_list[0].GetList();
if (!params[0].is_string()) {
LOG(ERROR) << "Configuration error: Configuration of uma_metric_prefix"
<< " is invalid";
return false;
}
cfg_->uma_metric_prefix = params[0].GetString();
return true;
}
} // namespace thermald