blob: 1c70d1f389b91819c633295a0e61ab5aa81ee772 [file] [log] [blame]
// Copyright 2019 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 "libmems/test_fakes.h"
#include <sys/eventfd.h>
#include <base/check.h>
#include <base/check_op.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include "base/posix/eintr_wrapper.h"
#include <base/stl_util.h>
#include "libmems/common_types.h"
namespace libmems {
namespace fakes {
FakeIioChannel::FakeIioChannel(const std::string& id, bool enabled)
: id_(id), enabled_(enabled) {}
void FakeIioChannel::SetEnabled(bool en) {
enabled_ = en;
}
bool FakeIioChannel::SetScanElementsEnabled(bool en) {
scan_elements_enabled_ = en;
return true;
}
template <typename T>
base::Optional<T> FakeReadAttributes(const std::string& name,
std::map<std::string, T> attributes) {
auto k = attributes.find(name);
if (k == attributes.end())
return base::nullopt;
return k->second;
}
base::Optional<std::string> FakeIioChannel::ReadStringAttribute(
const std::string& name) const {
return FakeReadAttributes<>(name, text_attributes_);
}
base::Optional<int64_t> FakeIioChannel::ReadNumberAttribute(
const std::string& name) const {
return FakeReadAttributes<>(name, numeric_attributes_);
}
base::Optional<double> FakeIioChannel::ReadDoubleAttribute(
const std::string& name) const {
return FakeReadAttributes<>(name, double_attributes_);
}
bool FakeIioChannel::WriteStringAttribute(const std::string& name,
const std::string& value) {
text_attributes_[name] = value;
return true;
}
bool FakeIioChannel::WriteNumberAttribute(const std::string& name,
int64_t value) {
numeric_attributes_[name] = value;
return true;
}
bool FakeIioChannel::WriteDoubleAttribute(const std::string& name,
double value) {
double_attributes_[name] = value;
return true;
}
base::Optional<int64_t> FakeIioChannel::GetData(int index) {
if (!enabled_ || index < 0 || index >= base::size(kFakeAccelSamples))
return base::nullopt;
auto raw = ReadNumberAttribute(kRawAttr);
if (raw.has_value())
return raw;
for (int i = 0; i < base::size(kFakeAccelChns); ++i) {
if (id_.compare(kFakeAccelChns[i]) == 0)
return kFakeAccelSamples[index][i];
}
return base::nullopt;
}
FakeIioDevice::FakeIioDevice(FakeIioContext* ctx,
const std::string& name,
int id)
: IioDevice(), context_(ctx), name_(name), id_(id) {}
base::FilePath FakeIioDevice::GetPath() const {
if (!path_.empty())
return path_;
std::string id_str(kDeviceIdPrefix);
id_str.append(std::to_string(GetId()));
return base::FilePath(kSysDevString).Append(id_str);
}
base::Optional<std::string> FakeIioDevice::ReadStringAttribute(
const std::string& name) const {
if (name.compare(kDeviceName) == 0)
return name_;
return FakeReadAttributes<>(name, text_attributes_);
}
base::Optional<int64_t> FakeIioDevice::ReadNumberAttribute(
const std::string& name) const {
return FakeReadAttributes<>(name, numeric_attributes_);
}
base::Optional<double> FakeIioDevice::ReadDoubleAttribute(
const std::string& name) const {
return FakeReadAttributes<>(name, double_attributes_);
}
bool FakeIioDevice::WriteStringAttribute(const std::string& name,
const std::string& value) {
text_attributes_[name] = value;
return true;
}
bool FakeIioDevice::WriteNumberAttribute(const std::string& name,
int64_t value) {
numeric_attributes_[name] = value;
return true;
}
bool FakeIioDevice::WriteDoubleAttribute(const std::string& name,
double value) {
double_attributes_[name] = value;
return true;
}
bool FakeIioDevice::SetTrigger(IioDevice* trigger) {
trigger_ = trigger;
return true;
}
bool FakeIioDevice::EnableBuffer(size_t n) {
buffer_length_ = n;
buffer_enabled_ = true;
return true;
}
bool FakeIioDevice::DisableBuffer() {
buffer_enabled_ = false;
return true;
}
bool FakeIioDevice::IsBufferEnabled(size_t* n) const {
if (n && buffer_enabled_)
*n = buffer_length_;
return buffer_enabled_;
}
bool FakeIioDevice::CreateBuffer() {
if (disabled_fd_ || sample_fd_.is_valid())
return false;
int fd = eventfd(0, 0);
CHECK_GE(fd, 0);
sample_fd_.reset(fd);
if (sample_index_ >= base::size(kFakeAccelSamples) || is_paused_)
return true;
if (!WriteByte()) {
ClosePipe();
return false;
}
return true;
}
base::Optional<int32_t> FakeIioDevice::GetBufferFd() {
if (disabled_fd_ || !sample_fd_.is_valid())
return base::nullopt;
return sample_fd_.get();
}
base::Optional<IioDevice::IioSample> FakeIioDevice::ReadSample() {
if (is_paused_ || disabled_fd_ || !sample_fd_.is_valid())
return base::nullopt;
if (!failed_read_queue_.empty()) {
CHECK_GE(failed_read_queue_.top(), sample_index_);
if (failed_read_queue_.top() == sample_index_) {
failed_read_queue_.pop();
return base::nullopt;
}
}
if (!ReadByte())
return base::nullopt;
base::Optional<double> freq_opt = ReadDoubleAttribute(kSamplingFrequencyAttr);
if (!freq_opt.has_value()) {
LOG(ERROR) << "sampling_frequency not set";
return base::nullopt;
}
double frequency = freq_opt.value();
if (frequency <= 0.0) {
LOG(ERROR) << "Invalid frequency: " << frequency;
return base::nullopt;
}
IioDevice::IioSample sample;
auto channels = GetAllChannels();
for (int32_t i = 0; i < channels.size(); ++i) {
FakeIioChannel* chn = dynamic_cast<FakeIioChannel*>(channels[i]);
auto value = chn->GetData(sample_index_);
if (!value.has_value()) {
LOG(ERROR) << "Channel: " << channels_[i].chn_id << " has no sample";
return base::nullopt;
}
sample[i] = value.value();
}
sample_index_ += 1;
if (sample_index_ < base::size(kFakeAccelSamples)) {
if (pause_index_.has_value() && sample_index_ == pause_index_.value())
SetPause();
else if (!WriteByte())
return base::nullopt;
}
return sample;
}
void FakeIioDevice::FreeBuffer() {
ClosePipe();
}
void FakeIioDevice::DisableFd() {
disabled_fd_ = true;
if (readable_fd_)
CHECK(ReadByte());
}
void FakeIioDevice::AddFailedReadAtKthSample(int k) {
CHECK_GE(k, sample_index_);
failed_read_queue_.push(k);
}
void FakeIioDevice::SetPauseCallbackAtKthSamples(
int k, base::OnceCallback<void()> callback) {
CHECK_GE(k, sample_index_);
CHECK_LE(k, base::size(kFakeAccelSamples));
CHECK(!pause_index_.has_value()); // pause callback hasn't been set
pause_index_ = k;
pause_callback_ = std::move(callback);
if (pause_index_.value() != sample_index_)
return;
SetPause();
}
void FakeIioDevice::ResumeReadingSamples() {
CHECK(is_paused_);
is_paused_ = false;
if (sample_fd_.is_valid() && !readable_fd_)
CHECK(WriteByte());
}
bool FakeIioDevice::WriteByte() {
if (!sample_fd_.is_valid())
return false;
CHECK(!readable_fd_);
uint64_t val = 1;
CHECK_EQ(write(sample_fd_.get(), &val, sizeof(uint64_t)), sizeof(uint64_t));
readable_fd_ = true;
return true;
}
bool FakeIioDevice::ReadByte() {
if (!sample_fd_.is_valid())
return false;
CHECK(readable_fd_);
int64_t val = 1;
CHECK_EQ(read(sample_fd_.get(), &val, sizeof(uint64_t)), sizeof(uint64_t));
readable_fd_ = false;
return true;
}
void FakeIioDevice::ClosePipe() {
sample_fd_.reset();
}
void FakeIioDevice::SetPause() {
is_paused_ = true;
pause_index_.reset();
std::move(pause_callback_).Run();
if (readable_fd_)
CHECK(ReadByte());
}
void FakeIioContext::AddDevice(std::unique_ptr<FakeIioDevice> device) {
CHECK(device.get());
devices_.emplace(device->GetId(), std::move(device));
}
void FakeIioContext::AddTrigger(std::unique_ptr<FakeIioDevice> trigger) {
CHECK(trigger.get());
triggers_.emplace(trigger->GetId(), std::move(trigger));
}
std::vector<IioDevice*> FakeIioContext::GetDevicesByName(
const std::string& name) {
return GetFakeByName(name, devices_);
}
IioDevice* FakeIioContext::GetDeviceById(int id) {
return GetFakeById(id, devices_);
}
std::vector<IioDevice*> FakeIioContext::GetAllDevices() {
return GetFakeAll(devices_);
}
std::vector<IioDevice*> FakeIioContext::GetTriggersByName(
const std::string& name) {
return GetFakeByName(name, triggers_);
}
IioDevice* FakeIioContext::GetTriggerById(int id) {
return GetFakeById(id, triggers_);
}
std::vector<IioDevice*> FakeIioContext::GetAllTriggers() {
return GetFakeAll(triggers_);
}
IioDevice* FakeIioContext::GetFakeById(
int id, const std::map<int, std::unique_ptr<FakeIioDevice>>& devices_map) {
auto k = devices_map.find(id);
return (k == devices_map.end()) ? nullptr : k->second.get();
}
std::vector<IioDevice*> FakeIioContext::GetFakeByName(
const std::string& name,
const std::map<int, std::unique_ptr<FakeIioDevice>>& devices_map) {
std::vector<IioDevice*> devices;
for (auto const& it : devices_map) {
if (name.compare(it.second->GetName()) == 0)
devices.push_back(it.second.get());
}
return devices;
}
std::vector<IioDevice*> FakeIioContext::GetFakeAll(
const std::map<int, std::unique_ptr<FakeIioDevice>>& devices_map) {
std::vector<IioDevice*> devices;
for (auto const& it : devices_map)
devices.push_back(it.second.get());
return devices;
}
} // namespace fakes
} // namespace libmems