blob: 1e47f6f75331ab54ee7178dbd40ab03e6574efb9 [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_value(
base::JSONReader::Read(data, base::JSON_ALLOW_TRAILING_COMMAS));
if (!root_node_value) {
LOG(ERROR) << "Configuration error: Configuration file is invalid";
return nullptr;
}
ConfigParser parser;
return parser.ParseConfiguration(&root_node_value.value());
}
Configuration *ConfigParser::ParseConfiguration(base::Value *root_node_value) {
base::DictionaryValue *root_node;
if (!root_node_value->GetAsDictionary(&root_node)) {
LOG(ERROR) << "Configuration error: Configuration file is invalid";
return nullptr;
}
unique_ptr<Configuration> cfg(new Configuration);
cfg_ = cfg.get();
base::ListValue *sensors;
if (!root_node->GetList("sensors", &sensors)) {
LOG(ERROR) << "Configuration error: Sensor configuration missing";
return nullptr;
}
if (!ProcessSensorConfig(sensors)) {
return nullptr;
}
base::ListValue *thermal_zones;
if (!root_node->GetList("thermal_zones", &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";
base::ListValue *uma_metric_prefix;
if (root_node->GetList("uma_metric_prefix", &uma_metric_prefix)) {
if (!ProcessUmaMetricPrefixConfig(uma_metric_prefix)) {
return nullptr;
}
}
return cfg.release();
}
bool ConfigParser::ProcessOneThermalState(ThermalZoneCfg *zone_cfg,
base::ListValue *state_node,
int index_state) {
unique_ptr<ThermalStateCfg> state_cfg(new ThermalStateCfg);
base::ListValue *threshold_list;
base::DictionaryValue *output_dict;
if ((state_node->GetSize() != 3) ||
!state_node->GetInteger(0, &state_cfg->id) ||
!state_node->GetList(1, &threshold_list) ||
!state_node->GetDictionary(2, &output_dict)) {
LOG(ERROR) << "Configuration error: State " << index_state
<< " in thermal zone '" << zone_cfg->name << "' is invalid";
return false;
}
const size_t num_thresholds = threshold_list->GetSize();
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) {
for (size_t i = 0; i < num_thresholds; i++) {
base::ListValue *thresholds_cfg;
if (!threshold_list->GetList(i, &thresholds_cfg)) {
LOG(ERROR) << "Configuration error: Threshold configuration of state "
<< state_cfg->id << " in thermal zone '" << zone_cfg->name
<< "' is invalid";
return false;
}
ThermalPointThresholds thresholds;
if ((thresholds_cfg->GetSize() != 2) ||
!thresholds_cfg->GetInteger(0, &thresholds.activation) ||
!thresholds_cfg->GetInteger(1, &thresholds.bottom)) {
LOG(ERROR) << "Configuration error: Threshold configuration of state "
<< state_cfg->id << " in thermal zone '" << zone_cfg->name
<< "' is invalid";
return false;
}
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));
}
} 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 (base::DictionaryValue::Iterator it(*output_dict); !it.IsAtEnd();
it.Advance()) {
int value;
if (!it.value().GetAsInteger(&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 = {it.key(), value};
state_cfg->outputs.push_back(output);
}
zone_cfg->states.push_back(std::move(state_cfg));
return true;
}
bool ConfigParser::ProcessOneThermalZone(base::ListValue *zone_node,
int index_zone) {
unique_ptr<ThermalZoneCfg> zone_cfg(new ThermalZoneCfg);
base::ListValue *sensors;
base::ListValue *thermal_states;
if ((zone_node->GetSize() != 3) ||
!zone_node->GetString(0, &zone_cfg->name) ||
!zone_node->GetList(1, &sensors) ||
!zone_node->GetList(2, &thermal_states)) {
LOG(ERROR) << "Configuration error: Configuration of thermal zone "
<< index_zone << " is invalid";
return false;
}
const size_t num_sensors = sensors->GetSize();
if (num_sensors == 0) {
LOG(ERROR) << "Configuration error: Thermal zone '" << zone_cfg->name
<< "' has no sensors";
return false;
}
for (size_t i = 0; i < num_sensors; i++) {
string sensor_name;
if (!sensors->GetString(i, &sensor_name)) {
LOG(ERROR) << "Configuration error: Sensor configuration of << "
<< "thermal zone '" << zone_cfg->name << "' is invalid";
return false;
}
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->GetSize();
if (num_states < 2) {
LOG(ERROR) << "Configuration error: Not enough thermal states in "
<< "thermal zone '" << zone_cfg->name << "'";
return false;
}
for (size_t i = 0; i < num_states; i++) {
base::ListValue *state_node;
if (!thermal_states->GetList(i, &state_node)) {
LOG(ERROR) << "Configuration error: State " << i << " in thermal zone '"
<< zone_cfg->name << "' is invalid";
return false;
}
if (!ProcessOneThermalState(zone_cfg.get(), state_node, i)) {
return false;
}
}
cfg_->zones.insert(make_pair(zone_cfg->name, std::move(zone_cfg)));
return true;
}
bool ConfigParser::ProcessThermalZoneConfig(base::ListValue *thermal_zones) {
for (size_t i = 0; i < thermal_zones->GetSize(); i++) {
base::ListValue *zone_node;
if (!thermal_zones->GetList(i, &zone_node)) {
LOG(ERROR) << "Configuration error: Configuration of thermal zone "
<< i << " is invalid";
return false;
}
if (!ProcessOneThermalZone(zone_node, i)) {
return false;
}
}
return true;
}
bool ConfigParser::ProcessOneSensor(base::ListValue *sensor_node,
int index_sensor) {
unique_ptr<SensorCfg> sensor_cfg(new SensorCfg);
string sensor_type;
base::ListValue *params;
if ((sensor_node->GetSize() != 4) ||
!sensor_node->GetString(0, &sensor_cfg->name) ||
!sensor_node->GetString(1, &sensor_type) ||
!sensor_node->GetList(2, &params) ||
!sensor_node->GetInteger(3, &sensor_cfg->sampling_period)) {
LOG(ERROR) << "Configuration error: Configuration of sensor "
<< index_sensor << " is invalid";
return false;
}
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") {
string wlan_interface;
if ((params->GetSize() != 1) ||
(!params->GetString(0, &wlan_interface))) {
LOG(ERROR) << "Configuration error: Sensor '" << sensor_cfg->name
<< "' has invalid parameters";
return false;
}
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->GetSize() != 2) ||
(!params->GetInteger(0, &sensor_cfg->hwmon_sensor.chip_id)) ||
(!params->GetInteger(1, &sensor_cfg->hwmon_sensor.sensor_id))) {
LOG(ERROR) << "Configuration error: Sensor '" << sensor_cfg->name
<< "' has invalid parameters";
return false;
}
sensor_cfg->type = kHwmonSensor;
} else if (sensor_type == "thermal_zone") {
if ((params->GetSize() != 1) ||
(!params->GetInteger(0, &sensor_cfg->thermal_zone_sensor.zone_id))) {
LOG(ERROR) << "Configuration error: Sensor '" << sensor_cfg->name
<< "' has invalid parameters";
return false;
}
sensor_cfg->type = kThermalZoneSensor;
} else if (sensor_type == "iio") {
string iio_device_name;
string iio_sensor_name;
if ((params->GetSize() != 2) ||
(!params->GetString(0, &iio_device_name)) ||
(!params->GetString(1, &iio_sensor_name))) {
LOG(ERROR) << "Configuration error: Sensor '" << sensor_cfg->name
<< "' has invalid parameters";
return false;
}
base::strlcpy(sensor_cfg->iio_sensor.iio_device_name,
iio_device_name.c_str(),
sizeof(sensor_cfg->iio_sensor.iio_device_name));
base::strlcpy(sensor_cfg->iio_sensor.iio_sensor_name,
iio_sensor_name.c_str(),
sizeof(sensor_cfg->iio_sensor.iio_sensor_name));
sensor_cfg->type = kIioSensor;
} else if (sensor_type == "fake") {
string fake_data_file;
if ((params->GetSize() != 1) ||
(!params->GetString(0, &fake_data_file))) {
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(fake_data_file.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(base::ListValue *sensors) {
for (size_t i = 0; i < sensors->GetSize(); i++) {
base::ListValue *sensor_node;
if (!sensors->GetList(i, &sensor_node)) {
LOG(ERROR) << "Configuration error: Configuration of sensor "
<< i << " is invalid";
return false;
}
if (!ProcessOneSensor(sensor_node, i)) {
return false;
}
}
return true;
}
bool ConfigParser::ProcessUmaMetricPrefixConfig(
base::ListValue *uma_metric_prefix) {
if(uma_metric_prefix->GetSize() != 1) {
LOG(ERROR) << "Configuration error: Configuration of uma_metric_prefix "
<< "is invalid";
return false;
}
base::ListValue *params;
if (!uma_metric_prefix->GetList(0, &params)) {
LOG(ERROR) << "Configuration error: uma_metric_prefix has"
<< " invalid parameters";
return false;
}
if (!params->GetString(0, &cfg_->uma_metric_prefix)) {
LOG(ERROR) << "Configuration error: Configuration of uma_metric_prefix"
<< " is invalid";
return false;
}
return true;
}
} // namespace thermald