/*************************************************************************** * Copyright (C) 2014-2025 Amlogic, Inc. All rights reserved. * * All information contained herein is Amlogic confidential. * * This software is provided to you pursuant to Software License Agreement * (SLA) with Amlogic Inc ("Amlogic"). This software may be used * only in accordance with the terms of this agreement. * * Redistribution and use in source and binary forms, with or without * modification is strictly prohibited without prior written permission from * Amlogic. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Description: * Linux DVB demux driver ***************************************************************************/ #define LOG_TAG "Demux" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "AmlogicUtil.h" #include "DemuxDriver.h" #include "SubtitleLog.h" #define open(a...) \ ({ \ int ret, times = 3; \ do { \ ret = open(a); \ if (ret == -1) { \ usleep(100 * 1000); \ } \ } while (ret == -1 && times--); \ ret; \ }) typedef struct { char dev_name[32]; int fd[DEMUX_FILTER_COUNT]; int event_fd; } DVBDmx_t; static int dvb_open(DemuxDeviceType *dev, const AmlogicDemuxOpenParameterType *para); static int dvb_close(DemuxDeviceType *dev); static int dvb_wake(DemuxDeviceType *dev); static int dvb_alloc_filter(DemuxDeviceType *dev, DemuxFilterType *filter); static int dvb_free_filter(DemuxDeviceType *dev, DemuxFilterType *filter); static int dvb_set_sec_filter(DemuxDeviceType *dev, DemuxFilterType *filter, const struct dmx_sct_filter_params *params); static int dvb_set_pes_filter(DemuxDeviceType *dev, DemuxFilterType *filter, const struct dmx_pes_filter_params *params); static int dvb_enable_filter(DemuxDeviceType *dev, DemuxFilterType *filter, bool enable); static int dvb_set_buf_size(DemuxDeviceType *dev, DemuxFilterType *filter, int size); static int dvb_poll(DemuxDeviceType *dev, Demux_FilterMaskType *mask, int timeout); static int dvb_poll_exit(DemuxDeviceType *dev); static int dvb_read(DemuxDeviceType *dev, DemuxFilterType *filter, uint8_t *buf, int *size); static int dvb_set_source(DemuxDeviceType *dev, AmlogicDemuxSourceType src); static int dvb_get_newest_pts(DemuxDeviceType *dev, amdmx_filter_mem_info *demuxFilterMemInfo); const DemuxDriverType linux_dvb_dmx_drv = {.open = dvb_open, .close = dvb_close, .alloc_filter = dvb_alloc_filter, .free_filter = dvb_free_filter, .set_sec_filter = dvb_set_sec_filter, .set_pes_filter = dvb_set_pes_filter, .enable_filter = dvb_enable_filter, .set_buf_size = dvb_set_buf_size, .poll = dvb_poll, .poll_exit = dvb_poll_exit, .read = dvb_read, .set_source = dvb_set_source, .get_newest_pts = dvb_get_newest_pts}; /**************************************************************************** * Static functions ***************************************************************************/ static int dvb_open(DemuxDeviceType *dev, const AmlogicDemuxOpenParameterType *para) { UNUSED(para); DVBDmx_t *dmx; dmx = (DVBDmx_t *)malloc(sizeof(DVBDmx_t)); if (!dmx) { SUBTITLE_LOGE("malloc fail: %m"); return DEMUX_ERROR_NO_MEM; } snprintf(dmx->dev_name, sizeof(dmx->dev_name), "/dev/dvb0.demux%d", dev->dev_no); for (int i = 0; i < DEMUX_FILTER_COUNT; ++i) { dmx->fd[i] = -1; } dmx->event_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); if (dmx->event_fd == -1) { SUBTITLE_LOGE("eventfd fail: %m"); } SUBTITLE_LOGI("event_fd = %d", dmx->event_fd); dev->drv_data = dmx; return AM_SUCCESS; } static int dvb_close(DemuxDeviceType *dev) { DVBDmx_t *dmx = (DVBDmx_t *)dev->drv_data; if (dmx->event_fd >= 0) { SUBTITLE_LOGI("event_fd = %d", dmx->event_fd); close(dmx->event_fd); dmx->event_fd = -1; } free(dmx); return AM_SUCCESS; } static int dvb_alloc_filter(DemuxDeviceType *dev, DemuxFilterType *filter) { DVBDmx_t *dmx = (DVBDmx_t *)dev->drv_data; int fd = open(dmx->dev_name, O_RDWR); if (fd == -1) { SUBTITLE_LOGE("fail to open %s: %m", dmx->dev_name); return DEMUX_ERROR_CANNOT_OPEN_DEV; } dmx->fd[filter->id] = fd; filter->drv_data = (void *)(long)fd; if (dmx->event_fd == -1) { dmx->event_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); if (dmx->event_fd == -1) { SUBTITLE_LOGE("eventfd fail: %m"); } } SUBTITLE_LOGI("fd = %d, event_fd = %d", fd, dmx->event_fd); return AM_SUCCESS; } static int dvb_free_filter(DemuxDeviceType *dev, DemuxFilterType *filter) { DVBDmx_t *dmx = (DVBDmx_t *)dev->drv_data; int fd = (long)filter->drv_data; SUBTITLE_LOGI("fd = %d, event_fd = %d", fd, dmx->event_fd); if (fd >= 0) { close(fd); dmx->fd[filter->id] = -1; } return AM_SUCCESS; } static int dvb_set_sec_filter(DemuxDeviceType *dev, DemuxFilterType *filter, const struct dmx_sct_filter_params *params) { struct dmx_sct_filter_params p; int fd = (long)filter->drv_data; int ret; UNUSED(dev); p = *params; /* if (p.filter.mask[0] == 0) { p.filter.filter[0] = 0x00; p.filter.mask[0] = 0x80; } */ ret = ioctl(fd, DMX_SET_FILTER, &p); if (ret == -1) { SUBTITLE_LOGE("set section filter failed (%s)", strerror(errno)); return DEMUX_ERROR_SYS; } return AM_SUCCESS; } static int dvb_set_pes_filter(DemuxDeviceType *dev, DemuxFilterType *filter, const struct dmx_pes_filter_params *params) { int fd = (long)filter->drv_data; int ret; UNUSED(dev); // TODO for coverity check_return ret = fcntl(fd, F_SETFL, O_NONBLOCK); if (ret == -1) { SUBTITLE_LOGE("fcntl failed (%s)", strerror(errno)); return DEMUX_ERROR_SYS; } ret = ioctl(fd, DMX_SET_PES_FILTER, params); if (ret == -1) { SUBTITLE_LOGE("set section filter failed (%s)", strerror(errno)); return DEMUX_ERROR_SYS; } return AM_SUCCESS; } static int dvb_enable_filter(DemuxDeviceType *dev, DemuxFilterType *filter, bool enable) { int fd = (long)filter->drv_data; int ret; UNUSED(dev); if (enable) { ret = ioctl(fd, DMX_START, 0); } else { ret = ioctl(fd, DMX_STOP, 0); } if (ret == -1) { SUBTITLE_LOGE("start filter failed (%s)", strerror(errno)); return DEMUX_ERROR_SYS; } return AM_SUCCESS; } static int dvb_set_buf_size(DemuxDeviceType *dev, DemuxFilterType *filter, int size) { int fd = (long)filter->drv_data; int ret; UNUSED(dev); ret = ioctl(fd, DMX_SET_BUFFER_SIZE, size); if (ret == -1) { SUBTITLE_LOGE("set buffer size failed (%s)", strerror(errno)); return DEMUX_ERROR_SYS; } return AM_SUCCESS; } static int dvb_poll(DemuxDeviceType *dev, Demux_FilterMaskType *mask, int timeout) { DVBDmx_t *dmx = (DVBDmx_t *)dev->drv_data; struct pollfd fds[DEMUX_FILTER_COUNT_EXT]; int fids[DEMUX_FILTER_COUNT_EXT] = {0}; int i, cnt = 0, ret; for (i = 0; i < DEMUX_FILTER_COUNT; i++) { if (dmx->fd[i] != -1) { fds[cnt].events = POLLIN | POLLERR; fds[cnt].fd = dmx->fd[i]; fids[cnt] = i; cnt++; } } // add notify event fd if (dmx->event_fd != -1) { fds[cnt].events = POLLIN | POLLERR; fds[cnt].fd = dmx->event_fd; cnt++; } if (!cnt) return DEMUX_ERROR_TIMEOUT; ret = poll(fds, cnt, timeout); if (ret <= 0) { return DEMUX_ERROR_TIMEOUT; } for (i = 0; i < cnt; i++) { if (fds[i].revents & (POLLIN | POLLERR)) { DEMUX_FILTER_MASK_SET(mask, fids[i]); if (fds[i].fd == dmx->event_fd) { int64_t data = 0; int event_fd = dmx->event_fd; if (event_fd != -1) { ret = read(event_fd, &data, sizeof(int64_t)); if (ret < 0 && errno == EBADF) { // Bad File Descriptor SUBTITLE_LOGW("event_fd was closed, ignoring read error"); } else { SUBTITLE_LOGI("dvb_poll_exit read:%d\n", ret); } } } } } return AM_SUCCESS; } static int dvb_poll_exit(DemuxDeviceType *dev) { SUBTITLE_LOGI_FUNC_IN(); DVBDmx_t *dmx = (DVBDmx_t *)dev->drv_data; int64_t pad = 1; ssize_t ret = write(dmx->event_fd, &pad, sizeof(int64_t)); if (ret != sizeof(int64_t)) { SUBTITLE_LOGE("%m"); } SUBTITLE_LOGI_FUNC_OUT(); return AM_SUCCESS; } static int dvb_read(DemuxDeviceType *dev, DemuxFilterType *filter, uint8_t *buf, int *size) { int fd = (long)filter->drv_data; int len = *size; int ret; struct pollfd pfd; UNUSED(dev); if (fd == -1) return DEMUX_ERROR_NOT_ALLOCATED; pfd.events = POLLIN | POLLERR; pfd.fd = fd; ret = poll(&pfd, 1, 0); if (ret <= 0) return DEMUX_ERROR_NO_DATA; ret = read(fd, buf, len); if (ret <= 0) { if (errno == ETIMEDOUT) return DEMUX_ERROR_TIMEOUT; SUBTITLE_LOGE("read demux failed (%s) %d", strerror(errno), errno); return DEMUX_ERROR_SYS; } *size = ret; return AM_SUCCESS; } static int dvb_set_source(DemuxDeviceType *dev, AmlogicDemuxSourceType src) { char buf[32]; char *cmd; snprintf(buf, sizeof(buf), "/sys/class/stb/demux%d_source", dev->dev_no); switch (src) { case DEMUX_SRC_TS0: cmd = "ts0"; break; case DEMUX_SRC_TS1: cmd = "ts1"; break; #if defined(CHIP_8226M) || defined(CHIP_8626X) case DEMUX_SRC_TS2: cmd = "ts2"; break; #endif case DEMUX_SRC_TS3: cmd = "ts3"; break; case DEMUX_SRC_HIU: cmd = "hiu"; break; case DEMUX_SRC_HIU1: cmd = "hiu1"; break; default: SUBTITLE_LOGE("do not support demux source %d", src); return DEMUX_ERROR_NOT_SUPPORTED; } return AM_SUCCESS; } static void dump_newest_pts(char *deviceName) { amdmx_filter_mem_info memInfo = {0}; int fd = open(deviceName, O_RDWR); if (fd == -1) { SUBTITLE_LOGE("[newest_avpts_tag] fail to open device %s: %m", deviceName); return; } SUBTITLE_LOGI("[newest_avpts_tag] device=%s, fd=%d", deviceName, fd); int ret = ioctl(fd, AMDMX_GET_FILTER_MEM_INFO, &memInfo); if (ret == -1) { SUBTITLE_LOGE( "[newest_avpts_tag] ioctl DMX_GET_FILTER_MEM_INFO failed," " fd=%d, error: %m", fd); } SUBTITLE_LOGI("[newest_avpts_tag] filter_num = %d", memInfo.filter_num); for (__u32 i = 0; i < memInfo.filter_num; ++i) { amfilter_mem_info info = memInfo.info[i]; SUBTITLE_LOGI("[newest_avpts_tag] [%2d] type = %d, pts = %lld", i, info.type, info.filter_info.newest_pts); } close(fd); } static int dvb_get_newest_pts(DemuxDeviceType *dev, amdmx_filter_mem_info *demuxFilterMemInfo) { if (!dev || !demuxFilterMemInfo || !dev->drv_data) { SUBTITLE_LOGE("[newest_avpts_tag] wrong input param"); return AM_FAILURE; } memset(demuxFilterMemInfo, 0, sizeof(*demuxFilterMemInfo)); DVBDmx_t *dmx = (DVBDmx_t *)dev->drv_data; if (!dmx) { SUBTITLE_LOGE("[newest_avpts_tag] no demux device"); return AM_FAILURE; } int fd = open(dmx->dev_name, O_RDWR); if (fd == -1) { SUBTITLE_LOGE("[newest_avpts_tag] fail to open device %s: %m", dmx->dev_name); return AM_FAILURE; } int ret = ioctl(fd, AMDMX_GET_FILTER_MEM_INFO, demuxFilterMemInfo); if (ret == -1) { SUBTITLE_LOGE( "[newest_avpts_tag] ioctl DMX_GET_FILTER_MEM_INFO failed," " device=%s fd=%d, error: %m", dmx->dev_name, fd); close(fd); return AM_FAILURE; } close(fd); return AM_SUCCESS; }