| // 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 |