blob: 5aa07d02aa7634909157856374d35cfd77c5e78a [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 <linux/iio/events.h>
#include <sys/eventfd.h>
#include <array>
#include <iterator>
#include <optional>
#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 "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>
std::optional<T> FakeReadAttributes(const std::string& name,
std::map<std::string, T> attributes) {
auto k = attributes.find(name);
if (k == attributes.end())
return std::nullopt;
return k->second;
}
std::optional<std::string> FakeIioChannel::ReadStringAttribute(
const std::string& name) const {
return FakeReadAttributes<>(name, text_attributes_);
}
std::optional<int64_t> FakeIioChannel::ReadNumberAttribute(
const std::string& name) const {
return FakeReadAttributes<>(name, numeric_attributes_);
}
std::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;
}
std::optional<int64_t> FakeIioChannel::GetData(int index) {
if (!enabled_ || index < 0 || index >= std::size(kFakeAccelSamples))
return std::nullopt;
auto raw = ReadNumberAttribute(kRawAttr);
if (raw.has_value())
return raw;
for (int i = 0; i < std::size(kFakeAccelChns); ++i) {
if (id_.compare(kFakeAccelChns[i]) == 0)
return kFakeAccelSamples[index][i];
}
return std::nullopt;
}
FakeIioEvent::FakeIioEvent(iio_chan_type chan_type,
iio_event_type event_type,
iio_event_direction direction,
int channel)
: IioEvent(chan_type, event_type, direction, channel) {}
void FakeIioEvent::SetEnabled(bool en) {
enabled_ = en;
}
std::optional<std::string> FakeIioEvent::ReadStringAttribute(
const std::string& name) const {
auto k = text_attributes_.find(name);
if (k == text_attributes_.end())
return std::nullopt;
return k->second;
}
bool FakeIioEvent::WriteStringAttribute(const std::string& name,
const std::string& value) {
text_attributes_[name] = value;
return true;
}
std::optional<uint64_t> FakeIioEvent::GetData(int index) {
if (index >= kEventNumber)
return std::nullopt;
iio_event_direction dir =
(direction_ == iio_event_direction::IIO_EV_DIR_EITHER)
? (dir_turn_ ? iio_event_direction::IIO_EV_DIR_RISING
: iio_event_direction::IIO_EV_DIR_FALLING)
: direction_;
dir_turn_ = !dir_turn_;
return IioEventCode(chan_type_, event_type_, dir, channel_);
}
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);
}
std::optional<std::string> FakeIioDevice::ReadStringAttribute(
const std::string& name) const {
if (name.compare(kDeviceName) == 0)
return name_;
return FakeReadAttributes<>(name, text_attributes_);
}
std::optional<int64_t> FakeIioDevice::ReadNumberAttribute(
const std::string& name) const {
return FakeReadAttributes<>(name, numeric_attributes_);
}
std::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_.fd.reset(fd);
if (sample_fd_.index >= std::size(kFakeAccelSamples) || sample_fd_.is_paused)
return true;
if (!sample_fd_.WriteByte()) {
sample_fd_.ClosePipe();
return false;
}
return true;
}
std::optional<int32_t> FakeIioDevice::GetBufferFd() {
if (disabled_fd_ || !sample_fd_.is_valid())
return std::nullopt;
return sample_fd_.get();
}
std::optional<IioDevice::IioSample> FakeIioDevice::ReadSample() {
if (sample_fd_.is_paused || disabled_fd_ || !sample_fd_.is_valid())
return std::nullopt;
if (!sample_fd_.failed_read_queue.empty()) {
CHECK_GE(sample_fd_.failed_read_queue.top(), sample_fd_.index);
if (sample_fd_.failed_read_queue.top() == sample_fd_.index) {
sample_fd_.failed_read_queue.pop();
return std::nullopt;
}
}
if (!sample_fd_.ReadByte())
return std::nullopt;
std::optional<double> freq_opt = ReadDoubleAttribute(kSamplingFrequencyAttr);
if (!freq_opt.has_value()) {
LOG(ERROR) << "sampling_frequency not set";
return std::nullopt;
}
double frequency = freq_opt.value();
if (frequency <= 0.0) {
LOG(ERROR) << "Invalid frequency: " << frequency;
return std::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_fd_.index);
if (!value.has_value()) {
LOG(ERROR) << "Channel: " << channels_[i].chn_id << " has no sample";
return std::nullopt;
}
sample[i] = value.value();
}
sample_fd_.index += 1;
if (sample_fd_.index < std::size(kFakeAccelSamples)) {
if (sample_fd_.pause_index.has_value() &&
sample_fd_.index == sample_fd_.pause_index.value()) {
sample_fd_.SetPause();
} else if (!sample_fd_.WriteByte()) {
return std::nullopt;
}
}
return sample;
}
void FakeIioDevice::FreeBuffer() {
sample_fd_.ClosePipe();
}
std::optional<int32_t> FakeIioDevice::GetEventFd() {
if (disabled_fd_)
return std::nullopt;
if (!event_fd_.is_valid()) {
int fd = eventfd(0, 0);
CHECK_GE(fd, 0);
event_fd_.fd.reset(fd);
if (event_fd_.index < kEventNumber && !event_fd_.is_paused &&
!event_fd_.readable) {
if (!event_fd_.WriteByte()) {
event_fd_.ClosePipe();
return std::nullopt;
}
}
}
return event_fd_.get();
}
std::optional<iio_event_data> FakeIioDevice::ReadEvent() {
if (event_fd_.is_paused || disabled_fd_ || !event_fd_.is_valid())
return std::nullopt;
if (!event_fd_.failed_read_queue.empty()) {
CHECK_GE(event_fd_.failed_read_queue.top(), event_fd_.index);
if (event_fd_.failed_read_queue.top() == event_fd_.index) {
event_fd_.failed_read_queue.pop();
return std::nullopt;
}
}
if (!event_fd_.ReadByte())
return std::nullopt;
iio_event_data data;
data.timestamp = 1000000000LL * (int64_t)event_fd_.index;
auto iio_events = GetAllEvents();
if (!iio_events.empty()) {
FakeIioEvent* iio_event = dynamic_cast<FakeIioEvent*>(
iio_events[event_fd_.index % iio_events.size()]);
auto value = iio_event->GetData(event_fd_.index);
if (value.has_value()) {
data.id = value.value();
} else {
LOG(ERROR) << "Event: " << event_fd_.index % iio_events.size()
<< " has no data";
}
}
event_fd_.index += 1;
if (event_fd_.index < kEventNumber) {
if (event_fd_.pause_index.has_value() &&
event_fd_.index == event_fd_.pause_index.value()) {
event_fd_.SetPause();
} else if (!event_fd_.WriteByte()) {
return std::nullopt;
}
}
return data;
}
void FakeIioDevice::DisableFd() {
disabled_fd_ = true;
if (sample_fd_.readable)
CHECK(sample_fd_.ReadByte());
}
void FakeIioDevice::AddFailedReadAtKthSample(int k) {
CHECK_GE(k, sample_fd_.index);
sample_fd_.failed_read_queue.push(k);
}
void FakeIioDevice::SetPauseCallbackAtKthSamples(
int k, base::OnceCallback<void()> callback) {
CHECK_GE(k, sample_fd_.index);
CHECK_LE(k, std::size(kFakeAccelSamples));
CHECK(!sample_fd_.pause_index.has_value()); // pause callback hasn't been set
sample_fd_.pause_index = k;
sample_fd_.pause_callback = std::move(callback);
if (sample_fd_.pause_index.value() != sample_fd_.index)
return;
sample_fd_.SetPause();
}
void FakeIioDevice::ResumeReadingSamples() {
sample_fd_.ResumeReading();
}
void FakeIioDevice::AddFailedReadAtKthEvent(int k) {
CHECK_GE(k, event_fd_.index);
event_fd_.failed_read_queue.push(k);
}
void FakeIioDevice::SetPauseCallbackAtKthEvents(
int k, base::OnceCallback<void()> callback) {
CHECK_GE(k, event_fd_.index);
CHECK_LE(k, kEventNumber);
CHECK(!event_fd_.pause_index.has_value()); // pause callback hasn't been set
event_fd_.pause_index = k;
event_fd_.pause_callback = std::move(callback);
if (event_fd_.pause_index.value() != event_fd_.index)
return;
event_fd_.SetPause();
}
void FakeIioDevice::ResumeReadingEvents() {
event_fd_.ResumeReading();
}
bool FakeIioDevice::FakeFD::WriteByte() {
if (!is_valid())
return false;
CHECK(!readable);
uint64_t val = 1;
CHECK_EQ(write(get(), &val, sizeof(uint64_t)), sizeof(uint64_t));
readable = true;
return true;
}
bool FakeIioDevice::FakeFD::ReadByte() {
if (!is_valid())
return false;
CHECK(readable);
int64_t val = 1;
CHECK_EQ(read(get(), &val, sizeof(uint64_t)), sizeof(uint64_t));
readable = false;
return true;
}
void FakeIioDevice::FakeFD::ClosePipe() {
fd.reset();
}
void FakeIioDevice::FakeFD::SetPause() {
is_paused = true;
pause_index.reset();
std::move(pause_callback).Run();
if (readable)
CHECK(ReadByte());
}
void FakeIioDevice::FakeFD::ResumeReading() {
CHECK(is_paused);
is_paused = false;
if (is_valid() && !readable)
CHECK(WriteByte());
}
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