blob: b16467b7fac5052cb49dfc464d8a48610a6c9b62 [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 "common/sensor_reader.h"
#include <iterator>
#include <utility>
#include <base/notreached.h>
#include <base/ranges/algorithm.h>
#include <base/strings/stringprintf.h>
#include "cros-camera/common.h"
namespace cros {
namespace {
constexpr char kChannelFormat[] = "%s_%c";
constexpr char kAxes[SensorReader::kNumberOfAxes] = {'x', 'y', 'z'};
std::string GetChannelPrefix(cros::mojom::DeviceType type) {
switch (type) {
case cros::mojom::DeviceType::ACCEL:
return mojom::kAccelerometerChannel;
case cros::mojom::DeviceType::ANGLVEL:
return mojom::kGyroscopeChannel;
case cros::mojom::DeviceType::GRAVITY:
return mojom::kGravityChannel;
default:
NOTREACHED() << "Unsupported type: " << type;
return "";
}
}
} // namespace
// static
constexpr int SensorReader::kNumberOfAxes;
SensorReader::SensorReader(
scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner,
int32_t iio_device_id,
cros::mojom::DeviceType type,
double frequency,
double scale,
SamplesObserver* samples_observer,
mojo::Remote<mojom::SensorDevice> remote)
: ipc_task_runner_(std::move(ipc_task_runner)),
iio_device_id_(iio_device_id),
type_(type),
frequency_(frequency),
scale_(scale),
samples_observer_(samples_observer),
sensor_device_remote_(std::move(remote)) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
DCHECK_GT(frequency_, 0.0);
DCHECK(samples_observer_);
sensor_device_remote_.set_disconnect_handler(base::BindOnce(
&SensorReader::OnSensorDeviceDisconnect, weak_ptr_factory_.GetWeakPtr()));
sensor_device_remote_->GetAllChannelIds(base::BindOnce(
&SensorReader::GetAllChannelIdsCallback, weak_ptr_factory_.GetWeakPtr()));
}
SensorReader::~SensorReader() {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
}
void SensorReader::OnSampleUpdated(
const base::flat_map<int32_t, int64_t>& sample) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
DCHECK(timestamp_index_);
if (sample.size() != kNumberOfAxes + 1) {
LOGF(ERROR) << "Invalid sample with size: " << sample.size();
OnErrorOccurred(mojom::ObserverErrorType::READ_FAILED);
return;
}
for (int i = 0; i < kNumberOfAxes; ++i) {
DCHECK(channel_indices_[i]);
if (sample.find(*channel_indices_[i]) == sample.end()) {
LOGF(ERROR) << "Missing channel: " << kAxes[i]
<< " in sample with device id: " << iio_device_id_;
OnErrorOccurred(mojom::ObserverErrorType::READ_FAILED);
return;
}
}
if (sample.find(*timestamp_index_) == sample.end()) {
LOGF(ERROR) << "Missing channel: " << mojom::kTimestampChannel
<< " in sample with device id: " << iio_device_id_;
OnErrorOccurred(mojom::ObserverErrorType::READ_FAILED);
return;
}
SamplesObserver::Sample reading_sample;
reading_sample.x_value = GetScaledValue(sample.at(*channel_indices_[0]));
reading_sample.y_value = GetScaledValue(sample.at(*channel_indices_[1]));
reading_sample.z_value = GetScaledValue(sample.at(*channel_indices_[2]));
// TODO(gwendal): Check the format of the timestamp.
reading_sample.timestamp = sample.at(*timestamp_index_);
samples_observer_->OnSampleUpdated(reading_sample);
}
void SensorReader::OnErrorOccurred(mojom::ObserverErrorType type) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
switch (type) {
case mojom::ObserverErrorType::ALREADY_STARTED:
LOGF(ERROR) << "Device " << iio_device_id_
<< ": Another observer has already started to read samples";
ResetOnError();
break;
case mojom::ObserverErrorType::FREQUENCY_INVALID:
LOGF(ERROR) << "Device " << iio_device_id_
<< ": Observer started with an invalid frequency";
ResetOnError();
break;
case mojom::ObserverErrorType::NO_ENABLED_CHANNELS:
LOGF(ERROR) << "Device " << iio_device_id_
<< ": Observer started with no channels enabled";
SetChannelsEnabled();
break;
case mojom::ObserverErrorType::SET_FREQUENCY_IO_FAILED:
LOGF(ERROR) << "Device " << iio_device_id_
<< ": Failed to set frequency to the physical device";
break;
case mojom::ObserverErrorType::GET_FD_FAILED:
LOGF(ERROR) << "Device " << iio_device_id_
<< ": Failed to get the device's fd to poll on";
break;
case mojom::ObserverErrorType::READ_FAILED:
LOGF(ERROR) << "Device " << iio_device_id_ << ": Failed to read a sample";
samples_observer_->OnErrorOccurred(
SamplesObserver::ErrorType::READ_FAILED);
break;
case mojom::ObserverErrorType::READ_TIMEOUT:
LOGF(ERROR) << "Device " << iio_device_id_ << ": A read timed out";
break;
default:
LOGF(ERROR) << "Device " << iio_device_id_ << ": error " << type;
break;
}
}
void SensorReader::ResetOnError() {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
LOGF(ERROR) << "ResetOnError";
sensor_device_remote_.reset();
receiver_.reset();
}
double SensorReader::GetScaledValue(int64_t value) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
return value * scale_;
}
void SensorReader::OnSensorDeviceDisconnect() {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
LOGF(ERROR) << "SensorDevice disconnected with id: " << iio_device_id_
<< ", and type: " << type_;
ResetOnError();
}
void SensorReader::GetAllChannelIdsCallback(
const std::vector<std::string>& iio_channel_ids) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
DCHECK(sensor_device_remote_.is_bound());
std::string prefix = GetChannelPrefix(type_);
for (int i = 0; i < kNumberOfAxes; ++i) {
std::string channel =
base::StringPrintf(kChannelFormat, prefix.c_str(), kAxes[i]);
auto it = base::ranges::find(iio_channel_ids, channel);
if (it == iio_channel_ids.end()) {
LOGF(ERROR) << "Missing channel: " << channel;
samples_observer_->OnErrorOccurred(
SamplesObserver::ErrorType::INVALID_ARGUMENT);
ResetOnError();
return;
}
channel_indices_[i] = std::distance(iio_channel_ids.begin(), it);
}
auto it = base::ranges::find(iio_channel_ids, mojom::kTimestampChannel);
if (it == iio_channel_ids.end()) {
LOGF(ERROR) << "Missing channel: " << mojom::kTimestampChannel;
samples_observer_->OnErrorOccurred(
SamplesObserver::ErrorType::INVALID_ARGUMENT);
ResetOnError();
return;
}
timestamp_index_ = std::distance(iio_channel_ids.begin(), it);
SetChannelsEnabled();
}
void SensorReader::SetChannelsEnabled() {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
DCHECK(timestamp_index_);
std::vector<int32_t> indices;
for (int i = 0; i < kNumberOfAxes; ++i) {
DCHECK(channel_indices_[i]);
indices.push_back(*channel_indices_[i]);
}
indices.push_back(*timestamp_index_);
sensor_device_remote_->SetChannelsEnabled(
indices, true,
base::BindOnce(&SensorReader::SetChannelsEnabledCallback,
weak_ptr_factory_.GetWeakPtr()));
sensor_device_remote_->SetFrequency(
frequency_, base::BindOnce(&SensorReader::SetFrequencyCallback,
weak_ptr_factory_.GetWeakPtr()));
sensor_device_remote_->StartReadingSamples(
receiver_.BindNewPipeAndPassRemote());
}
void SensorReader::SetChannelsEnabledCallback(
const std::vector<int32_t>& failed_indices) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
DCHECK(timestamp_index_);
if (failed_indices.empty()) {
return;
}
samples_observer_->OnErrorOccurred(
SamplesObserver::ErrorType::INVALID_ARGUMENT);
for (int32_t index : failed_indices) {
base::Optional<std::string> channel = base::nullopt;
if (index == *timestamp_index_) {
channel = mojom::kTimestampChannel;
} else {
std::string prefix = GetChannelPrefix(type_);
for (int i = 0; i < kNumberOfAxes; ++i) {
DCHECK(channel_indices_[i]);
if (index == *channel_indices_[i]) {
channel =
base::StringPrintf(kChannelFormat, prefix.c_str(), kAxes[i]);
break;
}
}
}
if (channel) {
LOGF(ERROR) << "Failed to enable channel " << *channel
<< ", in device with id: " << iio_device_id_;
} else {
LOGF(ERROR) << "Failed to enable channel with index: " << index
<< ", in device with id: " << iio_device_id_;
}
}
ResetOnError();
}
void SensorReader::SetFrequencyCallback(double result_freq) {
DCHECK(ipc_task_runner_->BelongsToCurrentThread());
if (result_freq > 0.0) {
return;
}
samples_observer_->OnErrorOccurred(
SamplesObserver::ErrorType::INVALID_ARGUMENT);
LOGF(ERROR) << "SetFrequency failed. Target frequency: " << frequency_
<< ", result requency: " << result_freq;
ResetOnError();
}
} // namespace cros