blob: d6838b9a20eea772f86d49b71d630bd9490452fe [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/ath10k_interface.h"
#include <map>
#include <string>
#include <utility>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "thermald/cooling_device.h"
using std::make_pair;
using std::map;
using std::string;
namespace thermald {
Ath10kInterface::Ath10kInterface(const string &name)
: NetworkInterface(name),
tx_duty_cycle_(-1) {
if (!IsAth10kInterface(name)) {
LOG(WARNING) << "[" << name << "] '" << name
<< "' is not an ath10k interface";
}
if (HasThermalSupport(name)) {
DetermineCoolingDevice();
}
}
bool Ath10kInterface::SetMaxTxDutyCycle(unsigned int value) {
if (value > 100) {
LOG(WARNING) << "[" << name()
<< "] Invalid value for maximum tx duty cycle: " << value;
return false;
}
if (!HasThermalSupport()) {
LOG(WARNING) << "[" << name() << "] Unable to set maximum tx duty cycle: "
<< "Interface has no thermal support";
return false;
}
if (value == tx_duty_cycle_) {
DLOG(INFO) << "[" << name() << "] Skipped setting of maximum tx duty cycle"
<< ", value unchanged (" << value << ")";
return true;
}
DLOG(INFO) << "[" << name() << "] Set maximum tx duty cycle to " << value;
return cooling_device_->SetThrottleState(100 - value);
}
void Ath10kInterface::DetermineCoolingDevice() {
string symlink(base::StringPrintf(
"/sys/class/net/%s/phy80211/device/cooling_device", name().c_str()));
base::FilePath target;
if (!base::ReadSymbolicLink(base::FilePath(symlink), &target)) {
LOG(ERROR) << "[" << name() << "] Unable to determine cooling device. "
<< "Failed to read symbolic link " << symlink;
return;
}
// The target of the symlink is /sys/class/thermal/cooling_deviceX. Extract
// the id of the cooling device from it.
string cooling_dev_path(target.value());
size_t pos = cooling_dev_path.find_last_not_of("0123456789");
if ((pos == string::npos) ||
(pos == cooling_dev_path.length() -1)) {
LOG(ERROR) << "[" << name() << "] Unable to determine cooling device from "
<< "symlink: " << symlink;
return;
}
int id;
base::StringToInt(cooling_dev_path.substr(pos + 1), &id);
cooling_device_.reset(new CoolingDevice(id));
}
bool Ath10kInterface::HasThermalSupport() const {
return cooling_device_.get() != nullptr;
}
bool Ath10kInterface::IsAth10kInterface(const std::string &name) {
base::FilePath target;
if (!base::ReadSymbolicLink(
base::FilePath(base::StringPrintf(
"/sys/class/net/%s/phy80211/device/driver/module", name.c_str())),
&target)) {
if (!Exists(name)) {
DLOG(INFO) << "Network device '" << name << "' does not exist";
return false;
} else {
DLOG(INFO) << "Network device '" << name
<< "' is not a wireless interface";
return false;
}
}
base::FilePath driver(target.BaseName());
return driver.value() == "ath10k_pci" || driver.value() == "ath10k_snoc";
}
bool Ath10kInterface::HasThermalSupport(const std::string &name) {
string phydev_path(base::StringPrintf("/sys/class/net/%s/phy80211/device",
name.c_str()));
if (base::DirectoryExists(base::FilePath(phydev_path + "/hwmon")) &&
base::DirectoryExists(base::FilePath(phydev_path + "/cooling_device"))) {
return true;
} else {
if (!IsAth10kInterface(name)) {
DLOG(INFO) << "'" << name << "' is not an ath10k interface";
}
return false;
}
}
} // namespace thermald