blob: 4dbd339706705e8ece62a2f17bacbee49fe870fa [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
Venkatarama Avadhani601d2992022-12-12 22:29:30 +053017#define LOG_TAG "android.hardware.tv.hdmi.cec"
Venkatarama Avadhani820b5482022-05-18 15:19:04 +053018#include <android-base/logging.h>
19#include <fcntl.h>
20#include <utils/Log.h>
21
22#include <hardware/hardware.h>
23#include <hardware/hdmi_cec.h>
24#include "HdmiCecMock.h"
25
26using ndk::ScopedAStatus;
27
28namespace android {
29namespace hardware {
30namespace tv {
Venkatarama Avadhani601d2992022-12-12 22:29:30 +053031namespace hdmi {
Venkatarama Avadhani820b5482022-05-18 15:19:04 +053032namespace cec {
33namespace implementation {
34
35void HdmiCecMock::serviceDied(void* cookie) {
36 ALOGE("HdmiCecMock died");
37 auto hdmiCecMock = static_cast<HdmiCecMock*>(cookie);
Shraddha Basantwani98b312a2024-02-19 09:39:01 +000038 hdmiCecMock->closeCallback();
Venkatarama Avadhani820b5482022-05-18 15:19:04 +053039}
40
41ScopedAStatus HdmiCecMock::addLogicalAddress(CecLogicalAddress addr, Result* _aidl_return) {
42 // Have a list to maintain logical addresses
43 mLogicalAddresses.push_back(addr);
44 *_aidl_return = Result::SUCCESS;
45 return ScopedAStatus::ok();
46}
47
48ScopedAStatus HdmiCecMock::clearLogicalAddress() {
49 // Remove logical address from the list
50 mLogicalAddresses = {};
51 return ScopedAStatus::ok();
52}
53
54ScopedAStatus HdmiCecMock::enableAudioReturnChannel(int32_t portId __unused, bool enable __unused) {
55 // Maintain ARC status
56 return ScopedAStatus::ok();
57}
58
59ScopedAStatus HdmiCecMock::getCecVersion(int32_t* _aidl_return) {
60 // Maintain a cec version and return it
61 *_aidl_return = mCecVersion;
62 return ScopedAStatus::ok();
63}
64
65ScopedAStatus HdmiCecMock::getPhysicalAddress(int32_t* _aidl_return) {
66 // Maintain a physical address and return it
67 // Default 0xFFFF, update on hotplug event
68 *_aidl_return = mPhysicalAddress;
69 return ScopedAStatus::ok();
70}
71
72ScopedAStatus HdmiCecMock::getVendorId(int32_t* _aidl_return) {
73 *_aidl_return = mCecVendorId;
74 return ScopedAStatus::ok();
75}
76
77ScopedAStatus HdmiCecMock::sendMessage(const CecMessage& message, SendMessageResult* _aidl_return) {
78 if (message.body.size() == 0) {
79 *_aidl_return = SendMessageResult::NACK;
80 } else {
81 sendMessageToFifo(message);
82 *_aidl_return = SendMessageResult::SUCCESS;
83 }
84 return ScopedAStatus::ok();
85}
86
87ScopedAStatus HdmiCecMock::setCallback(const std::shared_ptr<IHdmiCecCallback>& callback) {
Shraddha Basantwani98b312a2024-02-19 09:39:01 +000088 closeCallback();
Venkatarama Avadhani820b5482022-05-18 15:19:04 +053089
90 if (callback != nullptr) {
Shraddha Basantwani98b312a2024-02-19 09:39:01 +000091 mCallback = callback;
Venkatarama Avadhanie0178782023-09-11 09:03:18 +053092 mDeathRecipient =
93 ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(serviceDied));
94 AIBinder_linkToDeath(callback->asBinder().get(), mDeathRecipient.get(), this /* cookie */);
Venkatarama Avadhani820b5482022-05-18 15:19:04 +053095
96 mInputFile = open(CEC_MSG_IN_FIFO, O_RDWR | O_CLOEXEC);
97 mOutputFile = open(CEC_MSG_OUT_FIFO, O_RDWR | O_CLOEXEC);
98 pthread_create(&mThreadId, NULL, __threadLoop, this);
99 pthread_setname_np(mThreadId, "hdmi_cec_loop");
100 }
101 return ScopedAStatus::ok();
102}
103
104ScopedAStatus HdmiCecMock::setLanguage(const std::string& language) {
105 if (language.size() != 3) {
Shraddha Basantwani98b312a2024-02-19 09:39:01 +0000106 ALOGE("[halimp_aidl] Wrong language code: expected 3 letters, but it was %zu",
107 language.size());
Venkatarama Avadhani820b5482022-05-18 15:19:04 +0530108 return ScopedAStatus::ok();
109 }
110 // TODO Validate if language is a valid language code
111 const char* languageStr = language.c_str();
112 int convertedLanguage = ((languageStr[0] & 0xFF) << 16) | ((languageStr[1] & 0xFF) << 8) |
113 (languageStr[2] & 0xFF);
114 mOptionLanguage = convertedLanguage;
115 return ScopedAStatus::ok();
116}
117
118ScopedAStatus HdmiCecMock::enableWakeupByOtp(bool value) {
119 mOptionWakeUp = value;
120 return ScopedAStatus::ok();
121}
122
123ScopedAStatus HdmiCecMock::enableCec(bool value) {
124 mOptionEnableCec = value;
125 return ScopedAStatus::ok();
126}
127
128ScopedAStatus HdmiCecMock::enableSystemCecControl(bool value) {
129 mOptionSystemCecControl = value;
130 return ScopedAStatus::ok();
131}
132
133void* HdmiCecMock::__threadLoop(void* user) {
134 HdmiCecMock* const self = static_cast<HdmiCecMock*>(user);
135 self->threadLoop();
136 return 0;
137}
138
139int HdmiCecMock::readMessageFromFifo(unsigned char* buf, int msgCount) {
140 if (msgCount <= 0 || !buf) {
141 return 0;
142 }
143
144 int ret = -1;
145 // Maybe blocked at driver
146 ret = read(mInputFile, buf, msgCount);
147 if (ret < 0) {
148 ALOGE("[halimp_aidl] read :%s failed, ret:%d\n", CEC_MSG_IN_FIFO, ret);
149 return -1;
150 }
151
152 return ret;
153}
154
155int HdmiCecMock::sendMessageToFifo(const CecMessage& message) {
156 unsigned char msgBuf[CEC_MESSAGE_BODY_MAX_LENGTH + 1] = {0};
157 int ret = -1;
158
159 msgBuf[0] = ((static_cast<uint8_t>(message.initiator) & 0xf) << 4) |
160 (static_cast<uint8_t>(message.destination) & 0xf);
161
162 size_t length = std::min(static_cast<size_t>(message.body.size()),
163 static_cast<size_t>(CEC_MESSAGE_BODY_MAX_LENGTH));
164 for (size_t i = 0; i < length; ++i) {
165 msgBuf[i + 1] = static_cast<unsigned char>(message.body[i]);
166 }
167
168 // Open the output pipe for writing outgoing cec message
169 mOutputFile = open(CEC_MSG_OUT_FIFO, O_WRONLY | O_CLOEXEC);
170 if (mOutputFile < 0) {
Shraddha Basantwani98b312a2024-02-19 09:39:01 +0000171 ALOGE("[halimp_aidl] file open failed for writing");
Venkatarama Avadhani820b5482022-05-18 15:19:04 +0530172 return -1;
173 }
174
175 // Write message into the output pipe
176 ret = write(mOutputFile, msgBuf, length + 1);
177 close(mOutputFile);
178 if (ret < 0) {
179 ALOGE("[halimp_aidl] write :%s failed, ret:%d\n", CEC_MSG_OUT_FIFO, ret);
180 return -1;
181 }
182 return ret;
183}
184
185void HdmiCecMock::printCecMsgBuf(const char* msg_buf, int len) {
186 int i, size = 0;
187 const int bufSize = CEC_MESSAGE_BODY_MAX_LENGTH * 3;
188 // Use 2 characters for each byte in the message plus 1 space
189 char buf[bufSize] = {0};
190
191 // Messages longer than max length will be truncated.
192 for (i = 0; i < len && size < bufSize; i++) {
193 size += sprintf(buf + size, " %02x", msg_buf[i]);
194 }
195 ALOGD("[halimp_aidl] %s, msg:%.*s", __FUNCTION__, size, buf);
196}
197
198void HdmiCecMock::handleCecMessage(unsigned char* msgBuf, int msgSize) {
199 CecMessage message;
200 size_t length = std::min(static_cast<size_t>(msgSize - 1),
201 static_cast<size_t>(CEC_MESSAGE_BODY_MAX_LENGTH));
202 message.body.resize(length);
203
204 for (size_t i = 0; i < length; ++i) {
205 message.body[i] = static_cast<uint8_t>(msgBuf[i + 1]);
206 ALOGD("[halimp_aidl] msg body %x", message.body[i]);
207 }
208
209 message.initiator = static_cast<CecLogicalAddress>((msgBuf[0] >> 4) & 0xf);
210 ALOGD("[halimp_aidl] msg init %hhd", message.initiator);
211 message.destination = static_cast<CecLogicalAddress>((msgBuf[0] >> 0) & 0xf);
212 ALOGD("[halimp_aidl] msg dest %hhd", message.destination);
213
214 if (mCallback != nullptr) {
215 mCallback->onCecMessage(message);
216 }
217}
218
219void HdmiCecMock::threadLoop() {
220 ALOGD("[halimp_aidl] threadLoop start.");
221 unsigned char msgBuf[CEC_MESSAGE_BODY_MAX_LENGTH];
222 int r = -1;
223
224 // Open the input pipe
Venkatarama Avadhanie0178782023-09-11 09:03:18 +0530225 while (mCecThreadRun && mInputFile < 0) {
Venkatarama Avadhani820b5482022-05-18 15:19:04 +0530226 usleep(1000 * 1000);
227 mInputFile = open(CEC_MSG_IN_FIFO, O_RDONLY | O_CLOEXEC);
228 }
229 ALOGD("[halimp_aidl] file open ok, fd = %d.", mInputFile);
230
231 while (mCecThreadRun) {
232 if (!mOptionSystemCecControl) {
233 usleep(1000 * 1000);
234 continue;
235 }
236
237 memset(msgBuf, 0, sizeof(msgBuf));
238 // Try to get a message from dev.
239 // echo -n -e '\x04\x83' >> /dev/cec
240 r = readMessageFromFifo(msgBuf, CEC_MESSAGE_BODY_MAX_LENGTH);
241 if (r <= 1) {
242 // Ignore received ping messages
243 continue;
244 }
245
246 printCecMsgBuf((const char*)msgBuf, r);
247
248 if (((msgBuf[0] >> 4) & 0xf) == 0xf) {
249 // The message is a hotplug event, handled by HDMI HAL.
250 continue;
251 }
252
253 handleCecMessage(msgBuf, r);
254 }
255
256 ALOGD("[halimp_aidl] thread end.");
257}
258
259HdmiCecMock::HdmiCecMock() {
Shraddha Basantwani98b312a2024-02-19 09:39:01 +0000260 ALOGD("[halimp_aidl] Opening a virtual CEC HAL for testing and virtual machine.");
Venkatarama Avadhani820b5482022-05-18 15:19:04 +0530261 mCallback = nullptr;
Venkatarama Avadhanie0178782023-09-11 09:03:18 +0530262 mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(nullptr);
263}
264
Shraddha Basantwani98b312a2024-02-19 09:39:01 +0000265void HdmiCecMock::closeCallback() {
266 if (mCallback != nullptr) {
267 ALOGD("[halimp_aidl] HdmiCecMock close the current callback.");
268 mCallback = nullptr;
269 mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(nullptr);
270 mCecThreadRun = false;
271 pthread_join(mThreadId, NULL);
272 }
273}
274
Venkatarama Avadhanie0178782023-09-11 09:03:18 +0530275HdmiCecMock::~HdmiCecMock() {
Shraddha Basantwani98b312a2024-02-19 09:39:01 +0000276 ALOGD("[halimp_aidl] HdmiCecMock shutting down.");
277 closeCallback();
Venkatarama Avadhani820b5482022-05-18 15:19:04 +0530278}
279
280} // namespace implementation
281} // namespace cec
Venkatarama Avadhani601d2992022-12-12 22:29:30 +0530282} // namespace hdmi
Venkatarama Avadhani820b5482022-05-18 15:19:04 +0530283} // namespace tv
284} // namespace hardware
285} // namespace android