blob: 3a069a70035abed739db2cb5f865edafb55e502f [file] [log] [blame]
// Copyright 2021 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 "rmad/utils/iio_ec_sensor_utils_impl.h"
#include <re2/re2.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/process/launch.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
namespace {
constexpr int kMaxNumEntries = 1024;
constexpr char kIioDevicePathPrefix[] = "/sys/bus/iio/devices/iio:device";
constexpr char kIioDeviceEntryName[] = "name";
constexpr char kIioDeviceEntryLocation[] = "location";
constexpr char kIioDeviceEntryFrequency[] = "sampling_frequency";
constexpr char kIioDeviceEntryFrequencyAvailable[] =
"sampling_frequency_available";
constexpr char kIioDeviceEntryScale[] = "scale";
constexpr char kIioServiceClientCmdPath[] = "/usr/sbin/iioservice_simpleclient";
constexpr char kIioParameterChannelsPrefix[] = "--channels=";
constexpr char kIioParameterFrequencyPrefix[] = "--frequency=";
constexpr char kIioParameterDeviceIdPrefix[] = "--device_id=";
constexpr char kIioParameterSamplesPrefix[] = "--samples=";
} // namespace
namespace rmad {
IioEcSensorUtilsImpl::IioEcSensorUtilsImpl(const std::string& location,
const std::string& name)
: IioEcSensorUtils(location, name), initialized_(false) {
Initialize();
}
bool IioEcSensorUtilsImpl::GetAvgData(const std::vector<std::string>& channels,
int samples,
std::vector<double>* avg_data) {
CHECK(avg_data);
if (!initialized_) {
LOG(ERROR) << location_ << ":" << name_ << " is not initialized.";
return false;
}
std::string parameter_channels = kIioParameterChannelsPrefix;
for (auto channel : channels) {
parameter_channels += channel + " ";
}
std::vector<std::string> argv{
kIioServiceClientCmdPath, parameter_channels,
kIioParameterFrequencyPrefix + base::NumberToString(frequency_),
kIioParameterDeviceIdPrefix + base::NumberToString(id_),
kIioParameterSamplesPrefix + base::NumberToString(samples)};
std::string value;
if (!base::GetAppOutputAndError(argv, &value)) {
std::string whole_cmd;
for (auto arg : argv) {
whole_cmd += " " + arg;
}
LOG(ERROR) << location_ << ":" << name_ << ": Failed to get data by"
<< whole_cmd;
return false;
}
std::vector<double> data(channels.size(), 0.0);
for (int i = 0; i < channels.size(); i++) {
re2::StringPiece str_piece(value);
re2::RE2 reg(channels[i] + R"(: ([-+]?\d+))");
std::string match;
double raw_data;
int count = 0;
while (RE2::FindAndConsume(&str_piece, reg, &match)) {
if (!base::StringToDouble(match, &raw_data)) {
LOG(WARNING) << location_ << ":" << name_ << ": Failed to parse data ["
<< match << "]";
continue;
}
data.at(i) += raw_data;
count++;
}
if (count != samples) {
LOG(ERROR) << location_ << ":" << name_ << ":" << channels[i]
<< ": We received " << count << " instead of " << samples
<< " samples.";
return false;
}
}
avg_data->resize(channels.size());
for (int i = 0; i < channels.size(); i++) {
avg_data->at(i) = data.at(i) * scale_ / samples;
}
return true;
}
bool IioEcSensorUtilsImpl::SetSysValues(const std::vector<std::string>& entries,
const std::vector<int>& values) {
CHECK(entries.size() == values.size());
if (!initialized_) {
LOG(ERROR) << location_ << ":" << name_ << " is not initialized.";
return false;
}
for (int i = 0; i < entries.size(); i++) {
auto entry = sysfs_path_.Append(entries[i]);
auto bytes = base::NumberToString(values[i]);
int byte_size = bytes.size();
if (!base::PathExists(entry) ||
byte_size != base::WriteFile(entry, bytes.c_str(), byte_size)) {
LOG(ERROR) << "Failed to write sys value at " << entry << " to " << bytes;
return false;
}
}
return true;
}
void IioEcSensorUtilsImpl::Initialize() {
for (int i = 0; i < kMaxNumEntries; i++) {
base::FilePath sysfs_path(kIioDevicePathPrefix + base::NumberToString(i));
if (!base::PathExists(sysfs_path)) {
break;
}
base::FilePath entry_name = sysfs_path.Append(kIioDeviceEntryName);
if (std::string buf;
!base::PathExists(entry_name) ||
!base::ReadFileToString(entry_name, &buf) ||
name_ != base::TrimWhitespaceASCII(buf, base::TRIM_TRAILING)) {
continue;
}
base::FilePath entry_location = sysfs_path.Append(kIioDeviceEntryLocation);
if (std::string buf;
!base::PathExists(entry_location) ||
!base::ReadFileToString(entry_location, &buf) ||
location_ != base::TrimWhitespaceASCII(buf, base::TRIM_TRAILING)) {
continue;
}
base::FilePath entry_frequency =
sysfs_path.Append(kIioDeviceEntryFrequency);
if (std::string buf;
!base::PathExists(entry_frequency) ||
!base::ReadFileToString(entry_frequency, &buf) ||
!base::StringToDouble(
base::TrimWhitespaceASCII(buf, base::TRIM_TRAILING), &frequency_)) {
continue;
}
// This is a special case where the sensor has not been set. We should set
// it according to one of its available sampling frequency. We will use the
// slowest one for calibration.
if (frequency_ == 0) {
base::FilePath entry_frequency_available =
sysfs_path.Append(kIioDeviceEntryFrequencyAvailable);
if (std::string buf;
!base::PathExists(entry_frequency_available) ||
!base::ReadFileToString(entry_frequency_available, &buf)) {
continue;
} else {
// The value from sysfs could be like "0.000000 13.000000 208.000000"
// or "13.000000 208.000000", and we will choose the first non-zero
// frequency.
re2::StringPiece str_piece(buf);
re2::RE2 reg(R"((\d+.\d+))");
std::string match;
while (RE2::FindAndConsume(&str_piece, reg, &match)) {
if (base::StringToDouble(match, &frequency_) && frequency_ > 0) {
break;
}
}
if (frequency_ == 0) {
continue;
}
}
}
base::FilePath entry_scale = sysfs_path.Append(kIioDeviceEntryScale);
if (std::string buf;
!base::PathExists(entry_scale) ||
!base::ReadFileToString(entry_scale, &buf) ||
!base::StringToDouble(
base::TrimWhitespaceASCII(buf, base::TRIM_TRAILING), &scale_)) {
continue;
}
id_ = i;
sysfs_path_ = sysfs_path;
initialized_ = true;
break;
}
}
} // namespace rmad