blob: 18abcada12b24a863a1779499a24add5cea888c3 [file] [log] [blame]
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +05301/*
2 * Copyright (C) 2021 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.cec@1.0-impl"
18#include <android-base/logging.h>
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +053019#include <android-base/properties.h>
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +053020
Shraddha Basantwani33254102021-06-01 13:29:47 +053021#include <cutils/properties.h>
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +053022#include <errno.h>
23#include <fcntl.h>
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +053024#include <linux/ioctl.h>
Shraddha Basantwani0c8a0542021-06-04 15:10:06 +053025#include <poll.h>
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +053026#include <sys/eventfd.h>
Shraddha Basantwani0c8a0542021-06-04 15:10:06 +053027#include <algorithm>
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +053028
29#include "HdmiCecDefault.h"
30
31namespace android {
32namespace hardware {
33namespace tv {
34namespace cec {
35namespace V1_0 {
36namespace implementation {
37
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +053038using android::base::GetUintProperty;
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +053039
40HdmiCecDefault::HdmiCecDefault() {
41 mCecFd = -1;
42 mExitFd = -1;
Shraddha Basantwanid591d972021-06-15 19:40:16 +053043 mCecEnabled = false;
Shraddha Basantwani05c454f2021-06-18 12:42:27 +053044 mWakeupEnabled = false;
Shraddha Basantwani92fa8e92021-06-02 12:11:26 +053045 mCallback = nullptr;
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +053046}
47
48HdmiCecDefault::~HdmiCecDefault() {
49 release();
50}
51
52// Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow.
Shraddha Basantwanid50fd042021-06-02 09:35:53 +053053Return<Result> HdmiCecDefault::addLogicalAddress(CecLogicalAddress addr) {
54 if (addr < CecLogicalAddress::TV || addr >= CecLogicalAddress::BROADCAST) {
55 LOG(ERROR) << "Add logical address failed, Invalid address";
56 return Result::FAILURE_INVALID_ARGS;
57 }
58
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +053059 cec_log_addrs cecLogAddrs;
Shraddha Basantwanid50fd042021-06-02 09:35:53 +053060 int ret = ioctl(mCecFd, CEC_ADAP_G_LOG_ADDRS, &cecLogAddrs);
61 if (ret) {
62 LOG(ERROR) << "Add logical address failed, Error = " << strerror(errno);
63 return Result::FAILURE_BUSY;
64 }
65
66 cecLogAddrs.cec_version = getCecVersion();
67 cecLogAddrs.vendor_id = getVendorId();
68
69 unsigned int logAddrType = CEC_LOG_ADDR_TYPE_UNREGISTERED;
70 unsigned int allDevTypes = 0;
71 unsigned int primDevType = 0xff;
72 switch (addr) {
73 case CecLogicalAddress::TV:
74 primDevType = CEC_OP_PRIM_DEVTYPE_TV;
75 logAddrType = CEC_LOG_ADDR_TYPE_TV;
76 allDevTypes = CEC_OP_ALL_DEVTYPE_TV;
77 break;
78 case CecLogicalAddress::RECORDER_1:
79 case CecLogicalAddress::RECORDER_2:
80 case CecLogicalAddress::RECORDER_3:
81 primDevType = CEC_OP_PRIM_DEVTYPE_RECORD;
82 logAddrType = CEC_LOG_ADDR_TYPE_RECORD;
83 allDevTypes = CEC_OP_ALL_DEVTYPE_RECORD;
84 break;
85 case CecLogicalAddress::TUNER_1:
86 case CecLogicalAddress::TUNER_2:
87 case CecLogicalAddress::TUNER_3:
88 case CecLogicalAddress::TUNER_4:
89 primDevType = CEC_OP_PRIM_DEVTYPE_TUNER;
90 logAddrType = CEC_LOG_ADDR_TYPE_TUNER;
91 allDevTypes = CEC_OP_ALL_DEVTYPE_TUNER;
92 break;
93 case CecLogicalAddress::PLAYBACK_1:
94 case CecLogicalAddress::PLAYBACK_2:
95 case CecLogicalAddress::PLAYBACK_3:
96 primDevType = CEC_OP_PRIM_DEVTYPE_PLAYBACK;
97 logAddrType = CEC_LOG_ADDR_TYPE_PLAYBACK;
98 allDevTypes = CEC_OP_ALL_DEVTYPE_PLAYBACK;
99 cecLogAddrs.flags |= CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU;
100 break;
101 case CecLogicalAddress::AUDIO_SYSTEM:
102 primDevType = CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM;
103 logAddrType = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM;
104 allDevTypes = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM;
105 break;
106 case CecLogicalAddress::FREE_USE:
107 primDevType = CEC_OP_PRIM_DEVTYPE_PROCESSOR;
108 logAddrType = CEC_LOG_ADDR_TYPE_SPECIFIC;
109 allDevTypes = CEC_OP_ALL_DEVTYPE_SWITCH;
110 break;
111 case CecLogicalAddress::UNREGISTERED:
112 cecLogAddrs.flags |= CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK;
113 break;
114 }
115
116 int logAddrIndex = cecLogAddrs.num_log_addrs;
117
118 cecLogAddrs.num_log_addrs += 1;
119 cecLogAddrs.log_addr[logAddrIndex] = static_cast<cec_logical_address_t>(addr);
120 cecLogAddrs.log_addr_type[logAddrIndex] = logAddrType;
121 cecLogAddrs.primary_device_type[logAddrIndex] = primDevType;
122 cecLogAddrs.all_device_types[logAddrIndex] = allDevTypes;
123 cecLogAddrs.features[logAddrIndex][0] = 0;
124 cecLogAddrs.features[logAddrIndex][1] = 0;
125
126 ret = ioctl(mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
127 if (ret) {
128 LOG(ERROR) << "Add logical address failed, Error = " << strerror(errno);
129 return Result::FAILURE_BUSY;
130 }
131 return Result::SUCCESS;
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530132}
133
134Return<void> HdmiCecDefault::clearLogicalAddress() {
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +0530135 cec_log_addrs cecLogAddrs;
Shraddha Basantwani0dacc5c2021-06-01 10:32:22 +0530136 memset(&cecLogAddrs, 0, sizeof(cecLogAddrs));
137 int ret = ioctl(mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
138 if (ret) {
139 LOG(ERROR) << "Clear logical Address failed, Error = " << strerror(errno);
140 }
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530141 return Void();
142}
143
Shraddha Basantwani9b1e5292021-05-31 16:38:38 +0530144Return<void> HdmiCecDefault::getPhysicalAddress(getPhysicalAddress_cb callback) {
145 uint16_t addr;
146 int ret = ioctl(mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
147 if (ret) {
148 LOG(ERROR) << "Get physical address failed, Error = " << strerror(errno);
149 callback(Result::FAILURE_INVALID_STATE, addr);
150 return Void();
151 }
152 callback(Result::SUCCESS, addr);
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530153 return Void();
154}
155
Shraddha Basantwani971853b2021-05-31 16:28:23 +0530156Return<SendMessageResult> HdmiCecDefault::sendMessage(const CecMessage& message) {
Shraddha Basantwanid591d972021-06-15 19:40:16 +0530157 if (!mCecEnabled) {
158 return SendMessageResult::FAIL;
159 }
160
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +0530161 cec_msg cecMsg;
Shraddha Basantwani971853b2021-05-31 16:28:23 +0530162 memset(&cecMsg, 0, sizeof(cec_msg));
163
164 int initiator = static_cast<cec_logical_address_t>(message.initiator);
165 int destination = static_cast<cec_logical_address_t>(message.destination);
166
167 cecMsg.msg[0] = (initiator << 4) | destination;
168 for (size_t i = 0; i < message.body.size(); ++i) {
169 cecMsg.msg[i + 1] = message.body[i];
170 }
171 cecMsg.len = message.body.size() + 1;
172
173 int ret = ioctl(mCecFd, CEC_TRANSMIT, &cecMsg);
174
175 if (ret) {
176 LOG(ERROR) << "Send message failed, Error = " << strerror(errno);
177 return SendMessageResult::FAIL;
178 }
179
180 if (cecMsg.tx_status != CEC_TX_STATUS_OK) {
181 LOG(ERROR) << "Send message tx_status = " << cecMsg.tx_status;
182 }
183
184 switch (cecMsg.tx_status) {
185 case CEC_TX_STATUS_OK:
186 return SendMessageResult::SUCCESS;
187 case CEC_TX_STATUS_ARB_LOST:
188 return SendMessageResult::BUSY;
189 case CEC_TX_STATUS_NACK:
190 return SendMessageResult::NACK;
191 default:
192 return SendMessageResult::FAIL;
193 }
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530194}
195
Shraddha Basantwani92fa8e92021-06-02 12:11:26 +0530196Return<void> HdmiCecDefault::setCallback(const sp<IHdmiCecCallback>& callback) {
197 if (mCallback != nullptr) {
198 mCallback->unlinkToDeath(this);
199 mCallback = nullptr;
200 }
201
202 if (callback != nullptr) {
203 mCallback = callback;
204 mCallback->linkToDeath(this, 0 /*cookie*/);
205 }
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530206 return Void();
207}
208
209Return<int32_t> HdmiCecDefault::getCecVersion() {
Shraddha Basantwani105b1c32021-06-01 18:45:21 +0530210 return property_get_int32("ro.hdmi.cec_version", CEC_OP_CEC_VERSION_1_4);
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530211}
212
213Return<uint32_t> HdmiCecDefault::getVendorId() {
Shraddha Basantwani697e2802021-06-01 16:20:10 +0530214 return property_get_int32("ro.hdmi.vendor_id", 0x000c03 /* HDMI LLC vendor ID */);
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530215}
216
Shraddha Basantwani33254102021-06-01 13:29:47 +0530217Return<void> HdmiCecDefault::getPortInfo(getPortInfo_cb callback) {
218 uint16_t addr;
219 int ret = ioctl(mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
220 if (ret) {
221 LOG(ERROR) << "Get port info failed, Error = " << strerror(errno);
222 }
223
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +0530224 uint32_t type = GetUintProperty<uint32_t>("ro.hdmi.device_type", CEC_DEVICE_PLAYBACK);
Shraddha Basantwani33254102021-06-01 13:29:47 +0530225 hidl_vec<HdmiPortInfo> portInfos(1);
226 portInfos[0] = {.type = (type == CEC_DEVICE_TV ? HdmiPortType::INPUT : HdmiPortType::OUTPUT),
227 .portId = 1,
228 .cecSupported = true,
229 .arcSupported = false,
230 .physicalAddress = addr};
231 callback(portInfos);
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530232 return Void();
233}
234
Shraddha Basantwanid591d972021-06-15 19:40:16 +0530235Return<void> HdmiCecDefault::setOption(OptionKey key, bool value) {
236 switch (key) {
237 case OptionKey::ENABLE_CEC:
238 LOG(DEBUG) << "setOption: Enable CEC: " << value;
239 mCecEnabled = value;
240 break;
Shraddha Basantwani05c454f2021-06-18 12:42:27 +0530241 case OptionKey::WAKEUP:
242 LOG(DEBUG) << "setOption: WAKEUP: " << value;
243 mWakeupEnabled = value;
244 break;
Shraddha Basantwanid591d972021-06-15 19:40:16 +0530245 default:
246 break;
247 }
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530248 return Void();
249}
250
251Return<void> HdmiCecDefault::setLanguage(const hidl_string& /*language*/) {
252 return Void();
253}
254
255Return<void> HdmiCecDefault::enableAudioReturnChannel(int32_t /*portId*/, bool /*enable*/) {
256 return Void();
257}
258
259Return<bool> HdmiCecDefault::isConnected(int32_t /*portId*/) {
Shraddha Basantwani21207902021-06-01 16:35:07 +0530260 uint16_t addr;
261 int ret = ioctl(mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
262 if (ret) {
263 LOG(ERROR) << "Is connected failed, Error = " << strerror(errno);
264 return false;
265 }
266 if (addr == CEC_PHYS_ADDR_INVALID) {
267 return false;
268 }
269 return true;
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530270}
271
272// Initialise the cec file descriptor
273Return<Result> HdmiCecDefault::init() {
274 const char* path = "/dev/cec0";
275 mCecFd = open(path, O_RDWR);
276 if (mCecFd < 0) {
277 LOG(ERROR) << "Failed to open " << path << ", Error = " << strerror(errno);
278 return Result::FAILURE_NOT_SUPPORTED;
279 }
280 mExitFd = eventfd(0, EFD_NONBLOCK);
281 if (mExitFd < 0) {
282 LOG(ERROR) << "Failed to open eventfd, Error = " << strerror(errno);
283 release();
284 return Result::FAILURE_NOT_SUPPORTED;
285 }
286
287 // Ensure the CEC device supports required capabilities
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +0530288 cec_caps caps = {};
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530289 int ret = ioctl(mCecFd, CEC_ADAP_G_CAPS, &caps);
290 if (ret) {
291 LOG(ERROR) << "Unable to query cec adapter capabilities, Error = " << strerror(errno);
292 release();
293 return Result::FAILURE_NOT_SUPPORTED;
294 }
295
296 if (!(caps.capabilities & (CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | CEC_CAP_PASSTHROUGH))) {
297 LOG(ERROR) << "Wrong cec adapter capabilities " << caps.capabilities;
298 release();
299 return Result::FAILURE_NOT_SUPPORTED;
300 }
301
Shraddha Basantwani0c8a0542021-06-04 15:10:06 +0530302 uint32_t mode = CEC_MODE_INITIATOR | CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530303 ret = ioctl(mCecFd, CEC_S_MODE, &mode);
304 if (ret) {
305 LOG(ERROR) << "Unable to set initiator mode, Error = " << strerror(errno);
306 release();
307 return Result::FAILURE_NOT_SUPPORTED;
308 }
309
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +0530310 mEventThread = thread(&HdmiCecDefault::event_thread, this);
Shraddha Basantwani0c8a0542021-06-04 15:10:06 +0530311
Shraddha Basantwanid591d972021-06-15 19:40:16 +0530312 mCecEnabled = true;
Shraddha Basantwani05c454f2021-06-18 12:42:27 +0530313 mWakeupEnabled = true;
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530314 return Result::SUCCESS;
315}
316
317Return<void> HdmiCecDefault::release() {
318 if (mExitFd > 0) {
319 uint64_t tmp = 1;
320 write(mExitFd, &tmp, sizeof(tmp));
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +0530321 if (mEventThread.joinable()) {
322 mEventThread.join();
323 }
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530324 }
325 if (mExitFd > 0) {
326 close(mExitFd);
327 }
328 if (mCecFd > 0) {
329 close(mCecFd);
330 }
Shraddha Basantwanid591d972021-06-15 19:40:16 +0530331 mCecEnabled = false;
Shraddha Basantwani05c454f2021-06-18 12:42:27 +0530332 mWakeupEnabled = false;
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530333 setCallback(nullptr);
334 return Void();
335}
Shraddha Basantwani0c8a0542021-06-04 15:10:06 +0530336
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +0530337void HdmiCecDefault::event_thread() {
338 pollfd ufds[3] = {
Shraddha Basantwani0c8a0542021-06-04 15:10:06 +0530339 {mCecFd, POLLIN, 0},
340 {mCecFd, POLLERR, 0},
341 {mExitFd, POLLIN, 0},
342 };
343
344 while (1) {
345 ufds[0].revents = 0;
346 ufds[1].revents = 0;
347 ufds[2].revents = 0;
348
349 int ret = poll(ufds, /* size(ufds) = */ 3, /* timeout = */ -1);
350
351 if (ret <= 0) {
352 continue;
353 }
354
355 if (ufds[2].revents == POLLIN) { /* Exit */
356 break;
357 }
358
359 if (ufds[1].revents == POLLERR) { /* CEC Event */
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +0530360 cec_event ev;
Shraddha Basantwani0c8a0542021-06-04 15:10:06 +0530361 ret = ioctl(mCecFd, CEC_DQEVENT, &ev);
362
363 if (ret) {
364 LOG(ERROR) << "CEC_DQEVENT failed, Error = " << strerror(errno);
365 continue;
366 }
367
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +0530368 if (!mCecEnabled) {
369 continue;
370 }
371
Shraddha Basantwani0c8a0542021-06-04 15:10:06 +0530372 if (ev.event == CEC_EVENT_STATE_CHANGE) {
373 if (mCallback != nullptr) {
374 HotplugEvent hotplugEvent{
375 .connected = (ev.state_change.phys_addr != CEC_PHYS_ADDR_INVALID),
376 .portId = 1};
377 mCallback->onHotplugEvent(hotplugEvent);
378 } else {
379 LOG(ERROR) << "No event callback for hotplug";
380 }
381 }
382 }
383
384 if (ufds[0].revents == POLLIN) { /* CEC Driver */
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +0530385 cec_msg msg = {};
Shraddha Basantwani0c8a0542021-06-04 15:10:06 +0530386 ret = ioctl(mCecFd, CEC_RECEIVE, &msg);
387
388 if (ret) {
389 LOG(ERROR) << "CEC_RECEIVE failed, Error = " << strerror(errno);
390 continue;
391 }
392
393 if (msg.rx_status != CEC_RX_STATUS_OK) {
394 LOG(ERROR) << "msg rx_status = " << msg.rx_status;
395 continue;
396 }
397
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +0530398 if (!mCecEnabled) {
399 continue;
400 }
401
Shraddha Basantwani05c454f2021-06-18 12:42:27 +0530402 if (!mWakeupEnabled && isWakeupMessage(msg)) {
403 LOG(DEBUG) << "Filter wakeup message";
404 continue;
405 }
406
Shraddha Basantwani0c8a0542021-06-04 15:10:06 +0530407 if (mCallback != nullptr) {
408 size_t length = std::min(msg.len - 1, (uint32_t)MaxLength::MESSAGE_BODY);
409 CecMessage cecMessage{
410 .initiator = static_cast<CecLogicalAddress>(msg.msg[0] >> 4),
411 .destination = static_cast<CecLogicalAddress>(msg.msg[0] & 0xf),
412 };
413 cecMessage.body.resize(length);
414 for (size_t i = 0; i < length; ++i) {
415 cecMessage.body[i] = static_cast<uint8_t>(msg.msg[i + 1]);
416 }
417 mCallback->onCecMessage(cecMessage);
418 } else {
419 LOG(ERROR) << "no event callback for message";
420 }
421 }
422 }
Shraddha Basantwani0c8a0542021-06-04 15:10:06 +0530423}
424
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +0530425int HdmiCecDefault::getOpcode(cec_msg message) {
426 return static_cast<uint8_t>(message.msg[1]);
Shraddha Basantwani05c454f2021-06-18 12:42:27 +0530427}
428
Shraddha Basantwanidedd40e2021-09-16 15:56:20 +0530429bool HdmiCecDefault::isWakeupMessage(cec_msg message) {
Shraddha Basantwani05c454f2021-06-18 12:42:27 +0530430 int opcode = getOpcode(message);
431 switch (opcode) {
432 case CEC_MESSAGE_TEXT_VIEW_ON:
433 case CEC_MESSAGE_IMAGE_VIEW_ON:
434 return true;
435 default:
436 return false;
437 }
438}
439
Shraddha Basantwanif3a43c82021-05-27 19:40:17 +0530440} // namespace implementation
441} // namespace V1_0
442} // namespace cec
443} // namespace tv
444} // namespace hardware
445} // namespace android