blob: 0fb3c0e9b253cad260899520e77dc1e0e957c996 [file] [log] [blame]
// 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