Merge changes Ibd460229,I589668ef
* changes:
Re-organize NNAPI Burst utility classes
Change NN adapter to use ExecutionBurstServer utility
diff --git a/tv/cec/1.0/default/Android.bp b/tv/cec/1.0/default/Android.bp
index b4053df..e4c226d 100644
--- a/tv/cec/1.0/default/Android.bp
+++ b/tv/cec/1.0/default/Android.bp
@@ -15,6 +15,7 @@
srcs: [
"HdmiCec.cpp",
"HdmiCecDefault.cpp",
+ "HdmiCecPort.cpp",
],
shared_libs: [
diff --git a/tv/cec/1.0/default/HdmiCecDefault.cpp b/tv/cec/1.0/default/HdmiCecDefault.cpp
index 299bcf0..26ccb7d 100644
--- a/tv/cec/1.0/default/HdmiCecDefault.cpp
+++ b/tv/cec/1.0/default/HdmiCecDefault.cpp
@@ -16,19 +16,22 @@
#define LOG_TAG "android.hardware.tv.cec@1.0-impl"
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <cutils/properties.h>
+#include <dirent.h>
#include <errno.h>
#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"
+#define PROPERTY_DEVICE_TYPE "ro.hdmi.device_type"
+#define MIN_PORT_ID 0
+#define MAX_PORT_ID 15
+#define INVALID_PHYSICAL_ADDRESS 0xFFFF
+
namespace android {
namespace hardware {
namespace tv {
@@ -36,24 +39,14 @@
namespace V1_0 {
namespace implementation {
-// When set to false, all the CEC commands are discarded. True by default after initialization.
-bool mCecEnabled;
-/*
- * When set to false, HAL does not wake up the system upon receiving <Image View On> or
- * <Text View On>. True by default after initialization.
- */
-bool mWakeupEnabled;
-
-int mCecFd;
-int mExitFd;
-pthread_t mEventThread;
-sp<IHdmiCecCallback> mCallback;
+using android::base::GetUintProperty;
+using std::stoi;
+using std::string;
HdmiCecDefault::HdmiCecDefault() {
- mCecFd = -1;
- mExitFd = -1;
mCecEnabled = false;
mWakeupEnabled = false;
+ mCecControlEnabled = false;
mCallback = nullptr;
}
@@ -68,8 +61,8 @@
return Result::FAILURE_INVALID_ARGS;
}
- struct cec_log_addrs cecLogAddrs;
- int ret = ioctl(mCecFd, CEC_ADAP_G_LOG_ADDRS, &cecLogAddrs);
+ cec_log_addrs cecLogAddrs;
+ int ret = ioctl(mHdmiCecPorts[MIN_PORT_ID]->mCecFd, CEC_ADAP_G_LOG_ADDRS, &cecLogAddrs);
if (ret) {
LOG(ERROR) << "Add logical address failed, Error = " << strerror(errno);
return Result::FAILURE_BUSY;
@@ -135,27 +128,36 @@
cecLogAddrs.features[logAddrIndex][0] = 0;
cecLogAddrs.features[logAddrIndex][1] = 0;
- ret = ioctl(mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
- if (ret) {
- LOG(ERROR) << "Add logical address failed, Error = " << strerror(errno);
- return Result::FAILURE_BUSY;
+ // Return failure only if add logical address fails for all the ports
+ Return<Result> result = Result::FAILURE_BUSY;
+ for (int i = 0; i < mHdmiCecPorts.size(); i++) {
+ ret = ioctl(mHdmiCecPorts[i]->mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
+ if (ret) {
+ LOG(ERROR) << "Add logical address failed for port " << mHdmiCecPorts[i]->mPortId
+ << ", Error = " << strerror(errno);
+ } else {
+ result = Result::SUCCESS;
+ }
}
- return Result::SUCCESS;
+ return result;
}
Return<void> HdmiCecDefault::clearLogicalAddress() {
- struct cec_log_addrs cecLogAddrs;
+ cec_log_addrs cecLogAddrs;
memset(&cecLogAddrs, 0, sizeof(cecLogAddrs));
- int ret = ioctl(mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
- if (ret) {
- LOG(ERROR) << "Clear logical Address failed, Error = " << strerror(errno);
+ for (int i = 0; i < mHdmiCecPorts.size(); i++) {
+ int ret = ioctl(mHdmiCecPorts[i]->mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
+ if (ret) {
+ LOG(ERROR) << "Clear logical Address failed for port " << mHdmiCecPorts[i]->mPortId
+ << ", Error = " << strerror(errno);
+ }
}
return Void();
}
Return<void> HdmiCecDefault::getPhysicalAddress(getPhysicalAddress_cb callback) {
uint16_t addr;
- int ret = ioctl(mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
+ int ret = ioctl(mHdmiCecPorts[MIN_PORT_ID]->mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
if (ret) {
LOG(ERROR) << "Get physical address failed, Error = " << strerror(errno);
callback(Result::FAILURE_INVALID_STATE, addr);
@@ -170,7 +172,7 @@
return SendMessageResult::FAIL;
}
- struct cec_msg cecMsg;
+ cec_msg cecMsg;
memset(&cecMsg, 0, sizeof(cec_msg));
int initiator = static_cast<cec_logical_address_t>(message.initiator);
@@ -182,27 +184,25 @@
}
cecMsg.len = message.body.size() + 1;
- int ret = ioctl(mCecFd, CEC_TRANSMIT, &cecMsg);
+ // Return failure only if send message fails for all the ports
+ Return<SendMessageResult> result = SendMessageResult::FAIL;
+ for (int i = 0; i < mHdmiCecPorts.size(); i++) {
+ int ret = ioctl(mHdmiCecPorts[i]->mCecFd, CEC_TRANSMIT, &cecMsg);
- if (ret) {
- LOG(ERROR) << "Send message failed, Error = " << strerror(errno);
- return SendMessageResult::FAIL;
- }
+ if (ret) {
+ LOG(ERROR) << "Send message failed, Error = " << strerror(errno);
+ continue;
+ }
- if (cecMsg.tx_status != CEC_TX_STATUS_OK) {
- LOG(ERROR) << "Send message tx_status = " << cecMsg.tx_status;
- }
+ if (cecMsg.tx_status != CEC_TX_STATUS_OK) {
+ LOG(ERROR) << "Send message tx_status = " << cecMsg.tx_status;
+ }
- switch (cecMsg.tx_status) {
- case CEC_TX_STATUS_OK:
- return SendMessageResult::SUCCESS;
- case CEC_TX_STATUS_ARB_LOST:
- return SendMessageResult::BUSY;
- case CEC_TX_STATUS_NACK:
- return SendMessageResult::NACK;
- default:
- return SendMessageResult::FAIL;
+ if (result != SendMessageResult::SUCCESS) {
+ result = getSendMessageResult(cecMsg.tx_status);
+ }
}
+ return result;
}
Return<void> HdmiCecDefault::setCallback(const sp<IHdmiCecCallback>& callback) {
@@ -227,19 +227,25 @@
}
Return<void> HdmiCecDefault::getPortInfo(getPortInfo_cb callback) {
- uint16_t addr;
- int ret = ioctl(mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
- if (ret) {
- LOG(ERROR) << "Get port info failed, Error = " << strerror(errno);
+ hidl_vec<HdmiPortInfo> portInfos(mHdmiCecPorts.size());
+ for (int i = 0; i < mHdmiCecPorts.size(); i++) {
+ uint16_t addr = INVALID_PHYSICAL_ADDRESS;
+ int ret = ioctl(mHdmiCecPorts[i]->mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
+ if (ret) {
+ LOG(ERROR) << "Get port info failed for port : " << mHdmiCecPorts[i]->mPortId
+ << ", Error = " << strerror(errno);
+ }
+ HdmiPortType type = HdmiPortType::INPUT;
+ uint32_t deviceType = GetUintProperty<uint32_t>(PROPERTY_DEVICE_TYPE, CEC_DEVICE_PLAYBACK);
+ if (deviceType != CEC_DEVICE_TV && i == MIN_PORT_ID) {
+ type = HdmiPortType::OUTPUT;
+ }
+ portInfos[i] = {.type = type,
+ .portId = mHdmiCecPorts[i]->mPortId,
+ .cecSupported = true,
+ .arcSupported = false,
+ .physicalAddress = addr};
}
-
- unsigned int type = property_get_int32("ro.hdmi.device_type", CEC_DEVICE_PLAYBACK);
- hidl_vec<HdmiPortInfo> portInfos(1);
- portInfos[0] = {.type = (type == CEC_DEVICE_TV ? HdmiPortType::INPUT : HdmiPortType::OUTPUT),
- .portId = 1,
- .cecSupported = true,
- .arcSupported = false,
- .physicalAddress = addr};
callback(portInfos);
return Void();
}
@@ -254,7 +260,9 @@
LOG(DEBUG) << "setOption: WAKEUP: " << value;
mWakeupEnabled = value;
break;
- default:
+ case OptionKey::SYSTEM_CEC_CONTROL:
+ LOG(DEBUG) << "setOption: SYSTEM_CEC_CONTROL: " << value;
+ mCecControlEnabled = value;
break;
}
return Void();
@@ -268,9 +276,9 @@
return Void();
}
-Return<bool> HdmiCecDefault::isConnected(int32_t /*portId*/) {
+Return<bool> HdmiCecDefault::isConnected(int32_t portId) {
uint16_t addr;
- int ret = ioctl(mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
+ int ret = ioctl(mHdmiCecPorts[portId]->mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
if (ret) {
LOG(ERROR) << "Is connected failed, Error = " << strerror(errno);
return false;
@@ -281,79 +289,70 @@
return true;
}
-// Initialise the cec file descriptor
+int getPortId(string cecFilename) {
+ int portId = stoi(cecFilename.substr(3));
+ if (portId >= MIN_PORT_ID && portId <= MAX_PORT_ID) {
+ return portId;
+ } else {
+ return -1;
+ }
+}
+
+// Initialise the cec file descriptors
Return<Result> HdmiCecDefault::init() {
- const char* path = "/dev/cec0";
- mCecFd = open(path, O_RDWR);
- if (mCecFd < 0) {
- LOG(ERROR) << "Failed to open " << path << ", Error = " << strerror(errno);
- return Result::FAILURE_NOT_SUPPORTED;
- }
- mExitFd = eventfd(0, EFD_NONBLOCK);
- if (mExitFd < 0) {
- LOG(ERROR) << "Failed to open eventfd, Error = " << strerror(errno);
- release();
- return Result::FAILURE_NOT_SUPPORTED;
+ const char* parentPath = "/dev/";
+ DIR* dir = opendir(parentPath);
+ const char* cecFilename = "cec";
+
+ while (struct dirent* dirEntry = readdir(dir)) {
+ string filename = dirEntry->d_name;
+ if (filename.compare(0, 3, cecFilename, 0, 3) == 0) {
+ int portId = getPortId(filename);
+ if (portId == -1) {
+ continue;
+ }
+ shared_ptr<HdmiCecPort> hdmiCecPort(new HdmiCecPort(portId));
+ string filepath = parentPath + filename;
+ Result result = hdmiCecPort->init(filepath.c_str());
+ if (result != Result::SUCCESS) {
+ continue;
+ }
+ thread eventThread(&HdmiCecDefault::event_thread, this, hdmiCecPort.get());
+ mEventThreads.push_back(std::move(eventThread));
+ mHdmiCecPorts.push_back(std::move(hdmiCecPort));
+ }
}
- // Ensure the CEC device supports required capabilities
- struct cec_caps caps = {};
- int ret = ioctl(mCecFd, CEC_ADAP_G_CAPS, &caps);
- if (ret) {
- LOG(ERROR) << "Unable to query cec adapter capabilities, Error = " << strerror(errno);
- release();
- return Result::FAILURE_NOT_SUPPORTED;
- }
-
- if (!(caps.capabilities & (CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | CEC_CAP_PASSTHROUGH))) {
- LOG(ERROR) << "Wrong cec adapter capabilities " << caps.capabilities;
- release();
- return Result::FAILURE_NOT_SUPPORTED;
- }
-
- 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);
- release();
- 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();
+ if (mHdmiCecPorts.empty()) {
return Result::FAILURE_NOT_SUPPORTED;
}
mCecEnabled = true;
mWakeupEnabled = true;
+ mCecControlEnabled = true;
return Result::SUCCESS;
}
Return<void> HdmiCecDefault::release() {
- if (mExitFd > 0) {
- uint64_t tmp = 1;
- write(mExitFd, &tmp, sizeof(tmp));
- pthread_join(mEventThread, NULL);
- }
- if (mExitFd > 0) {
- close(mExitFd);
- }
- if (mCecFd > 0) {
- close(mCecFd);
- }
mCecEnabled = false;
mWakeupEnabled = false;
+ mCecControlEnabled = false;
+ for (thread& eventThread : mEventThreads) {
+ if (eventThread.joinable()) {
+ eventThread.join();
+ }
+ }
setCallback(nullptr);
+ mHdmiCecPorts.clear();
+ mEventThreads.clear();
return Void();
}
-void* HdmiCecDefault::event_thread(void*) {
+void HdmiCecDefault::event_thread(HdmiCecPort* hdmiCecPort) {
struct pollfd ufds[3] = {
- {mCecFd, POLLIN, 0},
- {mCecFd, POLLERR, 0},
- {mExitFd, POLLIN, 0},
+ {hdmiCecPort->mCecFd, POLLIN, 0},
+ {hdmiCecPort->mCecFd, POLLERR, 0},
+ {hdmiCecPort->mExitFd, POLLIN, 0},
};
while (1) {
@@ -372,23 +371,23 @@
}
if (ufds[1].revents == POLLERR) { /* CEC Event */
- struct cec_event ev;
- ret = ioctl(mCecFd, CEC_DQEVENT, &ev);
-
- if (!mCecEnabled) {
- continue;
- }
+ cec_event ev;
+ ret = ioctl(hdmiCecPort->mCecFd, CEC_DQEVENT, &ev);
if (ret) {
LOG(ERROR) << "CEC_DQEVENT failed, Error = " << strerror(errno);
continue;
}
+ if (!mCecEnabled) {
+ 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};
+ .portId = hdmiCecPort->mPortId};
mCallback->onHotplugEvent(hotplugEvent);
} else {
LOG(ERROR) << "No event callback for hotplug";
@@ -397,12 +396,8 @@
}
if (ufds[0].revents == POLLIN) { /* CEC Driver */
- struct cec_msg msg = {};
- ret = ioctl(mCecFd, CEC_RECEIVE, &msg);
-
- if (!mCecEnabled) {
- continue;
- }
+ cec_msg msg = {};
+ ret = ioctl(hdmiCecPort->mCecFd, CEC_RECEIVE, &msg);
if (ret) {
LOG(ERROR) << "CEC_RECEIVE failed, Error = " << strerror(errno);
@@ -414,11 +409,20 @@
continue;
}
+ if (!mCecEnabled) {
+ continue;
+ }
+
if (!mWakeupEnabled && isWakeupMessage(msg)) {
LOG(DEBUG) << "Filter wakeup message";
continue;
}
+ if (!mCecControlEnabled && !isTransferableInSleep(msg)) {
+ LOG(DEBUG) << "Filter message in standby mode";
+ continue;
+ }
+
if (mCallback != nullptr) {
size_t length = std::min(msg.len - 1, (uint32_t)MaxLength::MESSAGE_BODY);
CecMessage cecMessage{
@@ -435,14 +439,13 @@
}
}
}
- return NULL;
}
-int HdmiCecDefault::getOpcode(struct cec_msg message) {
- return (static_cast<uint8_t>(message.msg[1]) & 0xff);
+int HdmiCecDefault::getOpcode(cec_msg message) {
+ return static_cast<uint8_t>(message.msg[1]);
}
-bool HdmiCecDefault::isWakeupMessage(struct cec_msg message) {
+bool HdmiCecDefault::isWakeupMessage(cec_msg message) {
int opcode = getOpcode(message);
switch (opcode) {
case CEC_MESSAGE_TEXT_VIEW_ON:
@@ -453,6 +456,61 @@
}
}
+bool HdmiCecDefault::isTransferableInSleep(cec_msg message) {
+ int opcode = getOpcode(message);
+ switch (opcode) {
+ case CEC_MESSAGE_ABORT:
+ case CEC_MESSAGE_DEVICE_VENDOR_ID:
+ case CEC_MESSAGE_GET_CEC_VERSION:
+ case CEC_MESSAGE_GET_MENU_LANGUAGE:
+ case CEC_MESSAGE_GIVE_DEVICE_POWER_STATUS:
+ case CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID:
+ case CEC_MESSAGE_GIVE_OSD_NAME:
+ case CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS:
+ case CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS:
+ case CEC_MESSAGE_REPORT_POWER_STATUS:
+ case CEC_MESSAGE_SET_OSD_NAME:
+ case CEC_MESSAGE_DECK_CONTROL:
+ case CEC_MESSAGE_PLAY:
+ case CEC_MESSAGE_IMAGE_VIEW_ON:
+ case CEC_MESSAGE_TEXT_VIEW_ON:
+ case CEC_MESSAGE_SYSTEM_AUDIO_MODE_REQUEST:
+ return true;
+ case CEC_MESSAGE_USER_CONTROL_PRESSED:
+ return isPowerUICommand(message);
+ default:
+ return false;
+ }
+}
+
+int HdmiCecDefault::getFirstParam(cec_msg message) {
+ return static_cast<uint8_t>(message.msg[2]);
+}
+
+bool HdmiCecDefault::isPowerUICommand(cec_msg message) {
+ int uiCommand = getFirstParam(message);
+ switch (uiCommand) {
+ case CEC_OP_UI_CMD_POWER:
+ case CEC_OP_UI_CMD_DEVICE_ROOT_MENU:
+ case CEC_OP_UI_CMD_POWER_ON_FUNCTION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+Return<SendMessageResult> HdmiCecDefault::getSendMessageResult(int tx_status) {
+ switch (tx_status) {
+ case CEC_TX_STATUS_OK:
+ return SendMessageResult::SUCCESS;
+ case CEC_TX_STATUS_ARB_LOST:
+ return SendMessageResult::BUSY;
+ case CEC_TX_STATUS_NACK:
+ return SendMessageResult::NACK;
+ default:
+ return SendMessageResult::FAIL;
+ }
+}
} // 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 c1bb2c7..6574429 100644
--- a/tv/cec/1.0/default/HdmiCecDefault.h
+++ b/tv/cec/1.0/default/HdmiCecDefault.h
@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-#include <android/hardware/tv/cec/1.0/IHdmiCec.h>
#include <hardware/hdmi_cec.h>
+#include <linux/cec.h>
+#include <thread>
+#include <vector>
+#include "HdmiCecPort.h"
namespace android {
namespace hardware {
@@ -24,7 +26,12 @@
namespace V1_0 {
namespace implementation {
-struct HdmiCecDefault : public IHdmiCec, public hidl_death_recipient {
+using std::shared_ptr;
+using std::thread;
+using std::vector;
+
+class HdmiCecDefault : public IHdmiCec, public hidl_death_recipient {
+ public:
HdmiCecDefault();
~HdmiCecDefault();
// Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow.
@@ -47,11 +54,35 @@
Return<Result> init();
Return<void> release();
- static void* event_thread(void*);
- static int getOpcode(struct cec_msg message);
- static bool isWakeupMessage(struct cec_msg message);
-};
+ private:
+ void event_thread(HdmiCecPort* hdmiCecPort);
+ static int getOpcode(cec_msg message);
+ static int getFirstParam(cec_msg message);
+ static bool isWakeupMessage(cec_msg message);
+ static bool isTransferableInSleep(cec_msg message);
+ static bool isPowerUICommand(cec_msg message);
+ static Return<SendMessageResult> getSendMessageResult(int tx_status);
+
+ vector<thread> mEventThreads;
+ vector<shared_ptr<HdmiCecPort>> mHdmiCecPorts;
+
+ // When set to false, all the CEC commands are discarded. True by default after initialization.
+ bool mCecEnabled;
+ /*
+ * When set to false, HAL does not wake up the system upon receiving <Image View On> or
+ * <Text View On>. True by default after initialization.
+ */
+ bool mWakeupEnabled;
+ /*
+ * Updated when system goes into or comes out of standby mode.
+ * When set to true, Android system is handling CEC commands.
+ * When set to false, microprocessor is handling CEC commands.
+ * True by default after initialization.
+ */
+ bool mCecControlEnabled;
+ sp<IHdmiCecCallback> mCallback;
+};
} // namespace implementation
} // namespace V1_0
} // namespace cec
diff --git a/tv/cec/1.0/default/HdmiCecPort.cpp b/tv/cec/1.0/default/HdmiCecPort.cpp
new file mode 100755
index 0000000..73dda12
--- /dev/null
+++ b/tv/cec/1.0/default/HdmiCecPort.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "android.hardware.tv.cec@1.0-impl"
+
+#include <android-base/logging.h>
+#include <errno.h>
+#include <linux/cec.h>
+#include <linux/ioctl.h>
+#include <sys/eventfd.h>
+#include <algorithm>
+
+#include "HdmiCecPort.h"
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace cec {
+namespace V1_0 {
+namespace implementation {
+
+HdmiCecPort::HdmiCecPort(unsigned int portId) {
+ mPortId = portId;
+ mCecFd = -1;
+ mExitFd = -1;
+}
+
+HdmiCecPort::~HdmiCecPort() {
+ release();
+}
+
+// Initialise the cec file descriptor
+Return<Result> HdmiCecPort::init(const char* path) {
+ mCecFd = open(path, O_RDWR);
+ if (mCecFd < 0) {
+ LOG(ERROR) << "Failed to open " << path << ", Error = " << strerror(errno);
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+ mExitFd = eventfd(0, EFD_NONBLOCK);
+ if (mExitFd < 0) {
+ LOG(ERROR) << "Failed to open eventfd, Error = " << strerror(errno);
+ release();
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+
+ // Ensure the CEC device supports required capabilities
+ struct cec_caps caps = {};
+ int ret = ioctl(mCecFd, CEC_ADAP_G_CAPS, &caps);
+ if (ret) {
+ LOG(ERROR) << "Unable to query cec adapter capabilities, Error = " << strerror(errno);
+ release();
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+
+ if (!(caps.capabilities & (CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | CEC_CAP_PASSTHROUGH))) {
+ LOG(ERROR) << "Wrong cec adapter capabilities " << caps.capabilities;
+ release();
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+
+ 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);
+ release();
+ return Result::FAILURE_NOT_SUPPORTED;
+ }
+ return Result::SUCCESS;
+}
+
+Return<void> HdmiCecPort::release() {
+ if (mExitFd > 0) {
+ uint64_t tmp = 1;
+ write(mExitFd, &tmp, sizeof(tmp));
+ }
+ if (mExitFd > 0) {
+ close(mExitFd);
+ }
+ if (mCecFd > 0) {
+ close(mCecFd);
+ }
+ return Void();
+}
+} // namespace implementation
+} // namespace V1_0
+} // namespace cec
+} // namespace tv
+} // namespace hardware
+} // namespace android
diff --git a/tv/cec/1.0/default/HdmiCecPort.h b/tv/cec/1.0/default/HdmiCecPort.h
new file mode 100755
index 0000000..2a2fdef
--- /dev/null
+++ b/tv/cec/1.0/default/HdmiCecPort.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <android/hardware/tv/cec/1.0/IHdmiCec.h>
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace cec {
+namespace V1_0 {
+namespace implementation {
+
+class HdmiCecPort {
+ public:
+ HdmiCecPort(unsigned int portId);
+ ~HdmiCecPort();
+ Return<Result> init(const char* path);
+ Return<void> release();
+
+ unsigned int mPortId;
+ int mCecFd;
+ int mExitFd;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace cec
+} // namespace tv
+} // namespace hardware
+} // namespace android