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