/* * 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 #include #include #include #include #include #include "aml_audio_log.h" #include #include #include #include "aml_audio_uevent.h" #include #include #include #include #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; }