blob: 0cf5118b6b07f99e259b321c2edfd65cf8d7f098 [file] [log] [blame]
Venkatarama Avadhani820b5482022-05-18 15:19:04 +05301/*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "android.hardware.tv.hdmi"
18#include <android-base/logging.h>
19#include <fcntl.h>
20#include <utils/Log.h>
21
22#include "HdmiMock.h"
23
24using ndk::ScopedAStatus;
25
26namespace android {
27namespace hardware {
28namespace tv {
29namespace hdmi {
30namespace implementation {
31
32void HdmiMock::serviceDied(void* cookie) {
33 ALOGE("HdmiMock died");
34 auto hdmi = static_cast<HdmiMock*>(cookie);
35 hdmi->mHdmiThreadRun = false;
36}
37
38ScopedAStatus HdmiMock::getPortInfo(std::vector<HdmiPortInfo>* _aidl_return) {
39 *_aidl_return = mPortInfos;
40 return ScopedAStatus::ok();
41}
42
43ScopedAStatus HdmiMock::isConnected(int32_t portId, bool* _aidl_return) {
44 // Maintain port connection status and update on hotplug event
45 if (portId <= mTotalPorts && portId >= 1) {
46 *_aidl_return = mPortConnectionStatus[portId];
47 } else {
48 *_aidl_return = false;
49 }
50
51 return ScopedAStatus::ok();
52}
53
54ScopedAStatus HdmiMock::setCallback(const std::shared_ptr<IHdmiCallback>& callback) {
55 if (mCallback != nullptr) {
56 mCallback = nullptr;
57 }
58
59 if (callback != nullptr) {
60 mCallback = callback;
61 AIBinder_linkToDeath(this->asBinder().get(), mDeathRecipient.get(), 0 /* cookie */);
62
63 mInputFile = open(HDMI_MSG_IN_FIFO, O_RDWR | O_CLOEXEC);
64 pthread_create(&mThreadId, NULL, __threadLoop, this);
65 pthread_setname_np(mThreadId, "hdmi_loop");
66 }
67 return ScopedAStatus::ok();
68}
69
70void* HdmiMock::__threadLoop(void* user) {
71 HdmiMock* const self = static_cast<HdmiMock*>(user);
72 self->threadLoop();
73 return 0;
74}
75
76int HdmiMock::readMessageFromFifo(unsigned char* buf, int msgCount) {
77 if (msgCount <= 0 || !buf) {
78 return 0;
79 }
80
81 int ret = -1;
82 // Maybe blocked at driver
83 ret = read(mInputFile, buf, msgCount);
84 if (ret < 0) {
85 ALOGE("[halimp_aidl] read :%s failed, ret:%d\n", HDMI_MSG_IN_FIFO, ret);
86 return -1;
87 }
88
89 return ret;
90}
91
92void HdmiMock::printEventBuf(const char* msg_buf, int len) {
93 int i, size = 0;
94 const int bufSize = MESSAGE_BODY_MAX_LENGTH * 3;
95 // Use 2 characters for each byte in the message plus 1 space
96 char buf[bufSize] = {0};
97
98 // Messages longer than max length will be truncated.
99 for (i = 0; i < len && size < bufSize; i++) {
100 size += sprintf(buf + size, " %02x", msg_buf[i]);
101 }
102 ALOGD("[halimp_aidl] %s, msg:%.*s", __FUNCTION__, size, buf);
103}
104
105void HdmiMock::handleHotplugMessage(unsigned char* msgBuf) {
106 bool connected = ((msgBuf[3]) & 0xf) > 0;
107 int32_t portId = static_cast<uint32_t>(msgBuf[0] & 0xf);
108
109 if (portId > static_cast<int32_t>(mPortInfos.size())) {
110 ALOGD("[halimp_aidl] ignore hot plug message, id %x does not exist", portId);
111 return;
112 }
113
114 ALOGD("[halimp_aidl] hot plug port id %x, is connected %x", (msgBuf[0] & 0xf),
115 (msgBuf[3] & 0xf));
116 mPortConnectionStatus[portId] = connected;
117 if (mPortInfos[portId].type == HdmiPortType::OUTPUT) {
118 mPhysicalAddress = (connected ? 0xffff : ((msgBuf[1] << 8) | (msgBuf[2])));
119 mPortInfos[portId].physicalAddress = mPhysicalAddress;
120 ALOGD("[halimp_aidl] hot plug physical address %x", mPhysicalAddress);
121 }
122
123 if (mCallback != nullptr) {
124 mCallback->onHotplugEvent(connected, portId);
125 }
126}
127
128void HdmiMock::threadLoop() {
129 ALOGD("[halimp_aidl] threadLoop start.");
130 unsigned char msgBuf[MESSAGE_BODY_MAX_LENGTH];
131 int r = -1;
132
133 // Open the input pipe
134 while (mInputFile < 0) {
135 usleep(1000 * 1000);
136 mInputFile = open(HDMI_MSG_IN_FIFO, O_RDONLY | O_CLOEXEC);
137 }
138 ALOGD("[halimp_aidl] file open ok, fd = %d.", mInputFile);
139
140 while (mHdmiThreadRun) {
141 memset(msgBuf, 0, sizeof(msgBuf));
142 // Try to get a message from dev.
143 // echo -n -e '\x04\x83' >> /dev/cec
144 r = readMessageFromFifo(msgBuf, MESSAGE_BODY_MAX_LENGTH);
145 if (r <= 1) {
146 // Ignore received ping messages
147 continue;
148 }
149
150 printEventBuf((const char*)msgBuf, r);
151
152 if (((msgBuf[0] >> 4) & 0xf) == 0xf) {
153 handleHotplugMessage(msgBuf);
154 }
155 }
156
157 ALOGD("[halimp_aidl] thread end.");
158}
159
160HdmiMock::HdmiMock() {
161 ALOGE("[halimp_aidl] Opening a virtual HDMI HAL for testing and virtual machine.");
162 mCallback = nullptr;
163 mPortInfos.resize(mTotalPorts);
164 mPortConnectionStatus.resize(mTotalPorts);
165 mPortInfos[0] = {.type = HdmiPortType::OUTPUT,
166 .portId = static_cast<uint32_t>(1),
167 .cecSupported = true,
168 .arcSupported = false,
169 .physicalAddress = mPhysicalAddress};
170 mPortConnectionStatus[0] = false;
171 mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(serviceDied));
172}
173
174} // namespace implementation
175} // namespace hdmi
176} // namespace tv
177} // namespace hardware
178} // namespace android