blob: 5302039846cce73ef18ec2d4e96c23bb6d7fbeef [file] [log] [blame]
// Copyright 2020 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 "arc/vm/sensor_service/sensor_device_impl.h"
#include <fcntl.h>
#include <unistd.h>
#include <utility>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/posix/eintr_wrapper.h>
#include <base/strings/string_util.h>
#include <mojo/public/cpp/system/platform_handle.h>
namespace arc {
namespace {
// Returns the path of the specified attribute under |iio_sysfs_dir|.
base::FilePath GetAttributePath(const base::FilePath& iio_sysfs_dir,
const std::string& name) {
base::FilePath path = iio_sysfs_dir.Append(name);
if (!path.IsAbsolute() || path.ReferencesParent()) {
LOG(ERROR) << "Invalid path: " << path.value();
return {};
}
return path;
}
} // namespace
SensorDeviceImpl::SensorDeviceImpl(const base::FilePath& iio_sysfs_dir,
const base::FilePath& device_file)
: iio_sysfs_dir_(iio_sysfs_dir), device_file_(device_file) {
bindings_.set_connection_error_handler(base::BindRepeating(
[]() { LOG(INFO) << "SensorDevice connection closed."; }));
}
SensorDeviceImpl::~SensorDeviceImpl() = default;
void SensorDeviceImpl::Bind(mojom::SensorDeviceRequest request) {
bindings_.AddBinding(this, std::move(request));
}
void SensorDeviceImpl::GetAttribute(const std::string& name,
GetAttributeCallback callback) {
// Read /sys/bus/iio/devices/iio:deviceX/<name>.
base::FilePath path = GetAttributePath(iio_sysfs_dir_, name);
if (path.empty()) {
LOG(ERROR) << "Invalid name: " << name;
std::move(callback).Run(mojom::AttributeIOResult::ERROR_IO, {});
return;
}
std::string value;
if (!base::ReadFileToString(path, &value)) {
LOG(ERROR) << "Failed to read " << path.value();
std::move(callback).Run(mojom::AttributeIOResult::ERROR_IO, {});
return;
}
value = base::TrimString(value, "\n", base::TRIM_TRAILING).as_string();
std::move(callback).Run(mojom::AttributeIOResult::SUCCESS, std::move(value));
}
void SensorDeviceImpl::SetAttribute(const std::string& name,
const std::string& value,
SetAttributeCallback callback) {
// Write /sys/bus/iio/devices/iio:deviceX/<name>.
base::FilePath path = GetAttributePath(iio_sysfs_dir_, name);
if (path.empty()) {
LOG(ERROR) << "Invalid name: " << name;
std::move(callback).Run(mojom::AttributeIOResult::ERROR_IO);
return;
}
if (!base::WriteFile(path, value.data(), value.size())) {
LOG(ERROR) << "Failed to write " << path.value() << ", value = " << value;
std::move(callback).Run(mojom::AttributeIOResult::ERROR_IO);
return;
}
std::move(callback).Run(mojom::AttributeIOResult::SUCCESS);
}
void SensorDeviceImpl::OpenBuffer(OpenBufferCallback callback) {
// Open /dev/iio:deviceX.
base::ScopedFD device_fd(
HANDLE_EINTR(open(device_file_.value().c_str(), O_RDONLY)));
if (!device_fd.is_valid()) {
PLOG(ERROR) << "open failed: " << device_file_.value();
std::move(callback).Run({});
return;
}
// Create a pipe.
int pipe_fds[2];
if (pipe(pipe_fds) < 0) {
PLOG(ERROR) << "pipe failed";
std::move(callback).Run({});
return;
}
base::ScopedFD pipe_read_end(pipe_fds[0]), pipe_write_end(pipe_fds[1]);
// The device file cannot cross the VM boundary. Instead, we return a pipe
// from this method.
// Data read from the device file will be forwarded to the pipe.
device_fd_watcher_ = base::FileDescriptorWatcher::WatchReadable(
device_fd.get(),
base::BindRepeating(&SensorDeviceImpl::OnDeviceFdReadReady,
base::Unretained(this)));
device_fd_ = std::move(device_fd);
pipe_write_end_ = std::move(pipe_write_end);
// Return the pipe read end to the caller.
std::move(callback).Run(mojo::WrapPlatformFile(std::move(pipe_read_end)));
}
void SensorDeviceImpl::OnDeviceFdReadReady() {
char buf[4096];
ssize_t read_size = HANDLE_EINTR(read(device_fd_.get(), buf, sizeof(buf)));
if (read_size < 0) {
PLOG(ERROR) << "read failed.";
device_fd_watcher_.reset();
pipe_write_end_.reset();
return;
}
for (ssize_t written = 0; written < read_size;) {
ssize_t r = HANDLE_EINTR(
write(pipe_write_end_.get(), buf + written, read_size - written));
if (r < 0) {
PLOG(ERROR) << "write failed.";
device_fd_watcher_.reset();
pipe_write_end_.reset();
return;
}
written += r;
}
}
} // namespace arc