| // Copyright 2014 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 <unistd.h> |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/at_exit.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/logging.h" |
| #include "base/run_loop.h" |
| #include "base/task/single_thread_task_executor.h" |
| #include "base/time/time.h" |
| #include "brillo/flag_helper.h" |
| #include "brillo/syslog_logging.h" |
| #include "metrics/metrics_library.h" |
| |
| #include "thermald/ath10k_temperature_sensor.h" |
| #include "thermald/config_parser.h" |
| #include "thermald/configuration.h" |
| #include "thermald/fake_temperature_sensor.h" |
| #include "thermald/hwmon_temperature_sensor.h" |
| #include "thermald/iio_temperature_sensor.h" |
| #include "thermald/key_value_publisher.h" |
| #include "thermald/metrics_reporter.h" |
| #include "thermald/temperature_sensor_interface.h" |
| #include "thermald/temperature_sensor_monitor.h" |
| #include "thermald/thermal_output_processor.h" |
| #include "thermald/thermal_zone.h" |
| #include "thermald/thermal_zone_controller.h" |
| #include "thermald/thermal_zone_temperature_sensor.h" |
| |
| using std::make_pair; |
| using std::map; |
| using std::string; |
| using std::unique_ptr; |
| using std::vector; |
| |
| namespace thermald { |
| |
| static const int kMetricsReportingPeriodInSeconds = 5; |
| |
| struct ThermaldContext { |
| unique_ptr<Configuration> cfg; |
| std::vector<std::unique_ptr<TemperatureSensorInterface>> temperature_sensors; |
| map<string, unique_ptr<TemperatureMonitorInterface>> temperature_monitors; |
| KeyValuePublisher output_publisher; |
| std::vector<std::unique_ptr<ThermalZone>> thermal_zones; |
| std::vector<std::unique_ptr<ThermalZoneController>> thermal_zone_controllers; |
| MetricsLibrary metrics_lib; |
| unique_ptr<MetricsReporter> metrics_reporter; |
| }; |
| |
| static bool CreateTemperatureMonitors(ThermaldContext *context) { |
| for (const auto &it : context->cfg->sensors) { |
| const SensorCfg &sensor_cfg = *it.second; |
| std::unique_ptr<TemperatureSensorInterface> sensor; |
| if (sensor_cfg.type == kAth10kSensor) { |
| sensor = std::make_unique<Ath10kTemperatureSensor>( |
| sensor_cfg.ath10k_sensor.wlan_interface, sensor_cfg.name); |
| } else if (sensor_cfg.type == kHwmonSensor) { |
| sensor = std::make_unique<HwmonTemperatureSensor>( |
| sensor_cfg.hwmon_sensor.chip_id, sensor_cfg.hwmon_sensor.sensor_id, |
| sensor_cfg.name); |
| } else if (sensor_cfg.type == kThermalZoneSensor) { |
| sensor = std::make_unique<ThermalZoneTemperatureSensor>( |
| sensor_cfg.thermal_zone_sensor.zone_id, sensor_cfg.name); |
| } else if (sensor_cfg.type == kIioSensor) { |
| sensor = IioTemperatureSensor::Get(sensor_cfg.name, |
| sensor_cfg.iio_sensor.iio_device_name, |
| sensor_cfg.iio_sensor.iio_sensor_name); |
| if (sensor == NULL) { |
| return false; |
| } |
| } else if (sensor_cfg.type == kFakeSensor) { |
| sensor = std::make_unique<FakeTemperatureSensor>( |
| base::FilePath(sensor_cfg.fake_sensor.fake_data_file), |
| sensor_cfg.name); |
| } else { |
| LOG(ERROR) << "Unknown sensor type " << sensor_cfg.type; |
| return false; |
| } |
| |
| auto monitor = std::make_unique<TemperatureSensorMonitor>( |
| sensor.get(), |
| base::TimeDelta::FromMilliseconds(sensor_cfg.sampling_period)); |
| |
| context->temperature_sensors.push_back(std::move(sensor)); |
| |
| context->temperature_monitors.insert( |
| make_pair(sensor_cfg.name, std::move(monitor))); |
| } |
| |
| return true; |
| } |
| |
| static bool SetupMetricsReporter(ThermaldContext *context) { |
| context->metrics_reporter.reset(new MetricsReporter( |
| context->cfg->uma_metric_prefix, |
| &context->output_publisher, &context->metrics_lib, |
| base::TimeDelta::FromSeconds(kMetricsReportingPeriodInSeconds))); |
| |
| for (auto &it : context->temperature_monitors) { |
| context->metrics_reporter->AddTemperature(it.first, it.second.get()); |
| } |
| |
| return true; |
| } |
| |
| static bool CreateThermalZones(ThermaldContext *context) { |
| for (const auto &it : context->cfg->zones) { |
| const ThermalZoneCfg &zone_cfg = *it.second; |
| |
| std::unique_ptr<ThermalZone> zone = std::make_unique<ThermalZone>(); |
| zone->name = zone_cfg.name; |
| |
| // Create states in reverse order, the thermal state engine expects the |
| // states to be ordered from higher to lower temperatures/criticality. |
| for (auto it2 = zone_cfg.states.rbegin(); it2 != zone_cfg.states.rend(); |
| it2++) { |
| const ThermalStateCfg *state_cfg = it2->get(); |
| |
| std::unique_ptr<ThermalState> state = std::make_unique<ThermalState>(); |
| state->id = state_cfg->id; |
| state->thresholds = state_cfg->thresholds; |
| state->outputs = state_cfg->outputs; |
| |
| zone->states.push_back(std::move(state)); |
| } |
| |
| context->thermal_zones.push_back(std::move(zone)); |
| } |
| |
| return true; |
| } |
| |
| static bool CreateThermalZoneControllers(ThermaldContext *context) { |
| for (auto &zone : context->thermal_zones) { |
| vector<TemperatureMonitorInterface *> temperature_monitors; |
| |
| const ThermalZoneCfg &zone_cfg = *context->cfg->zones[zone->name]; |
| for (const string &sensor : zone_cfg.sensors) { |
| temperature_monitors.push_back( |
| context->temperature_monitors[sensor].get()); |
| } |
| |
| context->thermal_zone_controllers.push_back( |
| std::make_unique<ThermalZoneController>( |
| zone.get(), &context->output_publisher, temperature_monitors)); |
| } |
| |
| return true; |
| } |
| |
| } // namespace thermald |
| |
| int main(int argc, char *argv[]) { |
| base::AtExitManager exit_manager; |
| |
| brillo::FlagHelper::Init(argc, argv, "Thermal daemon"); |
| brillo::InitLog(brillo::kLogToSyslog | brillo::kLogToStderr); |
| |
| base::SingleThreadTaskExecutor main_loop; |
| base::RunLoop run_loop; |
| thermald::ThermaldContext context; |
| |
| thermald::ThermalOutputProcessor |
| thermal_output_processor(&context.output_publisher); |
| |
| base::FilePath config_file("/etc/thermald.cfg"); |
| if (!base::PathExists(config_file)) { |
| LOG(WARNING) << "Configuration file '" << config_file.value() |
| << "' does not exist. Exiting"; |
| return 0; |
| } |
| |
| context.cfg.reset(thermald::ConfigParser::ParseFile(config_file)); |
| if (!context.cfg.get()) { |
| return -1; |
| } |
| |
| if (!thermald::CreateTemperatureMonitors(&context)) { |
| return -1; |
| } |
| |
| if (!thermald::CreateThermalZones(&context)) { |
| return -1; |
| } |
| |
| if (!thermald::CreateThermalZoneControllers(&context)) { |
| return -1; |
| } |
| |
| SetupMetricsReporter(&context); |
| |
| for (auto &controller : context.thermal_zone_controllers) { |
| controller->Start(); |
| } |
| |
| context.metrics_reporter->Start(); |
| |
| run_loop.Run(); |
| } |