CEC: Add event handler to default HdmiCec
Event handler polls the file descriptor for
CEC messages and events
Bug: 185434120
Test: manual
Change-Id: Iaaec9a0a74b264e5ec8625d7fce3d821208fd5ac
diff --git a/tv/cec/1.0/default/HdmiCecDefault.cpp b/tv/cec/1.0/default/HdmiCecDefault.cpp
index 7dc0d24..c8dd511 100644
--- a/tv/cec/1.0/default/HdmiCecDefault.cpp
+++ b/tv/cec/1.0/default/HdmiCecDefault.cpp
@@ -22,7 +22,10 @@
#include <fcntl.h>
#include <linux/cec.h>
#include <linux/ioctl.h>
+#include <poll.h>
+#include <pthread.h>
#include <sys/eventfd.h>
+#include <algorithm>
#include "HdmiCecDefault.h"
@@ -35,6 +38,7 @@
int mCecFd;
int mExitFd;
+pthread_t mEventThread;
sp<IHdmiCecCallback> mCallback;
HdmiCecDefault::HdmiCecDefault() {
@@ -281,7 +285,7 @@
return Result::FAILURE_NOT_SUPPORTED;
}
- uint32_t mode = CEC_MODE_INITIATOR;
+ uint32_t mode = CEC_MODE_INITIATOR | CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
ret = ioctl(mCecFd, CEC_S_MODE, &mode);
if (ret) {
LOG(ERROR) << "Unable to set initiator mode, Error = " << strerror(errno);
@@ -289,6 +293,13 @@
return Result::FAILURE_NOT_SUPPORTED;
}
+ /* thread loop for receiving cec messages and hotplug events*/
+ if (pthread_create(&mEventThread, NULL, event_thread, NULL)) {
+ LOG(ERROR) << "Can't create event thread: " << strerror(errno);
+ release();
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+
return Result::SUCCESS;
}
@@ -296,6 +307,7 @@
if (mExitFd > 0) {
uint64_t tmp = 1;
write(mExitFd, &tmp, sizeof(tmp));
+ pthread_join(mEventThread, NULL);
}
if (mExitFd > 0) {
close(mExitFd);
@@ -306,6 +318,83 @@
setCallback(nullptr);
return Void();
}
+
+void* HdmiCecDefault::event_thread(void*) {
+ struct pollfd ufds[3] = {
+ {mCecFd, POLLIN, 0},
+ {mCecFd, POLLERR, 0},
+ {mExitFd, POLLIN, 0},
+ };
+
+ while (1) {
+ ufds[0].revents = 0;
+ ufds[1].revents = 0;
+ ufds[2].revents = 0;
+
+ int ret = poll(ufds, /* size(ufds) = */ 3, /* timeout = */ -1);
+
+ if (ret <= 0) {
+ continue;
+ }
+
+ if (ufds[2].revents == POLLIN) { /* Exit */
+ break;
+ }
+
+ if (ufds[1].revents == POLLERR) { /* CEC Event */
+ struct cec_event ev;
+ ret = ioctl(mCecFd, CEC_DQEVENT, &ev);
+
+ if (ret) {
+ LOG(ERROR) << "CEC_DQEVENT failed, Error = " << strerror(errno);
+ continue;
+ }
+
+ if (ev.event == CEC_EVENT_STATE_CHANGE) {
+ if (mCallback != nullptr) {
+ HotplugEvent hotplugEvent{
+ .connected = (ev.state_change.phys_addr != CEC_PHYS_ADDR_INVALID),
+ .portId = 1};
+ mCallback->onHotplugEvent(hotplugEvent);
+ } else {
+ LOG(ERROR) << "No event callback for hotplug";
+ }
+ }
+ }
+
+ if (ufds[0].revents == POLLIN) { /* CEC Driver */
+ struct cec_msg msg = {};
+ ret = ioctl(mCecFd, CEC_RECEIVE, &msg);
+
+ if (ret) {
+ LOG(ERROR) << "CEC_RECEIVE failed, Error = " << strerror(errno);
+ continue;
+ }
+
+ if (msg.rx_status != CEC_RX_STATUS_OK) {
+ LOG(ERROR) << "msg rx_status = " << msg.rx_status;
+ continue;
+ }
+
+ if (mCallback != nullptr) {
+ size_t length = std::min(msg.len - 1, (uint32_t)MaxLength::MESSAGE_BODY);
+ CecMessage cecMessage{
+ .initiator = static_cast<CecLogicalAddress>(msg.msg[0] >> 4),
+ .destination = static_cast<CecLogicalAddress>(msg.msg[0] & 0xf),
+ };
+ cecMessage.body.resize(length);
+ for (size_t i = 0; i < length; ++i) {
+ cecMessage.body[i] = static_cast<uint8_t>(msg.msg[i + 1]);
+ }
+ mCallback->onCecMessage(cecMessage);
+ } else {
+ LOG(ERROR) << "no event callback for message";
+ }
+ }
+ }
+ return NULL;
+}
+
} // namespace implementation
} // namespace V1_0
} // namespace cec
diff --git a/tv/cec/1.0/default/HdmiCecDefault.h b/tv/cec/1.0/default/HdmiCecDefault.h
index 4812eca..c3682f7 100644
--- a/tv/cec/1.0/default/HdmiCecDefault.h
+++ b/tv/cec/1.0/default/HdmiCecDefault.h
@@ -47,6 +47,7 @@
Return<Result> init();
Return<void> release();
+ static void* event_thread(void*);
};
} // namespace implementation