1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "hdf_audio_pnp_uevent_hdmi.h"
17 #include <asm/types.h>
18 #include <pthread.h>
19 #include <string.h>
20 #include <sys/epoll.h>
21 #include <sys/socket.h>
22 #include <sys/un.h>
23 #include <linux/if_packet.h>
24 #include <linux/netlink.h>
25 #include <netinet/in.h>
26 #include <net/if.h>
27 #include <unistd.h>
28 #include "audio_uhdf_log.h"
29 #include "hdf_audio_pnp_server.h"
30 #include "hdf_base.h"
31 #include "hdf_io_service.h"
32 #include "osal_time.h"
33 #include "securec.h"
34
35 #define HDF_LOG_TAG HDF_AUDIO_HAL_HOST
36
37 #define RECV_BUFFER_SIZE 2048
38 #define FILE_BUFFER_SIZE 7
39 #define UEVENT_STATE "STATE"
40 #define UEVENT_HDMI_STATE "HDMI="
41 #define UEVENT_HDMI_STATE_PLUG "HDMI=1"
42 #define UEVENT_HDMI_STATE_REMV "HDMI=0"
43
44 #define HDMI_STATUS_FILE_PATH "/sys/class/extcon/extcon2/state"
45
46 #define UEVENT_SOCKET_GROUPS 0xffffffff
47
48 #define MAXEVENTS 1
49 #define TIMEOUT (-1)
50
51 #define AUDIO_HDMI_CARD_NAME "hdf_audio_codec_hdmi_dev0"
52
AudioHdmiPnpUeventStatus(const char * statusStr,bool isPnp)53 static int32_t AudioHdmiPnpUeventStatus(const char *statusStr, bool isPnp)
54 {
55 if (statusStr == NULL) {
56 AUDIO_FUNC_LOGE("error statusStr is null");
57 return HDF_ERR_INVALID_PARAM;
58 }
59
60 struct AudioEvent audioEvent;
61 if (strncmp(statusStr, UEVENT_HDMI_STATE_PLUG, strlen(UEVENT_HDMI_STATE_PLUG)) == 0) {
62 audioEvent.eventType = AUDIO_DEVICE_ADD;
63 if (AudioUhdfLoadDriver(AUDIO_HDMI_CARD_NAME) != HDF_SUCCESS) {
64 AUDIO_FUNC_LOGW("AudioUhdfLoadDriver Failed");
65 }
66 AUDIO_FUNC_LOGI("An HDMI device is plugged in");
67 } else if (strncmp(statusStr, UEVENT_HDMI_STATE_REMV, strlen(UEVENT_HDMI_STATE_REMV)) == 0) {
68 audioEvent.eventType = AUDIO_DEVICE_REMOVE;
69 if (AudioUhdfUnloadDriver(AUDIO_HDMI_CARD_NAME) != HDF_SUCCESS) {
70 AUDIO_FUNC_LOGW("AudioUhdfUnloadDriver Failed");
71 }
72 AUDIO_FUNC_LOGI("The HDMI device is removed");
73 } else {
74 AUDIO_FUNC_LOGE("error HDMI status unknown! statusStr = %{public}s", statusStr);
75 return HDF_FAILURE;
76 }
77
78 audioEvent.deviceType = AUDIO_HDMI_DEVICE;
79 if (isPnp) {
80 return AudioPnpUpdateInfoOnly(audioEvent);
81 }
82
83 return HDF_SUCCESS;
84 }
85
AudioPnpUeventParse(const char * str)86 static int32_t AudioPnpUeventParse(const char *str)
87 {
88 if (str == NULL) {
89 AUDIO_FUNC_LOGE("error device is null");
90 return HDF_FAILURE;
91 }
92
93 while (*str != '\0') {
94 if (strncmp(str, UEVENT_STATE, strlen(UEVENT_STATE)) == 0) {
95 const char *temp = str + strlen(UEVENT_STATE) + 1; // 1 is a skip character '='
96 if (strncmp(temp, UEVENT_HDMI_STATE, strlen(UEVENT_HDMI_STATE)) == 0) {
97 return AudioHdmiPnpUeventStatus(temp, true);
98 }
99 }
100 str += strlen(str) + 1; // 1 is a skip character '\0'
101 }
102
103 return HDF_SUCCESS;
104 }
105
AudioHdmiOpenEventPoll(int32_t * sockFd,int * fdEpoll)106 static int32_t AudioHdmiOpenEventPoll(int32_t *sockFd, int *fdEpoll)
107 {
108 if (sockFd == NULL || fdEpoll == NULL) {
109 AUDIO_FUNC_LOGE("sockFd or fdEpoll is null");
110 return HDF_FAILURE;
111 }
112 struct sockaddr_nl snl;
113 struct epoll_event epollUdev;
114 int32_t buffSize = RECV_BUFFER_SIZE;
115
116 snl.nl_family = AF_NETLINK;
117 snl.nl_groups = UEVENT_SOCKET_GROUPS;
118
119 *fdEpoll = epoll_create1(EPOLL_CLOEXEC);
120 if (*fdEpoll < 0) {
121 AUDIO_FUNC_LOGE("error creating epoll fd: %{public}m");
122 return HDF_FAILURE;
123 }
124
125 OsalMSleep(30); // Wait 30ms to resolve the conflict with the pnp uevent "address already in use"
126 *sockFd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
127 if (*sockFd < 0) {
128 AUDIO_FUNC_LOGE("new socket failed, %{public}d", errno);
129 close(*fdEpoll);
130 return HDF_FAILURE;
131 }
132
133 if (setsockopt(*sockFd, SOL_SOCKET, SO_RCVBUF, &buffSize, sizeof(buffSize)) != 0) {
134 AUDIO_FUNC_LOGE("setsockopt failed %{public}m");
135 close(*fdEpoll);
136 close(*sockFd);
137 return HDF_FAILURE;
138 }
139
140 if (bind(*sockFd, (struct sockaddr *)&snl, sizeof(struct sockaddr_nl)) < 0) {
141 AUDIO_FUNC_LOGE("bind failed: %{public}m");
142 close(*fdEpoll);
143 close(*sockFd);
144 return HDF_FAILURE;
145 }
146
147 (void)memset_s(&epollUdev, sizeof(struct epoll_event), 0, sizeof(struct epoll_event));
148 epollUdev.events = EPOLLIN;
149 epollUdev.data.fd = *sockFd;
150 if (epoll_ctl(*fdEpoll, EPOLL_CTL_ADD, *sockFd, &epollUdev) < 0) {
151 AUDIO_FUNC_LOGE("fail to add fd to epoll: %{public}m");
152 close(*fdEpoll);
153 close(*sockFd);
154 return HDF_FAILURE;
155 }
156
157 return HDF_SUCCESS;
158 }
159
InitializeHdmiStateInternal(void)160 static int32_t InitializeHdmiStateInternal(void)
161 {
162 char buffer[FILE_BUFFER_SIZE] = {0};
163
164 FILE *pFile = fopen(HDMI_STATUS_FILE_PATH, "r");
165 if (pFile == NULL) {
166 AUDIO_FUNC_LOGE("open hdmi status file failed!");
167 return HDF_FAILURE;
168 }
169
170 size_t length = fread(buffer, 1, FILE_BUFFER_SIZE, pFile);
171 if (length != FILE_BUFFER_SIZE) {
172 (void)fclose(pFile);
173 AUDIO_FUNC_LOGE("fread hdmi status file failed!%{public}zu", length);
174 return HDF_FAILURE;
175 }
176
177 (void)fclose(pFile);
178 return AudioHdmiPnpUeventStatus(buffer, false);
179 }
180
181 static bool g_hdmiPnpThreadRunning = false;
AudioHdmiPnpUeventStart(void * useless)182 static void *AudioHdmiPnpUeventStart(void *useless)
183 {
184 (void)useless;
185
186 int fdEpoll = -1;
187 int32_t sockFd = -1;
188
189 if (InitializeHdmiStateInternal() != HDF_SUCCESS) {
190 AUDIO_FUNC_LOGW("booting check hdmi audio device statu failed!");
191 }
192
193 if (AudioHdmiOpenEventPoll(&sockFd, &fdEpoll) != HDF_SUCCESS) {
194 AUDIO_FUNC_LOGE("fail to open event poll");
195 return NULL;
196 }
197
198 while (g_hdmiPnpThreadRunning) {
199 struct epoll_event ev;
200 char buf[RECV_BUFFER_SIZE];
201
202 if (epoll_wait(fdEpoll, &ev, MAXEVENTS, TIMEOUT) < 0) {
203 AUDIO_FUNC_LOGW("error receiving uevent message: %{public}m");
204 continue;
205 }
206
207 (void)memset_s(buf, RECV_BUFFER_SIZE, 0, RECV_BUFFER_SIZE);
208
209 (void)recv(sockFd, buf, RECV_BUFFER_SIZE, 0);
210
211 if (AudioPnpUeventParse(buf) != HDF_SUCCESS) {
212 AUDIO_FUNC_LOGE("AudioPnpUeventParse failed");
213 }
214 }
215 close(fdEpoll);
216 close(sockFd);
217
218 return NULL;
219 }
220
AudioHdmiPnpUeventStartThread(void)221 int32_t AudioHdmiPnpUeventStartThread(void)
222 {
223 pthread_t thread;
224 pthread_attr_t tidsAttr;
225 const char *threadName = "pnp_hdmi";
226 g_hdmiPnpThreadRunning = true;
227
228 AUDIO_FUNC_LOGI("create audio hdmi pnp uevent thread");
229 pthread_attr_init(&tidsAttr);
230 pthread_attr_setdetachstate(&tidsAttr, PTHREAD_CREATE_DETACHED);
231 if (pthread_create(&thread, &tidsAttr, AudioHdmiPnpUeventStart, NULL) != 0) {
232 AUDIO_FUNC_LOGE("create AudioHdmiPnpUeventStart thread failed");
233 return HDF_FAILURE;
234 }
235
236 if (pthread_setname_np(thread, threadName) != 0) {
237 AUDIO_FUNC_LOGE("set tid name failed");
238 return HDF_FAILURE;
239 }
240
241 return HDF_SUCCESS;
242 }
243
AudioHdmiPnpUeventStopThread(void)244 void AudioHdmiPnpUeventStopThread(void)
245 {
246 AUDIO_FUNC_LOGI("audio hdmi pnp uevent thread exit");
247 g_hdmiPnpThreadRunning = false;
248 }
249