From bbfd9e6e925ff9647355e87fab3542bbaedb35d0 Mon Sep 17 00:00:00 2001 From: Konsta Date: Wed, 5 Feb 2025 20:03:05 +0200 Subject: [PATCH] cec: copy hidl hal * Copy from hardware/interfaces/tv/cec/1.0/default at b04e2f3df5ebbbeea46f555d0965357f05aa1457. --- cec/Android.bp | 76 +++ cec/HdmiCec.cpp | 435 +++++++++++++++ cec/HdmiCec.h | 109 ++++ cec/HdmiCecDefault.cpp | 523 ++++++++++++++++++ cec/HdmiCecDefault.h | 91 +++ cec/HdmiCecMock.cpp | 318 +++++++++++ cec/HdmiCecMock.h | 122 ++++ cec/HdmiCecPort.cpp | 101 ++++ cec/HdmiCecPort.h | 42 ++ ...ndroid.hardware.tv.cec@1.0-service.mock.rc | 5 + ...droid.hardware.tv.cec@1.0-service.mock.xml | 11 + cec/android.hardware.tv.cec@1.0-service.rc | 5 + cec/service.cpp | 27 + cec/serviceMock.cpp | 40 ++ 14 files changed, 1905 insertions(+) create mode 100644 cec/Android.bp create mode 100644 cec/HdmiCec.cpp create mode 100644 cec/HdmiCec.h create mode 100644 cec/HdmiCecDefault.cpp create mode 100644 cec/HdmiCecDefault.h create mode 100644 cec/HdmiCecMock.cpp create mode 100644 cec/HdmiCecMock.h create mode 100755 cec/HdmiCecPort.cpp create mode 100755 cec/HdmiCecPort.h create mode 100644 cec/android.hardware.tv.cec@1.0-service.mock.rc create mode 100644 cec/android.hardware.tv.cec@1.0-service.mock.xml create mode 100644 cec/android.hardware.tv.cec@1.0-service.rc create mode 100644 cec/service.cpp create mode 100644 cec/serviceMock.cpp diff --git a/cec/Android.bp b/cec/Android.bp new file mode 100644 index 0000000..abde1f2 --- /dev/null +++ b/cec/Android.bp @@ -0,0 +1,76 @@ +package { + default_team: "trendy_team_tv_os", + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "hardware_interfaces_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["hardware_interfaces_license"], +} + +cc_library_shared { + name: "android.hardware.tv.cec@1.0-impl", + defaults: ["hidl_defaults"], + vendor: true, + relative_install_path: "hw", + srcs: [ + "HdmiCec.cpp", + "HdmiCecDefault.cpp", + "HdmiCecPort.cpp", + ], + + shared_libs: [ + "libhidlbase", + "liblog", + "libbase", + "libcutils", + "libutils", + "libhardware", + "android.hardware.tv.cec@1.0", + ], + +} + +cc_binary { + name: "android.hardware.tv.cec@1.0-service", + defaults: ["hidl_defaults"], + relative_install_path: "hw", + vendor: true, + init_rc: ["android.hardware.tv.cec@1.0-service.rc"], + srcs: ["service.cpp"], + + shared_libs: [ + "liblog", + "libcutils", + "libdl", + "libbase", + "libutils", + "libhardware_legacy", + "libhardware", + "libhidlbase", + "android.hardware.tv.cec@1.0", + ], + +} + +cc_binary { + name: "android.hardware.tv.cec@1.0-service.mock", + vintf_fragments: ["android.hardware.tv.cec@1.0-service.mock.xml"], + relative_install_path: "hw", + vendor: true, + init_rc: ["android.hardware.tv.cec@1.0-service.mock.rc"], + srcs: [ + "serviceMock.cpp", + "HdmiCecMock.cpp", + ], + + shared_libs: [ + "liblog", + "libcutils", + "libbase", + "libutils", + "libhardware", + "libhidlbase", + "android.hardware.tv.cec@1.0", + ], +} diff --git a/cec/HdmiCec.cpp b/cec/HdmiCec.cpp new file mode 100644 index 0000000..f05f610 --- /dev/null +++ b/cec/HdmiCec.cpp @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2016 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 + +#include +#include +#include "HdmiCec.h" +#include "HdmiCecDefault.h" + +namespace android { +namespace hardware { +namespace tv { +namespace cec { +namespace V1_0 { +namespace implementation { + +static_assert(CEC_DEVICE_INACTIVE == static_cast(CecDeviceType::INACTIVE), + "CecDeviceType::INACTIVE must match legacy value."); +static_assert(CEC_DEVICE_TV == static_cast(CecDeviceType::TV), + "CecDeviceType::TV must match legacy value."); +static_assert(CEC_DEVICE_RECORDER == static_cast(CecDeviceType::RECORDER), + "CecDeviceType::RECORDER must match legacy value."); +static_assert(CEC_DEVICE_TUNER == static_cast(CecDeviceType::TUNER), + "CecDeviceType::TUNER must match legacy value."); +static_assert(CEC_DEVICE_PLAYBACK == static_cast(CecDeviceType::PLAYBACK), + "CecDeviceType::PLAYBACK must match legacy value."); +static_assert(CEC_DEVICE_AUDIO_SYSTEM == static_cast(CecDeviceType::AUDIO_SYSTEM), + "CecDeviceType::AUDIO_SYSTEM must match legacy value."); +static_assert(CEC_DEVICE_MAX == static_cast(CecDeviceType::MAX), + "CecDeviceType::MAX must match legacy value."); + +static_assert(CEC_ADDR_TV == static_cast(CecLogicalAddress::TV), + "CecLogicalAddress::TV must match legacy value."); +static_assert(CEC_ADDR_RECORDER_1 == static_cast(CecLogicalAddress::RECORDER_1), + "CecLogicalAddress::RECORDER_1 must match legacy value."); +static_assert(CEC_ADDR_RECORDER_2 == static_cast(CecLogicalAddress::RECORDER_2), + "CecLogicalAddress::RECORDER_2 must match legacy value."); +static_assert(CEC_ADDR_TUNER_1 == static_cast(CecLogicalAddress::TUNER_1), + "CecLogicalAddress::TUNER_1 must match legacy value."); +static_assert(CEC_ADDR_PLAYBACK_1 == static_cast(CecLogicalAddress::PLAYBACK_1), + "CecLogicalAddress::PLAYBACK_1 must match legacy value."); +static_assert(CEC_ADDR_AUDIO_SYSTEM == static_cast(CecLogicalAddress::AUDIO_SYSTEM), + "CecLogicalAddress::AUDIO_SYSTEM must match legacy value."); +static_assert(CEC_ADDR_TUNER_2 == static_cast(CecLogicalAddress::TUNER_2), + "CecLogicalAddress::TUNER_2 must match legacy value."); +static_assert(CEC_ADDR_TUNER_3 == static_cast(CecLogicalAddress::TUNER_3), + "CecLogicalAddress::TUNER_3 must match legacy value."); +static_assert(CEC_ADDR_PLAYBACK_2 == static_cast(CecLogicalAddress::PLAYBACK_2), + "CecLogicalAddress::PLAYBACK_2 must match legacy value."); +static_assert(CEC_ADDR_RECORDER_3 == static_cast(CecLogicalAddress::RECORDER_3), + "CecLogicalAddress::RECORDER_3 must match legacy value."); +static_assert(CEC_ADDR_TUNER_4 == static_cast(CecLogicalAddress::TUNER_4), + "CecLogicalAddress::TUNER_4 must match legacy value."); +static_assert(CEC_ADDR_PLAYBACK_3 == static_cast(CecLogicalAddress::PLAYBACK_3), + "CecLogicalAddress::PLAYBACK_3 must match legacy value."); +static_assert(CEC_ADDR_FREE_USE == static_cast(CecLogicalAddress::FREE_USE), + "CecLogicalAddress::FREE_USE must match legacy value."); +static_assert(CEC_ADDR_UNREGISTERED == static_cast(CecLogicalAddress::UNREGISTERED), + "CecLogicalAddress::UNREGISTERED must match legacy value."); +static_assert(CEC_ADDR_BROADCAST == static_cast(CecLogicalAddress::BROADCAST), + "CecLogicalAddress::BROADCAST must match legacy value."); + +static_assert(CEC_MESSAGE_FEATURE_ABORT == static_cast(CecMessageType::FEATURE_ABORT), + "CecMessageType::FEATURE_ABORT must match legacy value."); +static_assert(CEC_MESSAGE_IMAGE_VIEW_ON == static_cast(CecMessageType::IMAGE_VIEW_ON), + "CecMessageType::IMAGE_VIEW_ON must match legacy value."); +static_assert(CEC_MESSAGE_TUNER_STEP_INCREMENT == static_cast( + CecMessageType::TUNER_STEP_INCREMENT), + "CecMessageType::TUNER_STEP_INCREMENT must match legacy value."); +static_assert(CEC_MESSAGE_TUNER_STEP_DECREMENT == static_cast( + CecMessageType::TUNER_STEP_DECREMENT), + "CecMessageType::TUNER_STEP_DECREMENT must match legacy value."); +static_assert(CEC_MESSAGE_TUNER_DEVICE_STATUS == static_cast( + CecMessageType::TUNER_DEVICE_STATUS), + "CecMessageType::TUNER_DEVICE_STATUS must match legacy value."); +static_assert(CEC_MESSAGE_GIVE_TUNER_DEVICE_STATUS == static_cast( + CecMessageType::GIVE_TUNER_DEVICE_STATUS), + "CecMessageType::GIVE_TUNER_DEVICE_STATUS must match legacy value."); +static_assert(CEC_MESSAGE_RECORD_ON == static_cast(CecMessageType::RECORD_ON), + "CecMessageType::RECORD_ON must match legacy value."); +static_assert(CEC_MESSAGE_RECORD_STATUS == static_cast(CecMessageType::RECORD_STATUS), + "CecMessageType::RECORD_STATUS must match legacy value."); +static_assert(CEC_MESSAGE_RECORD_OFF == static_cast(CecMessageType::RECORD_OFF), + "CecMessageType::RECORD_OFF must match legacy value."); +static_assert(CEC_MESSAGE_TEXT_VIEW_ON == static_cast(CecMessageType::TEXT_VIEW_ON), + "CecMessageType::TEXT_VIEW_ON must match legacy value."); +static_assert(CEC_MESSAGE_RECORD_TV_SCREEN == static_cast(CecMessageType::RECORD_TV_SCREEN), + "CecMessageType::RECORD_TV_SCREEN must match legacy value."); +static_assert(CEC_MESSAGE_GIVE_DECK_STATUS == static_cast(CecMessageType::GIVE_DECK_STATUS), + "CecMessageType::GIVE_DECK_STATUS must match legacy value."); +static_assert(CEC_MESSAGE_STANDBY == static_cast(CecMessageType::STANDBY), + "CecMessageType::STANDBY must match legacy value."); +static_assert(CEC_MESSAGE_PLAY == static_cast(CecMessageType::PLAY), + "CecMessageType::PLAY must match legacy value."); +static_assert(CEC_MESSAGE_DECK_CONTROL == static_cast(CecMessageType::DECK_CONTROL), + "CecMessageType::DECK_CONTROL must match legacy value."); +static_assert(CEC_MESSAGE_TIMER_CLEARED_STATUS == static_cast( + CecMessageType::TIMER_CLEARED_STATUS), + "CecMessageType::TIMER_CLEARED_STATUS must match legacy value."); +static_assert(CEC_MESSAGE_USER_CONTROL_PRESSED == static_cast( + CecMessageType::USER_CONTROL_PRESSED), + "CecMessageType::USER_CONTROL_PRESSED must match legacy value."); +static_assert(CEC_MESSAGE_USER_CONTROL_RELEASED == static_cast( + CecMessageType::USER_CONTROL_RELEASED), + "CecMessageType::USER_CONTROL_RELEASED must match legacy value."); +static_assert(CEC_MESSAGE_GIVE_OSD_NAME == static_cast(CecMessageType::GIVE_OSD_NAME), + "CecMessageType::GIVE_OSD_NAME must match legacy value."); +static_assert(CEC_MESSAGE_SET_OSD_NAME == static_cast(CecMessageType::SET_OSD_NAME), + "CecMessageType::SET_OSD_NAME must match legacy value."); +static_assert(CEC_MESSAGE_SYSTEM_AUDIO_MODE_REQUEST == static_cast( + CecMessageType::SYSTEM_AUDIO_MODE_REQUEST), + "CecMessageType::SYSTEM_AUDIO_MODE_REQUEST must match legacy value."); +static_assert(CEC_MESSAGE_GIVE_AUDIO_STATUS == static_cast(CecMessageType::GIVE_AUDIO_STATUS), + "CecMessageType::GIVE_AUDIO_STATUS must match legacy value."); +static_assert(CEC_MESSAGE_SET_SYSTEM_AUDIO_MODE == static_cast( + CecMessageType::SET_SYSTEM_AUDIO_MODE), + "CecMessageType::SET_SYSTEM_AUDIO_MODE must match legacy value."); +static_assert(CEC_MESSAGE_REPORT_AUDIO_STATUS == static_cast( + CecMessageType::REPORT_AUDIO_STATUS), + "CecMessageType::REPORT_AUDIO_STATUS must match legacy value."); +static_assert(CEC_MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS == static_cast( + CecMessageType::GIVE_SYSTEM_AUDIO_MODE_STATUS), + "CecMessageType::GIVE_SYSTEM_AUDIO_MODE_STATUS must match legacy value."); +static_assert(CEC_MESSAGE_SYSTEM_AUDIO_MODE_STATUS == static_cast( + CecMessageType::SYSTEM_AUDIO_MODE_STATUS), + "CecMessageType::SYSTEM_AUDIO_MODE_STATUS must match legacy value."); +static_assert(CEC_MESSAGE_ROUTING_CHANGE == static_cast(CecMessageType::ROUTING_CHANGE), + "CecMessageType::ROUTING_CHANGE must match legacy value."); +static_assert(CEC_MESSAGE_ROUTING_INFORMATION == static_cast( + CecMessageType::ROUTING_INFORMATION), + "CecMessageType::ROUTING_INFORMATION must match legacy value."); +static_assert(CEC_MESSAGE_ACTIVE_SOURCE == static_cast(CecMessageType::ACTIVE_SOURCE), + "CecMessageType::ACTIVE_SOURCE must match legacy value."); +static_assert(CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS == static_cast( + CecMessageType::GIVE_PHYSICAL_ADDRESS), + "CecMessageType::GIVE_PHYSICAL_ADDRESS must match legacy value."); +static_assert(CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS == static_cast( + CecMessageType::REPORT_PHYSICAL_ADDRESS), + "CecMessageType::REPORT_PHYSICAL_ADDRESS must match legacy value."); +static_assert(CEC_MESSAGE_REQUEST_ACTIVE_SOURCE == static_cast( + CecMessageType::REQUEST_ACTIVE_SOURCE), + "CecMessageType::REQUEST_ACTIVE_SOURCE must match legacy value."); +static_assert(CEC_MESSAGE_SET_STREAM_PATH == static_cast(CecMessageType::SET_STREAM_PATH), + "CecMessageType::SET_STREAM_PATH must match legacy value."); +static_assert(CEC_MESSAGE_DEVICE_VENDOR_ID == static_cast(CecMessageType::DEVICE_VENDOR_ID), + "CecMessageType::DEVICE_VENDOR_ID must match legacy value."); +static_assert(CEC_MESSAGE_VENDOR_COMMAND == static_cast(CecMessageType::VENDOR_COMMAND), + "CecMessageType::VENDOR_COMMAND must match legacy value."); +static_assert(CEC_MESSAGE_VENDOR_REMOTE_BUTTON_DOWN == static_cast( + CecMessageType::VENDOR_REMOTE_BUTTON_DOWN), + "CecMessageType::VENDOR_REMOTE_BUTTON_DOWN must match legacy value."); +static_assert(CEC_MESSAGE_VENDOR_REMOTE_BUTTON_UP == static_cast( + CecMessageType::VENDOR_REMOTE_BUTTON_UP), + "CecMessageType::VENDOR_REMOTE_BUTTON_UP must match legacy value."); +static_assert(CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID == static_cast( + CecMessageType::GIVE_DEVICE_VENDOR_ID), + "CecMessageType::GIVE_DEVICE_VENDOR_ID must match legacy value."); +static_assert(CEC_MESSAGE_MENU_REQUEST == static_cast(CecMessageType::MENU_REQUEST), + "CecMessageType::MENU_REQUEST must match legacy value."); +static_assert(CEC_MESSAGE_MENU_STATUS == static_cast(CecMessageType::MENU_STATUS), + "CecMessageType::MENU_STATUS must match legacy value."); +static_assert(CEC_MESSAGE_GIVE_DEVICE_POWER_STATUS == static_cast( + CecMessageType::GIVE_DEVICE_POWER_STATUS), + "CecMessageType::GIVE_DEVICE_POWER_STATUS must match legacy value."); +static_assert(CEC_MESSAGE_REPORT_POWER_STATUS == static_cast( + CecMessageType::REPORT_POWER_STATUS), + "CecMessageType::REPORT_POWER_STATUS must match legacy value."); +static_assert(CEC_MESSAGE_GET_MENU_LANGUAGE == static_cast(CecMessageType::GET_MENU_LANGUAGE), + "CecMessageType::GET_MENU_LANGUAGE must match legacy value."); +static_assert(CEC_MESSAGE_SELECT_ANALOG_SERVICE == static_cast( + CecMessageType::SELECT_ANALOG_SERVICE), + "CecMessageType::SELECT_ANALOG_SERVICE must match legacy value."); +static_assert(CEC_MESSAGE_SELECT_DIGITAL_SERVICE == static_cast( + CecMessageType::SELECT_DIGITAL_SERVICE), + "CecMessageType::SELECT_DIGITAL_SERVICE must match legacy value."); +static_assert(CEC_MESSAGE_SET_DIGITAL_TIMER == static_cast(CecMessageType::SET_DIGITAL_TIMER), + "CecMessageType::SET_DIGITAL_TIMER must match legacy value."); +static_assert(CEC_MESSAGE_CLEAR_DIGITAL_TIMER == static_cast( + CecMessageType::CLEAR_DIGITAL_TIMER), + "CecMessageType::CLEAR_DIGITAL_TIMER must match legacy value."); +static_assert(CEC_MESSAGE_SET_AUDIO_RATE == static_cast(CecMessageType::SET_AUDIO_RATE), + "CecMessageType::SET_AUDIO_RATE must match legacy value."); +static_assert(CEC_MESSAGE_INACTIVE_SOURCE == static_cast(CecMessageType::INACTIVE_SOURCE), + "CecMessageType::INACTIVE_SOURCE must match legacy value."); +static_assert(CEC_MESSAGE_CEC_VERSION == static_cast(CecMessageType::CEC_VERSION), + "CecMessageType::CEC_VERSION must match legacy value."); +static_assert(CEC_MESSAGE_GET_CEC_VERSION == static_cast(CecMessageType::GET_CEC_VERSION), + "CecMessageType::GET_CEC_VERSION must match legacy value."); +static_assert(CEC_MESSAGE_VENDOR_COMMAND_WITH_ID == static_cast( + CecMessageType::VENDOR_COMMAND_WITH_ID), + "CecMessageType::VENDOR_COMMAND_WITH_ID must match legacy value."); +static_assert(CEC_MESSAGE_CLEAR_EXTERNAL_TIMER == static_cast( + CecMessageType::CLEAR_EXTERNAL_TIMER), + "CecMessageType::CLEAR_EXTERNAL_TIMER must match legacy value."); +static_assert(CEC_MESSAGE_SET_EXTERNAL_TIMER == static_cast( + CecMessageType::SET_EXTERNAL_TIMER), + "CecMessageType::SET_EXTERNAL_TIMER must match legacy value."); +static_assert(CEC_MESSAGE_INITIATE_ARC == static_cast(CecMessageType::INITIATE_ARC), + "CecMessageType::INITIATE_ARC must match legacy value."); +static_assert(CEC_MESSAGE_REPORT_ARC_INITIATED == static_cast( + CecMessageType::REPORT_ARC_INITIATED), + "CecMessageType::REPORT_ARC_INITIATED must match legacy value."); +static_assert(CEC_MESSAGE_REPORT_ARC_TERMINATED == static_cast( + CecMessageType::REPORT_ARC_TERMINATED), + "CecMessageType::REPORT_ARC_TERMINATED must match legacy value."); +static_assert(CEC_MESSAGE_REQUEST_ARC_INITIATION == static_cast( + CecMessageType::REQUEST_ARC_INITIATION), + "CecMessageType::REQUEST_ARC_INITIATION must match legacy value."); +static_assert(CEC_MESSAGE_REQUEST_ARC_TERMINATION == static_cast( + CecMessageType::REQUEST_ARC_TERMINATION), + "CecMessageType::REQUEST_ARC_TERMINATION must match legacy value."); +static_assert(CEC_MESSAGE_TERMINATE_ARC == static_cast(CecMessageType::TERMINATE_ARC), + "CecMessageType::TERMINATE_ARC must match legacy value."); +static_assert(CEC_MESSAGE_ABORT == static_cast(CecMessageType::ABORT), + "CecMessageType::ABORT must match legacy value."); + +static_assert(ABORT_UNRECOGNIZED_MODE == static_cast(AbortReason::UNRECOGNIZED_MODE), + "AbortReason::UNRECOGNIZED_MODE must match legacy value."); +static_assert(ABORT_NOT_IN_CORRECT_MODE == static_cast(AbortReason::NOT_IN_CORRECT_MODE), + "AbortReason::NOT_IN_CORRECT_MODE must match legacy value."); +static_assert(ABORT_CANNOT_PROVIDE_SOURCE == static_cast(AbortReason::CANNOT_PROVIDE_SOURCE), + "AbortReason::CANNOT_PROVIDE_SOURCE must match legacy value."); +static_assert(ABORT_INVALID_OPERAND == static_cast(AbortReason::INVALID_OPERAND), + "AbortReason::INVALID_OPERAND must match legacy value."); +static_assert(ABORT_REFUSED == static_cast(AbortReason::REFUSED), + "AbortReason::REFUSED must match legacy value."); +static_assert(ABORT_UNABLE_TO_DETERMINE == static_cast(AbortReason::UNABLE_TO_DETERMINE), + "AbortReason::UNABLE_TO_DETERMINE must match legacy value."); + +static_assert(HDMI_RESULT_SUCCESS == static_cast(SendMessageResult::SUCCESS), + "SendMessageResult::SUCCESS must match legacy value."); +static_assert(HDMI_RESULT_NACK == static_cast(SendMessageResult::NACK), + "SendMessageResult::NACK must match legacy value."); +static_assert(HDMI_RESULT_BUSY == static_cast(SendMessageResult::BUSY), + "SendMessageResult::BUSY must match legacy value."); +static_assert(HDMI_RESULT_FAIL == static_cast(SendMessageResult::FAIL), + "SendMessageResult::FAIL must match legacy value."); + +static_assert(HDMI_INPUT == static_cast(HdmiPortType::INPUT), + "HdmiPortType::INPUT must match legacy value."); +static_assert(HDMI_OUTPUT == static_cast(HdmiPortType::OUTPUT), + "HdmiPortType::OUTPUT must match legacy value."); + +static_assert(HDMI_OPTION_WAKEUP == static_cast(OptionKey::WAKEUP), + "OptionKey::WAKEUP must match legacy value."); +static_assert(HDMI_OPTION_ENABLE_CEC == static_cast(OptionKey::ENABLE_CEC), + "OptionKey::ENABLE_CEC must match legacy value."); +static_assert(HDMI_OPTION_SYSTEM_CEC_CONTROL == static_cast(OptionKey::SYSTEM_CEC_CONTROL), + "OptionKey::SYSTEM_CEC_CONTROL must match legacy value."); + +sp HdmiCec::mCallback = nullptr; + +HdmiCec::HdmiCec(hdmi_cec_device_t* device) : mDevice(device) {} + +// Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow. +Return HdmiCec::addLogicalAddress(CecLogicalAddress addr) { + int ret = mDevice->add_logical_address(mDevice, static_cast(addr)); + switch (ret) { + case 0: + return Result::SUCCESS; + case -EINVAL: + return Result::FAILURE_INVALID_ARGS; + case -ENOTSUP: + return Result::FAILURE_NOT_SUPPORTED; + case -EBUSY: + return Result::FAILURE_BUSY; + default: + return Result::FAILURE_UNKNOWN; + } +} + +Return HdmiCec::clearLogicalAddress() { + mDevice->clear_logical_address(mDevice); + return Void(); +} + +Return HdmiCec::getPhysicalAddress(getPhysicalAddress_cb _hidl_cb) { + uint16_t addr; + int ret = mDevice->get_physical_address(mDevice, &addr); + switch (ret) { + case 0: + _hidl_cb(Result::SUCCESS, addr); + break; + case -EBADF: + _hidl_cb(Result::FAILURE_INVALID_STATE, addr); + break; + default: + _hidl_cb(Result::FAILURE_UNKNOWN, addr); + break; + } + return Void(); +} + +Return HdmiCec::sendMessage(const CecMessage& message) { + if (message.body.size() > CEC_MESSAGE_BODY_MAX_LENGTH) { + return SendMessageResult::FAIL; + } + cec_message_t legacyMessage { + .initiator = static_cast(message.initiator), + .destination = static_cast(message.destination), + .length = message.body.size(), + }; + for (size_t i = 0; i < message.body.size(); ++i) { + legacyMessage.body[i] = static_cast(message.body[i]); + } + return static_cast(mDevice->send_message(mDevice, &legacyMessage)); +} + +Return HdmiCec::setCallback(const sp& callback) { + if (mCallback != nullptr) { + mCallback->unlinkToDeath(this); + mCallback = nullptr; + } + + if (callback != nullptr) { + mCallback = callback; + mCallback->linkToDeath(this, 0 /*cookie*/); + mDevice->register_event_callback(mDevice, eventCallback, nullptr); + } + return Void(); +} + +Return HdmiCec::getCecVersion() { + int version; + mDevice->get_version(mDevice, &version); + return static_cast(version); +} + +Return HdmiCec::getVendorId() { + uint32_t vendor_id; + mDevice->get_vendor_id(mDevice, &vendor_id); + return vendor_id; +} + +Return HdmiCec::getPortInfo(getPortInfo_cb _hidl_cb) { + struct hdmi_port_info* legacyPorts; + int numPorts; + hidl_vec portInfos; + mDevice->get_port_info(mDevice, &legacyPorts, &numPorts); + portInfos.resize(numPorts); + for (int i = 0; i < numPorts; ++i) { + portInfos[i] = { + .type = static_cast(legacyPorts[i].type), + .portId = static_cast(legacyPorts[i].port_id), + .cecSupported = legacyPorts[i].cec_supported != 0, + .arcSupported = legacyPorts[i].arc_supported != 0, + .physicalAddress = legacyPorts[i].physical_address + }; + } + _hidl_cb(portInfos); + return Void(); +} + +Return HdmiCec::setOption(OptionKey key, bool value) { + mDevice->set_option(mDevice, static_cast(key), value ? 1 : 0); + return Void(); +} + +Return HdmiCec::setLanguage(const hidl_string& language) { + if (language.size() != 3) { + LOG(ERROR) << "Wrong language code: expected 3 letters, but it was " << language.size() + << "."; + return Void(); + } + const char *languageStr = language.c_str(); + int convertedLanguage = ((languageStr[0] & 0xFF) << 16) + | ((languageStr[1] & 0xFF) << 8) + | (languageStr[2] & 0xFF); + mDevice->set_option(mDevice, HDMI_OPTION_SET_LANG, convertedLanguage); + return Void(); +} + +Return HdmiCec::enableAudioReturnChannel(int32_t portId, bool enable) { + mDevice->set_audio_return_channel(mDevice, portId, enable ? 1 : 0); + return Void(); +} + +Return HdmiCec::isConnected(int32_t portId) { + return mDevice->is_connected(mDevice, portId) > 0; +} + +IHdmiCec* getHdmiCecDefault() { + HdmiCecDefault* hdmiCecDefault = new HdmiCecDefault(); + Result result = hdmiCecDefault->init(); + if (result == Result::SUCCESS) { + return hdmiCecDefault; + } + LOG(ERROR) << "Failed to load default HAL."; + return nullptr; +} + +IHdmiCec* HIDL_FETCH_IHdmiCec(const char* hal) { + hdmi_cec_device_t* hdmi_cec_device; + int ret = 0; + const hw_module_t* hw_module = nullptr; + + ret = hw_get_module (HDMI_CEC_HARDWARE_MODULE_ID, &hw_module); + if (ret == 0) { + ret = hdmi_cec_open (hw_module, &hdmi_cec_device); + if (ret != 0) { + LOG(ERROR) << "hdmi_cec_open " << hal << " failed: " << ret; + } + } else { + LOG(ERROR) << "hw_get_module " << hal << " failed: " << ret; + } + + if (ret == 0) { + return new HdmiCec(hdmi_cec_device); + } else { + LOG(ERROR) << "Passthrough failed to load legacy HAL."; + return getHdmiCecDefault(); + } +} + +} // namespace implementation +} // namespace V1_0 +} // namespace cec +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/cec/HdmiCec.h b/cec/HdmiCec.h new file mode 100644 index 0000000..0133abc --- /dev/null +++ b/cec/HdmiCec.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef ANDROID_HARDWARE_TV_CEC_V1_0_HDMICEC_H +#define ANDROID_HARDWARE_TV_CEC_V1_0_HDMICEC_H + +#include + +#include +#include +#include +#include + +#include +namespace android { +namespace hardware { +namespace tv { +namespace cec { +namespace V1_0 { +namespace implementation { + +using ::android::hardware::tv::cec::V1_0::CecLogicalAddress; +using ::android::hardware::tv::cec::V1_0::CecMessage; +using ::android::hardware::tv::cec::V1_0::MaxLength; +using ::android::hardware::tv::cec::V1_0::HdmiPortInfo; +using ::android::hardware::tv::cec::V1_0::IHdmiCec; +using ::android::hardware::tv::cec::V1_0::IHdmiCecCallback; +using ::android::hardware::tv::cec::V1_0::OptionKey; +using ::android::hardware::tv::cec::V1_0::Result; +using ::android::hardware::tv::cec::V1_0::SendMessageResult; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::hidl_vec; +using ::android::hardware::hidl_string; +using ::android::sp; + +struct HdmiCec : public IHdmiCec, public hidl_death_recipient { + HdmiCec(hdmi_cec_device_t* device); + // Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow. + Return addLogicalAddress(CecLogicalAddress addr) override; + Return clearLogicalAddress() override; + Return getPhysicalAddress(getPhysicalAddress_cb _hidl_cb) override; + Return sendMessage(const CecMessage& message) override; + Return setCallback(const sp& callback) override; + Return getCecVersion() override; + Return getVendorId() override; + Return getPortInfo(getPortInfo_cb _hidl_cb) override; + Return setOption(OptionKey key, bool value) override; + Return setLanguage(const hidl_string& language) override; + Return enableAudioReturnChannel(int32_t portId, bool enable) override; + Return isConnected(int32_t portId) override; + + static void eventCallback(const hdmi_event_t* event, void* /* arg */) { + if (mCallback != nullptr && event != nullptr) { + if (event->type == HDMI_EVENT_CEC_MESSAGE) { + size_t length = std::min(event->cec.length, + static_cast(MaxLength::MESSAGE_BODY)); + CecMessage cecMessage { + .initiator = static_cast(event->cec.initiator), + .destination = static_cast(event->cec.destination), + }; + cecMessage.body.resize(length); + for (size_t i = 0; i < length; ++i) { + cecMessage.body[i] = static_cast(event->cec.body[i]); + } + mCallback->onCecMessage(cecMessage); + } else if (event->type == HDMI_EVENT_HOT_PLUG) { + HotplugEvent hotplugEvent { + .connected = event->hotplug.connected > 0, + .portId = static_cast(event->hotplug.port_id) + }; + mCallback->onHotplugEvent(hotplugEvent); + } + } + } + + virtual void serviceDied(uint64_t /*cookie*/, + const wp<::android::hidl::base::V1_0::IBase>& /*who*/) { + setCallback(nullptr); + } + + private: + static sp mCallback; + const hdmi_cec_device_t* mDevice; +}; + +extern "C" IHdmiCec* HIDL_FETCH_IHdmiCec(const char* name); + +} // namespace implementation +} // namespace V1_0 +} // namespace cec +} // namespace tv +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_TV_CEC_V1_0_HDMICEC_H diff --git a/cec/HdmiCecDefault.cpp b/cec/HdmiCecDefault.cpp new file mode 100644 index 0000000..2a5197c --- /dev/null +++ b/cec/HdmiCecDefault.cpp @@ -0,0 +1,523 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#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 { +namespace cec { +namespace V1_0 { +namespace implementation { + +using android::base::GetUintProperty; +using std::stoi; +using std::string; + +HdmiCecDefault::HdmiCecDefault() { + mCecEnabled = false; + mWakeupEnabled = false; + mCecControlEnabled = false; + mCallback = nullptr; +} + +HdmiCecDefault::~HdmiCecDefault() { + release(); +} + +// Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow. +Return HdmiCecDefault::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); + if (ret) { + LOG(ERROR) << "Add logical address failed, Error = " << strerror(errno); + return Result::FAILURE_BUSY; + } + + cecLogAddrs.cec_version = getCecVersion(); + cecLogAddrs.vendor_id = getVendorId(); + + unsigned int logAddrType = CEC_LOG_ADDR_TYPE_UNREGISTERED; + unsigned int allDevTypes = 0; + unsigned int primDevType = 0xff; + switch (addr) { + case CecLogicalAddress::TV: + primDevType = CEC_OP_PRIM_DEVTYPE_TV; + logAddrType = CEC_LOG_ADDR_TYPE_TV; + allDevTypes = CEC_OP_ALL_DEVTYPE_TV; + break; + case CecLogicalAddress::RECORDER_1: + case CecLogicalAddress::RECORDER_2: + case CecLogicalAddress::RECORDER_3: + primDevType = CEC_OP_PRIM_DEVTYPE_RECORD; + logAddrType = CEC_LOG_ADDR_TYPE_RECORD; + allDevTypes = CEC_OP_ALL_DEVTYPE_RECORD; + break; + case CecLogicalAddress::TUNER_1: + case CecLogicalAddress::TUNER_2: + case CecLogicalAddress::TUNER_3: + case CecLogicalAddress::TUNER_4: + primDevType = CEC_OP_PRIM_DEVTYPE_TUNER; + logAddrType = CEC_LOG_ADDR_TYPE_TUNER; + allDevTypes = CEC_OP_ALL_DEVTYPE_TUNER; + break; + case CecLogicalAddress::PLAYBACK_1: + case CecLogicalAddress::PLAYBACK_2: + case CecLogicalAddress::PLAYBACK_3: + primDevType = CEC_OP_PRIM_DEVTYPE_PLAYBACK; + logAddrType = CEC_LOG_ADDR_TYPE_PLAYBACK; + allDevTypes = CEC_OP_ALL_DEVTYPE_PLAYBACK; + cecLogAddrs.flags |= CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU; + break; + case CecLogicalAddress::AUDIO_SYSTEM: + primDevType = CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM; + logAddrType = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM; + allDevTypes = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM; + break; + case CecLogicalAddress::FREE_USE: + primDevType = CEC_OP_PRIM_DEVTYPE_PROCESSOR; + logAddrType = CEC_LOG_ADDR_TYPE_SPECIFIC; + allDevTypes = CEC_OP_ALL_DEVTYPE_SWITCH; + break; + case CecLogicalAddress::UNREGISTERED: + cecLogAddrs.flags |= CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK; + break; + } + + int logAddrIndex = cecLogAddrs.num_log_addrs; + + cecLogAddrs.num_log_addrs += 1; + cecLogAddrs.log_addr[logAddrIndex] = static_cast(addr); + cecLogAddrs.log_addr_type[logAddrIndex] = logAddrType; + cecLogAddrs.primary_device_type[logAddrIndex] = primDevType; + cecLogAddrs.all_device_types[logAddrIndex] = allDevTypes; + 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; + } + } + return result; +} + +Return HdmiCecDefault::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); + } + } + return Void(); +} + +Return HdmiCecDefault::getPhysicalAddress(getPhysicalAddress_cb callback) { + uint16_t 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); + return Void(); + } + callback(Result::SUCCESS, addr); + return Void(); +} + +Return HdmiCecDefault::sendMessage(const CecMessage& message) { + if (!mCecEnabled) { + return SendMessageResult::FAIL; + } + + cec_msg cecMsg; + memset(&cecMsg, 0, sizeof(cec_msg)); + + int initiator = static_cast(message.initiator); + int destination = static_cast(message.destination); + + cecMsg.msg[0] = (initiator << 4) | destination; + for (size_t i = 0; i < message.body.size(); ++i) { + cecMsg.msg[i + 1] = message.body[i]; + } + 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); + } + } + return result; +} + +Return HdmiCecDefault::setCallback(const sp& callback) { + if (mCallback != nullptr) { + mCallback->unlinkToDeath(this); + mCallback = nullptr; + } + + if (callback != nullptr) { + 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 HdmiCecDefault::getVendorId() { + return property_get_int32("ro.hdmi.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}; + } + callback(portInfos); + return Void(); +} + +Return HdmiCecDefault::setOption(OptionKey key, bool value) { + switch (key) { + case OptionKey::ENABLE_CEC: + LOG(DEBUG) << "setOption: Enable CEC: " << value; + mCecEnabled = value; + break; + case OptionKey::WAKEUP: + LOG(DEBUG) << "setOption: WAKEUP: " << value; + mWakeupEnabled = value; + break; + case OptionKey::SYSTEM_CEC_CONTROL: + LOG(DEBUG) << "setOption: SYSTEM_CEC_CONTROL: " << value; + mCecControlEnabled = value; + break; + } + return Void(); +} + +Return HdmiCecDefault::setLanguage(const hidl_string& /*language*/) { + return Void(); +} + +Return HdmiCecDefault::enableAudioReturnChannel(int32_t /*portId*/, bool /*enable*/) { + 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); + 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 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()) { + return Result::FAILURE_NOT_SUPPORTED; + } + + mCecEnabled = true; + mWakeupEnabled = true; + mCecControlEnabled = true; + return Result::SUCCESS; +} + +Return HdmiCecDefault::release() { + 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(HdmiCecPort* hdmiCecPort) { + struct pollfd ufds[3] = { + {hdmiCecPort->mCecFd, POLLIN, 0}, + {hdmiCecPort->mCecFd, POLLERR, 0}, + {hdmiCecPort->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 */ + 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 = hdmiCecPort->mPortId}; + mCallback->onHotplugEvent(hotplugEvent); + } else { + LOG(ERROR) << "No event callback for hotplug"; + } + } + } + + if (ufds[0].revents == POLLIN) { /* CEC Driver */ + cec_msg msg = {}; + ret = ioctl(hdmiCecPort->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 (!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{ + .initiator = static_cast(msg.msg[0] >> 4), + .destination = static_cast(msg.msg[0] & 0xf), + }; + cecMessage.body.resize(length); + for (size_t i = 0; i < length; ++i) { + cecMessage.body[i] = static_cast(msg.msg[i + 1]); + } + mCallback->onCecMessage(cecMessage); + } else { + LOG(ERROR) << "no event callback for message"; + } + } + } +} + +int HdmiCecDefault::getOpcode(cec_msg message) { + return static_cast(message.msg[1]); +} + +bool HdmiCecDefault::isWakeupMessage(cec_msg message) { + int opcode = getOpcode(message); + switch (opcode) { + case CEC_MESSAGE_TEXT_VIEW_ON: + case CEC_MESSAGE_IMAGE_VIEW_ON: + return true; + default: + return false; + } +} + +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(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 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 +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/cec/HdmiCecDefault.h b/cec/HdmiCecDefault.h new file mode 100644 index 0000000..6574429 --- /dev/null +++ b/cec/HdmiCecDefault.h @@ -0,0 +1,91 @@ +/* + * 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 +#include +#include +#include +#include "HdmiCecPort.h" + +namespace android { +namespace hardware { +namespace tv { +namespace cec { +namespace V1_0 { +namespace implementation { + +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. + Return addLogicalAddress(CecLogicalAddress addr) override; + Return clearLogicalAddress() override; + Return getPhysicalAddress(getPhysicalAddress_cb _hidl_cb) override; + Return sendMessage(const CecMessage& message) override; + Return setCallback(const sp& callback) override; + Return getCecVersion() override; + Return getVendorId() override; + Return getPortInfo(getPortInfo_cb _hidl_cb) override; + Return setOption(OptionKey key, bool value) override; + Return setLanguage(const hidl_string& language) override; + Return enableAudioReturnChannel(int32_t portId, bool enable) override; + Return isConnected(int32_t portId) override; + + virtual void serviceDied(uint64_t, const wp<::android::hidl::base::V1_0::IBase>&) { + setCallback(nullptr); + } + + Return init(); + Return release(); + + 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 getSendMessageResult(int tx_status); + + vector mEventThreads; + vector> 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 or + * . 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 mCallback; +}; +} // namespace implementation +} // namespace V1_0 +} // namespace cec +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/cec/HdmiCecMock.cpp b/cec/HdmiCecMock.cpp new file mode 100644 index 0000000..219be86 --- /dev/null +++ b/cec/HdmiCecMock.cpp @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2019 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-mock" +#include +#include + +#include +#include +#include "HdmiCecMock.h" + +namespace android { +namespace hardware { +namespace tv { +namespace cec { +namespace V1_0 { +namespace implementation { + +/* + * (*set_option)() passes flags controlling the way HDMI-CEC service works down + * to HAL implementation. Those flags will be used in case the feature needs + * update in HAL itself, firmware or microcontroller. + */ +void HdmiCecMock::cec_set_option(int flag, int value) { + // maintain options and set them accordingly + switch (flag) { + case HDMI_OPTION_WAKEUP: + mOptionWakeUp = value; + break; + case HDMI_OPTION_ENABLE_CEC: + mOptionEnableCec = value; + break; + case HDMI_OPTION_SYSTEM_CEC_CONTROL: + mOptionSystemCecControl = value; + break; + case HDMI_OPTION_SET_LANG: + mOptionLanguage = value; + } +} + +// Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow. +Return HdmiCecMock::addLogicalAddress(CecLogicalAddress addr) { + // have a list to maintain logical addresses + int size = mLogicalAddresses.size(); + mLogicalAddresses.resize(size + 1); + mLogicalAddresses[size + 1] = addr; + return Result::SUCCESS; +} + +Return HdmiCecMock::clearLogicalAddress() { + // remove logical address from the list + mLogicalAddresses = {}; + return Void(); +} + +Return HdmiCecMock::getPhysicalAddress(getPhysicalAddress_cb _hidl_cb) { + // maintain a physical address and return it + // default 0xFFFF, update on hotplug event + _hidl_cb(Result::SUCCESS, mPhysicalAddress); + return Void(); +} + +Return HdmiCecMock::sendMessage(const CecMessage& message) { + if (message.body.size() == 0) { + return SendMessageResult::NACK; + } + sendMessageToFifo(message); + return SendMessageResult::SUCCESS; +} + +Return HdmiCecMock::setCallback(const sp& callback) { + if (mCallback != nullptr) { + mCallback = nullptr; + } + + if (callback != nullptr) { + mCallback = callback; + mCallback->linkToDeath(this, 0 /*cookie*/); + + mInputFile = open(CEC_MSG_IN_FIFO, O_RDWR); + mOutputFile = open(CEC_MSG_OUT_FIFO, O_RDWR); + pthread_create(&mThreadId, NULL, __threadLoop, this); + pthread_setname_np(mThreadId, "hdmi_cec_loop"); + } + return Void(); +} + +Return HdmiCecMock::getCecVersion() { + // maintain a cec version and return it + return mCecVersion; +} + +Return HdmiCecMock::getVendorId() { + return mCecVendorId; +} + +Return HdmiCecMock::getPortInfo(getPortInfo_cb _hidl_cb) { + // TODO ready port info from device specific config + _hidl_cb(mPortInfo); + return Void(); +} + +Return HdmiCecMock::setOption(OptionKey key, bool value) { + cec_set_option(static_cast(key), value ? 1 : 0); + return Void(); +} + +Return HdmiCecMock::setLanguage(const hidl_string& language) { + if (language.size() != 3) { + LOG(ERROR) << "Wrong language code: expected 3 letters, but it was " << language.size() + << "."; + return Void(); + } + // TODO validate if language is a valid language code + const char* languageStr = language.c_str(); + int convertedLanguage = ((languageStr[0] & 0xFF) << 16) | ((languageStr[1] & 0xFF) << 8) | + (languageStr[2] & 0xFF); + cec_set_option(HDMI_OPTION_SET_LANG, convertedLanguage); + return Void(); +} + +Return HdmiCecMock::enableAudioReturnChannel(int32_t portId __unused, bool enable __unused) { + // Maintain ARC status + return Void(); +} + +Return HdmiCecMock::isConnected(int32_t portId) { + // maintain port connection status and update on hotplug event + if (portId < mTotalPorts && portId >= 0) { + return mPortConnectionStatus[portId]; + } + return false; +} + +void* HdmiCecMock::__threadLoop(void* user) { + HdmiCecMock* const self = static_cast(user); + self->threadLoop(); + return 0; +} + +int HdmiCecMock::readMessageFromFifo(unsigned char* buf, int msgCount) { + if (msgCount <= 0 || !buf) { + return 0; + } + + int ret = -1; + /* maybe blocked at driver */ + ret = read(mInputFile, buf, msgCount); + if (ret < 0) { + ALOGE("[halimp] read :%s failed, ret:%d\n", CEC_MSG_IN_FIFO, ret); + return -1; + } + + return ret; +} + +int HdmiCecMock::sendMessageToFifo(const CecMessage& message) { + unsigned char msgBuf[CEC_MESSAGE_BODY_MAX_LENGTH]; + int ret = -1; + + memset(msgBuf, 0, sizeof(msgBuf)); + msgBuf[0] = ((static_cast(message.initiator) & 0xf) << 4) | + (static_cast(message.destination) & 0xf); + + size_t length = std::min(static_cast(message.body.size()), + static_cast(MaxLength::MESSAGE_BODY)); + for (size_t i = 0; i < length; ++i) { + msgBuf[i + 1] = static_cast(message.body[i]); + } + + // open the output pipe for writing outgoing cec message + mOutputFile = open(CEC_MSG_OUT_FIFO, O_WRONLY); + if (mOutputFile < 0) { + ALOGD("[halimp] file open failed for writing"); + return -1; + } + + // write message into the output pipe + ret = write(mOutputFile, msgBuf, length + 1); + close(mOutputFile); + if (ret < 0) { + ALOGE("[halimp] write :%s failed, ret:%d\n", CEC_MSG_OUT_FIFO, ret); + return -1; + } + return ret; +} + +void HdmiCecMock::printCecMsgBuf(const char* msg_buf, int len) { + char buf[64] = {}; + int i, size = 0; + memset(buf, 0, sizeof(buf)); + for (i = 0; i < len; i++) { + size += sprintf(buf + size, " %02x", msg_buf[i]); + } + ALOGD("[halimp] %s, msg:%s", __FUNCTION__, buf); +} + +void HdmiCecMock::handleHotplugMessage(unsigned char* msgBuf) { + HotplugEvent hotplugEvent{.connected = ((msgBuf[3]) & 0xf) > 0, + .portId = static_cast(msgBuf[0] & 0xf)}; + + if (hotplugEvent.portId >= mPortInfo.size()) { + ALOGD("[halimp] ignore hot plug message, id %x does not exist", hotplugEvent.portId); + return; + } + + ALOGD("[halimp] hot plug port id %x, is connected %x", (msgBuf[0] & 0xf), (msgBuf[3] & 0xf)); + if (mPortInfo[hotplugEvent.portId].type == HdmiPortType::OUTPUT) { + mPhysicalAddress = + ((hotplugEvent.connected == 0) ? 0xffff : ((msgBuf[1] << 8) | (msgBuf[2]))); + mPortInfo[hotplugEvent.portId].physicalAddress = mPhysicalAddress; + ALOGD("[halimp] hot plug physical address %x", mPhysicalAddress); + } + + // todo update connection status + + if (mCallback != nullptr) { + mCallback->onHotplugEvent(hotplugEvent); + } +} + +void HdmiCecMock::handleCecMessage(unsigned char* msgBuf, int megSize) { + CecMessage message; + size_t length = std::min(static_cast(megSize - 1), + static_cast(MaxLength::MESSAGE_BODY)); + message.body.resize(length); + + for (size_t i = 0; i < length; ++i) { + message.body[i] = static_cast(msgBuf[i + 1]); + ALOGD("[halimp] msg body %x", message.body[i]); + } + + message.initiator = static_cast((msgBuf[0] >> 4) & 0xf); + ALOGD("[halimp] msg init %x", message.initiator); + message.destination = static_cast((msgBuf[0] >> 0) & 0xf); + ALOGD("[halimp] msg dest %x", message.destination); + + // messageValidateAndHandle(&event); + + if (mCallback != nullptr) { + mCallback->onCecMessage(message); + } +} + +void HdmiCecMock::threadLoop() { + ALOGD("[halimp] threadLoop start."); + unsigned char msgBuf[CEC_MESSAGE_BODY_MAX_LENGTH]; + int r = -1; + + // open the input pipe + while (mInputFile < 0) { + usleep(1000 * 1000); + mInputFile = open(CEC_MSG_IN_FIFO, O_RDONLY); + } + ALOGD("[halimp] file open ok, fd = %d.", mInputFile); + + while (mCecThreadRun) { + if (!mOptionSystemCecControl) { + usleep(1000 * 1000); + continue; + } + + memset(msgBuf, 0, sizeof(msgBuf)); + // try to get a message from dev. + // echo -n -e '\x04\x83' >> /dev/cec + r = readMessageFromFifo(msgBuf, CEC_MESSAGE_BODY_MAX_LENGTH); + if (r <= 1) { + // ignore received ping messages + continue; + } + + printCecMsgBuf((const char*)msgBuf, r); + + if (((msgBuf[0] >> 4) & 0xf) == 0xf) { + // the message is a hotplug event + handleHotplugMessage(msgBuf); + continue; + } + + handleCecMessage(msgBuf, r); + } + + ALOGD("[halimp] thread end."); + // mCecDevice.mExited = true; +} + +HdmiCecMock::HdmiCecMock() { + ALOGE("[halimp] Opening a virtual HAL for testing and virtual machine."); + mCallback = nullptr; + mPortInfo.resize(mTotalPorts); + mPortConnectionStatus.resize(mTotalPorts); + mPortInfo[0] = {.type = HdmiPortType::OUTPUT, + .portId = static_cast(0), + .cecSupported = true, + .arcSupported = false, + .physicalAddress = mPhysicalAddress}; + mPortConnectionStatus[0] = false; +} + +} // namespace implementation +} // namespace V1_0 +} // namespace cec +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/cec/HdmiCecMock.h b/cec/HdmiCecMock.h new file mode 100644 index 0000000..0a708fa --- /dev/null +++ b/cec/HdmiCecMock.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2019 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. + */ + +#ifndef ANDROID_HARDWARE_TV_CEC_V1_0_HDMICEC_H +#define ANDROID_HARDWARE_TV_CEC_V1_0_HDMICEC_H + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace android { +namespace hardware { +namespace tv { +namespace cec { +namespace V1_0 { +namespace implementation { + +using ::android::sp; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::tv::cec::V1_0::CecLogicalAddress; +using ::android::hardware::tv::cec::V1_0::CecMessage; +using ::android::hardware::tv::cec::V1_0::HdmiPortInfo; +using ::android::hardware::tv::cec::V1_0::IHdmiCec; +using ::android::hardware::tv::cec::V1_0::IHdmiCecCallback; +using ::android::hardware::tv::cec::V1_0::MaxLength; +using ::android::hardware::tv::cec::V1_0::OptionKey; +using ::android::hardware::tv::cec::V1_0::Result; +using ::android::hardware::tv::cec::V1_0::SendMessageResult; + +#define CEC_MSG_IN_FIFO "/dev/cec_in_pipe" +#define CEC_MSG_OUT_FIFO "/dev/cec_out_pipe" + +struct HdmiCecMock : public IHdmiCec, public hidl_death_recipient { + HdmiCecMock(); + // Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow. + Return addLogicalAddress(CecLogicalAddress addr) override; + Return clearLogicalAddress() override; + Return getPhysicalAddress(getPhysicalAddress_cb _hidl_cb) override; + Return sendMessage(const CecMessage& message) override; + Return setCallback(const sp& callback) override; + Return getCecVersion() override; + Return getVendorId() override; + Return getPortInfo(getPortInfo_cb _hidl_cb) override; + Return setOption(OptionKey key, bool value) override; + Return setLanguage(const hidl_string& language) override; + Return enableAudioReturnChannel(int32_t portId, bool enable) override; + Return isConnected(int32_t portId) override; + + virtual void serviceDied(uint64_t /*cookie*/, + const wp<::android::hidl::base::V1_0::IBase>& /*who*/) { + setCallback(nullptr); + } + + void cec_set_option(int flag, int value); + void printCecMsgBuf(const char* msg_buf, int len); + + private: + static void* __threadLoop(void* data); + void threadLoop(); + int readMessageFromFifo(unsigned char* buf, int msgCount); + int sendMessageToFifo(const CecMessage& message); + void handleHotplugMessage(unsigned char* msgBuf); + void handleCecMessage(unsigned char* msgBuf, int length); + + private: + sp mCallback; + + // Variables for the virtual cec hal impl + uint16_t mPhysicalAddress = 0xFFFF; + vector mLogicalAddresses; + int32_t mCecVersion = 0; + uint32_t mCecVendorId = 0; + + // Port configuration + int mTotalPorts = 1; + hidl_vec mPortInfo; + hidl_vec mPortConnectionStatus; + + // CEC Option value + int mOptionWakeUp = 0; + int mOptionEnableCec = 0; + int mOptionSystemCecControl = 0; + int mOptionLanguage = 0; + + // Testing variables + // Input file descriptor + int mInputFile; + // Output file descriptor + int mOutputFile; + bool mCecThreadRun = true; + pthread_t mThreadId = 0; +}; +} // namespace implementation +} // namespace V1_0 +} // namespace cec +} // namespace tv +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_TV_CEC_V1_0_HDMICEC_H diff --git a/cec/HdmiCecPort.cpp b/cec/HdmiCecPort.cpp new file mode 100755 index 0000000..73dda12 --- /dev/null +++ b/cec/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 +#include +#include +#include +#include +#include + +#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 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 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/cec/HdmiCecPort.h b/cec/HdmiCecPort.h new file mode 100755 index 0000000..2a2fdef --- /dev/null +++ b/cec/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 + +namespace android { +namespace hardware { +namespace tv { +namespace cec { +namespace V1_0 { +namespace implementation { + +class HdmiCecPort { + public: + HdmiCecPort(unsigned int portId); + ~HdmiCecPort(); + Return init(const char* path); + Return release(); + + unsigned int mPortId; + int mCecFd; + int mExitFd; +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace cec +} // namespace tv +} // namespace hardware +} // namespace android diff --git a/cec/android.hardware.tv.cec@1.0-service.mock.rc b/cec/android.hardware.tv.cec@1.0-service.mock.rc new file mode 100644 index 0000000..170b485 --- /dev/null +++ b/cec/android.hardware.tv.cec@1.0-service.mock.rc @@ -0,0 +1,5 @@ +service vendor.cec-hal-1-0-mock /vendor/bin/hw/android.hardware.tv.cec@1.0-service.mock + interface android.hardware.tv.cec@1.0::IHdmiCec default + class hal + user system + group system diff --git a/cec/android.hardware.tv.cec@1.0-service.mock.xml b/cec/android.hardware.tv.cec@1.0-service.mock.xml new file mode 100644 index 0000000..5afa59d --- /dev/null +++ b/cec/android.hardware.tv.cec@1.0-service.mock.xml @@ -0,0 +1,11 @@ + + + android.hardware.tv.cec + hwbinder + 1.0 + + IHdmiCec + default + + + diff --git a/cec/android.hardware.tv.cec@1.0-service.rc b/cec/android.hardware.tv.cec@1.0-service.rc new file mode 100644 index 0000000..6d25229 --- /dev/null +++ b/cec/android.hardware.tv.cec@1.0-service.rc @@ -0,0 +1,5 @@ +service vendor.cec-hal-1-0 /vendor/bin/hw/android.hardware.tv.cec@1.0-service + interface android.hardware.tv.cec@1.0::IHdmiCec default + class hal + user system + group system diff --git a/cec/service.cpp b/cec/service.cpp new file mode 100644 index 0000000..74b1f62 --- /dev/null +++ b/cec/service.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2016 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-service" + +#include +#include + +using android::hardware::tv::cec::V1_0::IHdmiCec; +using android::hardware::defaultPassthroughServiceImplementation; + +int main() { + return defaultPassthroughServiceImplementation(); +} diff --git a/cec/serviceMock.cpp b/cec/serviceMock.cpp new file mode 100644 index 0000000..c0af22f --- /dev/null +++ b/cec/serviceMock.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 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-service-shim" + +#include +#include +#include "HdmiCecMock.h" + +using android::hardware::configureRpcThreadpool; +using android::hardware::joinRpcThreadpool; +using android::hardware::tv::cec::V1_0::IHdmiCec; +using android::hardware::tv::cec::V1_0::implementation::HdmiCecMock; + +int main() { + configureRpcThreadpool(8, true /* callerWillJoin */); + + // Setup hwbinder service + android::sp service = new HdmiCecMock(); + android::status_t status; + status = service->registerAsService(); + LOG_ALWAYS_FATAL_IF(status != android::OK, "Error while registering mock cec service: %d", + status); + + joinRpcThreadpool(); + return 0; +}