Files
2026-08-24 10:03:24 +08:00

216 lines
5.5 KiB
C

/*
* Copyright (C) 2024 Amlogic Corporation.
*
* 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.
*/
#undef LOG_TAG
#define LOG_TAG "audio_hw_uevent"
//#define LOG_NDEBUG 0
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <inttypes.h>
#include "aml_audio_log.h"
#include <aml_dump_debug.h>
#include <sys/epoll.h>
#include <cutils/uevent.h>
#include "aml_audio_uevent.h"
#include <pthread.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include "audio_hw_utils.h"
#define EPOLL_MAX_EVENTS 16
#define INPUT_MAX_EVENTS 128
#define NO_TIMEOUT -1
#define UEVENT_MSG_LEN 2048
typedef struct audio_uevent_handle {
int epoll_fd;
int uevent_fd;
pthread_t thread_id;
bool bexit;
struct epoll_event pending_event_items[EPOLL_MAX_EVENTS];
uevent_callback_t event_callback;
} audio_uevent_handle_t;
struct aml_uevent_item {
int type;
char event_name[128];
};
static struct aml_uevent_item uevent_list[] = {
{ UEVENT_TYPE_VMODE_CHANGE, UEVENT_HDMITX_VMODE_CHANGE },
};
static audio_uevent_handle_t audio_uevent_listener = { 0 };
static int open_uevent_socket(void)
{
struct sockaddr_nl addr;
int sz = 64*1024;
int s;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
addr.nl_groups = 0xffffffff;
s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if (s < 0) {
AM_LOGE("socket fail, error info:%s", strerror(errno));
return -1;
}
if ((setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)))<0) {
AM_LOGE("setsockopt fail, error info:%s", strerror(errno));
close(s);
return -1;
}
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
AM_LOGE("bind fail, error info:%s", strerror(errno));
close(s);
return -1;
}
return s;
}
static void process_uevent(audio_uevent_handle_t * p_handle) {
char msg[UEVENT_MSG_LEN + 2];
char* cp;
int n;
int i = 0;
n = recv(p_handle->uevent_fd, msg, UEVENT_MSG_LEN, 0);
if (n <= 0) return;
if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
return;
//ALOGI("%s msg =%s", __func__, msg);
msg[n] = '\0';
msg[n + 1] = '\0';
for (i = 0; i < sizeof(uevent_list) / sizeof(struct aml_uevent_item); i++) {
cp = msg;
while (*cp) {
//ALOGI("%s uevent =%s", __func__, cp);
if (strstr(cp, uevent_list[i].event_name)) {
if (p_handle->event_callback) {
p_handle->event_callback(uevent_list[i].type);
}
}
/* advance to after the next \0 */
while (*cp++);
}
}
return;
}
static void *audio_uevent_thread(void *pArg)
{
audio_uevent_handle_t * p_handle = (audio_uevent_handle_t *)pArg;
ALOGI("enter %s", __FUNCTION__);
while (!p_handle->bexit) {
int eventNum = epoll_wait(p_handle->epoll_fd, p_handle->pending_event_items, EPOLL_MAX_EVENTS, NO_TIMEOUT);
if (eventNum <= 0) {
ALOGE("epoll_wait fails.");
continue;
}
for (int i = 0; i < eventNum; i++) {
if (p_handle->pending_event_items[i].events & EPOLLIN) {
process_uevent(p_handle);
}
}
}
ALOGI("exit %s", __FUNCTION__);
return ((void *)0);
}
void aml_audio_uevent_open(uevent_callback_t callback) {
int epoll_fd = 0;
int uevent_fd = 0;
memset(&audio_uevent_listener, 0, sizeof(struct audio_uevent_handle));
//uevent
uevent_fd = open_uevent_socket();
if (uevent_fd < 0) {
AM_LOGE("open_uevent_socket failed.");
return;
}
if (fcntl(uevent_fd, F_SETFL, O_NONBLOCK) == -1) {
AM_LOGE("fcntl mUeventFd failed.");
close(uevent_fd);
return;
}
//epoll
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd < 0) {
AM_LOGE("epoll_create failed.");
close(uevent_fd);
return;
}
struct epoll_event ev;
memset(&ev, 0, sizeof(ev));
ev.events = EPOLLIN;
ev.data.fd = uevent_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) {
AM_LOGE("epoll_ctl failed.");
close(uevent_fd);
return;
}
audio_uevent_listener.epoll_fd = epoll_fd;
audio_uevent_listener.uevent_fd = uevent_fd;
audio_uevent_listener.event_callback = callback;
if (pthread_create(&audio_uevent_listener.thread_id, NULL, &audio_uevent_thread, (void *)&audio_uevent_listener)) {
AM_LOGE("create audio_uevent_thread failed");
return;
}
AM_LOGI("create audio_uevent_thread success");
}
void aml_audio_uevent_close() {
audio_uevent_listener.bexit = true;
if (audio_uevent_listener.thread_id != 0) {
pthread_join(audio_uevent_listener.thread_id, NULL);
}
if (audio_uevent_listener.epoll_fd) {
close(audio_uevent_listener.epoll_fd);
}
if (audio_uevent_listener.uevent_fd) {
close(audio_uevent_listener.uevent_fd);
}
return;
}