| // 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 ¶ms = 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 ¶ms = 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 |