• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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.cec"
18 #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 
26 using ndk::ScopedAStatus;
27 
28 namespace android {
29 namespace hardware {
30 namespace tv {
31 namespace hdmi {
32 namespace cec {
33 namespace implementation {
34 
serviceDied(void * cookie)35 void HdmiCecMock::serviceDied(void* cookie) {
36     ALOGE("HdmiCecMock died");
37     auto hdmiCecMock = static_cast<HdmiCecMock*>(cookie);
38     hdmiCecMock->mCecThreadRun = false;
39 }
40 
addLogicalAddress(CecLogicalAddress addr,Result * _aidl_return)41 ScopedAStatus 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 
clearLogicalAddress()48 ScopedAStatus HdmiCecMock::clearLogicalAddress() {
49     // Remove logical address from the list
50     mLogicalAddresses = {};
51     return ScopedAStatus::ok();
52 }
53 
enableAudioReturnChannel(int32_t portId __unused,bool enable __unused)54 ScopedAStatus HdmiCecMock::enableAudioReturnChannel(int32_t portId __unused, bool enable __unused) {
55     // Maintain ARC status
56     return ScopedAStatus::ok();
57 }
58 
getCecVersion(int32_t * _aidl_return)59 ScopedAStatus HdmiCecMock::getCecVersion(int32_t* _aidl_return) {
60     // Maintain a cec version and return it
61     *_aidl_return = mCecVersion;
62     return ScopedAStatus::ok();
63 }
64 
getPhysicalAddress(int32_t * _aidl_return)65 ScopedAStatus 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 
getVendorId(int32_t * _aidl_return)72 ScopedAStatus HdmiCecMock::getVendorId(int32_t* _aidl_return) {
73     *_aidl_return = mCecVendorId;
74     return ScopedAStatus::ok();
75 }
76 
sendMessage(const CecMessage & message,SendMessageResult * _aidl_return)77 ScopedAStatus 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 
setCallback(const std::shared_ptr<IHdmiCecCallback> & callback)87 ScopedAStatus HdmiCecMock::setCallback(const std::shared_ptr<IHdmiCecCallback>& callback) {
88     // If callback is null, mCallback is also set to null so we do not call the old callback.
89     mCallback = callback;
90 
91     if (callback != nullptr) {
92         AIBinder_linkToDeath(this->asBinder().get(), mDeathRecipient.get(), 0 /* cookie */);
93 
94         mInputFile = open(CEC_MSG_IN_FIFO, O_RDWR | O_CLOEXEC);
95         mOutputFile = open(CEC_MSG_OUT_FIFO, O_RDWR | O_CLOEXEC);
96         pthread_create(&mThreadId, NULL, __threadLoop, this);
97         pthread_setname_np(mThreadId, "hdmi_cec_loop");
98     }
99     return ScopedAStatus::ok();
100 }
101 
setLanguage(const std::string & language)102 ScopedAStatus HdmiCecMock::setLanguage(const std::string& language) {
103     if (language.size() != 3) {
104         LOG(ERROR) << "Wrong language code: expected 3 letters, but it was " << language.size()
105                    << ".";
106         return ScopedAStatus::ok();
107     }
108     // TODO Validate if language is a valid language code
109     const char* languageStr = language.c_str();
110     int convertedLanguage = ((languageStr[0] & 0xFF) << 16) | ((languageStr[1] & 0xFF) << 8) |
111                             (languageStr[2] & 0xFF);
112     mOptionLanguage = convertedLanguage;
113     return ScopedAStatus::ok();
114 }
115 
enableWakeupByOtp(bool value)116 ScopedAStatus HdmiCecMock::enableWakeupByOtp(bool value) {
117     mOptionWakeUp = value;
118     return ScopedAStatus::ok();
119 }
120 
enableCec(bool value)121 ScopedAStatus HdmiCecMock::enableCec(bool value) {
122     mOptionEnableCec = value;
123     return ScopedAStatus::ok();
124 }
125 
enableSystemCecControl(bool value)126 ScopedAStatus HdmiCecMock::enableSystemCecControl(bool value) {
127     mOptionSystemCecControl = value;
128     return ScopedAStatus::ok();
129 }
130 
__threadLoop(void * user)131 void* HdmiCecMock::__threadLoop(void* user) {
132     HdmiCecMock* const self = static_cast<HdmiCecMock*>(user);
133     self->threadLoop();
134     return 0;
135 }
136 
readMessageFromFifo(unsigned char * buf,int msgCount)137 int HdmiCecMock::readMessageFromFifo(unsigned char* buf, int msgCount) {
138     if (msgCount <= 0 || !buf) {
139         return 0;
140     }
141 
142     int ret = -1;
143     // Maybe blocked at driver
144     ret = read(mInputFile, buf, msgCount);
145     if (ret < 0) {
146         ALOGE("[halimp_aidl] read :%s failed, ret:%d\n", CEC_MSG_IN_FIFO, ret);
147         return -1;
148     }
149 
150     return ret;
151 }
152 
sendMessageToFifo(const CecMessage & message)153 int HdmiCecMock::sendMessageToFifo(const CecMessage& message) {
154     unsigned char msgBuf[CEC_MESSAGE_BODY_MAX_LENGTH + 1] = {0};
155     int ret = -1;
156 
157     msgBuf[0] = ((static_cast<uint8_t>(message.initiator) & 0xf) << 4) |
158                 (static_cast<uint8_t>(message.destination) & 0xf);
159 
160     size_t length = std::min(static_cast<size_t>(message.body.size()),
161                              static_cast<size_t>(CEC_MESSAGE_BODY_MAX_LENGTH));
162     for (size_t i = 0; i < length; ++i) {
163         msgBuf[i + 1] = static_cast<unsigned char>(message.body[i]);
164     }
165 
166     // Open the output pipe for writing outgoing cec message
167     mOutputFile = open(CEC_MSG_OUT_FIFO, O_WRONLY | O_CLOEXEC);
168     if (mOutputFile < 0) {
169         ALOGD("[halimp_aidl] file open failed for writing");
170         return -1;
171     }
172 
173     // Write message into the output pipe
174     ret = write(mOutputFile, msgBuf, length + 1);
175     close(mOutputFile);
176     if (ret < 0) {
177         ALOGE("[halimp_aidl] write :%s failed, ret:%d\n", CEC_MSG_OUT_FIFO, ret);
178         return -1;
179     }
180     return ret;
181 }
182 
printCecMsgBuf(const char * msg_buf,int len)183 void HdmiCecMock::printCecMsgBuf(const char* msg_buf, int len) {
184     int i, size = 0;
185     const int bufSize = CEC_MESSAGE_BODY_MAX_LENGTH * 3;
186     // Use 2 characters for each byte in the message plus 1 space
187     char buf[bufSize] = {0};
188 
189     // Messages longer than max length will be truncated.
190     for (i = 0; i < len && size < bufSize; i++) {
191         size += sprintf(buf + size, " %02x", msg_buf[i]);
192     }
193     ALOGD("[halimp_aidl] %s, msg:%.*s", __FUNCTION__, size, buf);
194 }
195 
handleCecMessage(unsigned char * msgBuf,int msgSize)196 void HdmiCecMock::handleCecMessage(unsigned char* msgBuf, int msgSize) {
197     CecMessage message;
198     size_t length = std::min(static_cast<size_t>(msgSize - 1),
199                              static_cast<size_t>(CEC_MESSAGE_BODY_MAX_LENGTH));
200     message.body.resize(length);
201 
202     for (size_t i = 0; i < length; ++i) {
203         message.body[i] = static_cast<uint8_t>(msgBuf[i + 1]);
204         ALOGD("[halimp_aidl] msg body %x", message.body[i]);
205     }
206 
207     message.initiator = static_cast<CecLogicalAddress>((msgBuf[0] >> 4) & 0xf);
208     ALOGD("[halimp_aidl] msg init %hhd", message.initiator);
209     message.destination = static_cast<CecLogicalAddress>((msgBuf[0] >> 0) & 0xf);
210     ALOGD("[halimp_aidl] msg dest %hhd", message.destination);
211 
212     if (mCallback != nullptr) {
213         mCallback->onCecMessage(message);
214     }
215 }
216 
threadLoop()217 void HdmiCecMock::threadLoop() {
218     ALOGD("[halimp_aidl] threadLoop start.");
219     unsigned char msgBuf[CEC_MESSAGE_BODY_MAX_LENGTH];
220     int r = -1;
221 
222     // Open the input pipe
223     while (mInputFile < 0) {
224         usleep(1000 * 1000);
225         mInputFile = open(CEC_MSG_IN_FIFO, O_RDONLY | O_CLOEXEC);
226     }
227     ALOGD("[halimp_aidl] file open ok, fd = %d.", mInputFile);
228 
229     while (mCecThreadRun) {
230         if (!mOptionSystemCecControl) {
231             usleep(1000 * 1000);
232             continue;
233         }
234 
235         memset(msgBuf, 0, sizeof(msgBuf));
236         // Try to get a message from dev.
237         // echo -n -e '\x04\x83' >> /dev/cec
238         r = readMessageFromFifo(msgBuf, CEC_MESSAGE_BODY_MAX_LENGTH);
239         if (r <= 1) {
240             // Ignore received ping messages
241             continue;
242         }
243 
244         printCecMsgBuf((const char*)msgBuf, r);
245 
246         if (((msgBuf[0] >> 4) & 0xf) == 0xf) {
247             // The message is a hotplug event, handled by HDMI HAL.
248             continue;
249         }
250 
251         handleCecMessage(msgBuf, r);
252     }
253 
254     ALOGD("[halimp_aidl] thread end.");
255 }
256 
HdmiCecMock()257 HdmiCecMock::HdmiCecMock() {
258     ALOGE("[halimp_aidl] Opening a virtual CEC HAL for testing and virtual machine.");
259     mCallback = nullptr;
260     mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(serviceDied));
261 }
262 
263 }  // namespace implementation
264 }  // namespace cec
265 }  // namespace hdmi
266 }  // namespace tv
267 }  // namespace hardware
268 }  // namespace android
269