blob: a075ed9f4bf0af7d33fa8ab75de8172e4ab8be9a [file] [log] [blame]
/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/vmalloc.h>
#include <linux/dma-buf.h>
#include <linux/qcom_tspp.h>
#include "mpq_dvb_debug.h"
#include "mpq_dmx_plugin_common.h"
#define TSIF_COUNT 2
/* Max number of PID filters */
#define TSPP_MAX_PID_FILTER_NUM 128
/* Max number of user-defined HW PID filters */
#define TSPP_MAX_HW_PID_FILTER_NUM 15
/* HW index of the last entry in the TSPP HW filter table */
#define TSPP_LAST_HW_FILTER_INDEX 15
/* Number of filters required to accept all packets except NULL packets */
#define TSPP_BLOCK_NULLS_FILTERS_NUM 13
/* Max number of section filters */
#define TSPP_MAX_SECTION_FILTER_NUM 128
/* For each TSIF we use a single pipe holding the data after PID filtering */
#define TSPP_CHANNEL 0
/* the channel_id set to TSPP driver based on TSIF number and channel type */
#define TSPP_CHANNEL_ID(tsif, ch) ((tsif << 1) + ch)
#define TSPP_GET_TSIF_NUM(ch_id) (ch_id >> 1)
/* mask that set to care for all bits in pid filter */
#define TSPP_PID_MASK 0x1FFF
/* dvb-demux defines pid 0x2000 as full capture pid */
#define TSPP_PASS_THROUGH_PID 0x2000
/* NULL packets pid */
#define TSPP_NULL_PACKETS_PID 0x1FFF
#define TSPP_RAW_TTS_SIZE 192
#define TSPP_RAW_SIZE 188
#define MAX_BAM_DESCRIPTOR_SIZE (32 * 1024 - 1)
#define MAX_BAM_DESCRIPTOR_COUNT (8 * 1024 - 2)
#define TSPP_BUFFER_SIZE (500 * 1024) /* 500KB */
#define TSPP_DEFAULT_DESCRIPTOR_SIZE (TSPP_RAW_TTS_SIZE)
#define TSPP_BUFFER_COUNT(buffer_size) \
((buffer_size) / tspp_desc_size)
/* When TSPP notifies demux that new packets are received.
* Using max descriptor size (170 packets).
* Assuming 20MBit/sec stream, with 170 packets
* per descriptor there would be about 82 descriptors,
* Meaning about 82 notifications per second.
*/
#define TSPP_NOTIFICATION_SIZE(desc_size) \
(MAX_BAM_DESCRIPTOR_SIZE / (desc_size))
/* Channel timeout in msec */
#define TSPP_CHANNEL_TIMEOUT 100
enum mem_buffer_allocation_mode {
MPQ_DMX_TSPP_INTERNAL_ALLOC = 0,
MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC = 1
};
/* module parameters for load time configuration */
static int allocation_mode = MPQ_DMX_TSPP_INTERNAL_ALLOC;
static int tspp_out_buffer_size = TSPP_BUFFER_SIZE;
static int tspp_desc_size = TSPP_DEFAULT_DESCRIPTOR_SIZE;
static int tspp_notification_size =
TSPP_NOTIFICATION_SIZE(TSPP_DEFAULT_DESCRIPTOR_SIZE);
static int tspp_channel_timeout = TSPP_CHANNEL_TIMEOUT;
static int tspp_out_ion_heap = ION_QSECOM_HEAP_ID;
module_param(allocation_mode, int, 0644);
module_param(tspp_out_buffer_size, int, 0644);
module_param(tspp_desc_size, int, 0644);
module_param(tspp_notification_size, int, 0644);
module_param(tspp_channel_timeout, int, 0644);
module_param(tspp_out_ion_heap, int, 0644);
/* The following structure hold singleton information
* required for dmx implementation on top of TSPP.
*/
static struct
{
/* Information for each TSIF input processing */
struct {
/*
* TSPP pipe holding all TS packets after PID filtering.
* The following is reference count for number of feeds
* allocated on that pipe.
*/
int channel_ref;
/* Counter for data notifications on the pipe */
atomic_t data_cnt;
/* flag to indicate control operation is in progress */
atomic_t control_op;
/* ION handle used for TSPP data buffer allocation */
struct ion_handle *ch_mem_heap_handle;
/* TSPP data buffer heap virtual base address */
void *ch_mem_heap_virt_base;
/* TSPP data buffer heap physical base address */
ion_phys_addr_t ch_mem_heap_phys_base;
/* Buffer allocation index */
int buff_index;
/* Number of buffers */
u32 buffer_count;
/*
* Array holding the IDs of the TSPP buffer descriptors in the
* current aggregate, in order to release these descriptors at
* the end of processing.
*/
int *aggregate_ids;
/*
* Holds PIDs of allocated filters along with
* how many feeds are opened on the same PID. For
* TSPP HW filters, holds also the filter table index.
* When pid == -1, the entry is free.
*/
struct {
int pid;
int ref_count;
int hw_index;
} filters[TSPP_MAX_PID_FILTER_NUM];
/* Indicates available/allocated filter table indexes */
int hw_indexes[TSPP_MAX_HW_PID_FILTER_NUM];
/* Number of currently allocated PID filters */
u16 current_filter_count;
/*
* Flag to indicate whether the user added a filter to accept
* NULL packets (PID = 0x1FFF)
*/
int pass_nulls_flag;
/*
* Flag to indicate whether the user added a filter to accept
* all packets (PID = 0x2000)
*/
int pass_all_flag;
/*
* Flag to indicate whether the filter that accepts
* all packets has already been added and is
* currently enabled
*/
int accept_all_filter_exists_flag;
/* Thread processing TS packets from TSPP */
struct task_struct *thread;
wait_queue_head_t wait_queue;
/* TSIF alias */
char name[TSIF_NAME_LENGTH];
/* Pointer to the demux connected to this TSIF */
struct mpq_demux *mpq_demux;
/* Mutex protecting the data-structure */
struct mutex mutex;
/* ion dma buffer mapping structure */
struct tspp_ion_dma_buf_info ch_ion_dma_buf;
} tsif[TSIF_COUNT];
/* ION client used for TSPP data buffer allocation */
struct ion_client *ion_client;
} mpq_dmx_tspp_info;
static void *tspp_mem_allocator(int channel_id, u32 size,
phys_addr_t *phys_base, dma_addr_t *dma_base,
void *user)
{
void *virt_addr = NULL;
int i = TSPP_GET_TSIF_NUM(channel_id);
if (mpq_dmx_tspp_info.tsif[i].buff_index ==
mpq_dmx_tspp_info.tsif[i].buffer_count)
return NULL;
virt_addr =
(mpq_dmx_tspp_info.tsif[i].ch_mem_heap_virt_base +
(mpq_dmx_tspp_info.tsif[i].buff_index * size));
*phys_base =
(mpq_dmx_tspp_info.tsif[i].ch_mem_heap_phys_base +
(mpq_dmx_tspp_info.tsif[i].buff_index * size));
*dma_base =
(mpq_dmx_tspp_info.tsif[i].ch_ion_dma_buf.dma_map_base +
(mpq_dmx_tspp_info.tsif[i].buff_index * size));
mpq_dmx_tspp_info.tsif[i].buff_index++;
return virt_addr;
}
static void tspp_mem_free(int channel_id, u32 size,
void *virt_base, phys_addr_t phys_base, void *user)
{
int i = TSPP_GET_TSIF_NUM(channel_id);
/*
* actual buffer heap free is done in mpq_dmx_tspp_plugin_exit().
* we update index here, so if this function is called repetitively
* for all the buffers, then afterwards tspp_mem_allocator()
* can be called again.
* Note: it would be incorrect to call tspp_mem_allocator()
* a few times, then call tspp_mem_free(), then call
* tspp_mem_allocator() again.
*/
if (mpq_dmx_tspp_info.tsif[i].buff_index > 0)
mpq_dmx_tspp_info.tsif[i].buff_index--;
}
/**
* Returns a free HW filter index that can be used.
*
* @tsif: The TSIF to allocate filter from
*
* Return HW filter index or -ENOMEM if no filters available
*/
static int mpq_tspp_allocate_hw_filter_index(int tsif)
{
int i;
for (i = 0; i < TSPP_MAX_HW_PID_FILTER_NUM; i++) {
if (mpq_dmx_tspp_info.tsif[tsif].hw_indexes[i] == 0) {
mpq_dmx_tspp_info.tsif[tsif].hw_indexes[i] = 1;
return i;
}
}
return -ENOMEM;
}
/**
* Releases a HW filter index for future reuse.
*
* @tsif: The TSIF from which the filter should be released
* @hw_index: The HW index to release
*
*/
static inline void mpq_tspp_release_hw_filter_index(int tsif, int hw_index)
{
if ((hw_index >= 0) && (hw_index < TSPP_MAX_HW_PID_FILTER_NUM))
mpq_dmx_tspp_info.tsif[tsif].hw_indexes[hw_index] = 0;
}
/**
* Returns a free filter slot that can be used.
*
* @tsif: The TSIF to allocate filter from
*
* Return filter index or -ENOMEM if no filters available
*/
static int mpq_tspp_get_free_filter_slot(int tsif)
{
int slot;
for (slot = 0; slot < TSPP_MAX_PID_FILTER_NUM; slot++)
if (mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid == -1)
return slot;
return -ENOMEM;
}
/**
* Returns filter index of specific pid.
*
* @tsif: The TSIF to which the pid is allocated
* @pid: The pid to search for
*
* Return filter index or -1 if no filter available
*/
static int mpq_tspp_get_filter_slot(int tsif, int pid)
{
int slot;
for (slot = 0; slot < TSPP_MAX_PID_FILTER_NUM; slot++)
if (mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid == pid)
return slot;
return -EINVAL;
}
/**
* mpq_dmx_tspp_swfilter_desc - helper function
*
* Takes a tspp buffer descriptor and send it to the SW filter for demuxing,
* one TS packet at a time.
*
* @mpq_demux - mpq demux object
* @tspp_data_desc - tspp buffer descriptor
*/
static inline void mpq_dmx_tspp_swfilter_desc(struct mpq_demux *mpq_demux,
const struct tspp_data_descriptor *tspp_data_desc)
{
u32 notif_size;
int i;
notif_size = tspp_data_desc->size / TSPP_RAW_TTS_SIZE;
for (i = 0; i < notif_size; i++)
dvb_dmx_swfilter_packet(&mpq_demux->demux,
((u8 *)tspp_data_desc->virt_base) +
i * TSPP_RAW_TTS_SIZE,
((u8 *)tspp_data_desc->virt_base) +
i * TSPP_RAW_TTS_SIZE + TSPP_RAW_SIZE);
}
/**
* Demux TS packets from TSPP by secure-demux.
* The function assumes the buffer is physically contiguous
* and that TSPP descriptors are continuous in memory.
*
* @tsif: The TSIF interface to process its packets
* @channel_id: the TSPP output pipe with the TS packets
*/
static void mpq_dmx_tspp_aggregated_process(int tsif, int channel_id)
{
const struct tspp_data_descriptor *tspp_data_desc;
struct mpq_demux *mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
struct sdmx_buff_descr input;
size_t aggregate_len = 0;
size_t aggregate_count = 0;
phys_addr_t buff_start_addr_phys;
phys_addr_t buff_current_addr_phys = 0;
u32 notif_size;
int i;
while ((tspp_data_desc = tspp_get_buffer(0, channel_id)) != NULL) {
if (aggregate_count == 0)
buff_current_addr_phys = tspp_data_desc->phys_base;
notif_size = tspp_data_desc->size / TSPP_RAW_TTS_SIZE;
mpq_dmx_tspp_info.tsif[tsif].aggregate_ids[aggregate_count] =
tspp_data_desc->id;
aggregate_len += tspp_data_desc->size;
aggregate_count++;
mpq_demux->hw_notification_size += notif_size;
/* Let SW filter process only if it might be relevant */
if (mpq_demux->num_active_feeds > mpq_demux->num_secure_feeds)
mpq_dmx_tspp_swfilter_desc(mpq_demux, tspp_data_desc);
}
if (!aggregate_count)
return;
buff_start_addr_phys =
mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_phys_base;
input.base_addr = (u64)buff_start_addr_phys;
input.size = mpq_dmx_tspp_info.tsif[tsif].buffer_count * tspp_desc_size;
if (mpq_sdmx_is_loaded() && mpq_demux->sdmx_filter_count) {
MPQ_DVB_DBG_PRINT(
"%s: SDMX Processing %zu descriptors: %zu bytes at start address 0x%llx, read offset %d\n",
__func__, aggregate_count, aggregate_len,
input.base_addr,
(int)(buff_current_addr_phys - buff_start_addr_phys));
mpq_sdmx_process(mpq_demux, &input, aggregate_len,
buff_current_addr_phys - buff_start_addr_phys,
TSPP_RAW_TTS_SIZE);
}
for (i = 0; i < aggregate_count; i++)
tspp_release_buffer(0, channel_id,
mpq_dmx_tspp_info.tsif[tsif].aggregate_ids[i]);
}
/**
* Demux thread function handling data from specific TSIF.
*
* @arg: TSIF number
*/
static int mpq_dmx_tspp_thread(void *arg)
{
int tsif = (int)(uintptr_t)arg;
struct mpq_demux *mpq_demux;
const struct tspp_data_descriptor *tspp_data_desc;
atomic_t *data_cnt;
u32 notif_size;
int channel_id;
int ref_count;
int ret;
do {
ret = wait_event_interruptible(
mpq_dmx_tspp_info.tsif[tsif].wait_queue,
(atomic_read(&mpq_dmx_tspp_info.tsif[tsif].data_cnt) &&
!atomic_read(&mpq_dmx_tspp_info.tsif[tsif].control_op))
|| kthread_should_stop());
if ((ret < 0) || kthread_should_stop()) {
MPQ_DVB_ERR_PRINT("%s: exit\n", __func__);
break;
}
/* Lock against the TSPP filters data-structure */
if (mutex_lock_interruptible(
&mpq_dmx_tspp_info.tsif[tsif].mutex))
return -ERESTARTSYS;
channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL);
ref_count = mpq_dmx_tspp_info.tsif[tsif].channel_ref;
data_cnt = &mpq_dmx_tspp_info.tsif[tsif].data_cnt;
/* Make sure channel is still active */
if (ref_count == 0) {
mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
continue;
}
atomic_dec(data_cnt);
mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
mpq_demux->hw_notification_size = 0;
if (allocation_mode != MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC &&
mpq_sdmx_is_loaded())
pr_err_once(
"%s: TSPP Allocation mode does not support secure demux.\n",
__func__);
if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC &&
mpq_sdmx_is_loaded()) {
mpq_dmx_tspp_aggregated_process(tsif, channel_id);
} else {
/*
* Go through all filled descriptors
* and perform demuxing on them
*/
do {
if (atomic_read(&mpq_dmx_tspp_info.tsif[tsif].
control_op)) {
/* restore for next iteration */
atomic_inc(data_cnt);
break;
}
tspp_data_desc = tspp_get_buffer(0, channel_id);
if (!tspp_data_desc)
break;
notif_size = tspp_data_desc->size /
TSPP_RAW_TTS_SIZE;
mpq_demux->hw_notification_size += notif_size;
mpq_dmx_tspp_swfilter_desc(mpq_demux,
tspp_data_desc);
/*
* Notify TSPP that the buffer
* is no longer needed
*/
tspp_release_buffer(0, channel_id,
tspp_data_desc->id);
} while (1);
}
if (mpq_demux->hw_notification_size &&
(mpq_demux->hw_notification_size <
mpq_demux->hw_notification_min_size))
mpq_demux->hw_notification_min_size =
mpq_demux->hw_notification_size;
mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
} while (1);
return 0;
}
/**
* Callback function from TSPP when new data is ready.
*
* @channel_id: Channel with new TS packets
* @user: user-data holding TSIF number
*/
static void mpq_tspp_callback(int channel_id, void *user)
{
int tsif = (int)(uintptr_t)user;
struct mpq_demux *mpq_demux;
/* Save statistics on TSPP notifications */
mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
mpq_dmx_update_hw_statistics(mpq_demux);
atomic_inc(&mpq_dmx_tspp_info.tsif[tsif].data_cnt);
wake_up(&mpq_dmx_tspp_info.tsif[tsif].wait_queue);
}
/**
* Free memory of channel output of specific TSIF.
*
* @tsif: The TSIF id to which memory should be freed.
*/
static void mpq_dmx_channel_mem_free(int tsif)
{
MPQ_DVB_DBG_PRINT("%s(%d)\n", __func__, tsif);
mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_phys_base = 0;
if (!IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle)) {
if (!IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[tsif].
ch_mem_heap_virt_base))
ion_unmap_kernel(mpq_dmx_tspp_info.ion_client,
mpq_dmx_tspp_info.tsif[tsif].
ch_mem_heap_handle);
ion_free(mpq_dmx_tspp_info.ion_client,
mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle);
}
mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_virt_base = NULL;
mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle = NULL;
tspp_detach_ion_dma_buff(0,
&mpq_dmx_tspp_info.tsif[tsif].ch_ion_dma_buf);
}
/**
* Allocate memory for channel output of specific TSIF.
*
* @tsif: The TSIF id to which memory should be allocated.
*
* Return error status
*/
static int mpq_dmx_channel_mem_alloc(int tsif)
{
int result;
size_t len;
MPQ_DVB_DBG_PRINT("%s(%d)\n", __func__, tsif);
mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle =
ion_alloc(mpq_dmx_tspp_info.ion_client,
(mpq_dmx_tspp_info.tsif[tsif].buffer_count * tspp_desc_size),
SZ_4K,
ION_HEAP(tspp_out_ion_heap),
0); /* non-cached */
if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle)) {
MPQ_DVB_ERR_PRINT("%s: ion_alloc() failed\n", __func__);
mpq_dmx_channel_mem_free(tsif);
return -ENOMEM;
}
/* save virtual base address of heap */
mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_virt_base =
ion_map_kernel(mpq_dmx_tspp_info.ion_client,
mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle);
if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[tsif].
ch_mem_heap_virt_base)) {
MPQ_DVB_ERR_PRINT("%s: ion_map_kernel() failed\n", __func__);
mpq_dmx_channel_mem_free(tsif);
return -ENOMEM;
}
/* save physical base address of heap */
result = ion_phys(mpq_dmx_tspp_info.ion_client,
mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle,
&(mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_phys_base), &len);
if (result < 0) {
MPQ_DVB_ERR_PRINT("%s: ion_phys() failed\n", __func__);
mpq_dmx_channel_mem_free(tsif);
return -ENOMEM;
}
mpq_dmx_tspp_info.tsif[tsif].ch_ion_dma_buf.dbuf = ion_share_dma_buf(
mpq_dmx_tspp_info.ion_client,
mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle);
if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[tsif].ch_ion_dma_buf.dbuf)) {
MPQ_DVB_ERR_PRINT("%s: ion_share_dma_buf failed\n", __func__);
mpq_dmx_channel_mem_free(tsif);
return -ENOMEM;
}
result = tspp_attach_ion_dma_buff(0,
&mpq_dmx_tspp_info.tsif[tsif].ch_ion_dma_buf);
if (result) {
MPQ_DVB_ERR_PRINT("%s: tspp_attach_ion_dma_buff failed\n",
__func__);
mpq_dmx_channel_mem_free(tsif);
return result;
}
return 0;
}
/**
* Add a filter to accept all packets as the last entry
* of the TSPP HW filter table.
*
* @channel_id: Channel ID number.
* @source: TSPP source.
*
* Return error status
*/
static int mpq_tspp_add_accept_all_filter(int channel_id,
enum tspp_source source)
{
struct tspp_filter tspp_filter;
int tsif = TSPP_GET_TSIF_NUM(channel_id);
int ret;
MPQ_DVB_DBG_PRINT("%s: executed, channel id = %d, source = %d\n",
__func__, channel_id, source);
if (mpq_dmx_tspp_info.tsif[tsif].accept_all_filter_exists_flag) {
MPQ_DVB_DBG_PRINT("%s: accept all filter already exists\n",
__func__);
return 0;
}
/* This filter will be the last entry in the table */
tspp_filter.priority = TSPP_LAST_HW_FILTER_INDEX;
/* Pass all pids - set mask to 0 */
tspp_filter.pid = 0;
tspp_filter.mask = 0;
/*
* Include TTS in RAW packets, if you change this to
* TSPP_MODE_RAW_NO_SUFFIX you must also change TSPP_RAW_TTS_SIZE
* accordingly.
*/
tspp_filter.mode = TSPP_MODE_RAW;
tspp_filter.source = source;
tspp_filter.decrypt = 0;
ret = tspp_add_filter(0, channel_id, &tspp_filter);
if (!ret) {
mpq_dmx_tspp_info.tsif[tsif].accept_all_filter_exists_flag = 1;
MPQ_DVB_DBG_PRINT(
"%s: accept all filter added successfully\n",
__func__);
}
return ret;
}
/**
* Remove the filter that accepts all packets from the last entry
* of the TSPP HW filter table.
*
* @channel_id: Channel ID number.
* @source: TSPP source.
*
* Return error status
*/
static int mpq_tspp_remove_accept_all_filter(int channel_id,
enum tspp_source source)
{
struct tspp_filter tspp_filter;
int tsif = TSPP_GET_TSIF_NUM(channel_id);
int ret;
MPQ_DVB_DBG_PRINT("%s: executed, channel id = %d, source = %d\n",
__func__, channel_id, source);
if (mpq_dmx_tspp_info.tsif[tsif].accept_all_filter_exists_flag == 0) {
MPQ_DVB_DBG_PRINT("%s: accept all filter doesn't exist\n",
__func__);
return 0;
}
tspp_filter.priority = TSPP_LAST_HW_FILTER_INDEX;
ret = tspp_remove_filter(0, channel_id, &tspp_filter);
if (!ret) {
mpq_dmx_tspp_info.tsif[tsif].accept_all_filter_exists_flag = 0;
MPQ_DVB_DBG_PRINT(
"%s: accept all filter removed successfully\n",
__func__);
}
return ret;
}
/**
* Add filters designed to accept all packets except NULL packets, i.e.
* packets with PID = 0x1FFF.
* This function is called after user-defined filters were removed,
* so it assumes that the first 13 HW filters in the TSPP filter
* table are free for use.
*
* @channel_id: Channel ID number.
* @source: TSPP source.
*
* Return 0 on success, -1 otherwise
*/
static int mpq_tspp_add_null_blocking_filters(int channel_id,
enum tspp_source source)
{
struct tspp_filter tspp_filter;
int ret = 0;
int i, j;
u16 full_pid_mask = 0x1FFF;
u8 mask_shift;
u8 pid_shift;
int tsif = TSPP_GET_TSIF_NUM(channel_id);
MPQ_DVB_DBG_PRINT("%s: executed, channel id = %d, source = %d\n",
__func__, channel_id, source);
/*
* Add a total of 13 filters that will accept packets with
* every PID other than 0x1FFF, which is the NULL PID.
*
* Filter 0: accept all PIDs with bit 12 clear, i.e.
* PID = 0x0000 .. 0x0FFF (4096 PIDs in total):
* Mask = 0x1000, PID = 0x0000.
*
* Filter 12: Accept PID 0x1FFE:
* Mask = 0x1FFF, PID = 0x1FFE.
*
* In general: For N = 0 .. 12,
* Filter <N>: accept all PIDs with <N> MSBits set and bit <N-1> clear.
* Filter <N> Mask = N+1 MSBits set, others clear.
* Filter <N> PID = <N> MSBits set, others clear.
*/
/*
* Include TTS in RAW packets, if you change this to
* TSPP_MODE_RAW_NO_SUFFIX you must also change TSPP_RAW_TTS_SIZE
* accordingly.
*/
tspp_filter.mode = TSPP_MODE_RAW;
tspp_filter.source = source;
tspp_filter.decrypt = 0;
for (i = 0; i < TSPP_BLOCK_NULLS_FILTERS_NUM; i++) {
tspp_filter.priority = mpq_tspp_allocate_hw_filter_index(tsif);
if (tspp_filter.priority != i) {
MPQ_DVB_ERR_PRINT(
"%s: got unexpected HW index %d, expected %d\n",
__func__, tspp_filter.priority, i);
ret = -1;
break;
}
mask_shift = (TSPP_BLOCK_NULLS_FILTERS_NUM - 1 - i);
pid_shift = (TSPP_BLOCK_NULLS_FILTERS_NUM - i);
tspp_filter.mask =
((full_pid_mask >> mask_shift) << mask_shift);
tspp_filter.pid = ((full_pid_mask >> pid_shift) << pid_shift);
if (tspp_add_filter(0, channel_id, &tspp_filter)) {
ret = -1;
break;
}
}
if (ret) {
/* cleanup on failure */
for (j = 0; j < i; j++) {
tspp_filter.priority = j;
mpq_tspp_release_hw_filter_index(tsif, j);
tspp_remove_filter(0, channel_id, &tspp_filter);
}
} else {
MPQ_DVB_DBG_PRINT(
"%s: NULL blocking filters added successfully\n",
__func__);
}
return ret;
}
/**
* Remove filters designed to accept all packets except NULL packets, i.e.
* packets with PID = 0x1FFF.
*
* @channel_id: Channel ID number.
*
* @source: TSPP source.
*
* Return 0 on success, -1 otherwise
*/
static int mpq_tspp_remove_null_blocking_filters(int channel_id,
enum tspp_source source)
{
struct tspp_filter tspp_filter;
int tsif = TSPP_GET_TSIF_NUM(channel_id);
int ret = 0;
int i;
MPQ_DVB_DBG_PRINT("%s: executed, channel id = %d, source = %d\n",
__func__, channel_id, source);
for (i = 0; i < TSPP_BLOCK_NULLS_FILTERS_NUM; i++) {
tspp_filter.priority = i;
if (tspp_remove_filter(0, channel_id, &tspp_filter)) {
MPQ_DVB_ERR_PRINT("%s: failed to remove filter %d\n",
__func__, i);
ret = -1;
}
mpq_tspp_release_hw_filter_index(tsif, i);
}
return ret;
}
/**
* Add all current user-defined filters (up to 15) as HW filters
*
* @channel_id: Channel ID number.
*
* @source: TSPP source.
*
* Return 0 on success, -1 otherwise
*/
static int mpq_tspp_add_all_user_filters(int channel_id,
enum tspp_source source)
{
struct tspp_filter tspp_filter;
int tsif = TSPP_GET_TSIF_NUM(channel_id);
int slot;
u16 added_count = 0;
u16 total_filters_count = 0;
MPQ_DVB_DBG_PRINT("%s: executed\n", __func__);
/*
* Include TTS in RAW packets, if you change this to
* TSPP_MODE_RAW_NO_SUFFIX you must also change TSPP_RAW_TTS_SIZE
* accordingly.
*/
tspp_filter.mode = TSPP_MODE_RAW;
tspp_filter.source = source;
tspp_filter.decrypt = 0;
for (slot = 0; slot < TSPP_MAX_PID_FILTER_NUM; slot++) {
if (mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid == -1)
continue;
/*
* count total number of user filters to verify that it is
* exactly TSPP_MAX_HW_PID_FILTER_NUM as expected.
*/
total_filters_count++;
if (added_count > TSPP_MAX_HW_PID_FILTER_NUM)
continue;
tspp_filter.priority = mpq_tspp_allocate_hw_filter_index(tsif);
if (mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid ==
TSPP_PASS_THROUGH_PID) {
/* pass all pids */
tspp_filter.pid = 0;
tspp_filter.mask = 0;
} else {
tspp_filter.pid =
mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid;
tspp_filter.mask = TSPP_PID_MASK;
}
MPQ_DVB_DBG_PRINT(
"%s: adding HW filter, PID = %d, mask = 0x%X, index = %d\n",
__func__, tspp_filter.pid, tspp_filter.mask,
tspp_filter.priority);
if (!tspp_add_filter(0, channel_id, &tspp_filter)) {
mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index =
tspp_filter.priority;
added_count++;
} else {
MPQ_DVB_ERR_PRINT("%s: tspp_add_filter failed\n",
__func__);
}
}
if ((added_count != TSPP_MAX_HW_PID_FILTER_NUM) ||
(added_count != total_filters_count))
return -EINVAL;
return 0;
}
/**
* Remove all user-defined HW filters
*
* @channel_id: Channel ID number.
*
* @source: TSPP source.
*
* Return 0 on success, -1 otherwise
*/
static int mpq_tspp_remove_all_user_filters(int channel_id,
enum tspp_source source)
{
struct tspp_filter tspp_filter;
int ret = 0;
int tsif = TSPP_GET_TSIF_NUM(channel_id);
int i;
MPQ_DVB_DBG_PRINT("%s: executed\n", __func__);
for (i = 0; i < TSPP_MAX_HW_PID_FILTER_NUM; i++) {
tspp_filter.priority = i;
MPQ_DVB_DBG_PRINT("%s: Removing HW filter %d\n",
__func__, tspp_filter.priority);
if (tspp_remove_filter(0, channel_id, &tspp_filter))
ret = -1;
mpq_tspp_release_hw_filter_index(tsif, i);
mpq_dmx_tspp_info.tsif[tsif].filters[i].hw_index = -1;
}
return ret;
}
/**
* Configure TSPP channel to filter the PID of new feed.
*
* @feed: The feed to configure the channel with
*
* Return error status
*
* The function checks if the new PID can be added to an already
* allocated channel, if not, a new channel is allocated and configured.
*/
static int mpq_tspp_dmx_add_channel(struct dvb_demux_feed *feed)
{
struct mpq_demux *mpq_demux = feed->demux->priv;
struct tspp_select_source tspp_source;
struct tspp_filter tspp_filter;
int tsif;
int tsif_mode = mpq_dmx_get_param_tsif_mode();
int ret = 0;
int slot;
int channel_id;
int *channel_ref_count;
u32 buffer_size;
int restore_user_filters = 0;
int remove_accept_all_filter = 0;
int remove_null_blocking_filters = 0;
size_t agg_size;
tspp_source.clk_inverse = mpq_dmx_get_param_clock_inv();
tspp_source.data_inverse = 0;
tspp_source.sync_inverse = 0;
tspp_source.enable_inverse = 0;
MPQ_DVB_DBG_PRINT("%s: executed, PID = %d\n", __func__, feed->pid);
switch (tsif_mode) {
case 1:
tspp_source.mode = TSPP_TSIF_MODE_1;
break;
case 2:
tspp_source.mode = TSPP_TSIF_MODE_2;
break;
default:
tspp_source.mode = TSPP_TSIF_MODE_LOOPBACK;
break;
}
/* determine the TSIF we are reading from */
if (mpq_demux->source == DMX_SOURCE_FRONT0) {
tsif = 0;
tspp_source.source = TSPP_SOURCE_TSIF0;
} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
tsif = 1;
tspp_source.source = TSPP_SOURCE_TSIF1;
} else {
/* invalid source */
MPQ_DVB_ERR_PRINT(
"%s: invalid input source (%d)\n",
__func__,
mpq_demux->source);
return -EINVAL;
}
atomic_inc(&mpq_dmx_tspp_info.tsif[tsif].control_op);
if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex)) {
atomic_dec(&mpq_dmx_tspp_info.tsif[tsif].control_op);
return -ERESTARTSYS;
}
/*
* It is possible that this PID was already requested before.
* Can happen if we play and record same PES or PCR
* piggypacked on video packet.
*/
slot = mpq_tspp_get_filter_slot(tsif, feed->pid);
if (slot >= 0) {
/* PID already configured */
mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count++;
goto out;
}
channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL);
channel_ref_count = &mpq_dmx_tspp_info.tsif[tsif].channel_ref;
/*
* Recalculate 'tspp_notification_size' and buffer count in case
* 'tspp_desc_size' or 'tspp_out_buffer_size' parameters have changed.
*/
buffer_size = tspp_desc_size;
tspp_notification_size = TSPP_NOTIFICATION_SIZE(tspp_desc_size);
mpq_dmx_tspp_info.tsif[tsif].buffer_count =
TSPP_BUFFER_COUNT(tspp_out_buffer_size);
if (mpq_dmx_tspp_info.tsif[tsif].buffer_count >
MAX_BAM_DESCRIPTOR_COUNT)
mpq_dmx_tspp_info.tsif[tsif].buffer_count =
MAX_BAM_DESCRIPTOR_COUNT;
/* check if required TSPP pipe is already allocated or not */
if (*channel_ref_count == 0) {
if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
agg_size = mpq_dmx_tspp_info.tsif[tsif].buffer_count *
sizeof(int);
mpq_dmx_tspp_info.tsif[tsif].aggregate_ids =
vzalloc(agg_size);
if (!mpq_dmx_tspp_info.tsif[tsif].aggregate_ids) {
MPQ_DVB_ERR_PRINT(
"%s: Failed to allocate memory for buffer descriptors aggregation\n",
__func__);
ret = -ENOMEM;
goto out;
}
ret = mpq_dmx_channel_mem_alloc(tsif);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_dmx_channel_mem_alloc(%d) failed (%d)\n",
__func__,
channel_id,
ret);
goto add_channel_failed;
}
}
ret = tspp_open_channel(0, channel_id);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: tspp_open_channel(%d) failed (%d)\n",
__func__,
channel_id,
ret);
goto add_channel_failed;
}
/* set TSPP source */
ret = tspp_open_stream(0, channel_id, &tspp_source);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: tspp_select_source(%d,%d) failed (%d)\n",
__func__,
channel_id,
tspp_source.source,
ret);
goto add_channel_close_ch;
}
/* register notification on TS packets */
tspp_register_notification(0,
channel_id,
mpq_tspp_callback,
(void *)(uintptr_t)tsif,
tspp_channel_timeout);
/*
* Register allocator and provide allocation function
* that allocates from contiguous memory so that we can have
* big notification size, smallest descriptor, and still provide
* TZ with single big buffer based on notification size.
*/
if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
ret = tspp_allocate_buffers(0, channel_id,
mpq_dmx_tspp_info.tsif[tsif].buffer_count,
buffer_size, tspp_notification_size,
tspp_mem_allocator, tspp_mem_free, NULL);
} else {
ret = tspp_allocate_buffers(0, channel_id,
mpq_dmx_tspp_info.tsif[tsif].buffer_count,
buffer_size, tspp_notification_size,
NULL, NULL, NULL);
}
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: tspp_allocate_buffers(%d) failed (%d)\n",
__func__,
channel_id,
ret);
goto add_channel_unregister_notif;
}
mpq_dmx_tspp_info.tsif[tsif].mpq_demux = mpq_demux;
}
/* add new PID to the existing pipe */
slot = mpq_tspp_get_free_filter_slot(tsif);
if (slot < 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_tspp_get_free_filter_slot(%d) failed\n",
__func__, tsif);
goto add_channel_unregister_notif;
}
if (feed->pid == TSPP_PASS_THROUGH_PID)
mpq_dmx_tspp_info.tsif[tsif].pass_all_flag = 1;
else if (feed->pid == TSPP_NULL_PACKETS_PID)
mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag = 1;
mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid = feed->pid;
mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count++;
tspp_filter.priority = -1;
if (mpq_dmx_tspp_info.tsif[tsif].current_filter_count <
TSPP_MAX_HW_PID_FILTER_NUM) {
/* HW filtering mode */
tspp_filter.priority = mpq_tspp_allocate_hw_filter_index(tsif);
if (tspp_filter.priority < 0)
goto add_channel_free_filter_slot;
if (feed->pid == TSPP_PASS_THROUGH_PID) {
/* pass all pids */
tspp_filter.pid = 0;
tspp_filter.mask = 0;
} else {
tspp_filter.pid = feed->pid;
tspp_filter.mask = TSPP_PID_MASK;
}
/*
* Include TTS in RAW packets, if you change this to
* TSPP_MODE_RAW_NO_SUFFIX you must also change
* TSPP_RAW_TTS_SIZE accordingly.
*/
tspp_filter.mode = TSPP_MODE_RAW;
tspp_filter.source = tspp_source.source;
tspp_filter.decrypt = 0;
ret = tspp_add_filter(0, channel_id, &tspp_filter);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: tspp_add_filter(%d) failed (%d)\n",
__func__,
channel_id,
ret);
goto add_channel_free_filter_slot;
}
mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index =
tspp_filter.priority;
MPQ_DVB_DBG_PRINT(
"%s: HW filtering mode: added TSPP HW filter, PID = %d, mask = 0x%X, index = %d\n",
__func__, tspp_filter.pid, tspp_filter.mask,
tspp_filter.priority);
} else if (mpq_dmx_tspp_info.tsif[tsif].current_filter_count ==
TSPP_MAX_HW_PID_FILTER_NUM) {
/* Crossing the threshold - from HW to SW filtering mode */
/* Add a temporary filter to accept all packets */
ret = mpq_tspp_add_accept_all_filter(channel_id,
tspp_source.source);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_tspp_add_accept_all_filter(%d, %d) failed\n",
__func__, channel_id, tspp_source.source);
goto add_channel_free_filter_slot;
}
/* Remove all existing user filters */
ret = mpq_tspp_remove_all_user_filters(channel_id,
tspp_source.source);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_tspp_remove_all_user_filters(%d, %d) failed\n",
__func__, channel_id, tspp_source.source);
restore_user_filters = 1;
remove_accept_all_filter = 1;
goto add_channel_free_filter_slot;
}
/* Add HW filters to block NULL packets */
ret = mpq_tspp_add_null_blocking_filters(channel_id,
tspp_source.source);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_tspp_add_null_blocking_filters(%d, %d) failed\n",
__func__, channel_id, tspp_source.source);
restore_user_filters = 1;
remove_accept_all_filter = 1;
goto add_channel_free_filter_slot;
}
/* Remove filters that accepts all packets, if necessary */
if ((mpq_dmx_tspp_info.tsif[tsif].pass_all_flag == 0) &&
(mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag == 0)) {
ret = mpq_tspp_remove_accept_all_filter(channel_id,
tspp_source.source);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_tspp_remove_accept_all_filter(%d, %d) failed\n",
__func__, channel_id,
tspp_source.source);
remove_null_blocking_filters = 1;
restore_user_filters = 1;
remove_accept_all_filter = 1;
goto add_channel_free_filter_slot;
}
}
} else {
/* Already working in SW filtering mode */
if (mpq_dmx_tspp_info.tsif[tsif].pass_all_flag ||
mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag) {
ret = mpq_tspp_add_accept_all_filter(channel_id,
tspp_source.source);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_tspp_add_accept_all_filter(%d, %d) failed\n",
__func__, channel_id,
tspp_source.source);
goto add_channel_free_filter_slot;
}
}
}
(*channel_ref_count)++;
mpq_dmx_tspp_info.tsif[tsif].current_filter_count++;
MPQ_DVB_DBG_PRINT("%s: success, current_filter_count = %d\n",
__func__, mpq_dmx_tspp_info.tsif[tsif].current_filter_count);
goto out;
add_channel_free_filter_slot:
/* restore internal database state */
mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid = -1;
mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count--;
/* release HW index if we allocated one */
if (tspp_filter.priority >= 0) {
mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index = -1;
mpq_tspp_release_hw_filter_index(tsif, tspp_filter.priority);
}
/* restore HW filter table state if necessary */
if (remove_null_blocking_filters)
mpq_tspp_remove_null_blocking_filters(channel_id,
tspp_source.source);
if (restore_user_filters)
mpq_tspp_add_all_user_filters(channel_id, tspp_source.source);
if (remove_accept_all_filter)
mpq_tspp_remove_accept_all_filter(channel_id,
tspp_source.source);
/* restore flags. we can only get here if we changed the flags. */
if (feed->pid == TSPP_PASS_THROUGH_PID)
mpq_dmx_tspp_info.tsif[tsif].pass_all_flag = 0;
else if (feed->pid == TSPP_NULL_PACKETS_PID)
mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag = 0;
add_channel_unregister_notif:
if (*channel_ref_count == 0) {
tspp_unregister_notification(0, channel_id);
tspp_close_stream(0, channel_id);
}
add_channel_close_ch:
if (*channel_ref_count == 0)
tspp_close_channel(0, channel_id);
add_channel_failed:
if (*channel_ref_count == 0)
if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
vfree(mpq_dmx_tspp_info.tsif[tsif].aggregate_ids);
mpq_dmx_tspp_info.tsif[tsif].aggregate_ids = NULL;
mpq_dmx_channel_mem_free(tsif);
}
out:
mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
atomic_dec(&mpq_dmx_tspp_info.tsif[tsif].control_op);
return ret;
}
/**
* Removes filter from TSPP.
*
* @feed: The feed to remove
*
* Return error status
*
* The function checks if this is the only PID allocated within
* the channel, if so, the channel is closed as well.
*/
static int mpq_tspp_dmx_remove_channel(struct dvb_demux_feed *feed)
{
int tsif;
int ret = 0;
int channel_id;
int slot;
atomic_t *data_cnt;
int *channel_ref_count;
enum tspp_source tspp_source;
struct tspp_filter tspp_filter;
struct mpq_demux *mpq_demux = feed->demux->priv;
int restore_null_blocking_filters = 0;
int remove_accept_all_filter = 0;
int remove_user_filters = 0;
int accept_all_filter_existed = 0;
MPQ_DVB_DBG_PRINT("%s: executed, PID = %d\n", __func__, feed->pid);
/* determine the TSIF we are reading from */
if (mpq_demux->source == DMX_SOURCE_FRONT0) {
tsif = 0;
tspp_source = TSPP_SOURCE_TSIF0;
} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
tsif = 1;
tspp_source = TSPP_SOURCE_TSIF1;
} else {
/* invalid source */
MPQ_DVB_ERR_PRINT(
"%s: invalid input source (%d)\n",
__func__,
mpq_demux->source);
return -EINVAL;
}
atomic_inc(&mpq_dmx_tspp_info.tsif[tsif].control_op);
if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex)) {
atomic_dec(&mpq_dmx_tspp_info.tsif[tsif].control_op);
return -ERESTARTSYS;
}
channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL);
channel_ref_count = &mpq_dmx_tspp_info.tsif[tsif].channel_ref;
data_cnt = &mpq_dmx_tspp_info.tsif[tsif].data_cnt;
/* check if required TSPP pipe is already allocated or not */
if (*channel_ref_count == 0) {
/* invalid feed provided as the channel is not allocated */
MPQ_DVB_ERR_PRINT(
"%s: invalid feed (%d)\n",
__func__,
channel_id);
ret = -EINVAL;
goto out;
}
slot = mpq_tspp_get_filter_slot(tsif, feed->pid);
if (slot < 0) {
/* invalid feed provided as it has no filter allocated */
MPQ_DVB_ERR_PRINT(
"%s: mpq_tspp_get_filter_slot failed (%d,%d)\n",
__func__,
feed->pid,
tsif);
ret = -EINVAL;
goto out;
}
/* since filter was found, ref_count > 0 so it's ok to decrement it */
mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count--;
if (mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count) {
/*
* there are still references to this pid, do not
* remove the filter yet
*/
goto out;
}
if (feed->pid == TSPP_PASS_THROUGH_PID)
mpq_dmx_tspp_info.tsif[tsif].pass_all_flag = 0;
else if (feed->pid == TSPP_NULL_PACKETS_PID)
mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag = 0;
mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid = -1;
if (mpq_dmx_tspp_info.tsif[tsif].current_filter_count <=
TSPP_MAX_HW_PID_FILTER_NUM) {
/* staying in HW filtering mode */
tspp_filter.priority =
mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index;
ret = tspp_remove_filter(0, channel_id, &tspp_filter);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: tspp_remove_filter failed (%d,%d)\n",
__func__,
channel_id,
tspp_filter.priority);
goto remove_channel_failed_restore_count;
}
mpq_tspp_release_hw_filter_index(tsif, tspp_filter.priority);
mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index = -1;
MPQ_DVB_DBG_PRINT(
"%s: HW filtering mode: Removed TSPP HW filter, PID = %d, index = %d\n",
__func__, feed->pid, tspp_filter.priority);
} else if (mpq_dmx_tspp_info.tsif[tsif].current_filter_count ==
(TSPP_MAX_HW_PID_FILTER_NUM + 1)) {
/* Crossing the threshold - from SW to HW filtering mode */
accept_all_filter_existed =
mpq_dmx_tspp_info.tsif[tsif].
accept_all_filter_exists_flag;
/* Add a temporary filter to accept all packets */
ret = mpq_tspp_add_accept_all_filter(channel_id,
tspp_source);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_tspp_add_accept_all_filter(%d, %d) failed\n",
__func__, channel_id, tspp_source);
goto remove_channel_failed_restore_count;
}
ret = mpq_tspp_remove_null_blocking_filters(channel_id,
tspp_source);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_tspp_remove_null_blocking_filters(%d, %d) failed\n",
__func__, channel_id, tspp_source);
restore_null_blocking_filters = 1;
if (!accept_all_filter_existed)
remove_accept_all_filter = 1;
goto remove_channel_failed_restore_count;
}
ret = mpq_tspp_add_all_user_filters(channel_id,
tspp_source);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_tspp_add_all_user_filters(%d, %d) failed\n",
__func__, channel_id, tspp_source);
remove_user_filters = 1;
restore_null_blocking_filters = 1;
if (!accept_all_filter_existed)
remove_accept_all_filter = 1;
goto remove_channel_failed_restore_count;
}
ret = mpq_tspp_remove_accept_all_filter(channel_id,
tspp_source);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_tspp_remove_accept_all_filter(%d, %d) failed\n",
__func__, channel_id, tspp_source);
remove_user_filters = 1;
restore_null_blocking_filters = 1;
if (!accept_all_filter_existed)
remove_accept_all_filter = 1;
goto remove_channel_failed_restore_count;
}
} else {
/* staying in SW filtering mode */
if ((mpq_dmx_tspp_info.tsif[tsif].pass_all_flag == 0) &&
(mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag == 0)) {
ret = mpq_tspp_remove_accept_all_filter(channel_id,
tspp_source);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_tspp_remove_accept_all_filter(%d, %d) failed\n",
__func__, channel_id,
tspp_source);
goto remove_channel_failed_restore_count;
}
}
}
mpq_dmx_tspp_info.tsif[tsif].current_filter_count--;
(*channel_ref_count)--;
MPQ_DVB_DBG_PRINT("%s: success, current_filter_count = %d\n",
__func__, mpq_dmx_tspp_info.tsif[tsif].current_filter_count);
if (*channel_ref_count == 0) {
/* channel is not used any more, release it */
tspp_unregister_notification(0, channel_id);
tspp_close_stream(0, channel_id);
tspp_close_channel(0, channel_id);
atomic_set(data_cnt, 0);
if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
vfree(mpq_dmx_tspp_info.tsif[tsif].aggregate_ids);
mpq_dmx_tspp_info.tsif[tsif].aggregate_ids = NULL;
mpq_dmx_channel_mem_free(tsif);
}
}
goto out;
remove_channel_failed_restore_count:
/* restore internal database state */
mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid = feed->pid;
mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count++;
if (remove_user_filters)
mpq_tspp_remove_all_user_filters(channel_id, tspp_source);
if (restore_null_blocking_filters)
mpq_tspp_add_null_blocking_filters(channel_id, tspp_source);
if (remove_accept_all_filter)
mpq_tspp_remove_accept_all_filter(channel_id, tspp_source);
/* restore flags. we can only get here if we changed the flags. */
if (feed->pid == TSPP_PASS_THROUGH_PID)
mpq_dmx_tspp_info.tsif[tsif].pass_all_flag = 1;
else if (feed->pid == TSPP_NULL_PACKETS_PID)
mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag = 1;
out:
mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
atomic_dec(&mpq_dmx_tspp_info.tsif[tsif].control_op);
return ret;
}
static int mpq_tspp_dmx_start_filtering(struct dvb_demux_feed *feed)
{
int ret;
struct mpq_demux *mpq_demux = feed->demux->priv;
MPQ_DVB_DBG_PRINT(
"%s(pid=%d) executed\n",
__func__,
feed->pid);
if (mpq_demux == NULL) {
MPQ_DVB_ERR_PRINT(
"%s: invalid mpq_demux handle\n",
__func__);
return -EINVAL;
}
if (mpq_demux->source < DMX_SOURCE_DVR0) {
/* source from TSPP, need to configure tspp pipe */
ret = mpq_tspp_dmx_add_channel(feed);
if (ret < 0) {
MPQ_DVB_DBG_PRINT(
"%s: mpq_tspp_dmx_add_channel failed(%d)\n",
__func__,
ret);
return ret;
}
}
/*
* Always feed sections/PES starting from a new one and
* do not partial transfer data from older one
*/
feed->pusi_seen = 0;
ret = mpq_dmx_init_mpq_feed(feed);
if (ret) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_dmx_init_mpq_feed failed(%d)\n",
__func__,
ret);
if (mpq_demux->source < DMX_SOURCE_DVR0)
mpq_tspp_dmx_remove_channel(feed);
return ret;
}
return 0;
}
static int mpq_tspp_dmx_stop_filtering(struct dvb_demux_feed *feed)
{
int ret = 0;
struct mpq_demux *mpq_demux = feed->demux->priv;
MPQ_DVB_DBG_PRINT("%s(%d) executed\n", __func__, feed->pid);
mpq_dmx_terminate_feed(feed);
if (mpq_demux->source < DMX_SOURCE_DVR0) {
/* source from TSPP, need to configure tspp pipe */
ret = mpq_tspp_dmx_remove_channel(feed);
}
return ret;
}
static int mpq_tspp_dmx_write_to_decoder(
struct dvb_demux_feed *feed,
const u8 *buf,
size_t len)
{
/*
* It is assumed that this function is called once for each
* TS packet of the relevant feed.
*/
if (len > TSPP_RAW_TTS_SIZE)
MPQ_DVB_DBG_PRINT(
"%s: warnning - len larger than one packet\n",
__func__);
if (dvb_dmx_is_video_feed(feed))
return mpq_dmx_process_video_packet(feed, buf);
if (dvb_dmx_is_audio_feed(feed))
return mpq_dmx_process_audio_packet(feed, buf);
if (dvb_dmx_is_pcr_feed(feed))
return mpq_dmx_process_pcr_packet(feed, buf);
return 0;
}
/**
* Returns demux capabilities of TSPPv1 plugin
*
* @demux: demux device
* @caps: Returned capbabilities
*
* Return error code
*/
static int mpq_tspp_dmx_get_caps(struct dmx_demux *demux,
struct dmx_caps *caps)
{
struct dvb_demux *dvb_demux = demux->priv;
if ((dvb_demux == NULL) || (caps == NULL)) {
MPQ_DVB_ERR_PRINT(
"%s: invalid parameters\n",
__func__);
return -EINVAL;
}
caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_DECODER_DATA |
DMX_CAP_TS_INSERTION | DMX_CAP_VIDEO_INDEXING |
DMX_CAP_AUDIO_DECODER_DATA | DMX_CAP_AUTO_BUFFER_FLUSH;
caps->recording_max_video_pids_indexed = 0;
caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES;
caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
caps->num_pid_filters = TSPP_MAX_PID_FILTER_NUM;
caps->num_section_filters = dvb_demux->filternum;
caps->num_section_filters_per_pid = dvb_demux->filternum;
caps->section_filter_length = DMX_FILTER_SIZE;
caps->num_demod_inputs = TSIF_COUNT;
caps->num_memory_inputs = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
caps->max_bitrate = 192;
caps->demod_input_max_bitrate = 96;
caps->memory_input_max_bitrate = 96;
caps->num_cipher_ops = 1;
/* TSIF reports 3 bytes STC at unit of 27MHz/256 */
caps->max_stc = (u64)0xFFFFFF * 256;
/* Buffer requirements */
caps->section.flags =
DMX_BUFFER_EXTERNAL_SUPPORT |
DMX_BUFFER_INTERNAL_SUPPORT |
DMX_BUFFER_CACHED;
caps->section.max_buffer_num = 1;
caps->section.max_size = 0xFFFFFFFF;
caps->section.size_alignment = 0;
caps->pes.flags =
DMX_BUFFER_EXTERNAL_SUPPORT |
DMX_BUFFER_INTERNAL_SUPPORT |
DMX_BUFFER_CACHED;
caps->pes.max_buffer_num = 1;
caps->pes.max_size = 0xFFFFFFFF;
caps->pes.size_alignment = 0;
caps->recording_188_tsp.flags =
DMX_BUFFER_EXTERNAL_SUPPORT |
DMX_BUFFER_INTERNAL_SUPPORT |
DMX_BUFFER_CACHED;
caps->recording_188_tsp.max_buffer_num = 1;
caps->recording_188_tsp.max_size = 0xFFFFFFFF;
caps->recording_188_tsp.size_alignment = 0;
caps->recording_192_tsp.flags =
DMX_BUFFER_EXTERNAL_SUPPORT |
DMX_BUFFER_INTERNAL_SUPPORT |
DMX_BUFFER_CACHED;
caps->recording_192_tsp.max_buffer_num = 1;
caps->recording_192_tsp.max_size = 0xFFFFFFFF;
caps->recording_192_tsp.size_alignment = 0;
caps->playback_188_tsp.flags =
DMX_BUFFER_EXTERNAL_SUPPORT |
DMX_BUFFER_INTERNAL_SUPPORT |
DMX_BUFFER_CACHED;
caps->playback_188_tsp.max_buffer_num = 1;
caps->playback_188_tsp.max_size = 0xFFFFFFFF;
caps->playback_188_tsp.size_alignment = 188;
caps->playback_192_tsp.flags =
DMX_BUFFER_EXTERNAL_SUPPORT |
DMX_BUFFER_INTERNAL_SUPPORT |
DMX_BUFFER_CACHED;
caps->playback_192_tsp.max_buffer_num = 1;
caps->playback_192_tsp.max_size = 0xFFFFFFFF;
caps->playback_192_tsp.size_alignment = 192;
caps->decoder.flags =
DMX_BUFFER_SECURED_IF_DECRYPTED |
DMX_BUFFER_EXTERNAL_SUPPORT |
DMX_BUFFER_INTERNAL_SUPPORT |
DMX_BUFFER_LINEAR_GROUP_SUPPORT |
DMX_BUFFER_CACHED;
caps->decoder.max_buffer_num = DMX_MAX_DECODER_BUFFER_NUM;
caps->decoder.max_size = 0xFFFFFFFF;
caps->decoder.size_alignment = SZ_4K;
return 0;
}
/**
* Reads TSIF STC from TSPP
*
* @demux: demux device
* @num: STC number. 0 for TSIF0 and 1 for TSIF1.
* @stc: STC value
* @base: divisor to get 90KHz value
*
* Return error code
*/
static int mpq_tspp_dmx_get_stc(struct dmx_demux *demux, unsigned int num,
u64 *stc, unsigned int *base)
{
enum tspp_source source;
u32 tcr_counter;
u64 avtimer_stc = 0;
int tts_source = 0;
if (!demux || !stc || !base)
return -EINVAL;
if (num == 0)
source = TSPP_SOURCE_TSIF0;
else if (num == 1)
source = TSPP_SOURCE_TSIF1;
else
return -EINVAL;
if (tspp_get_tts_source(0, &tts_source) < 0)
tts_source = TSIF_TTS_TCR;
if (tts_source != TSIF_TTS_LPASS_TIMER) {
tspp_get_ref_clk_counter(0, source, &tcr_counter);
*stc = ((u64)tcr_counter) * 256; /* conversion to 27MHz */
*base = 300; /* divisor to get 90KHz clock from stc value */
} else {
if (tspp_get_lpass_time_counter(0, source, &avtimer_stc) < 0)
return -EINVAL;
*stc = avtimer_stc;
}
return 0;
}
static int mpq_tspp_dmx_init(
struct dvb_adapter *mpq_adapter,
struct mpq_demux *mpq_demux)
{
int result;
MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
mpq_dmx_tspp_info.ion_client = mpq_demux->ion_client;
/* Set the kernel-demux object capabilities */
mpq_demux->demux.dmx.capabilities =
DMX_TS_FILTERING |
DMX_PES_FILTERING |
DMX_SECTION_FILTERING |
DMX_MEMORY_BASED_FILTERING |
DMX_CRC_CHECKING |
DMX_TS_DESCRAMBLING;
mpq_demux->decoder_alloc_flags = ION_FLAG_CACHED;
/* Set dvb-demux "virtual" function pointers */
mpq_demux->demux.priv = (void *)mpq_demux;
mpq_demux->demux.filternum = TSPP_MAX_SECTION_FILTER_NUM;
mpq_demux->demux.feednum = MPQ_MAX_DMX_FILES;
mpq_demux->demux.start_feed = mpq_tspp_dmx_start_filtering;
mpq_demux->demux.stop_feed = mpq_tspp_dmx_stop_filtering;
mpq_demux->demux.write_to_decoder = mpq_tspp_dmx_write_to_decoder;
mpq_demux->demux.decoder_fullness_init = mpq_dmx_decoder_fullness_init;
mpq_demux->demux.decoder_fullness_wait = mpq_dmx_decoder_fullness_wait;
mpq_demux->demux.decoder_fullness_abort =
mpq_dmx_decoder_fullness_abort;
mpq_demux->demux.decoder_buffer_status = mpq_dmx_decoder_buffer_status;
mpq_demux->demux.reuse_decoder_buffer = mpq_dmx_reuse_decoder_buffer;
mpq_demux->demux.set_cipher_op = mpq_dmx_set_cipher_ops;
mpq_demux->demux.oob_command = mpq_dmx_oob_command;
mpq_demux->demux.convert_ts = mpq_dmx_convert_tts;
mpq_demux->demux.flush_decoder_buffer = NULL;
/* Initialize dvb_demux object */
result = dvb_dmx_init(&mpq_demux->demux);
if (result < 0) {
MPQ_DVB_ERR_PRINT("%s: dvb_dmx_init failed\n", __func__);
goto init_failed;
}
/* Now initailize the dmx-dev object */
mpq_demux->dmxdev.filternum = MPQ_MAX_DMX_FILES;
mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx;
mpq_demux->dmxdev.capabilities = DMXDEV_CAP_DUPLEX;
mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
mpq_demux->dmxdev.demux->get_stc = mpq_tspp_dmx_get_stc;
mpq_demux->dmxdev.demux->get_caps = mpq_tspp_dmx_get_caps;
mpq_demux->dmxdev.demux->map_buffer = mpq_dmx_map_buffer;
mpq_demux->dmxdev.demux->unmap_buffer = mpq_dmx_unmap_buffer;
mpq_demux->dmxdev.demux->write = mpq_dmx_write;
result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
if (result < 0) {
MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed (errno=%d)\n",
__func__,
result);
goto init_failed_dmx_release;
}
/* Extend dvb-demux debugfs with TSPP statistics. */
mpq_dmx_init_debugfs_entries(mpq_demux);
/* Get the TSIF TTS info */
if (tspp_get_tts_source(0, &mpq_demux->ts_packet_timestamp_source) < 0)
mpq_demux->ts_packet_timestamp_source = TSIF_TTS_TCR;
return 0;
init_failed_dmx_release:
dvb_dmx_release(&mpq_demux->demux);
init_failed:
return result;
}
static int __init mpq_dmx_tspp_plugin_init(void)
{
int i;
int j;
int ret;
MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
for (i = 0; i < TSIF_COUNT; i++) {
mpq_dmx_tspp_info.tsif[i].aggregate_ids = NULL;
mpq_dmx_tspp_info.tsif[i].channel_ref = 0;
mpq_dmx_tspp_info.tsif[i].buff_index = 0;
mpq_dmx_tspp_info.tsif[i].ch_mem_heap_handle = NULL;
mpq_dmx_tspp_info.tsif[i].ch_mem_heap_virt_base = NULL;
mpq_dmx_tspp_info.tsif[i].ch_mem_heap_phys_base = 0;
atomic_set(&mpq_dmx_tspp_info.tsif[i].data_cnt, 0);
atomic_set(&mpq_dmx_tspp_info.tsif[i].control_op, 0);
for (j = 0; j < TSPP_MAX_PID_FILTER_NUM; j++) {
mpq_dmx_tspp_info.tsif[i].filters[j].pid = -1;
mpq_dmx_tspp_info.tsif[i].filters[j].ref_count = 0;
mpq_dmx_tspp_info.tsif[i].filters[j].hw_index = -1;
}
for (j = 0; j < TSPP_MAX_HW_PID_FILTER_NUM; j++)
mpq_dmx_tspp_info.tsif[i].hw_indexes[j] = 0;
mpq_dmx_tspp_info.tsif[i].current_filter_count = 0;
mpq_dmx_tspp_info.tsif[i].pass_nulls_flag = 0;
mpq_dmx_tspp_info.tsif[i].pass_all_flag = 0;
mpq_dmx_tspp_info.tsif[i].accept_all_filter_exists_flag = 0;
snprintf(mpq_dmx_tspp_info.tsif[i].name,
TSIF_NAME_LENGTH,
"dmx_tsif%d",
i);
init_waitqueue_head(&mpq_dmx_tspp_info.tsif[i].wait_queue);
mpq_dmx_tspp_info.tsif[i].thread =
kthread_run(
mpq_dmx_tspp_thread, (void *)(uintptr_t)i,
mpq_dmx_tspp_info.tsif[i].name);
if (IS_ERR(mpq_dmx_tspp_info.tsif[i].thread)) {
for (j = 0; j < i; j++) {
kthread_stop(mpq_dmx_tspp_info.tsif[j].thread);
mutex_destroy(&mpq_dmx_tspp_info.tsif[j].mutex);
}
MPQ_DVB_ERR_PRINT(
"%s: kthread_run failed\n",
__func__);
return -ENOMEM;
}
mutex_init(&mpq_dmx_tspp_info.tsif[i].mutex);
}
ret = mpq_dmx_plugin_init(mpq_tspp_dmx_init);
if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_dmx_plugin_init failed (errno=%d)\n",
__func__,
ret);
for (i = 0; i < TSIF_COUNT; i++) {
kthread_stop(mpq_dmx_tspp_info.tsif[i].thread);
mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex);
}
}
return ret;
}
static void __exit mpq_dmx_tspp_plugin_exit(void)
{
int i;
MPQ_DVB_DBG_PRINT("%s executed\n", __func__);
for (i = 0; i < TSIF_COUNT; i++) {
mutex_lock(&mpq_dmx_tspp_info.tsif[i].mutex);
/*
* Note: tspp_close_channel will also free the TSPP buffers
* even if we allocated them ourselves,
* using our free function.
*/
if (mpq_dmx_tspp_info.tsif[i].channel_ref) {
tspp_unregister_notification(0,
TSPP_CHANNEL_ID(i, TSPP_CHANNEL));
tspp_close_channel(0,
TSPP_CHANNEL_ID(i, TSPP_CHANNEL));
if (allocation_mode ==
MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
vfree(mpq_dmx_tspp_info.tsif[i].aggregate_ids);
mpq_dmx_tspp_info.tsif[i].aggregate_ids = NULL;
mpq_dmx_channel_mem_free(i);
}
}
mutex_unlock(&mpq_dmx_tspp_info.tsif[i].mutex);
kthread_stop(mpq_dmx_tspp_info.tsif[i].thread);
mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex);
}
mpq_dmx_plugin_exit();
}
module_init(mpq_dmx_tspp_plugin_init);
module_exit(mpq_dmx_tspp_plugin_exit);
MODULE_DESCRIPTION("Qualcomm Technologies Inc. demux TSPP version 1 HW Plugin");
MODULE_LICENSE("GPL v2");