cec: copy hidl hal

* Copy from hardware/interfaces/tv/cec/1.0/default
  at b04e2f3df5ebbbeea46f555d0965357f05aa1457.
This commit is contained in:
Konsta
2025-02-05 20:03:05 +02:00
parent 67ee38657f
commit 72fbf40c67
14 changed files with 1905 additions and 0 deletions

76
cec/Android.bp Normal file
View File

@@ -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",
],
}

435
cec/HdmiCec.cpp Normal file
View File

@@ -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 <android-base/logging.h>
#include <hardware/hardware.h>
#include <hardware/hdmi_cec.h>
#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<int>(CecDeviceType::INACTIVE),
"CecDeviceType::INACTIVE must match legacy value.");
static_assert(CEC_DEVICE_TV == static_cast<int>(CecDeviceType::TV),
"CecDeviceType::TV must match legacy value.");
static_assert(CEC_DEVICE_RECORDER == static_cast<int>(CecDeviceType::RECORDER),
"CecDeviceType::RECORDER must match legacy value.");
static_assert(CEC_DEVICE_TUNER == static_cast<int>(CecDeviceType::TUNER),
"CecDeviceType::TUNER must match legacy value.");
static_assert(CEC_DEVICE_PLAYBACK == static_cast<int>(CecDeviceType::PLAYBACK),
"CecDeviceType::PLAYBACK must match legacy value.");
static_assert(CEC_DEVICE_AUDIO_SYSTEM == static_cast<int>(CecDeviceType::AUDIO_SYSTEM),
"CecDeviceType::AUDIO_SYSTEM must match legacy value.");
static_assert(CEC_DEVICE_MAX == static_cast<int>(CecDeviceType::MAX),
"CecDeviceType::MAX must match legacy value.");
static_assert(CEC_ADDR_TV == static_cast<int>(CecLogicalAddress::TV),
"CecLogicalAddress::TV must match legacy value.");
static_assert(CEC_ADDR_RECORDER_1 == static_cast<int>(CecLogicalAddress::RECORDER_1),
"CecLogicalAddress::RECORDER_1 must match legacy value.");
static_assert(CEC_ADDR_RECORDER_2 == static_cast<int>(CecLogicalAddress::RECORDER_2),
"CecLogicalAddress::RECORDER_2 must match legacy value.");
static_assert(CEC_ADDR_TUNER_1 == static_cast<int>(CecLogicalAddress::TUNER_1),
"CecLogicalAddress::TUNER_1 must match legacy value.");
static_assert(CEC_ADDR_PLAYBACK_1 == static_cast<int>(CecLogicalAddress::PLAYBACK_1),
"CecLogicalAddress::PLAYBACK_1 must match legacy value.");
static_assert(CEC_ADDR_AUDIO_SYSTEM == static_cast<int>(CecLogicalAddress::AUDIO_SYSTEM),
"CecLogicalAddress::AUDIO_SYSTEM must match legacy value.");
static_assert(CEC_ADDR_TUNER_2 == static_cast<int>(CecLogicalAddress::TUNER_2),
"CecLogicalAddress::TUNER_2 must match legacy value.");
static_assert(CEC_ADDR_TUNER_3 == static_cast<int>(CecLogicalAddress::TUNER_3),
"CecLogicalAddress::TUNER_3 must match legacy value.");
static_assert(CEC_ADDR_PLAYBACK_2 == static_cast<int>(CecLogicalAddress::PLAYBACK_2),
"CecLogicalAddress::PLAYBACK_2 must match legacy value.");
static_assert(CEC_ADDR_RECORDER_3 == static_cast<int>(CecLogicalAddress::RECORDER_3),
"CecLogicalAddress::RECORDER_3 must match legacy value.");
static_assert(CEC_ADDR_TUNER_4 == static_cast<int>(CecLogicalAddress::TUNER_4),
"CecLogicalAddress::TUNER_4 must match legacy value.");
static_assert(CEC_ADDR_PLAYBACK_3 == static_cast<int>(CecLogicalAddress::PLAYBACK_3),
"CecLogicalAddress::PLAYBACK_3 must match legacy value.");
static_assert(CEC_ADDR_FREE_USE == static_cast<int>(CecLogicalAddress::FREE_USE),
"CecLogicalAddress::FREE_USE must match legacy value.");
static_assert(CEC_ADDR_UNREGISTERED == static_cast<int>(CecLogicalAddress::UNREGISTERED),
"CecLogicalAddress::UNREGISTERED must match legacy value.");
static_assert(CEC_ADDR_BROADCAST == static_cast<int>(CecLogicalAddress::BROADCAST),
"CecLogicalAddress::BROADCAST must match legacy value.");
static_assert(CEC_MESSAGE_FEATURE_ABORT == static_cast<int>(CecMessageType::FEATURE_ABORT),
"CecMessageType::FEATURE_ABORT must match legacy value.");
static_assert(CEC_MESSAGE_IMAGE_VIEW_ON == static_cast<int>(CecMessageType::IMAGE_VIEW_ON),
"CecMessageType::IMAGE_VIEW_ON must match legacy value.");
static_assert(CEC_MESSAGE_TUNER_STEP_INCREMENT == static_cast<int>(
CecMessageType::TUNER_STEP_INCREMENT),
"CecMessageType::TUNER_STEP_INCREMENT must match legacy value.");
static_assert(CEC_MESSAGE_TUNER_STEP_DECREMENT == static_cast<int>(
CecMessageType::TUNER_STEP_DECREMENT),
"CecMessageType::TUNER_STEP_DECREMENT must match legacy value.");
static_assert(CEC_MESSAGE_TUNER_DEVICE_STATUS == static_cast<int>(
CecMessageType::TUNER_DEVICE_STATUS),
"CecMessageType::TUNER_DEVICE_STATUS must match legacy value.");
static_assert(CEC_MESSAGE_GIVE_TUNER_DEVICE_STATUS == static_cast<int>(
CecMessageType::GIVE_TUNER_DEVICE_STATUS),
"CecMessageType::GIVE_TUNER_DEVICE_STATUS must match legacy value.");
static_assert(CEC_MESSAGE_RECORD_ON == static_cast<int>(CecMessageType::RECORD_ON),
"CecMessageType::RECORD_ON must match legacy value.");
static_assert(CEC_MESSAGE_RECORD_STATUS == static_cast<int>(CecMessageType::RECORD_STATUS),
"CecMessageType::RECORD_STATUS must match legacy value.");
static_assert(CEC_MESSAGE_RECORD_OFF == static_cast<int>(CecMessageType::RECORD_OFF),
"CecMessageType::RECORD_OFF must match legacy value.");
static_assert(CEC_MESSAGE_TEXT_VIEW_ON == static_cast<int>(CecMessageType::TEXT_VIEW_ON),
"CecMessageType::TEXT_VIEW_ON must match legacy value.");
static_assert(CEC_MESSAGE_RECORD_TV_SCREEN == static_cast<int>(CecMessageType::RECORD_TV_SCREEN),
"CecMessageType::RECORD_TV_SCREEN must match legacy value.");
static_assert(CEC_MESSAGE_GIVE_DECK_STATUS == static_cast<int>(CecMessageType::GIVE_DECK_STATUS),
"CecMessageType::GIVE_DECK_STATUS must match legacy value.");
static_assert(CEC_MESSAGE_STANDBY == static_cast<int>(CecMessageType::STANDBY),
"CecMessageType::STANDBY must match legacy value.");
static_assert(CEC_MESSAGE_PLAY == static_cast<int>(CecMessageType::PLAY),
"CecMessageType::PLAY must match legacy value.");
static_assert(CEC_MESSAGE_DECK_CONTROL == static_cast<int>(CecMessageType::DECK_CONTROL),
"CecMessageType::DECK_CONTROL must match legacy value.");
static_assert(CEC_MESSAGE_TIMER_CLEARED_STATUS == static_cast<int>(
CecMessageType::TIMER_CLEARED_STATUS),
"CecMessageType::TIMER_CLEARED_STATUS must match legacy value.");
static_assert(CEC_MESSAGE_USER_CONTROL_PRESSED == static_cast<int>(
CecMessageType::USER_CONTROL_PRESSED),
"CecMessageType::USER_CONTROL_PRESSED must match legacy value.");
static_assert(CEC_MESSAGE_USER_CONTROL_RELEASED == static_cast<int>(
CecMessageType::USER_CONTROL_RELEASED),
"CecMessageType::USER_CONTROL_RELEASED must match legacy value.");
static_assert(CEC_MESSAGE_GIVE_OSD_NAME == static_cast<int>(CecMessageType::GIVE_OSD_NAME),
"CecMessageType::GIVE_OSD_NAME must match legacy value.");
static_assert(CEC_MESSAGE_SET_OSD_NAME == static_cast<int>(CecMessageType::SET_OSD_NAME),
"CecMessageType::SET_OSD_NAME must match legacy value.");
static_assert(CEC_MESSAGE_SYSTEM_AUDIO_MODE_REQUEST == static_cast<int>(
CecMessageType::SYSTEM_AUDIO_MODE_REQUEST),
"CecMessageType::SYSTEM_AUDIO_MODE_REQUEST must match legacy value.");
static_assert(CEC_MESSAGE_GIVE_AUDIO_STATUS == static_cast<int>(CecMessageType::GIVE_AUDIO_STATUS),
"CecMessageType::GIVE_AUDIO_STATUS must match legacy value.");
static_assert(CEC_MESSAGE_SET_SYSTEM_AUDIO_MODE == static_cast<int>(
CecMessageType::SET_SYSTEM_AUDIO_MODE),
"CecMessageType::SET_SYSTEM_AUDIO_MODE must match legacy value.");
static_assert(CEC_MESSAGE_REPORT_AUDIO_STATUS == static_cast<int>(
CecMessageType::REPORT_AUDIO_STATUS),
"CecMessageType::REPORT_AUDIO_STATUS must match legacy value.");
static_assert(CEC_MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS == static_cast<int>(
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<int>(
CecMessageType::SYSTEM_AUDIO_MODE_STATUS),
"CecMessageType::SYSTEM_AUDIO_MODE_STATUS must match legacy value.");
static_assert(CEC_MESSAGE_ROUTING_CHANGE == static_cast<int>(CecMessageType::ROUTING_CHANGE),
"CecMessageType::ROUTING_CHANGE must match legacy value.");
static_assert(CEC_MESSAGE_ROUTING_INFORMATION == static_cast<int>(
CecMessageType::ROUTING_INFORMATION),
"CecMessageType::ROUTING_INFORMATION must match legacy value.");
static_assert(CEC_MESSAGE_ACTIVE_SOURCE == static_cast<int>(CecMessageType::ACTIVE_SOURCE),
"CecMessageType::ACTIVE_SOURCE must match legacy value.");
static_assert(CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS == static_cast<int>(
CecMessageType::GIVE_PHYSICAL_ADDRESS),
"CecMessageType::GIVE_PHYSICAL_ADDRESS must match legacy value.");
static_assert(CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS == static_cast<int>(
CecMessageType::REPORT_PHYSICAL_ADDRESS),
"CecMessageType::REPORT_PHYSICAL_ADDRESS must match legacy value.");
static_assert(CEC_MESSAGE_REQUEST_ACTIVE_SOURCE == static_cast<int>(
CecMessageType::REQUEST_ACTIVE_SOURCE),
"CecMessageType::REQUEST_ACTIVE_SOURCE must match legacy value.");
static_assert(CEC_MESSAGE_SET_STREAM_PATH == static_cast<int>(CecMessageType::SET_STREAM_PATH),
"CecMessageType::SET_STREAM_PATH must match legacy value.");
static_assert(CEC_MESSAGE_DEVICE_VENDOR_ID == static_cast<int>(CecMessageType::DEVICE_VENDOR_ID),
"CecMessageType::DEVICE_VENDOR_ID must match legacy value.");
static_assert(CEC_MESSAGE_VENDOR_COMMAND == static_cast<int>(CecMessageType::VENDOR_COMMAND),
"CecMessageType::VENDOR_COMMAND must match legacy value.");
static_assert(CEC_MESSAGE_VENDOR_REMOTE_BUTTON_DOWN == static_cast<int>(
CecMessageType::VENDOR_REMOTE_BUTTON_DOWN),
"CecMessageType::VENDOR_REMOTE_BUTTON_DOWN must match legacy value.");
static_assert(CEC_MESSAGE_VENDOR_REMOTE_BUTTON_UP == static_cast<int>(
CecMessageType::VENDOR_REMOTE_BUTTON_UP),
"CecMessageType::VENDOR_REMOTE_BUTTON_UP must match legacy value.");
static_assert(CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID == static_cast<int>(
CecMessageType::GIVE_DEVICE_VENDOR_ID),
"CecMessageType::GIVE_DEVICE_VENDOR_ID must match legacy value.");
static_assert(CEC_MESSAGE_MENU_REQUEST == static_cast<int>(CecMessageType::MENU_REQUEST),
"CecMessageType::MENU_REQUEST must match legacy value.");
static_assert(CEC_MESSAGE_MENU_STATUS == static_cast<int>(CecMessageType::MENU_STATUS),
"CecMessageType::MENU_STATUS must match legacy value.");
static_assert(CEC_MESSAGE_GIVE_DEVICE_POWER_STATUS == static_cast<int>(
CecMessageType::GIVE_DEVICE_POWER_STATUS),
"CecMessageType::GIVE_DEVICE_POWER_STATUS must match legacy value.");
static_assert(CEC_MESSAGE_REPORT_POWER_STATUS == static_cast<int>(
CecMessageType::REPORT_POWER_STATUS),
"CecMessageType::REPORT_POWER_STATUS must match legacy value.");
static_assert(CEC_MESSAGE_GET_MENU_LANGUAGE == static_cast<int>(CecMessageType::GET_MENU_LANGUAGE),
"CecMessageType::GET_MENU_LANGUAGE must match legacy value.");
static_assert(CEC_MESSAGE_SELECT_ANALOG_SERVICE == static_cast<int>(
CecMessageType::SELECT_ANALOG_SERVICE),
"CecMessageType::SELECT_ANALOG_SERVICE must match legacy value.");
static_assert(CEC_MESSAGE_SELECT_DIGITAL_SERVICE == static_cast<int>(
CecMessageType::SELECT_DIGITAL_SERVICE),
"CecMessageType::SELECT_DIGITAL_SERVICE must match legacy value.");
static_assert(CEC_MESSAGE_SET_DIGITAL_TIMER == static_cast<int>(CecMessageType::SET_DIGITAL_TIMER),
"CecMessageType::SET_DIGITAL_TIMER must match legacy value.");
static_assert(CEC_MESSAGE_CLEAR_DIGITAL_TIMER == static_cast<int>(
CecMessageType::CLEAR_DIGITAL_TIMER),
"CecMessageType::CLEAR_DIGITAL_TIMER must match legacy value.");
static_assert(CEC_MESSAGE_SET_AUDIO_RATE == static_cast<int>(CecMessageType::SET_AUDIO_RATE),
"CecMessageType::SET_AUDIO_RATE must match legacy value.");
static_assert(CEC_MESSAGE_INACTIVE_SOURCE == static_cast<int>(CecMessageType::INACTIVE_SOURCE),
"CecMessageType::INACTIVE_SOURCE must match legacy value.");
static_assert(CEC_MESSAGE_CEC_VERSION == static_cast<int>(CecMessageType::CEC_VERSION),
"CecMessageType::CEC_VERSION must match legacy value.");
static_assert(CEC_MESSAGE_GET_CEC_VERSION == static_cast<int>(CecMessageType::GET_CEC_VERSION),
"CecMessageType::GET_CEC_VERSION must match legacy value.");
static_assert(CEC_MESSAGE_VENDOR_COMMAND_WITH_ID == static_cast<int>(
CecMessageType::VENDOR_COMMAND_WITH_ID),
"CecMessageType::VENDOR_COMMAND_WITH_ID must match legacy value.");
static_assert(CEC_MESSAGE_CLEAR_EXTERNAL_TIMER == static_cast<int>(
CecMessageType::CLEAR_EXTERNAL_TIMER),
"CecMessageType::CLEAR_EXTERNAL_TIMER must match legacy value.");
static_assert(CEC_MESSAGE_SET_EXTERNAL_TIMER == static_cast<int>(
CecMessageType::SET_EXTERNAL_TIMER),
"CecMessageType::SET_EXTERNAL_TIMER must match legacy value.");
static_assert(CEC_MESSAGE_INITIATE_ARC == static_cast<int>(CecMessageType::INITIATE_ARC),
"CecMessageType::INITIATE_ARC must match legacy value.");
static_assert(CEC_MESSAGE_REPORT_ARC_INITIATED == static_cast<int>(
CecMessageType::REPORT_ARC_INITIATED),
"CecMessageType::REPORT_ARC_INITIATED must match legacy value.");
static_assert(CEC_MESSAGE_REPORT_ARC_TERMINATED == static_cast<int>(
CecMessageType::REPORT_ARC_TERMINATED),
"CecMessageType::REPORT_ARC_TERMINATED must match legacy value.");
static_assert(CEC_MESSAGE_REQUEST_ARC_INITIATION == static_cast<int>(
CecMessageType::REQUEST_ARC_INITIATION),
"CecMessageType::REQUEST_ARC_INITIATION must match legacy value.");
static_assert(CEC_MESSAGE_REQUEST_ARC_TERMINATION == static_cast<int>(
CecMessageType::REQUEST_ARC_TERMINATION),
"CecMessageType::REQUEST_ARC_TERMINATION must match legacy value.");
static_assert(CEC_MESSAGE_TERMINATE_ARC == static_cast<int>(CecMessageType::TERMINATE_ARC),
"CecMessageType::TERMINATE_ARC must match legacy value.");
static_assert(CEC_MESSAGE_ABORT == static_cast<int>(CecMessageType::ABORT),
"CecMessageType::ABORT must match legacy value.");
static_assert(ABORT_UNRECOGNIZED_MODE == static_cast<int>(AbortReason::UNRECOGNIZED_MODE),
"AbortReason::UNRECOGNIZED_MODE must match legacy value.");
static_assert(ABORT_NOT_IN_CORRECT_MODE == static_cast<int>(AbortReason::NOT_IN_CORRECT_MODE),
"AbortReason::NOT_IN_CORRECT_MODE must match legacy value.");
static_assert(ABORT_CANNOT_PROVIDE_SOURCE == static_cast<int>(AbortReason::CANNOT_PROVIDE_SOURCE),
"AbortReason::CANNOT_PROVIDE_SOURCE must match legacy value.");
static_assert(ABORT_INVALID_OPERAND == static_cast<int>(AbortReason::INVALID_OPERAND),
"AbortReason::INVALID_OPERAND must match legacy value.");
static_assert(ABORT_REFUSED == static_cast<int>(AbortReason::REFUSED),
"AbortReason::REFUSED must match legacy value.");
static_assert(ABORT_UNABLE_TO_DETERMINE == static_cast<int>(AbortReason::UNABLE_TO_DETERMINE),
"AbortReason::UNABLE_TO_DETERMINE must match legacy value.");
static_assert(HDMI_RESULT_SUCCESS == static_cast<int>(SendMessageResult::SUCCESS),
"SendMessageResult::SUCCESS must match legacy value.");
static_assert(HDMI_RESULT_NACK == static_cast<int>(SendMessageResult::NACK),
"SendMessageResult::NACK must match legacy value.");
static_assert(HDMI_RESULT_BUSY == static_cast<int>(SendMessageResult::BUSY),
"SendMessageResult::BUSY must match legacy value.");
static_assert(HDMI_RESULT_FAIL == static_cast<int>(SendMessageResult::FAIL),
"SendMessageResult::FAIL must match legacy value.");
static_assert(HDMI_INPUT == static_cast<int>(HdmiPortType::INPUT),
"HdmiPortType::INPUT must match legacy value.");
static_assert(HDMI_OUTPUT == static_cast<int>(HdmiPortType::OUTPUT),
"HdmiPortType::OUTPUT must match legacy value.");
static_assert(HDMI_OPTION_WAKEUP == static_cast<int>(OptionKey::WAKEUP),
"OptionKey::WAKEUP must match legacy value.");
static_assert(HDMI_OPTION_ENABLE_CEC == static_cast<int>(OptionKey::ENABLE_CEC),
"OptionKey::ENABLE_CEC must match legacy value.");
static_assert(HDMI_OPTION_SYSTEM_CEC_CONTROL == static_cast<int>(OptionKey::SYSTEM_CEC_CONTROL),
"OptionKey::SYSTEM_CEC_CONTROL must match legacy value.");
sp<IHdmiCecCallback> HdmiCec::mCallback = nullptr;
HdmiCec::HdmiCec(hdmi_cec_device_t* device) : mDevice(device) {}
// Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow.
Return<Result> HdmiCec::addLogicalAddress(CecLogicalAddress addr) {
int ret = mDevice->add_logical_address(mDevice, static_cast<cec_logical_address_t>(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<void> HdmiCec::clearLogicalAddress() {
mDevice->clear_logical_address(mDevice);
return Void();
}
Return<void> 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<SendMessageResult> HdmiCec::sendMessage(const CecMessage& message) {
if (message.body.size() > CEC_MESSAGE_BODY_MAX_LENGTH) {
return SendMessageResult::FAIL;
}
cec_message_t legacyMessage {
.initiator = static_cast<cec_logical_address_t>(message.initiator),
.destination = static_cast<cec_logical_address_t>(message.destination),
.length = message.body.size(),
};
for (size_t i = 0; i < message.body.size(); ++i) {
legacyMessage.body[i] = static_cast<unsigned char>(message.body[i]);
}
return static_cast<SendMessageResult>(mDevice->send_message(mDevice, &legacyMessage));
}
Return<void> HdmiCec::setCallback(const sp<IHdmiCecCallback>& 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<int32_t> HdmiCec::getCecVersion() {
int version;
mDevice->get_version(mDevice, &version);
return static_cast<int32_t>(version);
}
Return<uint32_t> HdmiCec::getVendorId() {
uint32_t vendor_id;
mDevice->get_vendor_id(mDevice, &vendor_id);
return vendor_id;
}
Return<void> HdmiCec::getPortInfo(getPortInfo_cb _hidl_cb) {
struct hdmi_port_info* legacyPorts;
int numPorts;
hidl_vec<HdmiPortInfo> portInfos;
mDevice->get_port_info(mDevice, &legacyPorts, &numPorts);
portInfos.resize(numPorts);
for (int i = 0; i < numPorts; ++i) {
portInfos[i] = {
.type = static_cast<HdmiPortType>(legacyPorts[i].type),
.portId = static_cast<uint32_t>(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<void> HdmiCec::setOption(OptionKey key, bool value) {
mDevice->set_option(mDevice, static_cast<int>(key), value ? 1 : 0);
return Void();
}
Return<void> 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<void> HdmiCec::enableAudioReturnChannel(int32_t portId, bool enable) {
mDevice->set_audio_return_channel(mDevice, portId, enable ? 1 : 0);
return Void();
}
Return<bool> 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

109
cec/HdmiCec.h Normal file
View File

@@ -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 <algorithm>
#include <android/hardware/tv/cec/1.0/IHdmiCec.h>
#include <hidl/Status.h>
#include <hardware/hardware.h>
#include <hardware/hdmi_cec.h>
#include <hidl/MQDescriptor.h>
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<Result> addLogicalAddress(CecLogicalAddress addr) override;
Return<void> clearLogicalAddress() override;
Return<void> getPhysicalAddress(getPhysicalAddress_cb _hidl_cb) override;
Return<SendMessageResult> sendMessage(const CecMessage& message) override;
Return<void> setCallback(const sp<IHdmiCecCallback>& callback) override;
Return<int32_t> getCecVersion() override;
Return<uint32_t> getVendorId() override;
Return<void> getPortInfo(getPortInfo_cb _hidl_cb) override;
Return<void> setOption(OptionKey key, bool value) override;
Return<void> setLanguage(const hidl_string& language) override;
Return<void> enableAudioReturnChannel(int32_t portId, bool enable) override;
Return<bool> 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<size_t>(MaxLength::MESSAGE_BODY));
CecMessage cecMessage {
.initiator = static_cast<CecLogicalAddress>(event->cec.initiator),
.destination = static_cast<CecLogicalAddress>(event->cec.destination),
};
cecMessage.body.resize(length);
for (size_t i = 0; i < length; ++i) {
cecMessage.body[i] = static_cast<uint8_t>(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<uint32_t>(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<IHdmiCecCallback> 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

523
cec/HdmiCecDefault.cpp Normal file
View File

@@ -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 <android-base/logging.h>
#include <android-base/properties.h>
#include <cutils/properties.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/ioctl.h>
#include <poll.h>
#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<Result> 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<cec_logical_address_t>(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 = 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<void> 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<void> 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<SendMessageResult> HdmiCecDefault::sendMessage(const CecMessage& message) {
if (!mCecEnabled) {
return SendMessageResult::FAIL;
}
cec_msg cecMsg;
memset(&cecMsg, 0, sizeof(cec_msg));
int initiator = static_cast<cec_logical_address_t>(message.initiator);
int destination = static_cast<cec_logical_address_t>(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<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);
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<void> HdmiCecDefault::setCallback(const sp<IHdmiCecCallback>& callback) {
if (mCallback != nullptr) {
mCallback->unlinkToDeath(this);
mCallback = nullptr;
}
if (callback != nullptr) {
mCallback = callback;
mCallback->linkToDeath(this, 0 /*cookie*/);
}
return Void();
}
Return<int32_t> HdmiCecDefault::getCecVersion() {
return property_get_int32("ro.hdmi.cec_version", CEC_OP_CEC_VERSION_1_4);
}
Return<uint32_t> HdmiCecDefault::getVendorId() {
return property_get_int32("ro.hdmi.vendor_id", 0x000c03 /* HDMI LLC vendor ID */);
}
Return<void> HdmiCecDefault::getPortInfo(getPortInfo_cb callback) {
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};
}
callback(portInfos);
return Void();
}
Return<void> 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<void> HdmiCecDefault::setLanguage(const hidl_string& /*language*/) {
return Void();
}
Return<void> HdmiCecDefault::enableAudioReturnChannel(int32_t /*portId*/, bool /*enable*/) {
return Void();
}
Return<bool> 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<Result> 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> 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<void> 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<CecLogicalAddress>(msg.msg[0] >> 4),
.destination = static_cast<CecLogicalAddress>(msg.msg[0] & 0xf),
};
cecMessage.body.resize(length);
for (size_t i = 0; i < length; ++i) {
cecMessage.body[i] = static_cast<uint8_t>(msg.msg[i + 1]);
}
mCallback->onCecMessage(cecMessage);
} else {
LOG(ERROR) << "no event callback for message";
}
}
}
}
int HdmiCecDefault::getOpcode(cec_msg message) {
return static_cast<uint8_t>(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<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
} // namespace tv
} // namespace hardware
} // namespace android

91
cec/HdmiCecDefault.h Normal file
View File

@@ -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 <hardware/hdmi_cec.h>
#include <linux/cec.h>
#include <thread>
#include <vector>
#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<Result> addLogicalAddress(CecLogicalAddress addr) override;
Return<void> clearLogicalAddress() override;
Return<void> getPhysicalAddress(getPhysicalAddress_cb _hidl_cb) override;
Return<SendMessageResult> sendMessage(const CecMessage& message) override;
Return<void> setCallback(const sp<IHdmiCecCallback>& callback) override;
Return<int32_t> getCecVersion() override;
Return<uint32_t> getVendorId() override;
Return<void> getPortInfo(getPortInfo_cb _hidl_cb) override;
Return<void> setOption(OptionKey key, bool value) override;
Return<void> setLanguage(const hidl_string& language) override;
Return<void> enableAudioReturnChannel(int32_t portId, bool enable) override;
Return<bool> isConnected(int32_t portId) override;
virtual void serviceDied(uint64_t, const wp<::android::hidl::base::V1_0::IBase>&) {
setCallback(nullptr);
}
Return<Result> init();
Return<void> 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<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
} // namespace tv
} // namespace hardware
} // namespace android

318
cec/HdmiCecMock.cpp Normal file
View File

@@ -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 <android-base/logging.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/hdmi_cec.h>
#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<Result> 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<void> HdmiCecMock::clearLogicalAddress() {
// remove logical address from the list
mLogicalAddresses = {};
return Void();
}
Return<void> 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<SendMessageResult> HdmiCecMock::sendMessage(const CecMessage& message) {
if (message.body.size() == 0) {
return SendMessageResult::NACK;
}
sendMessageToFifo(message);
return SendMessageResult::SUCCESS;
}
Return<void> HdmiCecMock::setCallback(const sp<IHdmiCecCallback>& 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<int32_t> HdmiCecMock::getCecVersion() {
// maintain a cec version and return it
return mCecVersion;
}
Return<uint32_t> HdmiCecMock::getVendorId() {
return mCecVendorId;
}
Return<void> HdmiCecMock::getPortInfo(getPortInfo_cb _hidl_cb) {
// TODO ready port info from device specific config
_hidl_cb(mPortInfo);
return Void();
}
Return<void> HdmiCecMock::setOption(OptionKey key, bool value) {
cec_set_option(static_cast<int>(key), value ? 1 : 0);
return Void();
}
Return<void> 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<void> HdmiCecMock::enableAudioReturnChannel(int32_t portId __unused, bool enable __unused) {
// Maintain ARC status
return Void();
}
Return<bool> 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<HdmiCecMock*>(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<uint8_t>(message.initiator) & 0xf) << 4) |
(static_cast<uint8_t>(message.destination) & 0xf);
size_t length = std::min(static_cast<size_t>(message.body.size()),
static_cast<size_t>(MaxLength::MESSAGE_BODY));
for (size_t i = 0; i < length; ++i) {
msgBuf[i + 1] = static_cast<unsigned char>(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<uint32_t>(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<size_t>(megSize - 1),
static_cast<size_t>(MaxLength::MESSAGE_BODY));
message.body.resize(length);
for (size_t i = 0; i < length; ++i) {
message.body[i] = static_cast<uint8_t>(msgBuf[i + 1]);
ALOGD("[halimp] msg body %x", message.body[i]);
}
message.initiator = static_cast<CecLogicalAddress>((msgBuf[0] >> 4) & 0xf);
ALOGD("[halimp] msg init %x", message.initiator);
message.destination = static_cast<CecLogicalAddress>((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<uint32_t>(0),
.cecSupported = true,
.arcSupported = false,
.physicalAddress = mPhysicalAddress};
mPortConnectionStatus[0] = false;
}
} // namespace implementation
} // namespace V1_0
} // namespace cec
} // namespace tv
} // namespace hardware
} // namespace android

122
cec/HdmiCecMock.h Normal file
View File

@@ -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 <android/hardware/tv/cec/1.0/IHdmiCec.h>
#include <hardware/hardware.h>
#include <hardware/hdmi_cec.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <algorithm>
#include <vector>
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<Result> addLogicalAddress(CecLogicalAddress addr) override;
Return<void> clearLogicalAddress() override;
Return<void> getPhysicalAddress(getPhysicalAddress_cb _hidl_cb) override;
Return<SendMessageResult> sendMessage(const CecMessage& message) override;
Return<void> setCallback(const sp<IHdmiCecCallback>& callback) override;
Return<int32_t> getCecVersion() override;
Return<uint32_t> getVendorId() override;
Return<void> getPortInfo(getPortInfo_cb _hidl_cb) override;
Return<void> setOption(OptionKey key, bool value) override;
Return<void> setLanguage(const hidl_string& language) override;
Return<void> enableAudioReturnChannel(int32_t portId, bool enable) override;
Return<bool> 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<IHdmiCecCallback> mCallback;
// Variables for the virtual cec hal impl
uint16_t mPhysicalAddress = 0xFFFF;
vector<CecLogicalAddress> mLogicalAddresses;
int32_t mCecVersion = 0;
uint32_t mCecVendorId = 0;
// Port configuration
int mTotalPorts = 1;
hidl_vec<HdmiPortInfo> mPortInfo;
hidl_vec<bool> 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

101
cec/HdmiCecPort.cpp Executable file
View File

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

42
cec/HdmiCecPort.h Executable file
View File

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

View File

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

View File

@@ -0,0 +1,11 @@
<manifest version="1.0" type="device">
<hal format="hidl">
<name>android.hardware.tv.cec</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>IHdmiCec</name>
<instance>default</instance>
</interface>
</hal>
</manifest>

View File

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

27
cec/service.cpp Normal file
View File

@@ -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 <android/hardware/tv/cec/1.0/IHdmiCec.h>
#include <hidl/LegacySupport.h>
using android::hardware::tv::cec::V1_0::IHdmiCec;
using android::hardware::defaultPassthroughServiceImplementation;
int main() {
return defaultPassthroughServiceImplementation<IHdmiCec>();
}

40
cec/serviceMock.cpp Normal file
View File

@@ -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 <android/hardware/tv/cec/1.0/IHdmiCec.h>
#include <hidl/LegacySupport.h>
#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<IHdmiCec> 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;
}