blob: 9bd4fed8e9dd2be11b7245c5f5ba9e4e0b7a3fcb [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_output_processor.h"
#include <iterator>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/check.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_split.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "thermald/ath10k_interface.h"
#include "thermald/cpufreq_device.h"
#include "thermald/key_value_publisher.h"
using std::make_pair;
using std::map;
using std::string;
using std::unique_ptr;
using std::vector;
namespace thermald {
static const int kDeferredOpsPeriodInSeconds = 10;
ThermalOutputProcessor::ThermalOutputProcessor(KeyValuePublisher *outputs)
: weak_ptr_factory_(this) {
DCHECK(outputs);
if (!CpufreqDevice::GetDevices(&cpufreq_devices_)) {
LOG(WARNING) << "Failed to get cpufreq devices";
}
#if BASE_VER < 860220
subscription_.reset(outputs->Subscribe(base::Bind(
&ThermalOutputProcessor::OnOutputSet,
weak_ptr_factory_.GetWeakPtr())).release());
#else
subscription_ = outputs->Subscribe(base::Bind(
&ThermalOutputProcessor::OnOutputSet, weak_ptr_factory_.GetWeakPtr()));
#endif
}
ThermalOutputProcessor::Status ThermalOutputProcessor::ProcessValueAth10k(
const vector<string> &key_parts, int value) {
if (key_parts.size() != 3) {
return kInvalidKey;
}
if (key_parts[2] != "max_tx_duty_cycle") {
return kInvalidKey;
}
string name_interface(key_parts[1]);
Ath10kInterface *ath10k_if;
auto it = ath10k_interfaces_.find(name_interface);
if (it != ath10k_interfaces_.end()) {
ath10k_if = it->second.get();
} else {
if (!Ath10kInterface::IsAth10kInterface(name_interface)) {
return kInvalidKey;
}
ath10k_if = new Ath10kInterface(name_interface);
ath10k_interfaces_.insert(
make_pair(name_interface, unique_ptr<Ath10kInterface>(ath10k_if)));
}
if (!ath10k_if->SetMaxTxDutyCycle(value)) {
// To configure the duty cycle the ath10k interface needs to be enabled and
// be in AP mode. Assume that the interface hasn't been fully configured yet
// and try again later.
return kDeferred;
}
return kOk;
}
ThermalOutputProcessor::Status ThermalOutputProcessor::ProcessValueCpu(
const vector<string> &key_parts, int value) {
if (key_parts.size() != 2) {
return kInvalidKey;
}
if (key_parts[1] != "max_freq") {
return kInvalidKey;
}
for (auto &cpufreq_dev : cpufreq_devices_) {
cpufreq_dev->SetMaximumFrequency(value);
}
return kOk;
}
ThermalOutputProcessor::Status ThermalOutputProcessor::ProcessOutput(
const string &key, int value) {
vector<string> key_parts =
base::SplitString(key, ".", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (key_parts.empty()) {
LOG(ERROR) << "Invalid key: " << key;
return kInvalidKey;
}
Status status;
if (key_parts[0] == "ath10k") {
status = ProcessValueAth10k(key_parts, value);
} else if(key_parts[0] == "cpu") {
status = ProcessValueCpu(key_parts, value);
} else {
return kSkipped;
}
if (status == kInvalidKey) {
LOG(ERROR) << "Invalid key: " << key;
return kInvalidKey;
}
return status;
}
void ThermalOutputProcessor::OnOutputSet(const string &key, int value) {
Status status = ProcessOutput(key, value);
if (status == kOk) {
// Processing was successful, make sure to remove a potential entry from
// the list of deferred values.
deferred_values_.erase(key);
if (deferred_values_.empty() && deferred_values_timer_.IsRunning()) {
deferred_values_timer_.Stop();
}
} else if (status == kDeferred) {
// The system could not complete the processing of the value, but later
// attempts might succeed. Set up deferred processing for this value.
deferred_values_[key] = value;
if (!deferred_values_timer_.IsRunning()) {
deferred_values_timer_.Start(
FROM_HERE,
base::TimeDelta::FromSeconds(kDeferredOpsPeriodInSeconds),
base::Bind(&ThermalOutputProcessor::ProcessDeferredOutputs,
weak_ptr_factory_.GetWeakPtr()));
}
}
}
void ThermalOutputProcessor::ProcessDeferredOutputs() {
LOG(INFO) << "Processing deferred outputs";
auto it = deferred_values_.begin();
while (it != deferred_values_.end()) {
Status status = ProcessOutput(it->first, it->second);
auto it_next = std::next(it);
if (status == kOk) {
deferred_values_.erase(it);
}
it = it_next;
}
if (deferred_values_.empty()) {
LOG(INFO) << "All deferred outputs processed";
deferred_values_timer_.Stop();
}
}
} // namespace thermald