blob: 7846c97170cda0b4de266be5dc48eef1a204705e [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 "src/media/audio/audio_core/thermal_agent.h"
#include <set>
#include "src/lib/syslog/cpp/logger.h"
#include "src/media/audio/audio_core/audio_device_manager.h"
namespace media::audio {
namespace {
std::vector<std::string> ConfigsByState(const std::vector<ThermalConfig::State>& config_states,
const std::string& nominal_config,
const std::vector<uint32_t>& trip_points) {
std::vector<std::string> result;
// The number of states is one greater than the number of trip points.
result.reserve(trip_points.size() + 1);
// Push the nominal config as the config for state 0.
result.push_back(nominal_config);
// Push the rest of the state configs. Configured states correspond to one or more merged states.
auto config_state_iter = config_states.begin();
for (auto trip_point : trip_points) {
// If there are no more config states, just copy the previous config.
if (config_state_iter != config_states.end()) {
auto& config_state = *config_state_iter;
// If we haven't hit the trip point of the current config state, just copy the previous
// config.
if (config_state.trip_point() == trip_point) {
// We've reached the trip point of the current config state. Use its config.
result.push_back(config_state.config());
++config_state_iter;
continue;
}
}
result.push_back(result.back());
}
return result;
}
} // namespace
// static
std::unique_ptr<ThermalAgent> ThermalAgent::CreateAndServe(Context* context) {
auto& thermal_config = context->process_config().thermal_config();
if (thermal_config.entries().empty()) {
// No thermal config so we don't start the thermal agent.
return nullptr;
}
return std::make_unique<ThermalAgent>(
context->component_context().svc()->Connect<fuchsia::thermal::Controller>(), thermal_config,
context->process_config().device_config(),
[context](const std::string& target_name, const std::string& config) {
context->device_manager().SetEffectConfig(target_name, config);
});
}
ThermalAgent::ThermalAgent(fuchsia::thermal::ControllerPtr thermal_controller,
const ThermalConfig& thermal_config, const DeviceConfig& device_config,
SetConfigCallback set_config_callback)
: thermal_controller_(std::move(thermal_controller)),
binding_(this),
set_config_callback_(std::move(set_config_callback)) {
FX_DCHECK(thermal_controller_);
FX_DCHECK(set_config_callback_);
if (thermal_config.entries().empty()) {
// No thermal config. Nothing to do.
thermal_controller_ = nullptr;
return;
}
std::set<uint32_t> trip_points_set;
for (auto& entry : thermal_config.entries()) {
for (auto& state : entry.states()) {
trip_points_set.insert(state.trip_point());
}
}
std::vector<uint32_t> trip_points(trip_points_set.begin(), trip_points_set.end());
for (auto& entry : thermal_config.entries()) {
auto nominal_config = FindNominalConfigForTarget(entry.target_name(), device_config);
if (nominal_config.has_value()) {
targets_.emplace_back(entry.target_name(),
ConfigsByState(entry.states(), nominal_config.value(), trip_points));
} else {
FX_LOGS(ERROR) << "Thermal config references unknown target '" << entry.target_name() << "'.";
}
}
thermal_controller_.set_error_handler([this](zx_status_t status) {
FX_PLOGS(ERROR, status) << "Connection to fuchsia.thermal.Controller failed: ";
thermal_controller_.set_error_handler(nullptr);
thermal_controller_.Unbind();
});
thermal_controller_->Subscribe(
binding_.NewBinding(), fuchsia::thermal::ActorType::AUDIO, std::move(trip_points),
[this](fuchsia::thermal::Controller_Subscribe_Result result) {
if (result.is_err()) {
FX_CHECK(result.err() != fuchsia::thermal::Error::INVALID_ARGUMENTS);
FX_LOGS(ERROR) << "fuchsia.thermal.Controller/Subscribe failed";
}
thermal_controller_.set_error_handler(nullptr);
thermal_controller_.Unbind();
});
}
void ThermalAgent::SetThermalState(uint32_t state, SetThermalStateCallback callback) {
if (current_state_ != state) {
for (auto& target : targets_) {
FX_CHECK(state < target.configs_by_state().size());
FX_CHECK(current_state_ < target.configs_by_state().size());
if (target.configs_by_state()[state] != target.configs_by_state()[current_state_]) {
set_config_callback_(target.name(), target.configs_by_state()[state]);
}
}
current_state_ = state;
}
callback();
}
std::optional<std::string> ThermalAgent::FindNominalConfigForTarget(
const std::string& target_name, const DeviceConfig& device_config) {
// For 'special' target names (not effect names), this method must return a string. An empty
// string is fine. The remainder of this method assumes the |target_name| references an effect.
const PipelineConfig::Effect* effect = device_config.FindEffect(target_name);
return effect ? std::optional(effect->effect_config) : std::nullopt;
}
} // namespace media::audio