1 /*--------------------------------------------------------------------------
2 Copyright (c) 2017, 2019, The Linux Foundation. All rights reserved.
3
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are
6 met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above
10 copyright notice, this list of conditions and the following
11 disclaimer in the documentation and/or other materials provided
12 with the distribution.
13 * Neither the name of The Linux Foundation nor the names of its
14 contributors may be used to endorse or promote products derived
15 from this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 --------------------------------------------------------------------------*/
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdint.h>
32 #include <fcntl.h>
33 #include <string.h>
34 #include <dlfcn.h>
35 #include <sys/ioctl.h>
36 #include <pthread.h>
37 #include <time.h>
38 #include <errno.h>
39 #include <unistd.h>
40 #include <sys/mman.h>
41 #include "hypv_intercept.h"
42 #ifdef _ANDROID_
43 #include <cutils/properties.h>
44 #endif
45
46 #define MAX_HVFE_HANDLE 32
47 #define HYPV_HANDLE_SIGNATURE 0x2bcd0000
48 #define HYPV_HANDLE_SIGNATURE_MASK 0xffff0000
49 #define HYPV_HANDLE_MASK 0x0000ffff
50 #define POLL_TIMEOUT 0xffffffff
51 #define MAX_EVENTS 32
52
53 #define IS_HYPERVISOR_VIDEO_HANDLE(fd) ((fd & HYPV_HANDLE_SIGNATURE_MASK)==HYPV_HANDLE_SIGNATURE)
54 #define HYP_INITIALIZED (g_hvfe_handle_count > 0)
55 #define NUM_PENDING_EVENTS(a, b) ((a > b) ? (a - b) : (b - a))
56
57 typedef void* HVFE_HANDLE;
58 typedef int (*hvfe_callback_handler_t)(void *context, void *message);
59
60 struct hypv_intercept {
61 HVFE_HANDLE handle;
62 short event_flags[MAX_EVENTS];
63 bool exit_flag;
64 unsigned int event_q_front;
65 unsigned int event_q_rear;
66 pthread_t thread_id;
67 pthread_cond_t cond;
68 pthread_mutex_t lock;
69 };
70
71 struct hvfe_callback_t
72 {
73 hvfe_callback_handler_t handler;
74 void* context;
75 };
76
77 typedef void (*cb)(int flag);
78 typedef HVFE_HANDLE (*video_fe_open_func)(const char*, int, hvfe_callback_t*);
79 typedef int (*video_fe_ioctl_func)(HVFE_HANDLE, int, void*);
80 typedef int (*video_fe_close_func)(HVFE_HANDLE);
81
82 static void *hvfe_lib_handle = NULL;
83 static video_fe_open_func video_fe_open = NULL;
84 static video_fe_ioctl_func video_fe_ioctl = NULL;
85 static video_fe_close_func video_fe_close = NULL;
86 static pthread_mutex_t g_hvfe_handle_lock = PTHREAD_MUTEX_INITIALIZER;
87 static struct hypv_intercept g_hvfe_handle[MAX_HVFE_HANDLE];
88 static int g_hvfe_handle_count = 0;
89 static int event_notify(void *context, void *messages);
90 int debug_level = 0x1;
91
add_handle_to_index(HVFE_HANDLE handle,int index)92 static int add_handle_to_index(HVFE_HANDLE handle, int index)
93 {
94 int rc = 0;
95
96 memset(&g_hvfe_handle[index], 0, sizeof(struct hypv_intercept));
97 if (pthread_mutex_init(&g_hvfe_handle[index].lock, NULL) != 0) {
98 HYP_VIDEO_MSG_ERROR("error initializing pthread lock");
99 rc = -1;
100 } else if (pthread_cond_init(&g_hvfe_handle[index].cond, NULL) != 0) {
101 HYP_VIDEO_MSG_ERROR("error initializing pthread cond");
102 rc = -1;
103 } else {
104 g_hvfe_handle[index].handle = handle;
105 g_hvfe_handle_count++;
106 }
107
108 return rc;
109 }
110
find_empty_handle_index(void)111 static int find_empty_handle_index(void)
112 {
113 int rc = 0;
114
115 if (g_hvfe_handle_count >= MAX_HVFE_HANDLE) {
116 HYP_VIDEO_MSG_ERROR("reached max handle count. handle count %d",
117 g_hvfe_handle_count);
118 rc = -1;
119 } else {
120 int i;
121
122 for (i = 0; i < MAX_HVFE_HANDLE; i++) {
123 if (g_hvfe_handle[i].handle == 0) {
124 rc = i;
125 break;
126 }
127 }
128
129 if (i >= MAX_HVFE_HANDLE) {
130 HYP_VIDEO_MSG_ERROR("failed to find empty slot");
131 rc = -1;
132 }
133 }
134
135 return rc;
136 }
137
hypv_init(void)138 static int hypv_init(void)
139 {
140 int rc = 0;
141
142 hvfe_lib_handle = dlopen("libhyp_video_fe.so", RTLD_NOW);
143 if (hvfe_lib_handle == NULL) {
144 HYP_VIDEO_MSG_ERROR("failed to open libhyp_video_fe");
145 rc = -1;
146 } else {
147 video_fe_open = (video_fe_open_func)dlsym(hvfe_lib_handle, "video_fe_open");
148 if (video_fe_open == NULL) {
149 HYP_VIDEO_MSG_ERROR("failed to get video_fe_open handle");
150 rc = -1;
151 } else {
152 video_fe_ioctl = (video_fe_ioctl_func)dlsym(hvfe_lib_handle, "video_fe_ioctl");
153 if (video_fe_ioctl == NULL) {
154 HYP_VIDEO_MSG_ERROR("failed to get video_fe_ioctl handle");
155 rc = -1;
156 } else {
157 video_fe_close = (video_fe_close_func)dlsym(hvfe_lib_handle, "video_fe_close");
158 if (video_fe_close == 0) {
159 HYP_VIDEO_MSG_ERROR("failed to get video_fe_close handle");
160 rc = -1;
161 }//video_fe_close
162 } //video_fe_ioctl
163 } //video_fe_open
164 } //hvfe_lib_handle
165
166 if (rc < 0 && hvfe_lib_handle) {
167 dlclose(hvfe_lib_handle);
168 hvfe_lib_handle = NULL;
169 }
170
171 return rc;
172 }
173
hypv_deinit(void)174 static void hypv_deinit(void)
175 {
176 dlclose(hvfe_lib_handle);
177 hvfe_lib_handle = NULL;
178
179 return;
180 }
181
hypv_open(const char * str,int flag)182 int hypv_open(const char *str, int flag)
183 {
184 int rc = 0;
185
186 #ifdef _LINUX_
187 char *env_ptr = getenv("HYPV_DEBUG_LEVEL");
188 debug_level = env_ptr ? atoi(env_ptr) : 0;
189 #elif defined _ANDROID_
190 char property_value[PROPERTY_VALUE_MAX] = {0};
191
192 property_get("vendor.hypv.debug.level", property_value, "1");
193 debug_level = atoi(property_value);
194 #endif
195
196 pthread_mutex_lock(&g_hvfe_handle_lock);
197
198 if (!HYP_INITIALIZED) {
199 if ((rc = hypv_init()) < 0) {
200 HYP_VIDEO_MSG_ERROR("hypervisor init failed");
201 pthread_mutex_unlock(&g_hvfe_handle_lock);
202 return rc;
203 }
204 }
205
206 int index = find_empty_handle_index();
207 if (index < 0) {
208 rc = -1;
209 } else {
210 struct hvfe_callback_t cb;
211
212 cb.handler = event_notify;
213 cb.context = &g_hvfe_handle[index];
214 HVFE_HANDLE hvfe_handle = video_fe_open(str, flag, &cb);
215 HYP_VIDEO_MSG_INFO("video fe open handle = %p", hvfe_handle);
216
217 if (hvfe_handle == NULL) {
218 HYP_VIDEO_MSG_ERROR("video fe open failed");
219 rc = -1;
220 } else {
221 if (add_handle_to_index(hvfe_handle, index) < 0) {
222 HYP_VIDEO_MSG_ERROR("failed to add hvfe handle");
223 video_fe_close(hvfe_handle);
224 rc = -1;
225 } else {
226 rc = (HYPV_HANDLE_SIGNATURE | index);
227 }
228 }
229 }
230
231 pthread_mutex_unlock(&g_hvfe_handle_lock);
232
233 if (rc < 0)
234 hypv_deinit();
235
236 return rc;
237 }
238
hypv_ioctl(int fd,int cmd,void * data)239 int hypv_ioctl(int fd, int cmd, void *data)
240 {
241 int rc = 0;
242
243 if (!HYP_INITIALIZED) {
244 HYP_VIDEO_MSG_ERROR("hypervisor not initialized");
245 return -1;
246 }
247
248 if (IS_HYPERVISOR_VIDEO_HANDLE(fd)) {
249 int fd_index = fd & HYPV_HANDLE_MASK;
250 if (fd_index >= MAX_HVFE_HANDLE) {
251 HYP_VIDEO_MSG_ERROR("invalid fd_index = %d", fd_index);
252 rc = -1;
253 } else {
254 rc = video_fe_ioctl(g_hvfe_handle[fd_index].handle, cmd, data);
255 HYP_VIDEO_MSG_INFO("fd %d, fd_index %d, cmd 0x%x, data 0x%p, rc %d",
256 fd, fd_index, cmd, data, rc);
257 }
258 } else {
259 HYP_VIDEO_MSG_ERROR("native ioctl: fd %d, cmd 0x%x, data 0x%p",
260 fd, cmd, data);
261 rc = ioctl(fd, cmd, data);
262 }
263
264 return rc;
265 }
266
event_notify(void * context,void * messages)267 static int event_notify(void *context, void *messages)
268 {
269 struct hypv_intercept *handle = (struct hypv_intercept *)context;
270 int flags = *(int *)messages;
271
272 HYP_VIDEO_MSG_INFO("event flag 0x%x", flags);
273 pthread_mutex_lock(&handle->lock);
274 handle->event_flags[handle->event_q_rear++] = flags;
275 handle->event_q_rear %= MAX_EVENTS;
276 HYP_VIDEO_MSG_INFO("cond signal. num_pending_events %d event_q_front %d event_q_rear %d",
277 NUM_PENDING_EVENTS(handle->event_q_front, handle->event_q_rear),
278 handle->event_q_front, handle->event_q_rear);
279 pthread_cond_signal(&handle->cond);
280 pthread_mutex_unlock(&handle->lock);
281
282 return 0;
283 }
284
exit_thread(void * fds)285 static void* exit_thread(void *fds)
286 {
287 struct pollfd *pfds = (struct pollfd *)fds;
288 int fd_index = pfds[0].fd & HYPV_HANDLE_MASK;
289 struct hypv_intercept *handle = &g_hvfe_handle[fd_index];
290 struct pollfd exit_fd;
291
292 HYP_VIDEO_MSG_INFO("exit thread created. fd = %d", fd_index);
293 exit_fd.events = POLLIN | POLLERR;
294 exit_fd.fd = pfds[1].fd;
295
296 poll(&exit_fd, 1, POLL_TIMEOUT);
297
298 if ((exit_fd.revents & POLLIN) || (exit_fd.revents & POLLERR)) {
299 handle->exit_flag = true;
300 pthread_cond_signal(&handle->cond);
301 }
302
303 return NULL;
304 }
305
hypv_poll(struct pollfd * fds,nfds_t nfds,int timeout)306 int hypv_poll(struct pollfd *fds, nfds_t nfds, int timeout)
307 {
308 struct timespec ts;
309 int ret = 0;
310
311 if (nfds == 0)
312 return -1;
313
314 if (!HYP_INITIALIZED) {
315 HYP_VIDEO_MSG_ERROR("hypervisor not initialized");
316 return -1;
317 }
318
319 if (IS_HYPERVISOR_VIDEO_HANDLE(fds[0].fd)) {
320 int fd_index = fds[0].fd & HYPV_HANDLE_MASK;
321
322 if (fd_index >= MAX_HVFE_HANDLE) {
323 HYP_VIDEO_MSG_ERROR("invalid fd index %d", fd_index);
324 ret = -1;
325 } else {
326 struct hypv_intercept *handle = &g_hvfe_handle[fd_index];
327
328 clock_gettime(CLOCK_REALTIME, &ts);
329 ts.tv_sec += timeout / 1000;
330 ts.tv_nsec = 0;
331 fds[1].revents = fds[0].revents = 0;
332
333 if (handle->thread_id == 0) {
334 if (pthread_create(&handle->thread_id, 0, exit_thread, fds)) {
335 handle->thread_id = 0;
336 return -1;
337 }
338 }
339
340 pthread_mutex_lock(&handle->lock);
341 if (!NUM_PENDING_EVENTS(handle->event_q_front, handle->event_q_rear) &&
342 !handle->exit_flag) {
343 ret = pthread_cond_timedwait(&handle->cond, &handle->lock, &ts);
344 }
345 else
346 {
347 HYP_VIDEO_MSG_INFO("hypv_poll: process pending flag");
348 }
349
350 if (ret == ETIMEDOUT) {
351 HYP_VIDEO_MSG_INFO("hyp poll timeout");
352 ret = 0;
353 } else if (ret == 0) {
354 if (handle->exit_flag == true) {
355 HYP_VIDEO_MSG_INFO("hyp poll exit");
356 fds[1].revents = POLLIN;
357 handle->exit_flag = false;
358 handle->thread_id = 0;
359 } else {
360 fds[0].revents = handle->event_flags[handle->event_q_front++];
361 handle->event_q_front %= MAX_EVENTS;
362 HYP_VIDEO_MSG_INFO("hyp poll fd %d events 0x%x pending events %d",
363 fds[0].fd, fds[0].revents,
364 NUM_PENDING_EVENTS(handle->event_q_front, handle->event_q_rear));
365 }
366 ret = 1;
367 }
368
369 pthread_mutex_unlock(&handle->lock);
370 }
371 } else {
372 HYP_VIDEO_MSG_ERROR("unknown fd = %d", fds[0].fd);
373 }
374
375 return ret;
376 }
377
hypv_close(int fd)378 int hypv_close(int fd)
379 {
380 int rc = 0;
381
382 if (!HYP_INITIALIZED) {
383 HYP_VIDEO_MSG_ERROR("hypervisor not initialized");
384 return -1;
385 }
386
387 if (IS_HYPERVISOR_VIDEO_HANDLE(fd)) {
388 int fd_index = fd & HYPV_HANDLE_MASK;
389
390 if ((fd_index >= MAX_HVFE_HANDLE) || (fd_index < 0)) {
391 HYP_VIDEO_MSG_ERROR("invalid fd %d", fd_index);
392 rc = -1;
393 } else {
394 pthread_mutex_lock(&g_hvfe_handle_lock);
395 rc = video_fe_close(g_hvfe_handle[fd_index].handle);
396 g_hvfe_handle[fd_index].handle = 0;
397 pthread_cond_destroy(&g_hvfe_handle[fd_index].cond);
398 pthread_mutex_destroy(&g_hvfe_handle[fd_index].lock);
399 if (--g_hvfe_handle_count == 0)
400 hypv_deinit();
401 pthread_mutex_unlock(&g_hvfe_handle_lock);
402 }
403 } else {
404 rc = close(fd);
405 }
406
407 return rc;
408 }
409