| // 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 |