cec: copy hidl hal
* Copy from hardware/interfaces/tv/cec/1.0/default at b04e2f3df5ebbbeea46f555d0965357f05aa1457.
This commit is contained in:
76
cec/Android.bp
Normal file
76
cec/Android.bp
Normal 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
435
cec/HdmiCec.cpp
Normal 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
109
cec/HdmiCec.h
Normal 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
523
cec/HdmiCecDefault.cpp
Normal 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
91
cec/HdmiCecDefault.h
Normal 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
318
cec/HdmiCecMock.cpp
Normal 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
122
cec/HdmiCecMock.h
Normal 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
101
cec/HdmiCecPort.cpp
Executable 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
42
cec/HdmiCecPort.h
Executable 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
|
5
cec/android.hardware.tv.cec@1.0-service.mock.rc
Normal file
5
cec/android.hardware.tv.cec@1.0-service.mock.rc
Normal 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
|
11
cec/android.hardware.tv.cec@1.0-service.mock.xml
Normal file
11
cec/android.hardware.tv.cec@1.0-service.mock.xml
Normal 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>
|
5
cec/android.hardware.tv.cec@1.0-service.rc
Normal file
5
cec/android.hardware.tv.cec@1.0-service.rc
Normal 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
27
cec/service.cpp
Normal 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
40
cec/serviceMock.cpp
Normal 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;
|
||||||
|
}
|
Reference in New Issue
Block a user