| // 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, ¶ms) || |
| !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, ¶ms)) { |
| 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 |