From 2cc1d0e2ab93f240fe1e664aa16165dfa17cf2d2 Mon Sep 17 00:00:00 2001 From: Konsta Date: Tue, 4 Feb 2025 20:18:32 +0200 Subject: [PATCH] cec: add rpi hal implementation * TODO: Convert to AIDL. --- cec/Android.bp | 49 ++-- cec/{HdmiCecDefault.cpp => HdmiCec.cpp} | 232 ++++++++---------- cec/{HdmiCecDefault.h => HdmiCec.h} | 10 +- cec/HdmiCecPort.cpp | 5 +- cec/HdmiCecPort.h | 2 + ...ndroid.hardware.tv.cec@1.0-service.rpi.rc} | 2 +- ...ndroid.hardware.tv.cec@1.0-service.rpi.xml | 11 + cec/service.cpp | 30 ++- device.mk | 7 + keylayout/Generic.kl | 6 + .../AndroidRpiOverlay/res/values/config.xml | 4 + .../AndroidTvRpiOverlay/res/values/config.xml | 4 + ramdisk/ueventd.rpi4.rc | 4 + sepolicy/device.te | 1 + sepolicy/file_contexts | 5 + sepolicy/hal_tv_cec_default.te | 1 + vendor.prop | 4 + 17 files changed, 207 insertions(+), 170 deletions(-) rename cec/{HdmiCecDefault.cpp => HdmiCec.cpp} (66%) rename cec/{HdmiCecDefault.h => HdmiCec.h} (96%) mode change 100755 => 100644 cec/HdmiCecPort.cpp mode change 100755 => 100644 cec/HdmiCecPort.h rename cec/{android.hardware.tv.cec@1.0-service.rc => android.hardware.tv.cec@1.0-service.rpi.rc} (55%) create mode 100644 cec/android.hardware.tv.cec@1.0-service.rpi.xml create mode 100644 sepolicy/device.te create mode 100644 sepolicy/hal_tv_cec_default.te diff --git a/cec/Android.bp b/cec/Android.bp index 70617d8..9ad85c9 100644 --- a/cec/Android.bp +++ b/cec/Android.bp @@ -1,43 +1,26 @@ -cc_library_shared { - name: "android.hardware.tv.cec@1.0-impl", - defaults: ["hidl_defaults"], - vendor: true, - relative_install_path: "hw", - srcs: [ - "HdmiCecDefault.cpp", - "HdmiCecPort.cpp", - ], - - shared_libs: [ - "libhidlbase", - "liblog", - "libbase", - "libcutils", - "libutils", - "libhardware", - "android.hardware.tv.cec@1.0", - ], - -} +// Copyright (C) 2021 The Android Open Source Project +// Copyright (C) 2025 KonstaKANG +// +// SPDX-License-Identifier: Apache-2.0 cc_binary { - name: "android.hardware.tv.cec@1.0-service", - defaults: ["hidl_defaults"], + name: "android.hardware.tv.cec@1.0-service.rpi", relative_install_path: "hw", + init_rc: ["android.hardware.tv.cec@1.0-service.rpi.rc"], + vintf_fragments: ["android.hardware.tv.cec@1.0-service.rpi.xml"], vendor: true, - init_rc: ["android.hardware.tv.cec@1.0-service.rc"], - srcs: ["service.cpp"], - + srcs: [ + "HdmiCec.cpp", + "HdmiCecPort.cpp", + "service.cpp", + ], shared_libs: [ - "liblog", - "libcutils", - "libdl", + "android.hardware.tv.cec@1.0", "libbase", - "libutils", - "libhardware_legacy", + "libcutils", "libhardware", "libhidlbase", - "android.hardware.tv.cec@1.0", + "liblog", + "libutils", ], - } diff --git a/cec/HdmiCecDefault.cpp b/cec/HdmiCec.cpp similarity index 66% rename from cec/HdmiCecDefault.cpp rename to cec/HdmiCec.cpp index 2a5197c..158e801 100644 --- a/cec/HdmiCecDefault.cpp +++ b/cec/HdmiCec.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2025 KonstaKANG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +15,8 @@ * limitations under the License. */ -#define LOG_TAG "android.hardware.tv.cec@1.0-impl" +#define LOG_TAG "android.hardware.tv.cec@1.0-service.rpi" + #include #include @@ -25,12 +27,11 @@ #include #include -#include "HdmiCecDefault.h" +#include "HdmiCec.h" -#define PROPERTY_DEVICE_TYPE "ro.hdmi.device_type" -#define MIN_PORT_ID 0 -#define MAX_PORT_ID 15 -#define INVALID_PHYSICAL_ADDRESS 0xFFFF +#define PROPERTY_CEC_DEVICE "persist.hdmi.cec_device" +#define PROPERTY_CEC_VERSION "ro.hdmi.cec_version" +#define PROPERTY_VENDOR_ID "ro.hdmi.vendor_id" namespace android { namespace hardware { @@ -39,30 +40,34 @@ namespace cec { namespace V1_0 { namespace implementation { -using android::base::GetUintProperty; -using std::stoi; +using android::base::GetProperty; using std::string; -HdmiCecDefault::HdmiCecDefault() { +HdmiCec::HdmiCec() { mCecEnabled = false; mWakeupEnabled = false; mCecControlEnabled = false; mCallback = nullptr; + + Result result = init(); + if (result != Result::SUCCESS) { + LOG(ERROR) << "Failed to init HDMI-CEC HAL"; + } } -HdmiCecDefault::~HdmiCecDefault() { +HdmiCec::~HdmiCec() { release(); } // Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow. -Return HdmiCecDefault::addLogicalAddress(CecLogicalAddress addr) { +Return HdmiCec::addLogicalAddress(CecLogicalAddress addr) { if (addr < CecLogicalAddress::TV || addr >= CecLogicalAddress::BROADCAST) { LOG(ERROR) << "Add logical address failed, Invalid address"; return Result::FAILURE_INVALID_ARGS; } cec_log_addrs cecLogAddrs; - int ret = ioctl(mHdmiCecPorts[MIN_PORT_ID]->mCecFd, CEC_ADAP_G_LOG_ADDRS, &cecLogAddrs); + int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_G_LOG_ADDRS, &cecLogAddrs); if (ret) { LOG(ERROR) << "Add logical address failed, Error = " << strerror(errno); return Result::FAILURE_BUSY; @@ -119,7 +124,6 @@ Return HdmiCecDefault::addLogicalAddress(CecLogicalAddress addr) { } int logAddrIndex = cecLogAddrs.num_log_addrs; - cecLogAddrs.num_log_addrs += 1; cecLogAddrs.log_addr[logAddrIndex] = static_cast(addr); cecLogAddrs.log_addr_type[logAddrIndex] = logAddrType; @@ -128,46 +132,45 @@ Return HdmiCecDefault::addLogicalAddress(CecLogicalAddress addr) { cecLogAddrs.features[logAddrIndex][0] = 0; cecLogAddrs.features[logAddrIndex][1] = 0; - // Return failure only if add logical address fails for all the ports - Return 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; - } + ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs); + if (ret) { + LOG(ERROR) << "Add logical address failed for port " << mHdmiCecPorts[0]->mPortId + << ", Error = " << strerror(errno); + return Result::FAILURE_BUSY; } - return result; + + return Result::SUCCESS; } -Return HdmiCecDefault::clearLogicalAddress() { +Return HdmiCec::clearLogicalAddress() { cec_log_addrs cecLogAddrs; memset(&cecLogAddrs, 0, sizeof(cecLogAddrs)); - 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); - } + + int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs); + if (ret) { + LOG(ERROR) << "Clear logical Address failed for port " << mHdmiCecPorts[0]->mPortId + << ", Error = " << strerror(errno); } + return Void(); } -Return HdmiCecDefault::getPhysicalAddress(getPhysicalAddress_cb callback) { +Return HdmiCec::getPhysicalAddress(getPhysicalAddress_cb callback) { uint16_t addr; - int ret = ioctl(mHdmiCecPorts[MIN_PORT_ID]->mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr); + + int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr); if (ret) { LOG(ERROR) << "Get physical address failed, Error = " << strerror(errno); callback(Result::FAILURE_INVALID_STATE, addr); return Void(); } + callback(Result::SUCCESS, addr); + return Void(); } -Return HdmiCecDefault::sendMessage(const CecMessage& message) { +Return HdmiCec::sendMessage(const CecMessage& message) { if (!mCecEnabled) { return SendMessageResult::FAIL; } @@ -184,28 +187,20 @@ Return HdmiCecDefault::sendMessage(const CecMessage& message) } cecMsg.len = message.body.size() + 1; - // Return failure only if send message fails for all the ports - Return 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); - continue; - } - - if (cecMsg.tx_status != CEC_TX_STATUS_OK) { - LOG(ERROR) << "Send message tx_status = " << cecMsg.tx_status; - } - - if (result != SendMessageResult::SUCCESS) { - result = getSendMessageResult(cecMsg.tx_status); - } + int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_TRANSMIT, &cecMsg); + if (ret) { + LOG(ERROR) << "Send message failed, Error = " << strerror(errno); + return SendMessageResult::FAIL; } - return result; + + if (cecMsg.tx_status != CEC_TX_STATUS_OK) { + LOG(ERROR) << "Send message tx_status = " << cecMsg.tx_status; + } + + return getSendMessageResult(cecMsg.tx_status); } -Return HdmiCecDefault::setCallback(const sp& callback) { +Return HdmiCec::setCallback(const sp& callback) { if (mCallback != nullptr) { mCallback->unlinkToDeath(this); mCallback = nullptr; @@ -215,42 +210,40 @@ Return HdmiCecDefault::setCallback(const sp& callback) { mCallback = callback; mCallback->linkToDeath(this, 0 /*cookie*/); } + return Void(); } -Return HdmiCecDefault::getCecVersion() { - return property_get_int32("ro.hdmi.cec_version", CEC_OP_CEC_VERSION_1_4); +Return HdmiCec::getCecVersion() { + return property_get_int32(PROPERTY_CEC_VERSION, CEC_OP_CEC_VERSION_1_4); } -Return HdmiCecDefault::getVendorId() { - return property_get_int32("ro.hdmi.vendor_id", 0x000c03 /* HDMI LLC vendor ID */); +Return HdmiCec::getVendorId() { + return property_get_int32(PROPERTY_VENDOR_ID, 0x000c03 /* HDMI LLC vendor ID */); } -Return HdmiCecDefault::getPortInfo(getPortInfo_cb callback) { - hidl_vec 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(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}; +Return HdmiCec::getPortInfo(getPortInfo_cb callback) { + uint16_t addr = CEC_PHYS_ADDR_INVALID; + + int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr); + if (ret) { + LOG(ERROR) << "Get port info failed for port : " << mHdmiCecPorts[0]->mPortId + << ", Error = " << strerror(errno); } + + hidl_vec portInfos { + {.type = HdmiPortType::OUTPUT, + .portId = mHdmiCecPorts[0]->mPortId, + .cecSupported = true, + .arcSupported = false, + .physicalAddress = addr} + }; callback(portInfos); + return Void(); } -Return HdmiCecDefault::setOption(OptionKey key, bool value) { +Return HdmiCec::setOption(OptionKey key, bool value) { switch (key) { case OptionKey::ENABLE_CEC: LOG(DEBUG) << "setOption: Enable CEC: " << value; @@ -265,82 +258,63 @@ Return HdmiCecDefault::setOption(OptionKey key, bool value) { mCecControlEnabled = value; break; } + return Void(); } -Return HdmiCecDefault::setLanguage(const hidl_string& /*language*/) { +Return HdmiCec::setLanguage(const hidl_string& language __unused) { return Void(); } -Return HdmiCecDefault::enableAudioReturnChannel(int32_t /*portId*/, bool /*enable*/) { +Return HdmiCec::enableAudioReturnChannel(int32_t portId __unused, bool enable __unused) { return Void(); } -Return HdmiCecDefault::isConnected(int32_t portId) { - uint16_t addr; - if (portId < 0 || portId >= mHdmiCecPorts.size()) { - LOG(ERROR) << "Port id is out of bounds, portId = " << portId; - return false; - } - int ret = ioctl(mHdmiCecPorts[portId]->mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr); +Return HdmiCec::isConnected(int32_t portId __unused) { + uint16_t addr = CEC_PHYS_ADDR_INVALID; + + int ret = ioctl(mHdmiCecPorts[0]->mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr); if (ret) { LOG(ERROR) << "Is connected failed, Error = " << strerror(errno); return false; } + if (addr == CEC_PHYS_ADDR_INVALID) { return false; } + return true; } -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 descriptor +Return HdmiCec::init() { + string cecDevice = GetProperty(PROPERTY_CEC_DEVICE, "cec0"); + string devicePath = "/dev/" + cecDevice; + int portId = stoi(cecDevice.substr(3)); -// Initialise the cec file descriptors -Return HdmiCecDefault::init() { - 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(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)); - } - } - - if (mHdmiCecPorts.empty()) { + shared_ptr hdmiCecPort(new HdmiCecPort(portId)); + Result result = hdmiCecPort->init(devicePath.c_str()); + if (result != Result::SUCCESS) { return Result::FAILURE_NOT_SUPPORTED; } + thread eventThread(&HdmiCec::event_thread, this, hdmiCecPort.get()); + mEventThreads.push_back(std::move(eventThread)); + mHdmiCecPorts.push_back(std::move(hdmiCecPort)); + LOG(INFO) << "Using CEC device " << devicePath; + mCecEnabled = true; mWakeupEnabled = true; mCecControlEnabled = true; + return Result::SUCCESS; } -Return HdmiCecDefault::release() { +Return HdmiCec::release() { mCecEnabled = false; mWakeupEnabled = false; mCecControlEnabled = false; + for (thread& eventThread : mEventThreads) { if (eventThread.joinable()) { eventThread.join(); @@ -349,10 +323,11 @@ Return HdmiCecDefault::release() { setCallback(nullptr); mHdmiCecPorts.clear(); mEventThreads.clear(); + return Void(); } -void HdmiCecDefault::event_thread(HdmiCecPort* hdmiCecPort) { +void HdmiCec::event_thread(HdmiCecPort* hdmiCecPort) { struct pollfd ufds[3] = { {hdmiCecPort->mCecFd, POLLIN, 0}, {hdmiCecPort->mCecFd, POLLERR, 0}, @@ -445,11 +420,11 @@ void HdmiCecDefault::event_thread(HdmiCecPort* hdmiCecPort) { } } -int HdmiCecDefault::getOpcode(cec_msg message) { +int HdmiCec::getOpcode(cec_msg message) { return static_cast(message.msg[1]); } -bool HdmiCecDefault::isWakeupMessage(cec_msg message) { +bool HdmiCec::isWakeupMessage(cec_msg message) { int opcode = getOpcode(message); switch (opcode) { case CEC_MESSAGE_TEXT_VIEW_ON: @@ -460,7 +435,7 @@ bool HdmiCecDefault::isWakeupMessage(cec_msg message) { } } -bool HdmiCecDefault::isTransferableInSleep(cec_msg message) { +bool HdmiCec::isTransferableInSleep(cec_msg message) { int opcode = getOpcode(message); switch (opcode) { case CEC_MESSAGE_ABORT: @@ -487,11 +462,11 @@ bool HdmiCecDefault::isTransferableInSleep(cec_msg message) { } } -int HdmiCecDefault::getFirstParam(cec_msg message) { +int HdmiCec::getFirstParam(cec_msg message) { return static_cast(message.msg[2]); } -bool HdmiCecDefault::isPowerUICommand(cec_msg message) { +bool HdmiCec::isPowerUICommand(cec_msg message) { int uiCommand = getFirstParam(message); switch (uiCommand) { case CEC_OP_UI_CMD_POWER: @@ -503,7 +478,7 @@ bool HdmiCecDefault::isPowerUICommand(cec_msg message) { } } -Return HdmiCecDefault::getSendMessageResult(int tx_status) { +Return HdmiCec::getSendMessageResult(int tx_status) { switch (tx_status) { case CEC_TX_STATUS_OK: return SendMessageResult::SUCCESS; @@ -515,6 +490,7 @@ Return HdmiCecDefault::getSendMessageResult(int tx_status) { return SendMessageResult::FAIL; } } + } // namespace implementation } // namespace V1_0 } // namespace cec diff --git a/cec/HdmiCecDefault.h b/cec/HdmiCec.h similarity index 96% rename from cec/HdmiCecDefault.h rename to cec/HdmiCec.h index 6574429..4875e02 100644 --- a/cec/HdmiCecDefault.h +++ b/cec/HdmiCec.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2025 KonstaKANG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include #include #include @@ -30,10 +32,10 @@ using std::shared_ptr; using std::thread; using std::vector; -class HdmiCecDefault : public IHdmiCec, public hidl_death_recipient { +class HdmiCec : public IHdmiCec, public hidl_death_recipient { public: - HdmiCecDefault(); - ~HdmiCecDefault(); + HdmiCec(); + ~HdmiCec(); // Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow. Return addLogicalAddress(CecLogicalAddress addr) override; Return clearLogicalAddress() override; @@ -81,8 +83,10 @@ class HdmiCecDefault : public IHdmiCec, public hidl_death_recipient { * True by default after initialization. */ bool mCecControlEnabled; + sp mCallback; }; + } // namespace implementation } // namespace V1_0 } // namespace cec diff --git a/cec/HdmiCecPort.cpp b/cec/HdmiCecPort.cpp old mode 100755 new mode 100644 index 73dda12..6eddf1a --- a/cec/HdmiCecPort.cpp +++ b/cec/HdmiCecPort.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2025 KonstaKANG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#define LOG_TAG "android.hardware.tv.cec@1.0-impl" + +#define LOG_TAG "android.hardware.tv.cec@1.0-service.rpi" #include #include @@ -93,6 +95,7 @@ Return HdmiCecPort::release() { } return Void(); } + } // namespace implementation } // namespace V1_0 } // namespace cec diff --git a/cec/HdmiCecPort.h b/cec/HdmiCecPort.h old mode 100755 new mode 100644 index 2a2fdef..bf343f8 --- a/cec/HdmiCecPort.h +++ b/cec/HdmiCecPort.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2025 KonstaKANG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include namespace android { diff --git a/cec/android.hardware.tv.cec@1.0-service.rc b/cec/android.hardware.tv.cec@1.0-service.rpi.rc similarity index 55% rename from cec/android.hardware.tv.cec@1.0-service.rc rename to cec/android.hardware.tv.cec@1.0-service.rpi.rc index 6d25229..fcfe084 100644 --- a/cec/android.hardware.tv.cec@1.0-service.rc +++ b/cec/android.hardware.tv.cec@1.0-service.rpi.rc @@ -1,4 +1,4 @@ -service vendor.cec-hal-1-0 /vendor/bin/hw/android.hardware.tv.cec@1.0-service +service vendor.cec-hal-1-0-rpi /vendor/bin/hw/android.hardware.tv.cec@1.0-service.rpi interface android.hardware.tv.cec@1.0::IHdmiCec default class hal user system diff --git a/cec/android.hardware.tv.cec@1.0-service.rpi.xml b/cec/android.hardware.tv.cec@1.0-service.rpi.xml new file mode 100644 index 0000000..5afa59d --- /dev/null +++ b/cec/android.hardware.tv.cec@1.0-service.rpi.xml @@ -0,0 +1,11 @@ + + + android.hardware.tv.cec + hwbinder + 1.0 + + IHdmiCec + default + + + diff --git a/cec/service.cpp b/cec/service.cpp index 74b1f62..bb86b74 100644 --- a/cec/service.cpp +++ b/cec/service.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2025 KonstaKANG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +15,35 @@ * limitations under the License. */ -#define LOG_TAG "android.hardware.tv.cec@1.0-service" +#define LOG_TAG "android.hardware.tv.cec@1.0-service-rpi" #include #include +#include "HdmiCec.h" +using android::hardware::configureRpcThreadpool; +using android::hardware::joinRpcThreadpool; using android::hardware::tv::cec::V1_0::IHdmiCec; -using android::hardware::defaultPassthroughServiceImplementation; +using android::hardware::tv::cec::V1_0::implementation::HdmiCec; + +using android::OK; +using android::status_t; int main() { - return defaultPassthroughServiceImplementation(); + configureRpcThreadpool(1, true /* callerWillJoin */); + + android::sp service = new HdmiCec(); + + status_t status = service->registerAsService(); + if (status != OK) { + ALOGE("Cannot register HDMI-CEC HAL service."); + return 1; + } + + ALOGI("HDMI-CEC HAL ready."); + joinRpcThreadpool(); + + // Under normal cases, execution will not reach this line. + ALOGE("HDMI-CEC HAL failed to join thread pool."); + return 1; } diff --git a/device.mk b/device.mk index 9f1530b..4b94c3d 100644 --- a/device.mk +++ b/device.mk @@ -134,6 +134,13 @@ PRODUCT_COPY_FILES += \ PRODUCT_COPY_FILES += \ $(DEVICE_PATH)/camera/media_profiles_V1_0.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_profiles_V1_0.xml +# CEC +PRODUCT_PACKAGES += \ + android.hardware.tv.cec@1.0-service.rpi + +PRODUCT_COPY_FILES += \ + frameworks/native/data/etc/android.hardware.hdmi.cec.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.hdmi.cec.xml + # Debugfs PRODUCT_SET_DEBUGFS_RESTRICTIONS := false diff --git a/keylayout/Generic.kl b/keylayout/Generic.kl index f8d3bff..284b4c4 100644 --- a/keylayout/Generic.kl +++ b/keylayout/Generic.kl @@ -430,6 +430,12 @@ key 657 MACRO_2 key 658 MACRO_3 key 659 MACRO_4 +# CEC +key 352 ENTER +key 618 HOME +key 141 POWER +key 174 BACK + # Keys defined by HID usages key usage 0x0c0065 SCREENSHOT FALLBACK_USAGE_MAPPING key usage 0x0c0067 WINDOW FALLBACK_USAGE_MAPPING diff --git a/overlay/AndroidRpiOverlay/res/values/config.xml b/overlay/AndroidRpiOverlay/res/values/config.xml index 4a06fd4..e17fa96 100644 --- a/overlay/AndroidRpiOverlay/res/values/config.xml +++ b/overlay/AndroidRpiOverlay/res/values/config.xml @@ -117,4 +117,8 @@ --> 2 + + false + true + diff --git a/overlay/AndroidTvRpiOverlay/res/values/config.xml b/overlay/AndroidTvRpiOverlay/res/values/config.xml index 2022d9e..ebf4b84 100644 --- a/overlay/AndroidTvRpiOverlay/res/values/config.xml +++ b/overlay/AndroidTvRpiOverlay/res/values/config.xml @@ -55,4 +55,8 @@ --> 1 + + false + true + diff --git a/ramdisk/ueventd.rpi4.rc b/ramdisk/ueventd.rpi4.rc index 05ce8a1..27e1d93 100644 --- a/ramdisk/ueventd.rpi4.rc +++ b/ramdisk/ueventd.rpi4.rc @@ -6,6 +6,10 @@ /dev/v4l-subdev* 0660 system camera /dev/video* 0660 system camera +# CEC +/dev/cec0 0660 system graphics +/dev/cec1 0660 system graphics + # DMA /dev/dma_heap/linux,cma 0666 system graphics diff --git a/sepolicy/device.te b/sepolicy/device.te new file mode 100644 index 0000000..14cdde4 --- /dev/null +++ b/sepolicy/device.te @@ -0,0 +1 @@ +type cec_device, dev_type; diff --git a/sepolicy/file_contexts b/sepolicy/file_contexts index 30d753b..4cba698 100644 --- a/sepolicy/file_contexts +++ b/sepolicy/file_contexts @@ -2,6 +2,11 @@ /sys/class/rfkill/rfkill[0-9]/state u:object_r:sysfs_bluetooth_writable:s0 /vendor/bin/hw/android\.hardware\.bluetooth-service\.rpi u:object_r:hal_bluetooth_default_exec:s0 +# CEC +/dev/cec0 u:object_r:cec_device:s0 +/dev/cec1 u:object_r:cec_device:s0 +/vendor/bin/hw/android\.hardware\.tv\.cec@1\.0-service\.rpi u:object_r:hal_tv_cec_default_exec:s0 + # DRM /vendor/bin/hw/android\.hardware\.drm-service\.clearkey u:object_r:hal_drm_clearkey_exec:s0 /vendor/bin/hw/android\.hardware\.drm-service\.widevine(.*)? u:object_r:hal_drm_widevine_exec:s0 diff --git a/sepolicy/hal_tv_cec_default.te b/sepolicy/hal_tv_cec_default.te new file mode 100644 index 0000000..cbb5e36 --- /dev/null +++ b/sepolicy/hal_tv_cec_default.te @@ -0,0 +1 @@ +allow hal_tv_cec_default cec_device:chr_file rw_file_perms; diff --git a/vendor.prop b/vendor.prop index cb17e95..5bff5ac 100644 --- a/vendor.prop +++ b/vendor.prop @@ -36,6 +36,10 @@ persist.bluetooth.a2dp_aac.vbr_supported=true media.settings.xml=/vendor/etc/media_profiles_V1_0.xml ro.hardware.camera=libcamera +# CEC +persist.hdmi.cec_device=cec0 +ro.hdmi.device_type=4 + # Display debug.drm.mode.force=1920x1080