• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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