| // 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/thermal_state_engine.h" |
| |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| #include "base/check.h" |
| #include "base/logging.h" |
| #include "base/strings/stringprintf.h" |
| |
| #include "thermald/thermal_zone.h" |
| |
| using std::pair; |
| using std::string; |
| using std::vector; |
| |
| namespace thermald { |
| |
| static const int kTemperatureUnknown = kTemp0K; |
| |
| static const ThermalState kInitialState = { |
| .id = 0, |
| }; |
| |
| ThermalStateEngine::ThermalStateEngine(const ThermalZone *zone) |
| : zone_(zone), current_state_(&kInitialState) { |
| DCHECK(zone); |
| } |
| |
| bool ThermalStateEngine::ProcessTemperatureUpdate(const string &thermal_point, |
| int temperature) { |
| if (!UpdateThermalPointStatus(thermal_point, temperature)) { |
| return false; |
| } |
| |
| const ThermalState *state = DetermineThermalState(); |
| if (state != current_state_) { |
| string temperatures; |
| for (auto &it : thermal_point_status_map_) { |
| if (!temperatures.empty()) { |
| temperatures += ", "; |
| } |
| temperatures += base::StringPrintf("%s = %d", it.first.c_str(), |
| it.second.temperature / 1000); |
| } |
| |
| LOG(INFO) << "[" << zone_->name << "] Thermal state changed from " |
| << current_state_->id << " to " << state->id |
| << " (" << temperatures << ")"; |
| |
| current_state_ = state; |
| InitNewThermalState(); |
| } |
| |
| return true; |
| } |
| |
| ThermalPointStatus *ThermalStateEngine::GetThermalPointStatus( |
| const string &thermal_point) { |
| auto it = thermal_point_status_map_.find(thermal_point); |
| if (it != thermal_point_status_map_.end()) { |
| return &it->second; |
| } |
| |
| ThermalPointStatus thermal_point_status; |
| thermal_point_status.temperature = kTemperatureUnknown; |
| thermal_point_status.threshold_cleared = true; |
| |
| thermal_point_status_map_.insert( |
| make_pair(thermal_point, thermal_point_status)); |
| it = thermal_point_status_map_.find(thermal_point); |
| |
| return &it->second; |
| } |
| |
| bool ThermalStateEngine::UpdateThermalPointStatus(const string &thermal_point, |
| int temperature) { |
| ThermalPointStatus *thermal_point_status = |
| GetThermalPointStatus(thermal_point); |
| thermal_point_status->temperature = temperature; |
| |
| if (current_state_ != &kInitialState) { |
| ThermalPointThresholds thresholds; |
| if (!GetThermalPointThresholds(*current_state_, thermal_point, |
| &thresholds)) { |
| return false; |
| } |
| |
| if (thermal_point_status->threshold_cleared) { |
| if (temperature >= thresholds.activation) { |
| thermal_point_status->threshold_cleared = false; |
| } |
| } else { |
| if (temperature <= thresholds.bottom) { |
| thermal_point_status->threshold_cleared = true; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| const ThermalState *ThermalStateEngine::DetermineThermalState() { |
| for (auto &state : zone_->states) { |
| // Find the first state with a surpassed (>=) temperature threshold. |
| for (auto &it : thermal_point_status_map_) { |
| const auto &thermal_point = it.first; |
| const ThermalPointStatus &thermal_point_status = it.second; |
| |
| ThermalPointThresholds thresholds; |
| if (!GetThermalPointThresholds(*state, thermal_point, &thresholds)) { |
| return nullptr; |
| } |
| |
| if (thermal_point_status.temperature >= thresholds.activation) { |
| return state.get(); |
| } |
| |
| if (state.get() == current_state_) { |
| if (!thermal_point_status.threshold_cleared && |
| (thermal_point_status.temperature > thresholds.bottom)) { |
| // Block transition to a less critical ("cooler") state until the |
| // bottom thresholds of all thermal points were reached. |
| return current_state_; |
| } |
| } |
| } |
| } |
| |
| LOG(ERROR) << "[" << zone_->name << "] No applicable thermal state found!!!"; |
| |
| return nullptr; |
| } |
| |
| bool ThermalStateEngine::GetThermalPointThresholds( |
| const ThermalState &state, const string &thermal_point, |
| ThermalPointThresholds *thresholds) const { |
| auto it = state.thresholds.find(thermal_point); |
| if (it == state.thresholds.end()) { |
| LOG(ERROR) << "[" << zone_->name << "] Thermal state " << state.id |
| << " has no thresholds for thermal point '" << thermal_point |
| << "'"; |
| return false; |
| } |
| |
| *thresholds = it->second; |
| |
| return true; |
| } |
| |
| void ThermalStateEngine::InitNewThermalState() { |
| // Initialize the threshold_cleared flag for each thermal point. The flag is |
| // set if the current temperature of the thermal point is below the activation |
| // threshold and cleared otherwise. |
| for (auto &it : thermal_point_status_map_) { |
| ThermalPointThresholds thresholds; |
| if (!GetThermalPointThresholds(*current_state_, it.first, &thresholds)) { |
| return; |
| } |
| |
| ThermalPointStatus &thermal_point_status = it.second; |
| if (thermal_point_status.temperature >= thresholds.activation) { |
| thermal_point_status.threshold_cleared = false; |
| } else { |
| thermal_point_status.threshold_cleared = true; |
| } |
| } |
| } |
| |
| } // namespace thermald |