blob: 6b36425bf20c18ed684b5557661c887ad3140aed [file] [log] [blame]
Chuanzhi Wang1b3a5de2020-09-04 17:02:00 +08001/***************************************************************************
2 * Copyright (c) 2014 Amlogic, Inc. All rights reserved.
3 *
4 * This source code is subject to the terms and conditions defined in the
5 * file 'LICENSE' which is part of this source code package.
6 *
7 * Description:
8 *
9 * @brief linux dvb demux wrapper
10 * @file dvb_dmx_wrapper.c
11 *
12 * \author Chuanzhi Wang <chuanzhi.wang@amlogic.com>
13 * \date 2020-07-16: create the document
14 ***************************************************************************/
15
16#include <sys/types.h>
17#include <sys/ioctl.h>
18#include <sys/prctl.h>
19
20#include <poll.h>
21#include <fcntl.h>
22#include <errno.h>
23#include <pthread.h>
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27
28#include "dmx.h"
29#include "dvb_dmx_wrapper.h"
30
31#define DMX_COUNT (3)
32#define DMX_FILTER_COUNT (32*DMX_COUNT)
33#define SEC_BUF_SIZE (4096)
34#define DMX_POLL_TIMEOUT (200)
35
36/*#define DEBUG_DEMUX_DATA*/
37#define DMX_DBG(fmt, ...) fprintf(stderr, "error:" fmt, ##__VA_ARGS__)
38
39typedef struct
40{
41 int dev_no;
42 int fd;
43 int used;
44 int enable;
45 int need_free;
46 AML_DMX_DataCb cb;
47 void *user_data;
48}dvb_dmx_filter_t;
49
50typedef struct
51{
52 int dev_no;
53 int running;
54 pthread_t thread;
55 pthread_mutex_t lock;
56
57 dvb_dmx_filter_t filter[DMX_FILTER_COUNT];
58}dvb_dmx_t;
59
60static dvb_dmx_t dmx_devices[DMX_COUNT];
61
62static inline BOOLEAN dmx_get_dev(int dev_no, dvb_dmx_t **dev)
63{
64 if ((dev_no < 0) || (dev_no >= DMX_COUNT))
65 {
66 DMX_DBG("invalid demux device number %d, must in(%d~%d)", dev_no, 0, DMX_COUNT-1);
67 return FALSE;
68 }
69
70 *dev = &dmx_devices[dev_no];
71 return TRUE;
72}
73
74static void* dmx_data_thread(void *arg)
75{
76 int i, fid;
77 int ret;
78 int cnt, len;
79 uint32_t mask;
80 uint8_t *sec_buf = NULL;
81 int fids[DMX_FILTER_COUNT];
82 struct pollfd fds[DMX_FILTER_COUNT];
83 dvb_dmx_filter_t *filter = NULL;
84 dvb_dmx_t *dmx = (dvb_dmx_t *)arg;
85
86 sec_buf = (uint8_t *)malloc(SEC_BUF_SIZE);
87 prctl(PR_SET_NAME, "dmx_data_thread");
88 while (dmx->running)
89 {
90 cnt = 0;
91 mask = 0;
92
93 pthread_mutex_lock(&dmx->lock);
94 for (fid = 0; fid < DMX_FILTER_COUNT; fid++)
95 {
96 if (dmx->filter[fid].need_free)
97 {
98 filter = &dmx->filter[fid];
99 close(filter->fd);
100 filter->used = 0;
101 filter->need_free = 0;
102 filter->cb = NULL;
103 }
104
105 if (dmx->filter[fid].used)
106 {
107 fds[cnt].events = POLLIN | POLLERR;
108 fds[cnt].fd = dmx->filter[fid].fd;
109 fids[cnt] = fid;
110 cnt++;
111 }
112 }
113
114 pthread_mutex_unlock(&dmx->lock);
115
116 if (!cnt)
117 {
118 usleep(20*1000);
119 continue;
120 }
121
122 ret = poll(fds, cnt, DMX_POLL_TIMEOUT);
123 if (ret <= 0)
124 {
125 continue;
126 }
127
128 for (i = 0; i < cnt; i++)
129 {
130 if (fds[i].revents & (POLLIN | POLLERR))
131 {
132 pthread_mutex_lock(&dmx->lock);
133 filter = &dmx->filter[fids[i]];
134 if (!filter->enable || !filter->used || filter->need_free)
135 {
136 DMX_DBG("ch[%d] not used, not read", fids[i], len);
137 len = 0;
138 }
139 else
140 {
141 len = read(filter->fd, sec_buf, SEC_BUF_SIZE);
142 if (len <= 0)
143 {
144 DMX_DBG("read demux filter[%d] failed (%s) %d", fids[i], strerror(errno), errno);
145 }
146 }
147 pthread_mutex_unlock(&dmx->lock);
148#ifdef DEBUG_DEMUX_DATA
149 if (len)
150 DMX_DBG("tid[%#x] ch[%d] %#x bytes", sec_buf[0], fids[i], len);
151#endif
152 if (len > 0 && filter->cb)
153 {
154 filter->cb(filter->dev_no, fids[i], sec_buf, len, filter->user_data);
155 }
156 }
157 }
158 }
159
160 if (sec_buf)
161 {
162 free(sec_buf);
163 }
164
165 return NULL;
166}
167
168static dvb_dmx_filter_t* dmx_get_filter(dvb_dmx_t * dev, int fhandle)
169{
170 if (fhandle >= DMX_FILTER_COUNT)
171 {
172 DMX_DBG("wrong filter no");
173 return NULL;
174 }
175
176 if (!dev->filter[fhandle].used)
177 {
178 DMX_DBG("filter %d not allocated", fhandle);
179 return NULL;
180 }
181 return &dev->filter[fhandle];
182}
183
184BOOLEAN AML_DMX_Open(int dev_no)
185{
186 dvb_dmx_t *dev = NULL;
187
188 if (!dmx_get_dev(dev_no, &dev))
189 return FALSE;
190
191 if (dev->running)
192 {
193 DMX_DBG("dmx already initialized");
194 return FALSE;
195 }
196
197 dev->dev_no = dev_no;
198
199 pthread_mutex_init(&dev->lock, NULL);
200 dev->running = 1;
201 pthread_create(&dev->thread, NULL, dmx_data_thread, dev);
202
203 return TRUE;
204}
205
206BOOLEAN AML_DMX_AllocateFilter(int dev_no, int *fhandle)
207{
208 int fd;
209 int fid;
210 dvb_dmx_filter_t *filter = NULL;
211 char dev_name[32];
212
213 dvb_dmx_t *dev = NULL;
214
215 if (!dmx_get_dev(dev_no, &dev))
216 {
217 DMX_DBG("demux allocate failed, wrong dmx device no %d", dev_no);
218 return FALSE;
219 }
220
221 pthread_mutex_lock(&dev->lock);
222 filter = &dev->filter[0];
223 for (fid = 0; fid < DMX_FILTER_COUNT; fid++)
224 {
225 if (!filter[fid].used)
226 {
227 break;
228 }
229 }
230
231 if (fid >= DMX_FILTER_COUNT)
232 {
233 DMX_DBG("filter id:%d, have no filter to alloc", fid);
234 pthread_mutex_unlock(&dev->lock);
235 return FALSE;
236 }
237
238 memset(dev_name, 0, sizeof(dev_name));
239 sprintf(dev_name, "/dev/dvb0.demux%d", dev_no);
240 fd = open(dev_name, O_RDWR);
241 if (fd == -1)
242 {
243 DMX_DBG("cannot open \"%s\" (%s)", dev_name, strerror(errno));
244 pthread_mutex_unlock(&dev->lock);
245 return FALSE;
246 }
247
248 memset(&filter[fid], 0, sizeof(dvb_dmx_filter_t));
249 filter[fid].dev_no = dev_no;
250 filter[fid].fd = fd;
251 filter[fid].used = 1;
252 *fhandle = fid;
253
254 pthread_mutex_unlock(&dev->lock);
255
256 //DMX_DBG("fhandle = %d", fid);
257 return TRUE;
258}
259
260BOOLEAN AML_DMX_SetSecFilter(int dev_no, int fhandle, const struct dmx_sct_filter_params *params)
261{
262 BOOLEAN ret = TRUE;
263 dvb_dmx_t *dev = NULL;
264 dvb_dmx_filter_t *filter = NULL;
265
266 if (!dmx_get_dev(dev_no, &dev))
267 {
268 DMX_DBG("Wrong dmx device no %d", dev_no);
269 return FALSE;
270 }
271
272 if (!params)
273 return FALSE;
274
275 pthread_mutex_lock(&dev->lock);
276
277 filter = dmx_get_filter(dev, fhandle);
278 if (filter)
279 {
280 if (ioctl(filter->fd, DMX_STOP, 0) < 0)
281 {
282 DMX_DBG("dmx stop filter failed error:%s", strerror(errno));
283 ret = FALSE;
284 }
285 else if (ioctl(filter->fd, DMX_SET_FILTER, params) < 0)
286 {
287 DMX_DBG("set filter failed error:%s", strerror(errno));
288 ret = FALSE;
289 }
290 }
291
292 pthread_mutex_unlock(&dev->lock);
293 //DMX_DBG("pid = %#x", params->pid);
294 return ret;
295}
296
297BOOLEAN AML_DMX_SetPesFilter(int dev_no, int fhandle, const struct dmx_pes_filter_params *params)
298{
299 BOOLEAN ret = TRUE;
300 dvb_dmx_t *dev = NULL;
301 dvb_dmx_filter_t *filter = NULL;
302
303 if (!dmx_get_dev(dev_no, &dev))
304 {
305 DMX_DBG("wrong dmx device no %d", dev_no);
306 return FALSE;
307 }
308
309 if (!params)
310 return FALSE;
311
312 pthread_mutex_lock(&dev->lock);
313
314 filter = dmx_get_filter(dev, fhandle);
315 if (filter)
316 {
317 if (ioctl(filter->fd, DMX_STOP, 0) < 0)
318 {
319 DMX_DBG("dmx stop filter failed error:%s", strerror(errno));
320 ret = FALSE;
321 }
322 else
323 {
324 fcntl(filter->fd, F_SETFL, O_NONBLOCK);
325 if (ioctl(filter->fd, DMX_SET_PES_FILTER, params) < 0)
326 {
327 DMX_DBG("set filter failed error:%s", strerror(errno));
328 ret = FALSE;
329 }
330 }
331 }
332
333 pthread_mutex_unlock(&dev->lock);
334 //DMX_DBG("pid = %#x", params->pid);
335 return ret;
336}
337
338
339BOOLEAN AML_DMX_SetBufferSize(int dev_no, int fhandle, int size)
340{
341 BOOLEAN ret = TRUE;
342 dvb_dmx_t *dev = NULL;
343 dvb_dmx_filter_t *filter = NULL;
344
345 if (!dmx_get_dev(dev_no, &dev))
346 {
347 DMX_DBG("wrong dmx device no %d", dev_no);
348 return FALSE;
349 }
350
351 pthread_mutex_lock(&dev->lock);
352
353 filter = dmx_get_filter(dev, fhandle);
354 if (filter)
355 {
356 if (ioctl(filter->fd, DMX_SET_BUFFER_SIZE, size) < 0)
357 {
358 DMX_DBG("set buf size failed error:%s", strerror(errno));
359 ret = FALSE;
360 }
361 }
362
363 pthread_mutex_unlock(&dev->lock);
364 return ret;
365}
366
367BOOLEAN AML_DMX_FreeFilter(int dev_no, int fhandle)
368{
369 dvb_dmx_t *dev = NULL;
370 dvb_dmx_filter_t *filter = NULL;
371
372 if (!dmx_get_dev(dev_no, &dev))
373 {
374 DMX_DBG("wrong dmx device no %d", dev_no);
375 return FALSE;
376 }
377
378 pthread_mutex_lock(&dev->lock);
379
380 filter = dmx_get_filter(dev, fhandle);
381 if (filter)
382 {
383 filter->need_free = 1;
384 }
385
386 pthread_mutex_unlock(&dev->lock);
387
388 //DMX_DBG("fhandle = %d", fhandle);
389 return TRUE;
390}
391
392BOOLEAN AML_DMX_StartFilter(int dev_no, int fhandle)
393{
394 BOOLEAN ret = TRUE;
395 dvb_dmx_t *dev = NULL;
396 dvb_dmx_filter_t *filter = NULL;
397
398 if (!dmx_get_dev(dev_no, &dev))
399 {
400 DMX_DBG("wrong dmx device no %d", dev_no);
401 return FALSE;
402 }
403
404 pthread_mutex_lock(&dev->lock);
405
406 filter = dmx_get_filter(dev, fhandle);
407 if (filter && !filter->enable)
408 {
409 if (ioctl(filter->fd, DMX_START, 0) < 0)
410 {
411 DMX_DBG("dmx start filter failed error:%s", strerror(errno));
412 ret = FALSE;
413 }
414 else
415 {
416 filter->enable = 1;
417 }
418 }
419
420 pthread_mutex_unlock(&dev->lock);
421 //DMX_DBG("ret = %d", ret);
422 return ret;
423}
424
425BOOLEAN AML_DMX_StopFilter(int dev_no, int fhandle)
426{
427 BOOLEAN ret = TRUE;
428 dvb_dmx_t *dev = NULL;
429 dvb_dmx_filter_t *filter = NULL;
430
431 if (!dmx_get_dev(dev_no, &dev))
432 {
433 DMX_DBG("wrong dmx device no %d", dev_no);
434 return FALSE;
435 }
436
437 pthread_mutex_lock(&dev->lock);
438
439 filter = dmx_get_filter(dev, fhandle);
440 if (filter && filter->enable)
441 {
442 if (ioctl(filter->fd, DMX_STOP, 0) < 0)
443 {
444 DMX_DBG("dmx stop filter failed error:%s", strerror(errno));
445 ret = FALSE;
446 }
447 else
448 {
449 filter->enable = 0;
450 }
451 }
452
453 pthread_mutex_unlock(&dev->lock);
454 //DMX_DBG("ret = %d", ret);
455 return ret;
456}
457
458BOOLEAN AML_DMX_SetSource(int dev_no, AML_DMX_Source_t src)
459{
460 char buf[32];
461 char *cmd;
462
463 snprintf(buf, sizeof(buf), "/sys/class/stb/demux%d_source", dev_no);
464 switch(src)
465 {
466 case AML_DMX_SRC_TS0:
467 cmd = "ts0";
468 break;
469 case AML_DMX_SRC_TS1:
470 cmd = "ts1";
471 break;
472 case AML_DMX_SRC_TS2:
473 cmd = "ts2";
474 break;
475 case AML_DMX_SRC_TS3:
476 cmd = "ts3";
477 break;
478 case AML_DMX_SRC_HIU:
479 cmd = "hiu";
480 break;
481 case AML_DMX_SRC_HIU1:
482 cmd = "hiu1";
483 break;
484 default:
485 DMX_DBG("do not support demux source %d", src);
486 return FALSE;
487 }
488
489 return AML_DMX_FileEcho(buf, cmd);
490}
491
492
493BOOLEAN AML_DMX_SetCallback(int dev_no, int fhandle, AML_DMX_DataCb cb, void *user_data)
494{
495 BOOLEAN ret = TRUE;
496 dvb_dmx_t *dev = NULL;
497 dvb_dmx_filter_t *filter = NULL;
498
499 if (!dmx_get_dev(dev_no, &dev))
500 {
501 DMX_DBG("wrong dmx device no %d", dev_no);
502 return FALSE;
503 }
504
505 pthread_mutex_lock(&dev->lock);
506 filter = dmx_get_filter(dev, fhandle);
507 if (filter)
508 {
509 filter->cb = cb;
510 filter->user_data = user_data;
511 }
512 else
513 {
514 ret = FALSE;
515 }
516
517 pthread_mutex_unlock(&dev->lock);
518
519 //DMX_DBG("ret = %d", ret);
520 return ret;
521}
522
523BOOLEAN AML_DMX_Close(int dev_no)
524{
525 int i;
526 int open_count = 0;
527 dvb_dmx_t *dev = NULL;
528 dvb_dmx_filter_t *filter = NULL;
529
530 if (!dmx_get_dev(dev_no, &dev))
531 {
532 DMX_DBG("wrong dmx device no %d", dev_no);
533 return FALSE;
534 }
535
536 pthread_mutex_lock(&dev->lock);
537
538 for (i = 0; i < DMX_FILTER_COUNT; i++)
539 {
540 filter = &dev->filter[i];
541 if (filter->used && filter->dev_no == dev_no)
542 {
543 if (filter->enable)
544 {
545 ioctl(filter->fd, DMX_STOP, 0);
546 }
547 close(filter->fd);
548 }
549 else if (filter->used)
550 {
551 open_count++;
552 }
553 }
554
555 if (open_count == 0)
556 {
557 dev->running = 0;
558 pthread_join(dev->thread, NULL);
559 pthread_mutex_destroy(&dev->lock);
560 }
561
562 pthread_mutex_unlock(&dev->lock);
563 return TRUE;
564}
565
566BOOLEAN AML_DMX_FileEcho(const char *name, const char *cmd)
567{
568 int fd, len, ret;
569
570 if (!name || !cmd)
571 return FALSE;
572
573 fd = open(name, O_WRONLY);
574 if (fd == -1)
575 {
576 DMX_DBG("cannot open file \"%s\"", name);
577 return FALSE;
578 }
579
580 len = strlen(cmd);
581 ret = write(fd, cmd, len);
582 if (ret != len)
583 {
584 DMX_DBG("write failed file:\"%s\" cmd:\"%s\" error:\"%s\"", name, cmd, strerror(errno));
585 close(fd);
586 return FALSE;
587 }
588
589 close(fd);
590 return TRUE;
591}