blob: b62d1f68895de24a54e2b5acc9d34ccc8e54ccae [file] [log] [blame]
// Copyright 2018 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 "cecservice/cec_fd.h"
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <utility>
#include <base/bind.h>
#include <base/posix/eintr_wrapper.h>
namespace cecservice {
CecFdImpl::CecFdImpl(base::ScopedFD fd, base::ScopedFD epoll_fd)
: fd_(std::move(fd)), epoll_fd_(std::move(epoll_fd)) {}
CecFdImpl::~CecFdImpl() {
brillo::MessageLoop::current()->CancelTask(read_taskid_);
brillo::MessageLoop::current()->CancelTask(priority_taskid_);
brillo::MessageLoop::current()->CancelTask(write_taskid_);
}
bool CecFdImpl::SetLogicalAddresses(struct cec_log_addrs* addresses) const {
if (HANDLE_EINTR(ioctl(fd_.get(), CEC_ADAP_S_LOG_ADDRS, addresses))) {
PLOG(ERROR) << "Failed to set logical addresses";
return false;
}
return true;
}
bool CecFdImpl::GetLogicalAddresses(struct cec_log_addrs* addresses) const {
if (HANDLE_EINTR(ioctl(fd_.get(), CEC_ADAP_G_LOG_ADDRS, addresses))) {
PLOG(ERROR) << "Failed to get logical addresses";
return false;
}
return true;
}
bool CecFdImpl::ReceiveMessage(struct cec_msg* message) const {
if (HANDLE_EINTR(ioctl(fd_.get(), CEC_RECEIVE, message))) {
PLOG(ERROR) << "Failed to receive message";
return false;
}
return true;
}
bool CecFdImpl::ReceiveEvent(struct cec_event* event) const {
if (HANDLE_EINTR(ioctl(fd_.get(), CEC_DQEVENT, event))) {
PLOG(ERROR) << "Failed to read event";
return false;
}
return true;
}
CecFd::TransmitResult CecFdImpl::TransmitMessage(
struct cec_msg* message) const {
if (HANDLE_EINTR(ioctl(fd_.get(), CEC_TRANSMIT, message))) {
switch (errno) {
case ENONET:
return TransmitResult::kNoNet;
case EBUSY:
return TransmitResult::kBusy;
case EINVAL:
return TransmitResult::kInvalidValue;
default:
PLOG(ERROR) << "Failed to transmit message";
return TransmitResult::kError;
}
}
return TransmitResult::kOk;
}
bool CecFdImpl::GetCapabilities(struct cec_caps* capabilities) const {
if (HANDLE_EINTR(ioctl(fd_.get(), CEC_ADAP_G_CAPS, capabilities))) {
PLOG(ERROR) << "Failed to query capabilities";
return false;
}
return true;
}
bool CecFdImpl::SetMode(uint32_t mode) const {
if (HANDLE_EINTR(ioctl(fd_.get(), CEC_S_MODE, &mode))) {
PLOG(ERROR) << "Failed to set device mode";
return false;
}
return true;
}
bool CecFdImpl::SetEventCallback(const Callback& callback) {
DCHECK_EQ(read_taskid_, kTaskIdNull);
DCHECK_EQ(priority_taskid_, kTaskIdNull);
callback_ = callback;
priority_taskid_ = brillo::MessageLoop::current()->WatchFileDescriptor(
FROM_HERE, epoll_fd_.get(), brillo::MessageLoop::kWatchRead, true,
base::Bind(&CecFdImpl::OnPriorityDataReady, weak_factory_.GetWeakPtr()));
read_taskid_ = brillo::MessageLoop::current()->WatchFileDescriptor(
FROM_HERE, fd_.get(), brillo::MessageLoop::kWatchRead, true,
base::Bind(&CecFdImpl::OnDataReady, weak_factory_.GetWeakPtr()));
if (priority_taskid_ == kTaskIdNull || read_taskid_ == kTaskIdNull) {
LOG_IF(ERROR, priority_taskid_ == kTaskIdNull)
<< "Failed to register watcher for epoll FD read readiness";
LOG_IF(ERROR, read_taskid_ == kTaskIdNull)
<< "Failed to register watcher for FD read readiness";
return false;
}
return true;
}
bool CecFdImpl::WriteWatch() {
if (write_taskid_ != kTaskIdNull) {
return true;
}
write_taskid_ = brillo::MessageLoop::current()->WatchFileDescriptor(
FROM_HERE, fd_.get(), brillo::MessageLoop::kWatchWrite, false,
base::Bind(&CecFdImpl::OnWriteReady, weak_factory_.GetWeakPtr()));
if (write_taskid_ == kTaskIdNull) {
LOG(ERROR) << "Failed to register watcher for FD write readiness";
return false;
}
return true;
}
void CecFdImpl::OnPriorityDataReady() {
callback_.Run(EventType::kPriorityRead);
}
void CecFdImpl::OnDataReady() {
callback_.Run(EventType::kRead);
}
void CecFdImpl::OnWriteReady() {
write_taskid_ = brillo::MessageLoop::kTaskIdNull;
callback_.Run(EventType::kWrite);
}
CecFdOpenerImpl::CecFdOpenerImpl() = default;
CecFdOpenerImpl::~CecFdOpenerImpl() = default;
std::unique_ptr<CecFd> CecFdOpenerImpl::Open(const base::FilePath& path,
int flags) const {
base::ScopedFD fd(HANDLE_EINTR(open(path.value().c_str(), flags)));
if (!fd.is_valid()) {
PLOG(ERROR) << "Failed to open: " << path.value();
return nullptr;
}
base::ScopedFD epoll_fd(epoll_create(1));
if (!epoll_fd.is_valid()) {
PLOG(ERROR) << "Failed to create epoll descriptor";
return nullptr;
}
epoll_event event;
event.events = EPOLLPRI;
if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, fd.get(), &event)) {
PLOG(ERROR) << "Failed to register device fd on epoll fd";
return nullptr;
}
return std::make_unique<CecFdImpl>(std::move(fd), std::move(epoll_fd));
}
} // namespace cecservice