blob: f7f956c2ffec6c46dc48f8d1b87682111d06e9c9 [file] [log] [blame]
/*
* Copyright (c) 2021 Amlogic, Inc. All rights reserved.
*
* This source code is subject to the terms and conditions defined in the
* file 'LICENSE' which is part of this source code package.
*
* Description:
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <libudev.h>
#include <linux/string.h>
#include "meson_drm_event.h"
#include "meson_drm_settings.h"
#include "meson_drm_log.h"
#ifndef XDG_RUNTIME_DIR
#define XDG_RUNTIME_DIR "/run"
#endif
#define LIBUDEV_EVT_TYPE_KERNEL "kernel"
#define LIBUDEV_SUBSYSTEM_DRM "drm"
#define LIBUDEV_SUBSYSTEM_HDMITX "amhdmitx"
static bool isMonitoringAlive = false;
displayEventCallback _DisplayEventCb = NULL;
static void* uevent_monitor_thread(void *arg);
static void* hdmitx_uevent_monitor_thread(void *arg);
void startDisplayUeventMonitor()
{
int err = -1;
isMonitoringAlive = true;
pthread_t event_monitor_threadId;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
DEBUG("[%s:%d]", __FUNCTION__, __LINE__);
err = pthread_create (&event_monitor_threadId, &attr,uevent_monitor_thread,NULL);
if (err) {
ERROR("%s %d Failed to Ceate HDMI Hot Plug Thread ....\n",__FUNCTION__, __LINE__);
event_monitor_threadId = -1;
}
pthread_t hdmitx_event_monitor_threadId;
pthread_attr_t hdmitx_thread_attr;
pthread_attr_init(&hdmitx_thread_attr);
pthread_attr_setdetachstate(&hdmitx_thread_attr,PTHREAD_CREATE_DETACHED);
err = pthread_create (&hdmitx_event_monitor_threadId, &hdmitx_thread_attr,hdmitx_uevent_monitor_thread,NULL);
if (err) {
ERROR("%s %d Failed to Create hdmi tx monitor Thread ....\n",__FUNCTION__, __LINE__);
hdmitx_event_monitor_threadId = -1;
}
}
void stopDisplayUeventMonitor()
{
isMonitoringAlive = false;
}
bool RegisterDisplayEventCallback(displayEventCallback cb)
{
bool ret = true;
if (cb == NULL) {
ERROR("ERROR[%s:%d] argument NULL",__FUNCTION__, __LINE__);
ret = false;
} else {
_DisplayEventCb = cb;
}
return ret;
}
bool get_hdcp_status()
{
bool ret = false;
int fd = meson_open_drm();
if (MESON_AUTH_STATUS_SUCCESS == meson_drm_getHdcpAuthStatus(fd, MESON_CONNECTOR_HDMIA)) {
ret = true;
}
meson_close_drm(fd);
return ret;
}
static void* uevent_monitor_thread(void *arg)
{
DEBUG("[%s:%d]start", __FUNCTION__, __LINE__);
bool wasConnected = false;
struct udev *udev = NULL;
struct udev_device *dev = NULL;
struct udev_monitor *mon = NULL;
ENUM_MESON_CONN_CONNECTION enConnection = MESON_UNKNOWNCONNECTION;
ENUM_MESON_CONN_CONNECTION enPreConnection = MESON_UNKNOWNCONNECTION;
ENUM_DISPLAY_EVENT enDisplayEvent = DISPLAY_EVENT_MAX;
int drm_fd = meson_open_drm();
/* create udev object */
udev = udev_new();
if (!udev) {
ERROR("ERROR[%s:%d] Can't create udev monitor for DRM", __FUNCTION__, __LINE__);
} else {
mon = udev_monitor_new_from_netlink(udev, LIBUDEV_EVT_TYPE_KERNEL);
if (mon) {
int fd = -1;
fd_set fds;
struct timeval tv;
int ret;
bool isHPD;
bool isConnectorEvent;
if ((fd = udev_monitor_get_fd(mon)) < 0) {
ERROR("ERROR[%s:%d] udev_monitor_get_fd failed", __FUNCTION__, __LINE__);
} else {
if (udev_monitor_filter_add_match_subsystem_devtype(mon, LIBUDEV_SUBSYSTEM_DRM, NULL) < 0) {
ERROR("ERROR[%s:%d] udev_monitor_filter_add_match_subsystem_devtype failed", __FUNCTION__, __LINE__);
} else {
if (udev_monitor_enable_receiving(mon) < 0) {
ERROR("ERROR[%s:%d] udev_monitor_enable_receiving", __FUNCTION__, __LINE__);
} else {
while (isMonitoringAlive) {
isHPD = false;
isConnectorEvent = false;
FD_ZERO(&fds);
FD_SET(fd, &fds);
tv.tv_sec = 5; /* FIXME: fine tune the select timeout. */
tv.tv_usec = 0;
ret = select(fd+1, &fds, NULL, NULL, &tv);
if (ret > 0 && FD_ISSET(fd, &fds)) {
dev = udev_monitor_receive_device(mon);
if (dev) {
if (!strcmp(udev_device_get_action(dev), "change")) {
DEBUG("%s %d I: ACTION=%s",__FUNCTION__, __LINE__, udev_device_get_action(dev));
DEBUG("%s %d I: DEVNAME=%s",__FUNCTION__, __LINE__, udev_device_get_sysname(dev));
DEBUG("%s %d I: DEVPATH=%s",__FUNCTION__, __LINE__, udev_device_get_devpath(dev));
struct udev_list_entry *list_entry;
udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev)) {
INFO("%s=%s\n",
udev_list_entry_get_name(list_entry),
udev_list_entry_get_value(list_entry));
if (!strcmp(udev_list_entry_get_name(list_entry), "HOTPLUG"))
isHPD = true;
if (!strcmp(udev_list_entry_get_name(list_entry), "CONNECTOR"))
isConnectorEvent = true;
}
if (isHPD && !isConnectorEvent) {
enConnection = meson_drm_getConnectionStatus(drm_fd, MESON_CONNECTOR_HDMIA);
if ( enPreConnection != enConnection) {
enPreConnection = enConnection;
INFO("%s %d Send %s HDMI Hot Plug Event !!!",__FUNCTION__, __LINE__,
(enConnection ? "Connect":"DisConnect"));
enDisplayEvent = enConnection ? DISPLAY_EVENT_CONNECTED:DISPLAY_EVENT_DISCONNECTED;
if (_DisplayEventCb) {
_DisplayEventCb( enDisplayEvent, NULL );
}
}
}
}
/* free dev */
udev_device_unref(dev);
}
else {
INFO("I:[%s:%d] no udev_monitor_receive_device ", __FUNCTION__, __LINE__);
enConnection = MESON_UNKNOWNCONNECTION;
enPreConnection = MESON_UNKNOWNCONNECTION;
}
} else {
/* TODO: Select timeout or error; handle accordingly. */
}
}
}
}
close(fd);
}
udev_monitor_unref(mon);
} else {
ERROR("ERROR[%s:%d] udev_monitor_new_from_netlink failed", __FUNCTION__, __LINE__);
}
udev_unref(udev);
}
udev = NULL;
dev = NULL;
mon = NULL;
meson_close_drm(drm_fd);
pthread_exit(NULL);
return NULL;
}
static void* hdmitx_uevent_monitor_thread(void *arg)
{
DEBUG("[%s:%d]start", __FUNCTION__, __LINE__);
struct udev *udev = NULL;
struct udev_device *dev = NULL;
struct udev_monitor *mon = NULL;
ENUM_DISPLAY_EVENT enDisplayEvent = DISPLAY_EVENT_MAX;
/* create udev object */
udev = udev_new();
if (!udev) {
ERROR("ERROR[%s:%d] Can't create udev monitor for DRM", __FUNCTION__, __LINE__);
} else {
mon = udev_monitor_new_from_netlink(udev, LIBUDEV_EVT_TYPE_KERNEL);
if (mon) {
int fd = -1;
fd_set fds;
struct timeval tv;
int ret;
int curHdcp = 0;
int preHdcp = 0;
if ((fd = udev_monitor_get_fd(mon)) < 0) {
ERROR("ERROR[%s:%d] udev_monitor_get_fd failed", __FUNCTION__, __LINE__);
} else {
if (udev_monitor_filter_add_match_subsystem_devtype(mon, LIBUDEV_SUBSYSTEM_HDMITX, NULL) < 0) {
ERROR("ERROR[%s:%d] udev_monitor_filter_add_match_subsystem_devtype failed", __FUNCTION__, __LINE__);
} else {
if (udev_monitor_enable_receiving(mon) < 0) {
ERROR("ERROR[%s:%d] udev_monitor_enable_receiving", __FUNCTION__, __LINE__);
} else {
while (isMonitoringAlive) {
FD_ZERO(&fds);
FD_SET(fd, &fds);
tv.tv_sec = 5;
tv.tv_usec = 0;
ret = select(fd+1, &fds, NULL, NULL, &tv);
if (ret > 0 && FD_ISSET(fd, &fds)) {
dev = udev_monitor_receive_device(mon);
if (dev) {
if (!strcmp(udev_device_get_action(dev), "change")) {
DEBUG("%s %d I: ACTION=%s",__FUNCTION__, __LINE__, udev_device_get_action(dev));
DEBUG("%s %d I: DEVNAME=%s",__FUNCTION__, __LINE__, udev_device_get_sysname(dev));
DEBUG("%s %d I: DEVPATH=%s",__FUNCTION__, __LINE__, udev_device_get_devpath(dev));
struct udev_list_entry *list_entry;
udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev)) {
INFO("%s=%s\n",
udev_list_entry_get_name(list_entry),
udev_list_entry_get_value(list_entry));
if (!strcmp(udev_list_entry_get_name(list_entry), "hdmitx_hdcp"))
curHdcp = atoi(udev_list_entry_get_value(list_entry));
}
if (curHdcp != preHdcp) {
preHdcp = curHdcp;
INFO("%s %d Send %s hdcp event !!!",__FUNCTION__, __LINE__,
(curHdcp ? "AUTHENTICATED":"AUTHENTICATIONFAILURE"));
enDisplayEvent = curHdcp ? DISPLAY_HDCP_AUTHENTICATED:DISPLAY_HDCP_AUTHENTICATIONFAILURE;
if (_DisplayEventCb) {
_DisplayEventCb( enDisplayEvent, NULL);
}
}
}
/* free dev */
udev_device_unref(dev);
}
}
}
}
}
close(fd);
}
udev_monitor_unref(mon);
} else {
ERROR("ERROR[%s:%d] udev_monitor_new_from_netlink failed", __FUNCTION__, __LINE__);
}
udev_unref(udev);
}
udev = NULL;
dev = NULL;
mon = NULL;
pthread_exit(NULL);
return NULL;
}