1
0
Fork 0
android_device_rockchip_tart/hdmi/connection/HdmiConnection.cpp
Brendan Szymanski 8b7cdd6f38 Enable HDMI CEC and add HDMI hotplug monitoring
Install the HDMI CEC and HDMI connection APEX packages plus the
android.hardware.hdmi.cec.xml feature file, set the playback-device
CEC properties, and give system ownership of /dev/cec0 in ueventd.

The connection HAL now reports a single HDMI output port and monitors
/sys/class/drm/card0-HDMI-A-1/status with poll(), reporting hotplug
transitions through the AIDL callback and shutting the monitor thread
down through an eventfd.

The CEC HAL now polls POLLPRI for CEC_DQEVENT and POLLIN for
CEC_RECEIVE, guards the ioctl paths against an uninitialised adapter,
and bounds message length by CEC_MAX_MSG_SIZE.
2026-08-09 22:18:52 -04:00

243 lines
6.6 KiB
C++

/*
* Copyright (C) 2022 The Android Open Source Project
* Copyright (C) 2025 KonstaKANG
*
* 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.hdmi.connection-service.opi"
#include "HdmiConnection.h"
#include <android-base/logging.h>
#include <cerrno>
#include <cstring>
#include <fcntl.h>
#include <poll.h>
#include <sys/eventfd.h>
#include <unistd.h>
using ndk::ScopedAStatus;
namespace android {
namespace hardware {
namespace tv {
namespace hdmi {
namespace connection {
namespace implementation {
static constexpr char kHdmiStatusPath[] = "/sys/class/drm/card0-HDMI-A-1/status";
static bool readStatus(int fd, bool* connected) {
char status[32] = {};
if (lseek(fd, 0, SEEK_SET) < 0) {
return false;
}
ssize_t length = read(fd, status, sizeof(status) - 1);
if (length < 0) {
return false;
}
*connected = length >= 9 && !strncmp(status, "connected", 9);
return true;
}
HdmiConnection::HdmiConnection() {
mPortInfos.push_back({.type = HdmiPortType::OUTPUT,
.portId = 1,
.cecSupported = true,
.arcSupported = false,
.eArcSupported = false,
.physicalAddress = 0xFFFF});
mHpdSignal.push_back(HpdSignal::HDMI_HPD_PHYSICAL);
mStopFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (mStopFd < 0) {
PLOG(ERROR) << "Failed to create HDMI monitor eventfd";
return;
}
mMonitorThread = std::thread(&HdmiConnection::monitorLoop, this);
}
HdmiConnection::~HdmiConnection() {
signalMonitorStop();
if (mMonitorThread.joinable()) {
mMonitorThread.join();
}
if (mStopFd >= 0) {
close(mStopFd);
mStopFd = -1;
}
}
ScopedAStatus HdmiConnection::getPortInfo(std::vector<HdmiPortInfo>* _aidl_return) {
*_aidl_return = mPortInfos;
return ScopedAStatus::ok();
}
ScopedAStatus HdmiConnection::isConnected(int32_t portId, bool* _aidl_return) {
if (portId != 1) {
*_aidl_return = false;
return ScopedAStatus::ok();
}
int fd = open(kHdmiStatusPath, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
*_aidl_return = false;
return ScopedAStatus::ok();
}
bool connected = false;
if (!readStatus(fd, &connected)) {
connected = false;
}
close(fd);
LOG(INFO) << "portId: " << portId << ", connected: " << connected;
*_aidl_return = connected;
return ScopedAStatus::ok();
}
ScopedAStatus HdmiConnection::setCallback(
const std::shared_ptr<IHdmiConnectionCallback>& callback) {
std::lock_guard<std::mutex> lock(mCallbackMutex);
mCallback = callback;
return ScopedAStatus::ok();
}
ScopedAStatus HdmiConnection::setHpdSignal(HpdSignal signal, int32_t portId) {
if (portId != 1) {
return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
mHpdSignal[0] = signal;
return ScopedAStatus::ok();
}
ScopedAStatus HdmiConnection::getHpdSignal(int32_t portId, HpdSignal* _aidl_return) {
if (portId != 1) {
return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
*_aidl_return = mHpdSignal[0];
return ScopedAStatus::ok();
}
void HdmiConnection::signalMonitorStop() {
if (mStopFd < 0) {
return;
}
uint64_t value = 1;
if (write(mStopFd, &value, sizeof(value)) < 0 && errno != EAGAIN) {
PLOG(ERROR) << "Failed to stop HDMI monitor";
}
}
void HdmiConnection::monitorLoop() {
int statusFd = -1;
bool haveStatus = false;
bool lastConnected = false;
bool openErrorReported = false;
while (true) {
if (statusFd < 0) {
pollfd stopPoll = {.fd = mStopFd, .events = POLLIN, .revents = 0};
int ret = poll(&stopPoll, 1, 1000);
if (ret < 0 && errno == EINTR) {
continue;
}
if (ret > 0 && (stopPoll.revents & POLLIN)) {
break;
}
if (ret == 0) {
statusFd = open(kHdmiStatusPath, O_RDONLY | O_CLOEXEC);
if (statusFd >= 0) {
haveStatus = false;
openErrorReported = false;
} else if (!openErrorReported) {
PLOG(ERROR) << "Failed to open HDMI status file " << kHdmiStatusPath;
openErrorReported = true;
}
}
continue;
}
if (!haveStatus && !readStatus(statusFd, &lastConnected)) {
close(statusFd);
statusFd = -1;
continue;
}
haveStatus = true;
pollfd fds[2] = {
{.fd = statusFd, .events = POLLPRI | POLLERR, .revents = 0},
{.fd = mStopFd, .events = POLLIN, .revents = 0},
};
int ret = poll(fds, 2, 1000);
if (ret < 0) {
if (errno == EINTR) {
continue;
}
close(statusFd);
statusFd = -1;
haveStatus = false;
continue;
}
if (fds[1].revents & POLLIN) {
break;
}
if (ret == 0 || !(fds[0].revents & (POLLPRI | POLLERR))) {
continue;
}
bool connected = false;
if (!readStatus(statusFd, &connected)) {
close(statusFd);
statusFd = -1;
haveStatus = false;
continue;
}
if (connected == lastConnected) {
continue;
}
lastConnected = connected;
std::shared_ptr<IHdmiConnectionCallback> callback;
{
std::lock_guard<std::mutex> lock(mCallbackMutex);
callback = mCallback;
}
if (callback != nullptr) {
ScopedAStatus status = callback->onHotplugEvent(connected, 1);
if (!status.isOk()) {
LOG(ERROR) << "HDMI hotplug callback failed";
}
}
}
if (statusFd >= 0) {
close(statusFd);
}
}
} // namespace implementation
} // namespace connection
} // namespace hdmi
} // namespace tv
} // namespace hardware
} // namespace android