media: dvb: mpq: Secure demux support
This change adds support for demuxing of encrypted streams which current
software demux implementation does not support.
User-space API is extended to allow setting of key ladder information
needed in order to perform decryption.
If secure demux application is available, demuxing does not take place in
the dvb demux sw filter anymore but instead is performed in the new secure
demux, regardless of whether the stream is encrypted or not.
Otherwise, the dvb demux sw filter will be used as before and demuxing of
encrypted streams will not be available.
Change-Id: If75a7b03d1f13d3b012250b87c25eaf6d6cbc399
Signed-off-by: Hamad Kadmany <hkadmany@codeaurora.org>
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
index 844c65f..df29108 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -360,7 +360,6 @@
struct dmx_frontend* frontend; /* Front-end connected to the demux */
void* priv; /* Pointer to private data of the API client */
struct data_buffer dvr_input; /* DVR input buffer */
-
struct dentry *debugfs_demux_dir; /* debugfs dir */
int (*open) (struct dmx_demux* demux);
@@ -410,6 +409,9 @@
int (*unmap_buffer) (struct dmx_demux *demux,
void *priv_handle);
+
+ int (*set_secure_mode) (struct dmx_demux *demux,
+ struct dmx_secure_mode *sec_mode);
};
#endif /* #ifndef __DEMUX_H */
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index bddbb5f..0255cdc 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -473,16 +473,18 @@
struct dvb_ringbuffer *src = &dmxdev->dvr_input_buffer;
int ret;
size_t todo;
+ int bytes_written;
size_t split;
while (1) {
/* wait for input */
- ret = wait_event_interruptible(src->queue,
- (!src->data) ||
- (dvb_ringbuffer_avail(src)) ||
- (src->error != 0) ||
- (dmxdev->dvr_in_exit) ||
- kthread_should_stop());
+ ret = wait_event_interruptible(
+ src->queue,
+ (!src->data) ||
+ (dvb_ringbuffer_avail(src) > 188) ||
+ (src->error != 0) ||
+ (dmxdev->dvr_in_exit) ||
+ kthread_should_stop());
if ((ret < 0) || kthread_should_stop())
break;
@@ -513,40 +515,60 @@
* In DVR PULL mode, write might block.
* Lock on DVR buffer is released before calling to
* write, if DVR was released meanwhile, dvr_in_exit is
- * prompted. Lock is aquired when updating the read pointer
- * again to preserve read/write pointers consistancy
+ * prompted. Lock is acquired when updating the read pointer
+ * again to preserve read/write pointers consistency
*/
if (split > 0) {
spin_unlock(&dmxdev->dvr_in_lock);
- dmxdev->demux->write(dmxdev->demux,
+ bytes_written = dmxdev->demux->write(dmxdev->demux,
src->data + src->pread,
split);
+ if (bytes_written < 0) {
+ printk(KERN_ERR "dmxdev: dvr write error %d\n",
+ bytes_written);
+ continue;
+ }
+
if (dmxdev->dvr_in_exit)
break;
spin_lock(&dmxdev->dvr_in_lock);
- todo -= split;
- DVB_RINGBUFFER_SKIP(src, split);
+ todo -= bytes_written;
+ DVB_RINGBUFFER_SKIP(src, bytes_written);
+ if (bytes_written < split) {
+ dmxdev->dvr_processing_input = 0;
+ spin_unlock(&dmxdev->dvr_in_lock);
+ wake_up_all(&src->queue);
+ continue;
+ }
+
}
spin_unlock(&dmxdev->dvr_in_lock);
- dmxdev->demux->write(dmxdev->demux,
+ bytes_written = dmxdev->demux->write(dmxdev->demux,
src->data + src->pread, todo);
+ if (bytes_written < 0) {
+ printk(KERN_ERR "dmxdev: dvr write error %d\n",
+ bytes_written);
+ continue;
+ }
+
if (dmxdev->dvr_in_exit)
break;
spin_lock(&dmxdev->dvr_in_lock);
- DVB_RINGBUFFER_SKIP(src, todo);
+ DVB_RINGBUFFER_SKIP(src, bytes_written);
dmxdev->dvr_processing_input = 0;
spin_unlock(&dmxdev->dvr_in_lock);
wake_up_all(&src->queue);
}
+
return 0;
}
@@ -1427,8 +1449,7 @@
struct dmxdev_events_queue *events;
int ret;
- if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
- || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP) {
+ if (dmxdevfilter->params.pes.output != DMX_OUT_TS_TAP) {
src = &dmxdevfilter->buffer;
events = &dmxdevfilter->events;
} else {
@@ -1762,9 +1783,6 @@
event.params.section.start_offset = dmxdevfilter->buffer.pwrite;
del_timer(&dmxdevfilter->timer);
- dprintk("dmxdev: section callback %02x %02x %02x %02x %02x %02x\n",
- buffer1[0], buffer1[1],
- buffer1[2], buffer1[3], buffer1[4], buffer1[5]);
ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1,
buffer1_len);
if (ret == buffer1_len)
@@ -2019,6 +2037,7 @@
}
if (dmx_data_ready->status == DMX_OK_PCR) {
+ dprintk("dmxdev: event callback DMX_OK_PCR\n");
event.type = DMX_EVENT_NEW_PCR;
event.params.pcr.pcr = dmx_data_ready->pcr.pcr;
event.params.pcr.stc = dmx_data_ready->pcr.stc;
@@ -2596,10 +2615,37 @@
static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev,
struct dmxdev_filter *dmxdevfilter)
{
+ struct dmxdev_feed *feed;
+ struct dmx_secure_mode sec_mode;
+
mutex_lock(&dmxdev->mutex);
mutex_lock(&dmxdevfilter->mutex);
dvb_dmxdev_filter_stop(dmxdevfilter);
+
+ if (dmxdev->demux->set_secure_mode) {
+ sec_mode.is_secured = 0;
+ switch (dmxdevfilter->type) {
+ case DMXDEV_TYPE_PES:
+ list_for_each_entry(feed,
+ &dmxdevfilter->feed.ts, next) {
+ sec_mode.pid = feed->pid;
+ dmxdev->demux->set_secure_mode(
+ dmxdev->demux, &sec_mode);
+ }
+ break;
+
+ case DMXDEV_TYPE_SEC:
+ sec_mode.pid = dmxdevfilter->params.sec.pid;
+ dmxdev->demux->set_secure_mode(
+ dmxdev->demux, &sec_mode);
+ break;
+
+ default:
+ break;
+ }
+ }
+
dvb_dmxdev_filter_reset(dmxdevfilter);
if (dmxdevfilter->buffer.data) {
@@ -2703,6 +2749,54 @@
return 0;
}
+static int dvb_dmxdev_set_secure_mode(
+ struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter,
+ struct dmx_secure_mode *sec_mode)
+{
+ struct dmxdev_feed *feed;
+ int found = 0;
+
+ if (NULL == dmxdev || NULL == filter || NULL == sec_mode)
+ return -EINVAL;
+ if (NULL == dmxdev->demux->set_secure_mode)
+ return -ENODEV;
+
+ if (filter->state < DMXDEV_STATE_SET ||
+ filter->state > DMXDEV_STATE_GO) {
+ printk(KERN_ERR "%s: invalid filter state\n", __func__);
+ return -EPERM;
+ }
+ dprintk(KERN_DEBUG "%s: key_id=%d, secure=%d, looking for pid=%d...\n",
+ __func__, sec_mode->key_ladder_id, sec_mode->is_secured,
+ sec_mode->pid);
+ switch (filter->type) {
+ case DMXDEV_TYPE_PES:
+ list_for_each_entry(feed, &filter->feed.ts, next) {
+ if (feed->pid == sec_mode->pid) {
+ found = 1;
+ break;
+ }
+ }
+ break;
+ case DMXDEV_TYPE_SEC:
+ if (filter->params.sec.pid == sec_mode->pid)
+ found = 1;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (!found) {
+ printk(KERN_ERR "%s: pid %d is undefined for this filter\n",
+ __func__, sec_mode->pid);
+ return -EINVAL;
+ }
+
+ return dmxdev->demux->set_secure_mode(dmxdev->demux, sec_mode);
+}
+
static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
struct dmxdev_filter *dmxdevfilter,
struct dmx_pes_filter_params *params)
@@ -2764,8 +2858,10 @@
dec_buffs = &filter->decoder_buffers;
dmxdev->demux->get_caps(dmxdev->demux, &caps);
- if (0 == buffs->buffers_size ||
- (buffs->is_linear && buffs->buffers_num <= 1))
+ if ((buffs->buffers_size == 0) ||
+ (buffs->is_linear &&
+ ((buffs->buffers_num <= 1) ||
+ (buffs->buffers_num > DMX_MAX_DECODER_BUFFER_NUM))))
return -EINVAL;
if (0 == buffs->buffers_num) {
@@ -3091,6 +3187,15 @@
mutex_unlock(&dmxdevfilter->mutex);
break;
+ case DMX_SET_SECURE_MODE:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ ret = dvb_dmxdev_set_secure_mode(dmxdev, dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
case DMX_REUSE_DECODER_BUFFER:
if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
mutex_unlock(&dmxdev->mutex);
@@ -3201,9 +3306,7 @@
{
struct dmxdev_filter *dmxdevfilter = file->private_data;
struct dmxdev *dmxdev = dmxdevfilter->dev;
-
int ret;
-
ret = dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
mutex_lock(&dmxdev->mutex);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index 488f42c..262de7d 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -1775,6 +1775,19 @@
mutex_lock(&dvbdemux->mutex);
dvbdemux->tsp_format = tsp_format;
+ switch (tsp_format) {
+ case DMX_TSP_FORMAT_188:
+ dvbdemux->ts_packet_size = 188;
+ break;
+ case DMX_TSP_FORMAT_192_TAIL:
+ case DMX_TSP_FORMAT_192_HEAD:
+ dvbdemux->ts_packet_size = 192;
+ break;
+ case DMX_TSP_FORMAT_204:
+ dvbdemux->ts_packet_size = 204;
+ break;
+ }
+
mutex_unlock(&dvbdemux->mutex);
return 0;
}
@@ -1849,6 +1862,7 @@
dvbdemux->tsbufp = 0;
dvbdemux->tsp_format = DMX_TSP_FORMAT_188;
+ dvbdemux->ts_packet_size = 188;
if (!dvbdemux->check_crc32)
dvbdemux->check_crc32 = dvb_dmx_crc32;
@@ -1878,6 +1892,7 @@
dmx->get_pes_pids = dvbdmx_get_pes_pids;
dmx->set_tsp_format = dvbdmx_set_tsp_format;
+ dmx->set_secure_mode = NULL;
mutex_init(&dvbdemux->mutex);
spin_lock_init(&dvbdemux->lock);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index 5e98ea1..746cf75 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -163,6 +163,7 @@
uint32_t speed_pkts_cnt; /* for TS speed check */
enum dmx_tsp_format_t tsp_format;
+ size_t ts_packet_size;
enum dmx_playback_mode_t playback_mode;
int sw_filter_abort;
diff --git a/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c b/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c
index 6840858..6ec1994 100644
--- a/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c
+++ b/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -177,13 +177,6 @@
if ((NULL == sbuff) || (NULL == packet))
return -EINVAL;
- MPQ_DVB_DBG_PRINT(
- "%s: handle=%d, offset=%d, len=%d\n",
- __func__,
- packet->raw_data_handle,
- packet->raw_data_offset,
- packet->raw_data_len);
-
len = sizeof(struct mpq_streambuffer_packet_header) +
packet->user_data_len;
@@ -270,9 +263,6 @@
}
memcpy(desc->base + desc->write_ptr, buf, len);
desc->write_ptr += len;
- MPQ_DVB_DBG_PRINT(
- "%s: copied %d data bytes. handle=%d, write_ptr=%d\n",
- __func__, len, desc->handle, desc->write_ptr);
res = len;
}
@@ -288,10 +278,10 @@
if (NULL == sbuff)
return -EINVAL;
- if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len))
- return -ENOSPC;
-
if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == sbuff->mode) {
+ if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len))
+ return -ENOSPC;
+
DVB_RINGBUFFER_PUSH(&sbuff->raw_data, len);
wake_up_all(&sbuff->raw_data.queue);
} else {
diff --git a/drivers/media/dvb/mpq/demux/Makefile b/drivers/media/dvb/mpq/demux/Makefile
index b9310c3..9f4a03e 100644
--- a/drivers/media/dvb/mpq/demux/Makefile
+++ b/drivers/media/dvb/mpq/demux/Makefile
@@ -1,14 +1,18 @@
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
-EXTRA_CFLAGS += -Idrivers/media/dvb/mpq/include/
+ccflags-y += -Idrivers/media/dvb/dvb-core/
+ccflags-y += -Idrivers/media/dvb/mpq/include/
+ccflags-y += -Idrivers/misc/
obj-$(CONFIG_DVB_MPQ_DEMUX) += mpq-dmx-hw-plugin.o
mpq-dmx-hw-plugin-y := mpq_dmx_plugin_common.o
+mpq-dmx-hw-plugin-y += mpq_sdmx.o
+
mpq-dmx-hw-plugin-$(CONFIG_DVB_MPQ_TSPP1) += mpq_dmx_plugin_tspp_v1.o
mpq-dmx-hw-plugin-$(CONFIG_DVB_MPQ_TSPP2) += mpq_dmx_plugin_tspp_v2.o
mpq-dmx-hw-plugin-$(CONFIG_DVB_MPQ_TSIF) += mpq_dmx_plugin_tsif.o
+
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
index 4b67477..1cd9816 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -16,11 +16,34 @@
#include <linux/file.h>
#include "mpq_dvb_debug.h"
#include "mpq_dmx_plugin_common.h"
+#include "mpq_sdmx.h"
+#define TS_PACKET_HEADER_LENGTH (4)
/* Length of mandatory fields that must exist in header of video PES */
#define PES_MANDATORY_FIELDS_LEN 9
+#define MAX_PES_LENGTH (SZ_64K)
+
+/*
+ * PES header length field is 8 bits so PES header length after this field
+ * can be up to 256 bytes.
+ * Preceding fields of the PES header total to 9 bytes
+ * (including the PES header length field).
+ */
+#define MAX_PES_HEADER_LENGTH (256 + PES_MANDATORY_FIELDS_LEN)
+
+/* TS packet with adaptation field only can take up the entire TSP */
+#define MAX_TSP_ADAPTATION_LENGTH (184)
+
+#define MAX_SDMX_METADATA_LENGTH \
+ (TS_PACKET_HEADER_LENGTH + \
+ MAX_TSP_ADAPTATION_LENGTH + \
+ MAX_PES_HEADER_LENGTH)
+
+#define SDMX_METADATA_BUFFER_SIZE (64*1024)
+#define SDMX_SECTION_BUFFER_SIZE (64*1024)
+#define SDMX_PCR_BUFFER_SIZE (64*1024)
/*
* 500 PES header packets in the meta-data buffer,
@@ -52,6 +75,18 @@
module_param(generate_es_events, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(generate_es_events, "Generate new elementary stream data events");
+/* Value of TS packet scramble bits field for even key */
+static int mpq_sdmx_scramble_even = 0x2;
+module_param(mpq_sdmx_scramble_even, int, S_IRUGO | S_IWUSR);
+
+/* Value of TS packet scramble bits field for odd key */
+static int mpq_sdmx_scramble_odd = 0x3;
+module_param(mpq_sdmx_scramble_odd, int, S_IRUGO | S_IWUSR);
+
+/* Whether to use secure demux or bypass it. Use for debugging */
+static int mpq_bypass_sdmx = 1;
+module_param(mpq_bypass_sdmx, int, S_IRUGO | S_IWUSR);
+
/**
* Maximum allowed framing pattern size
*/
@@ -144,6 +179,9 @@
* in the meta-data passed to the decoder.
*/
int decoder_framing;
+
+ /* Indicates whether secure demux TZ application is available */
+ int secure_demux_app_loaded;
} mpq_dmx_info;
/* Check that PES header is valid and that it is a video PES */
@@ -477,6 +515,104 @@
return 0;
}
+/*
+ * mpq_dmx_calc_time_delta -
+ * Calculate delta in msec between two time snapshots.
+ *
+ * @curr_time: value of current time
+ * @prev_time: value of previous time
+ *
+ * Return time-delta in msec
+ */
+static inline u32 mpq_dmx_calc_time_delta(struct timespec *curr_time,
+ struct timespec *prev_time)
+{
+ struct timespec delta_time;
+ u64 delta_time_ms;
+
+ delta_time = timespec_sub(*curr_time, *prev_time);
+
+ delta_time_ms = ((u64)delta_time.tv_sec * MSEC_PER_SEC) +
+ delta_time.tv_nsec / NSEC_PER_MSEC;
+
+ return (u32)delta_time_ms;
+}
+
+/*
+ * mpq_dmx_update_decoder_stat -
+ * Update decoder output statistics in debug-fs.
+ *
+ * @mpq_demux: mpq_demux object
+ */
+static inline void mpq_dmx_update_decoder_stat(struct mpq_demux *mpq_demux)
+{
+ struct timespec curr_time;
+ u64 delta_time_ms;
+
+ curr_time = current_kernel_time();
+ if (unlikely(!mpq_demux->decoder_out_count)) {
+ mpq_demux->decoder_out_last_time = curr_time;
+ mpq_demux->decoder_out_count++;
+ return;
+ }
+
+ /* calculate time-delta between frame */
+ delta_time_ms = mpq_dmx_calc_time_delta(&curr_time,
+ &mpq_demux->decoder_out_last_time);
+
+ mpq_demux->decoder_out_interval_sum += (u32)delta_time_ms;
+
+ mpq_demux->decoder_out_interval_average =
+ mpq_demux->decoder_out_interval_sum /
+ mpq_demux->decoder_out_count;
+
+ if (delta_time_ms > mpq_demux->decoder_out_interval_max)
+ mpq_demux->decoder_out_interval_max = delta_time_ms;
+
+ mpq_demux->decoder_out_last_time = curr_time;
+ mpq_demux->decoder_out_count++;
+}
+
+/*
+ * mpq_dmx_update_sdmx_stat -
+ * Update SDMX statistics in debug-fs.
+ *
+ * @mpq_demux: mpq_demux object
+ * @bytes_processed: number of bytes processed by sdmx
+ * @process_start_time: time before sdmx process was triggered
+ * @process_end_time: time after sdmx process finished
+ */
+static inline void mpq_dmx_update_sdmx_stat(struct mpq_demux *mpq_demux,
+ u32 bytes_processed, struct timespec *process_start_time,
+ struct timespec *process_end_time)
+{
+ u32 packets_num;
+ u64 process_time;
+
+ mpq_demux->sdmx_process_count++;
+ packets_num = bytes_processed / mpq_demux->demux.ts_packet_size;
+ mpq_demux->sdmx_process_packets_sum += packets_num;
+ mpq_demux->sdmx_process_packets_average =
+ mpq_demux->sdmx_process_packets_sum /
+ mpq_demux->sdmx_process_count;
+
+ process_time =
+ mpq_dmx_calc_time_delta(process_end_time, process_start_time);
+
+ mpq_demux->sdmx_process_time_sum += process_time;
+ mpq_demux->sdmx_process_time_average =
+ mpq_demux->sdmx_process_time_sum /
+ mpq_demux->sdmx_process_count;
+
+ if ((mpq_demux->sdmx_process_count == 1) ||
+ (packets_num < mpq_demux->sdmx_process_packets_min))
+ mpq_demux->sdmx_process_packets_min = packets_num;
+
+ if ((mpq_demux->sdmx_process_count == 1) ||
+ (process_time > mpq_demux->sdmx_process_time_max))
+ mpq_demux->sdmx_process_time_max = process_time;
+}
+
/* Extend dvb-demux debugfs with HW statistics */
void mpq_dmx_init_hw_statistics(struct mpq_demux *mpq_demux)
{
@@ -490,93 +626,130 @@
mpq_demux->hw_notification_size = 0;
mpq_demux->hw_notification_min_size = 0xFFFFFFFF;
- if (mpq_demux->demux.dmx.debugfs_demux_dir != NULL) {
- debugfs_create_u32(
- "hw_notification_interval",
- S_IRUGO|S_IWUGO,
- mpq_demux->demux.dmx.debugfs_demux_dir,
- &mpq_demux->hw_notification_interval);
+ if (mpq_demux->demux.dmx.debugfs_demux_dir == NULL)
+ return;
- debugfs_create_u32(
- "hw_notification_min_interval",
- S_IRUGO|S_IWUGO,
- mpq_demux->demux.dmx.debugfs_demux_dir,
- &mpq_demux->hw_notification_min_interval);
+ debugfs_create_u32(
+ "hw_notification_interval",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->hw_notification_interval);
- debugfs_create_u32(
- "hw_notification_count",
- S_IRUGO|S_IWUGO,
- mpq_demux->demux.dmx.debugfs_demux_dir,
- &mpq_demux->hw_notification_count);
+ debugfs_create_u32(
+ "hw_notification_min_interval",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->hw_notification_min_interval);
- debugfs_create_u32(
- "hw_notification_size",
- S_IRUGO|S_IWUGO,
- mpq_demux->demux.dmx.debugfs_demux_dir,
- &mpq_demux->hw_notification_size);
+ debugfs_create_u32(
+ "hw_notification_count",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->hw_notification_count);
- debugfs_create_u32(
- "hw_notification_min_size",
- S_IRUGO|S_IWUGO,
- mpq_demux->demux.dmx.debugfs_demux_dir,
- &mpq_demux->hw_notification_min_size);
+ debugfs_create_u32(
+ "hw_notification_size",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->hw_notification_size);
- debugfs_create_u32(
- "decoder_drop_count",
- S_IRUGO|S_IWUGO,
- mpq_demux->demux.dmx.debugfs_demux_dir,
- &mpq_demux->decoder_drop_count);
+ debugfs_create_u32(
+ "hw_notification_min_size",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->hw_notification_min_size);
- debugfs_create_u32(
- "decoder_out_count",
- S_IRUGO|S_IWUGO,
- mpq_demux->demux.dmx.debugfs_demux_dir,
- &mpq_demux->decoder_out_count);
+ debugfs_create_u32(
+ "decoder_drop_count",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->decoder_drop_count);
- debugfs_create_u32(
- "decoder_out_interval_sum",
- S_IRUGO|S_IWUGO,
- mpq_demux->demux.dmx.debugfs_demux_dir,
- &mpq_demux->decoder_out_interval_sum);
+ debugfs_create_u32(
+ "decoder_out_count",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->decoder_out_count);
- debugfs_create_u32(
- "decoder_out_interval_average",
- S_IRUGO|S_IWUGO,
- mpq_demux->demux.dmx.debugfs_demux_dir,
- &mpq_demux->decoder_out_interval_average);
+ debugfs_create_u32(
+ "decoder_out_interval_sum",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->decoder_out_interval_sum);
- debugfs_create_u32(
- "decoder_out_interval_max",
- S_IRUGO|S_IWUGO,
- mpq_demux->demux.dmx.debugfs_demux_dir,
- &mpq_demux->decoder_out_interval_max);
+ debugfs_create_u32(
+ "decoder_out_interval_average",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->decoder_out_interval_average);
- debugfs_create_u32(
- "decoder_ts_errors",
- S_IRUGO|S_IWUGO,
- mpq_demux->demux.dmx.debugfs_demux_dir,
- &mpq_demux->decoder_ts_errors);
- }
+ debugfs_create_u32(
+ "decoder_out_interval_max",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->decoder_out_interval_max);
+
+ debugfs_create_u32(
+ "decoder_ts_errors",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->decoder_ts_errors);
+
+ debugfs_create_u32(
+ "sdmx_process_count",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->sdmx_process_count);
+
+ debugfs_create_u32(
+ "sdmx_process_time_sum",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->sdmx_process_time_sum);
+
+ debugfs_create_u32(
+ "sdmx_process_time_average",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->sdmx_process_time_average);
+
+ debugfs_create_u32(
+ "sdmx_process_time_max",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->sdmx_process_time_max);
+
+ debugfs_create_u32(
+ "sdmx_process_packets_sum",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->sdmx_process_packets_sum);
+
+ debugfs_create_u32(
+ "sdmx_process_packets_average",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->sdmx_process_packets_average);
+
+ debugfs_create_u32(
+ "sdmx_process_packets_min",
+ S_IRUGO|S_IWUGO,
+ mpq_demux->demux.dmx.debugfs_demux_dir,
+ &mpq_demux->sdmx_process_packets_min);
}
EXPORT_SYMBOL(mpq_dmx_init_hw_statistics);
-
/* Update dvb-demux debugfs with HW notification statistics */
void mpq_dmx_update_hw_statistics(struct mpq_demux *mpq_demux)
{
- struct timespec curr_time, delta_time;
+ struct timespec curr_time;
u64 delta_time_ms;
curr_time = current_kernel_time();
if (likely(mpq_demux->hw_notification_count)) {
/* calculate time-delta between notifications */
- delta_time =
- timespec_sub(
- curr_time,
- mpq_demux->last_notification_time);
-
- delta_time_ms = ((u64)delta_time.tv_sec * MSEC_PER_SEC) +
- delta_time.tv_nsec / NSEC_PER_MSEC;
+ delta_time_ms = mpq_dmx_calc_time_delta(&curr_time,
+ &mpq_demux->last_notification_time);
mpq_demux->hw_notification_interval = delta_time_ms;
@@ -593,13 +766,32 @@
}
EXPORT_SYMBOL(mpq_dmx_update_hw_statistics);
+static void mpq_sdmx_check_app_loaded(void)
+{
+ int session;
+ int ret;
+
+ ret = sdmx_open_session(&session);
+ if (ret != SDMX_SUCCESS) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Could not initialize session with SDMX. ret = %d\n",
+ __func__, ret);
+ mpq_dmx_info.secure_demux_app_loaded = 0;
+ return;
+ }
+
+ mpq_dmx_info.secure_demux_app_loaded = 1;
+ sdmx_close_session(session);
+}
int mpq_dmx_plugin_init(mpq_dmx_init dmx_init_func)
{
int i;
+ int j;
int result;
struct mpq_demux *mpq_demux;
struct dvb_adapter *mpq_adapter;
+ struct mpq_feed *feed;
MPQ_DVB_DBG_PRINT("%s executed, device num %d\n",
__func__,
@@ -627,6 +819,8 @@
mpq_dmx_info.devices = NULL;
mpq_dmx_info.ion_client = NULL;
+ mpq_sdmx_check_app_loaded();
+
/*
* TODO: the following should be set based on the decoder:
* 0 means the decoder doesn't handle framing, so framing
@@ -636,7 +830,7 @@
/* Allocate memory for all MPQ devices */
mpq_dmx_info.devices =
- vmalloc(mpq_demux_device_num*sizeof(struct mpq_demux));
+ vzalloc(mpq_demux_device_num*sizeof(struct mpq_demux));
if (!mpq_dmx_info.devices) {
MPQ_DVB_ERR_PRINT(
@@ -647,11 +841,6 @@
goto init_failed;
}
- /* Zero allocated memory */
- memset(mpq_dmx_info.devices,
- 0,
- mpq_demux_device_num*sizeof(struct mpq_demux));
-
/*
* Create a new ION client used by demux to allocate memory
* for decoder's buffers.
@@ -684,7 +873,29 @@
*/
mpq_demux->ion_client = mpq_dmx_info.ion_client;
- spin_lock_init(&mpq_demux->feed_lock);
+ mutex_init(&mpq_demux->mutex);
+
+ INIT_LIST_HEAD(&mpq_demux->keyladder_list);
+ mpq_demux->secure_mode_count = 0;
+ mpq_demux->sdmx_filter_count = 0;
+ mpq_demux->sdmx_session_handle = SDMX_INVALID_SESSION_HANDLE;
+
+ if (mpq_demux->demux.feednum > MPQ_MAX_DMX_FILES) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: err - actual feednum (%d) larger than max, enlarge MPQ_MAX_DMX_FILES!\n",
+ __func__,
+ mpq_demux->demux.feednum);
+ result = -EINVAL;
+ goto init_failed_free_demux_devices;
+ }
+
+ /* Initialize private feed info */
+ for (j = 0; j < MPQ_MAX_DMX_FILES; j++) {
+ feed = &mpq_demux->feeds[j];
+ memset(feed, 0, sizeof(*feed));
+ feed->sdmx_filter_handle = SDMX_INVALID_FILTER_HANDLE;
+ feed->mpq_demux = mpq_demux;
+ }
/*
* mpq_demux_plugin_hw_init should be implemented
@@ -703,6 +914,16 @@
mpq_demux->is_initialized = 1;
/*
+ * dvb-demux is now initialized,
+ * update back-pointers of private feeds
+ */
+ for (j = 0; j < MPQ_MAX_DMX_FILES; j++) {
+ feed = &mpq_demux->feeds[j];
+ feed->dvb_demux_feed = &mpq_demux->demux.feed[j];
+ mpq_demux->demux.feed[j].priv = feed;
+ }
+
+ /*
* Add capability of receiving input from memory.
* Every demux in our system may be connected to memory input,
* or any live input.
@@ -732,6 +953,16 @@
}
EXPORT_SYMBOL(mpq_dmx_plugin_init);
+static void mpq_dmx_keyladder_list_cleanup(struct mpq_demux *mpq_demux)
+{
+ struct mpq_demux_keyladder_info *key;
+ struct mpq_demux_keyladder_info *tmp;
+
+ list_for_each_entry_safe(key, tmp, &mpq_demux->keyladder_list, list) {
+ list_del(&key->list);
+ vfree(key);
+ }
+}
void mpq_dmx_plugin_exit(void)
{
@@ -754,6 +985,10 @@
&mpq_demux->demux.dmx,
&mpq_demux->fe_memory);
+ mpq_dmx_keyladder_list_cleanup(mpq_demux);
+ if (mpq_sdmx_is_loaded())
+ mpq_sdmx_close_session(mpq_demux);
+ mutex_destroy(&mpq_demux->mutex);
dvb_dmxdev_release(&mpq_demux->dmxdev);
dvb_dmx_release(&mpq_demux->demux);
}
@@ -853,7 +1088,7 @@
unsigned long ionflag = 0;
int ret;
- if (NULL == client || priv_handle == NULL || kernel_mem == NULL) {
+ if (client == NULL || priv_handle == NULL || kernel_mem == NULL) {
MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
return -EINVAL;
}
@@ -880,6 +1115,7 @@
MPQ_DVB_DBG_PRINT("%s: secured buffer\n", __func__);
*kernel_mem = NULL;
} else {
+ unsigned long tmp;
*kernel_mem = ion_map_kernel(client, ion_handle);
if (*kernel_mem == NULL) {
MPQ_DVB_ERR_PRINT("%s: ion_map_kernel failed\n",
@@ -887,6 +1123,10 @@
ret = -ENOMEM;
goto map_buffer_failed_free_buff;
}
+ ion_handle_get_size(client, ion_handle, &tmp);
+ MPQ_DVB_DBG_PRINT(
+ "%s: mapped to address 0x%p, size=%lu\n",
+ __func__, *kernel_mem, tmp);
}
*priv_handle = ion_handle;
@@ -978,32 +1218,28 @@
if (mpq_dmx_is_video_feed(feed)) {
struct mpq_video_feed_info *feed_data;
+ struct mpq_feed *mpq_feed;
struct mpq_streambuffer *stream_buffer;
int ret;
- spin_lock(&mpq_demux->feed_lock);
+ mutex_lock(&mpq_demux->mutex);
+ mpq_feed = feed->priv;
+ feed_data = &mpq_feed->video_info;
- if (feed->priv == NULL) {
- MPQ_DVB_ERR_PRINT(
- "%s: invalid feed, feed->priv is NULL\n",
- __func__);
- spin_unlock(&mpq_demux->feed_lock);
- return -EINVAL;
- }
-
- feed_data = feed->priv;
+ spin_lock(&feed_data->video_buffer_lock);
stream_buffer = feed_data->video_buffer;
if (stream_buffer == NULL) {
MPQ_DVB_ERR_PRINT(
"%s: invalid feed, feed_data->video_buffer is NULL\n",
__func__);
- spin_unlock(&mpq_demux->feed_lock);
+ spin_unlock(&feed_data->video_buffer_lock);
+ mutex_unlock(&mpq_demux->mutex);
return -EINVAL;
}
ret = mpq_streambuffer_pkt_dispose(stream_buffer, cookie, 1);
-
- spin_unlock(&mpq_demux->feed_lock);
+ spin_unlock(&feed_data->video_buffer_lock);
+ mutex_unlock(&mpq_demux->mutex);
return ret;
}
@@ -1193,18 +1429,18 @@
* Return error status
*/
static int mpq_dmx_init_streambuffer(
- struct dvb_demux_feed *feed,
+ struct mpq_feed *feed,
struct mpq_video_feed_info *feed_data,
struct mpq_streambuffer *stream_buffer)
{
int ret;
void *packet_buffer = NULL;
- struct mpq_demux *mpq_demux = feed->demux->priv;
+ struct mpq_demux *mpq_demux = feed->mpq_demux;
struct ion_client *client = mpq_demux->ion_client;
struct dmx_decoder_buffers *dec_buffs = NULL;
enum mpq_streambuffer_mode mode;
- dec_buffs = feed->feed.ts.decoder_buffers;
+ dec_buffs = feed->dvb_demux_feed->feed.ts.decoder_buffers;
/* Allocate packet buffer holding the meta-data */
packet_buffer = vmalloc(VIDEO_META_DATA_BUFFER_SIZE);
@@ -1258,17 +1494,19 @@
}
static void mpq_dmx_release_streambuffer(
- struct dvb_demux_feed *feed,
+ struct mpq_feed *feed,
struct mpq_video_feed_info *feed_data,
+ struct mpq_streambuffer *video_buffer,
struct ion_client *client)
{
int buf_num = 0;
int i;
- struct dmx_decoder_buffers *dec_buffs = feed->feed.ts.decoder_buffers;
+ struct dmx_decoder_buffers *dec_buffs =
+ feed->dvb_demux_feed->feed.ts.decoder_buffers;
mpq_adapter_unregister_stream_if(feed_data->stream_interface);
- vfree(feed_data->video_buffer->packet_data.data);
+ vfree(video_buffer->packet_data.data);
buf_num = feed_data->buffer_desc.decoder_buffers_num;
@@ -1303,29 +1541,26 @@
}
}
-int mpq_dmx_init_video_feed(struct dvb_demux_feed *feed)
+/**
+ * mpq_dmx_init_video_feed - Initializes of video feed information
+ * used to pass data directly to decoder.
+ *
+ * @mpq_feed: The mpq feed object
+ *
+ * Return error code.
+ */
+static int mpq_dmx_init_video_feed(struct mpq_feed *mpq_feed)
{
int ret;
- struct mpq_video_feed_info *feed_data;
- struct mpq_demux *mpq_demux = feed->demux->priv;
+ struct mpq_video_feed_info *feed_data = &mpq_feed->video_info;
+ struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
struct mpq_streambuffer *stream_buffer;
- /* Allocate memory for private feed data */
- feed_data = vzalloc(sizeof(struct mpq_video_feed_info));
-
- if (feed_data == NULL) {
- MPQ_DVB_ERR_PRINT(
- "%s: FAILED to allocate private video feed data\n",
- __func__);
-
- ret = -ENOMEM;
- goto init_failed;
- }
-
/* get and store framing information if required */
if (!mpq_dmx_info.decoder_framing) {
- mpq_dmx_get_pattern_params(&feed->indexing_params,
- &feed_data->patterns, &feed_data->patterns_num);
+ mpq_dmx_get_pattern_params(
+ &mpq_feed->dvb_demux_feed->indexing_params,
+ &feed_data->patterns, &feed_data->patterns_num);
if (feed_data->patterns == NULL) {
MPQ_DVB_ERR_PRINT(
"%s: FAILED to get framing pattern parameters\n",
@@ -1337,7 +1572,7 @@
}
/* Register the new stream-buffer interface to MPQ adapter */
- switch (feed->pes_type) {
+ switch (mpq_feed->dvb_demux_feed->pes_type) {
case DMX_TS_PES_VIDEO0:
feed_data->stream_interface =
MPQ_ADAPTER_VIDEO0_STREAM_IF;
@@ -1362,7 +1597,7 @@
MPQ_DVB_ERR_PRINT(
"%s: Invalid pes type %d\n",
__func__,
- feed->pes_type);
+ mpq_feed->dvb_demux_feed->pes_type);
ret = -EINVAL;
goto init_failed_free_priv_data;
}
@@ -1385,7 +1620,7 @@
&mpq_dmx_info.decoder_buffers[feed_data->stream_interface];
ret = mpq_dmx_init_streambuffer(
- feed, feed_data, feed_data->video_buffer);
+ mpq_feed, feed_data, feed_data->video_buffer);
if (ret) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_dmx_init_streambuffer failed, err = %d\n",
@@ -1405,13 +1640,12 @@
goto init_failed_free_stream_buffer;
}
- feed_data->pes_payload_address =
- (u32)feed_data->video_buffer->raw_data.data;
+ spin_lock_init(&feed_data->video_buffer_lock);
feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN;
feed_data->pes_header_offset = 0;
- feed->pusi_seen = 0;
- feed->peslen = 0;
+ mpq_feed->dvb_demux_feed->pusi_seen = 0;
+ mpq_feed->dvb_demux_feed->peslen = 0;
feed_data->fullness_wait_cancel = 0;
mpq_streambuffer_get_data_rw_offset(feed_data->video_buffer, NULL,
&feed_data->frame_offset);
@@ -1442,76 +1676,306 @@
mpq_demux->decoder_out_interval_max = 0;
mpq_demux->decoder_ts_errors = 0;
- spin_lock(&mpq_demux->feed_lock);
- feed->priv = (void *)feed_data;
- spin_unlock(&mpq_demux->feed_lock);
-
return 0;
init_failed_free_stream_buffer:
- mpq_dmx_release_streambuffer(feed, feed_data, mpq_demux->ion_client);
+ mpq_dmx_release_streambuffer(mpq_feed, feed_data,
+ feed_data->video_buffer, mpq_demux->ion_client);
mpq_adapter_unregister_stream_if(feed_data->stream_interface);
init_failed_free_priv_data:
- vfree(feed_data);
- feed->priv = NULL;
-init_failed:
-
+ feed_data->video_buffer = NULL;
return ret;
}
-EXPORT_SYMBOL(mpq_dmx_init_video_feed);
-int mpq_dmx_terminate_video_feed(struct dvb_demux_feed *feed)
+/**
+ * mpq_dmx_terminate_video_feed - terminate video feed information
+ * that was previously initialized in mpq_dmx_init_video_feed
+ *
+ * @mpq_feed: The mpq feed used for the video TS packets
+ *
+ * Return error code.
+ */
+static int mpq_dmx_terminate_video_feed(struct mpq_feed *mpq_feed)
{
+ struct mpq_streambuffer *video_buffer;
struct mpq_video_feed_info *feed_data;
- struct mpq_demux *mpq_demux;
+ struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
- if (feed->priv == NULL) {
- MPQ_DVB_ERR_PRINT(
- "%s: invalid feed, feed->priv is NULL\n",
- __func__);
-
+ if (mpq_feed == NULL)
return -EINVAL;
- }
- mpq_demux = feed->demux->priv;
- feed_data = feed->priv;
+ feed_data = &mpq_feed->video_info;
- spin_lock(&mpq_demux->feed_lock);
- feed->priv = NULL;
- spin_unlock(&mpq_demux->feed_lock);
+ spin_lock(&feed_data->video_buffer_lock);
+ video_buffer = feed_data->video_buffer;
+ feed_data->video_buffer = NULL;
+ wake_up_all(&video_buffer->raw_data.queue);
+ spin_unlock(&feed_data->video_buffer_lock);
- wake_up_all(&feed_data->video_buffer->raw_data.queue);
-
- mpq_dmx_release_streambuffer(feed, feed_data, mpq_demux->ion_client);
-
- vfree(feed_data);
+ mpq_dmx_release_streambuffer(mpq_feed, feed_data,
+ video_buffer, mpq_demux->ion_client);
return 0;
}
-EXPORT_SYMBOL(mpq_dmx_terminate_video_feed);
+
+/**
+ * mpq_sdmx_lookup_feed() - Search for a feed object that shares the same
+ * filter of the specified feed object, and return it
+ *
+ * @feed: dvb demux feed object
+ *
+ * Return the mpq_feed sharing the same filter's buffer or NULL if no
+ * such is found.
+ */
+static struct mpq_feed *mpq_sdmx_lookup_feed(struct dvb_demux_feed *feed)
+{
+ int i;
+ struct dvb_demux_feed *tmp;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
+
+ for (i = 0; i < MPQ_MAX_DMX_FILES; i++) {
+ tmp = mpq_demux->feeds[i].dvb_demux_feed;
+ if ((tmp->state == DMX_STATE_GO) &&
+ (tmp != feed) &&
+ (tmp->feed.ts.buffer.ringbuff ==
+ feed->feed.ts.buffer.ringbuff)) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: main feed pid=%d, secondary feed pid=%d\n",
+ __func__, tmp->pid, feed->pid);
+ return &mpq_demux->feeds[i];
+ }
+ }
+
+ return NULL;
+}
+
+static int mpq_sdmx_alloc_data_buf(struct mpq_feed *mpq_feed, size_t size)
+{
+ struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
+ void *buf_base;
+ int ret;
+
+ mpq_feed->sdmx_buf_handle = ion_alloc(mpq_demux->ion_client,
+ size,
+ SZ_4K,
+ ION_HEAP(ION_QSECOM_HEAP_ID),
+ 0);
+ if (IS_ERR_OR_NULL(mpq_feed->sdmx_buf_handle)) {
+ ret = PTR_ERR(mpq_feed->sdmx_buf_handle);
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to allocate sdmx buffer %d\n",
+ __func__, ret);
+ if (!ret)
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ buf_base = ion_map_kernel(mpq_demux->ion_client,
+ mpq_feed->sdmx_buf_handle);
+ if (IS_ERR_OR_NULL(buf_base)) {
+ ret = PTR_ERR(buf_base);
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to map sdmx buffer %d\n",
+ __func__, ret);
+ if (!ret)
+ ret = -ENOMEM;
+ goto failed_free_buf;
+ }
+
+ dvb_ringbuffer_init(&mpq_feed->sdmx_buf, buf_base, size);
+
+ return 0;
+
+failed_free_buf:
+ ion_free(mpq_demux->ion_client, mpq_feed->sdmx_buf_handle);
+ mpq_feed->sdmx_buf_handle = NULL;
+end:
+ return ret;
+}
+
+static int mpq_sdmx_free_data_buf(struct mpq_feed *mpq_feed)
+{
+ struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
+
+ if (mpq_feed->sdmx_buf_handle) {
+ ion_unmap_kernel(mpq_demux->ion_client,
+ mpq_feed->sdmx_buf_handle);
+ mpq_feed->sdmx_buf.data = NULL;
+ ion_free(mpq_demux->ion_client,
+ mpq_feed->sdmx_buf_handle);
+ mpq_feed->sdmx_buf_handle = NULL;
+ }
+
+ return 0;
+}
+
+static int mpq_sdmx_init_metadata_buffer(struct mpq_demux *mpq_demux,
+ struct mpq_feed *feed, struct sdmx_buff_descr *metadata_buff_desc)
+{
+ void *metadata_buff_base;
+ ion_phys_addr_t temp;
+ int ret;
+
+ feed->metadata_buf_handle = ion_alloc(mpq_demux->ion_client,
+ SDMX_METADATA_BUFFER_SIZE,
+ SZ_4K,
+ ION_HEAP(ION_QSECOM_HEAP_ID),
+ 0);
+ if (IS_ERR_OR_NULL(feed->metadata_buf_handle)) {
+ ret = PTR_ERR(feed->metadata_buf_handle);
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to allocate metadata buffer %d\n",
+ __func__, ret);
+ if (!ret)
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ metadata_buff_base = ion_map_kernel(mpq_demux->ion_client,
+ feed->metadata_buf_handle);
+ if (IS_ERR_OR_NULL(metadata_buff_base)) {
+ ret = PTR_ERR(metadata_buff_base);
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to map metadata buffer %d\n",
+ __func__, ret);
+ if (!ret)
+ ret = -ENOMEM;
+ goto failed_free_metadata_buf;
+ }
+
+ ret = ion_phys(mpq_demux->ion_client,
+ feed->metadata_buf_handle,
+ &temp,
+ &metadata_buff_desc->size);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to get physical address %d\n",
+ __func__, ret);
+ goto failed_unmap_metadata_buf;
+ }
+ metadata_buff_desc->base_addr = (void *)temp;
+
+ dvb_ringbuffer_init(&feed->metadata_buf, metadata_buff_base,
+ SDMX_METADATA_BUFFER_SIZE);
+
+ return 0;
+
+failed_unmap_metadata_buf:
+ ion_unmap_kernel(mpq_demux->ion_client, feed->metadata_buf_handle);
+failed_free_metadata_buf:
+ ion_free(mpq_demux->ion_client, feed->metadata_buf_handle);
+ feed->metadata_buf_handle = NULL;
+end:
+ return ret;
+}
+
+static int mpq_sdmx_terminate_metadata_buffer(struct mpq_feed *mpq_feed)
+{
+ struct mpq_demux *mpq_demux = mpq_feed->mpq_demux;
+
+ if (mpq_feed->metadata_buf_handle) {
+ ion_unmap_kernel(mpq_demux->ion_client,
+ mpq_feed->metadata_buf_handle);
+ mpq_feed->metadata_buf.data = NULL;
+ ion_free(mpq_demux->ion_client,
+ mpq_feed->metadata_buf_handle);
+ mpq_feed->metadata_buf_handle = NULL;
+ }
+
+ return 0;
+}
+
+int mpq_dmx_terminate_feed(struct dvb_demux_feed *feed)
+{
+ int ret = 0;
+ struct mpq_demux *mpq_demux;
+ struct mpq_feed *mpq_feed;
+ struct mpq_feed *main_rec_feed;
+
+ if (feed == NULL)
+ return -EINVAL;
+
+ mpq_demux = feed->demux->priv;
+
+ mutex_lock(&mpq_demux->mutex);
+ mpq_feed = feed->priv;
+
+ if (mpq_feed->sdmx_filter_handle != SDMX_INVALID_FILTER_HANDLE) {
+ if (mpq_feed->filter_type == SDMX_RAW_FILTER)
+ main_rec_feed = mpq_sdmx_lookup_feed(feed);
+ else
+ main_rec_feed = NULL;
+
+ if (main_rec_feed) {
+ /* This feed is part of a recording filter */
+ MPQ_DVB_DBG_PRINT(
+ "%s: Removing raw pid %d from filter %d\n",
+ __func__, feed->pid,
+ mpq_feed->sdmx_filter_handle);
+ ret = sdmx_remove_raw_pid(
+ mpq_demux->sdmx_session_handle,
+ mpq_feed->sdmx_filter_handle, feed->pid);
+ if (ret)
+ MPQ_DVB_ERR_PRINT(
+ "%s: SDMX_remove_raw_pid failed. ret = %d\n",
+ __func__, ret);
+
+ /* If this feed that we are removing was set as primary,
+ * now other feeds should be set as primary
+ */
+ if (!mpq_feed->secondary_feed)
+ main_rec_feed->secondary_feed = 0;
+ } else {
+ MPQ_DVB_DBG_PRINT("%s: Removing filter %d, pid %d\n",
+ __func__, mpq_feed->sdmx_filter_handle,
+ feed->pid);
+ ret = sdmx_remove_filter(mpq_demux->sdmx_session_handle,
+ mpq_feed->sdmx_filter_handle);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: SDMX_remove_filter failed. ret = %d\n",
+ __func__, ret);
+ }
+
+ mpq_demux->sdmx_filter_count--;
+ mpq_feed->sdmx_filter_handle =
+ SDMX_INVALID_FILTER_HANDLE;
+ }
+
+ mpq_sdmx_close_session(mpq_demux);
+ }
+
+ if (mpq_dmx_is_video_feed(feed)) {
+ ret = mpq_dmx_terminate_video_feed(mpq_feed);
+ if (ret)
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_dmx_terminate_video_feed failed. ret = %d\n",
+ __func__, ret);
+ }
+
+ if (mpq_feed->sdmx_buf_handle) {
+ wake_up_all(&mpq_feed->sdmx_buf.queue);
+ mpq_sdmx_free_data_buf(mpq_feed);
+ }
+
+ mpq_sdmx_terminate_metadata_buffer(mpq_feed);
+
+ mutex_unlock(&mpq_demux->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(mpq_dmx_terminate_feed);
int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed)
{
- struct mpq_demux *mpq_demux = feed->demux->priv;
-
if (mpq_dmx_is_video_feed(feed)) {
+ struct mpq_feed *mpq_feed;
struct mpq_video_feed_info *feed_data;
- spin_lock(&mpq_demux->feed_lock);
-
- if (feed->priv == NULL) {
- MPQ_DVB_ERR_PRINT(
- "%s: invalid feed, feed->priv is NULL\n",
- __func__);
- spin_unlock(&mpq_demux->feed_lock);
- return -EINVAL;
- }
-
- feed_data = feed->priv;
+ mpq_feed = feed->priv;
+ feed_data = &mpq_feed->video_info;
feed_data->fullness_wait_cancel = 0;
- spin_unlock(&mpq_demux->feed_lock);
-
return 0;
}
@@ -1531,8 +1995,6 @@
size_t required_space)
{
u32 free = mpq_streambuffer_data_free(sbuff);
- MPQ_DVB_DBG_PRINT("%s: stream buffer free = %d, required = %d\n",
- __func__, free, required_space);
/*
* For linear buffers, verify there's enough space for this TSP
@@ -1554,7 +2016,9 @@
struct mpq_demux *mpq_demux = feed->demux->priv;
struct mpq_streambuffer *sbuff = NULL;
struct mpq_video_feed_info *feed_data;
- int ret;
+ struct mpq_feed *mpq_feed;
+ int ret = 0;
+ int was_locked;
if (!mpq_dmx_is_video_feed(feed)) {
MPQ_DVB_DBG_PRINT("%s: Invalid feed type %d\n",
@@ -1563,21 +2027,26 @@
return -EINVAL;
}
- spin_lock(&mpq_demux->feed_lock);
- if (feed->priv == NULL) {
- spin_unlock(&mpq_demux->feed_lock);
- return -EINVAL;
+ if (mutex_is_locked(&mpq_demux->mutex)) {
+ was_locked = 1;
+ } else {
+ mutex_lock(&mpq_demux->mutex);
+ was_locked = 0;
}
- feed_data = feed->priv;
+
+ mpq_feed = feed->priv;
+ feed_data = &mpq_feed->video_info;
+
sbuff = feed_data->video_buffer;
if (sbuff == NULL) {
- spin_unlock(&mpq_demux->feed_lock);
+ if (!was_locked)
+ mutex_unlock(&mpq_demux->mutex);
MPQ_DVB_ERR_PRINT("%s: mpq_streambuffer object is NULL\n",
__func__);
return -EINVAL;
}
- if ((feed_data != NULL) &&
+ if ((feed_data->video_buffer != NULL) &&
(!feed_data->fullness_wait_cancel) &&
(!mpq_dmx_check_decoder_fullness(sbuff, required_space))) {
DEFINE_WAIT(__wait);
@@ -1585,17 +2054,16 @@
prepare_to_wait(&sbuff->raw_data.queue,
&__wait,
TASK_INTERRUPTIBLE);
-
- if ((feed->priv == NULL) ||
+ if (!feed_data->video_buffer ||
feed_data->fullness_wait_cancel ||
mpq_dmx_check_decoder_fullness(sbuff,
required_space))
break;
if (!signal_pending(current)) {
- spin_unlock(&mpq_demux->feed_lock);
+ mutex_unlock(&mpq_demux->mutex);
schedule();
- spin_lock(&mpq_demux->feed_lock);
+ mutex_lock(&mpq_demux->mutex);
continue;
}
@@ -1606,46 +2074,47 @@
}
if (ret < 0) {
- spin_unlock(&mpq_demux->feed_lock);
+ if (!was_locked)
+ mutex_unlock(&mpq_demux->mutex);
return ret;
}
- if ((feed->priv == NULL) || (feed_data->fullness_wait_cancel)) {
- spin_unlock(&mpq_demux->feed_lock);
+ if ((feed_data->fullness_wait_cancel) ||
+ (feed_data->video_buffer == NULL)) {
+ if (!was_locked)
+ mutex_unlock(&mpq_demux->mutex);
return -EINVAL;
}
- spin_unlock(&mpq_demux->feed_lock);
+ mutex_unlock(&mpq_demux->mutex);
return 0;
}
EXPORT_SYMBOL(mpq_dmx_decoder_fullness_wait);
int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed)
{
- struct mpq_demux *mpq_demux = feed->demux->priv;
-
if (mpq_dmx_is_video_feed(feed)) {
+ struct mpq_feed *mpq_feed;
struct mpq_video_feed_info *feed_data;
struct dvb_ringbuffer *video_buff;
- spin_lock(&mpq_demux->feed_lock);
-
- if (feed->priv == NULL) {
- MPQ_DVB_ERR_PRINT(
- "%s: invalid feed, feed->priv is NULL\n",
- __func__);
- spin_unlock(&mpq_demux->feed_lock);
- return -EINVAL;
- }
-
- feed_data = feed->priv;
-
- video_buff = &feed_data->video_buffer->raw_data;
+ mpq_feed = feed->priv;
+ feed_data = &mpq_feed->video_info;
feed_data->fullness_wait_cancel = 1;
- spin_unlock(&mpq_demux->feed_lock);
+ spin_lock(&feed_data->video_buffer_lock);
+ if (feed_data->video_buffer == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: video_buffer released\n",
+ __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
+ return 0;
+ }
+
+ video_buff = &feed_data->video_buffer->raw_data;
wake_up_all(&video_buff->queue);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0;
}
@@ -1920,6 +2389,11 @@
struct dmx_data_ready *data)
{
size_t len = 0;
+ struct dmx_pts_dts_info *pts_dts;
+
+ pts_dts = meta_data->packet_type == DMX_PES_PACKET ?
+ &meta_data->info.pes.pts_dts_info :
+ &meta_data->info.framing.pts_dts_info;
data->data_length = 0;
data->buf.handle = packet->raw_data_handle;
@@ -1928,10 +2402,10 @@
feed_data->last_pkt_index, &len);
data->buf.offset = packet->raw_data_offset;
data->buf.len = packet->raw_data_len;
- data->buf.pts_exists = meta_data->info.framing.pts_dts_info.pts_exist;
- data->buf.pts = meta_data->info.framing.pts_dts_info.pts;
- data->buf.dts_exists = meta_data->info.framing.pts_dts_info.dts_exist;
- data->buf.dts = meta_data->info.framing.pts_dts_info.dts;
+ data->buf.pts_exists = pts_dts->pts_exist;
+ data->buf.pts = pts_dts->pts;
+ data->buf.dts_exists = pts_dts->dts_exist;
+ data->buf.dts = pts_dts->dts;
data->buf.tei_counter = feed_data->tei_errs;
data->buf.cont_err_counter = feed_data->continuity_errs;
data->buf.ts_packets_num = feed_data->ts_packets_num;
@@ -1959,6 +2433,7 @@
struct mpq_streambuffer *stream_buffer;
struct pes_packet_header *pes_header;
struct mpq_demux *mpq_demux;
+ struct mpq_feed *mpq_feed;
struct mpq_framing_pattern_lookup_results framing_res;
struct mpq_streambuffer_packet_header packet;
@@ -1976,18 +2451,28 @@
mpq_demux = feed->demux->priv;
- spin_lock(&mpq_demux->feed_lock);
+ mpq_feed = feed->priv;
+ feed_data = &mpq_feed->video_info;
- feed_data = feed->priv;
- if (unlikely(feed_data == NULL)) {
- spin_unlock(&mpq_demux->feed_lock);
+ /*
+ * spin-lock is taken to protect against manipulation of video
+ * output buffer by the API (terminate video feed, re-use of video
+ * buffers). Mutex on the video-feed cannot be held here
+ * since SW demux holds a spin-lock while calling write_to_decoder
+ */
+ spin_lock(&feed_data->video_buffer_lock);
+ stream_buffer = feed_data->video_buffer;
+
+ if (stream_buffer == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: video_buffer released\n",
+ __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0;
}
ts_header = (const struct ts_packet_header *)buf;
- stream_buffer = feed_data->video_buffer;
-
pes_header = &feed_data->pes_header;
/* Make sure this TS packet has a payload and not scrambled */
@@ -1996,7 +2481,7 @@
(ts_header->adaptation_field_control == 2) ||
(ts_header->transport_scrambling_control)) {
/* continue to next packet */
- spin_unlock(&mpq_demux->feed_lock);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0;
}
@@ -2028,7 +2513,7 @@
* otherwise the data is dropped
*/
if (!feed->pusi_seen) {
- spin_unlock(&mpq_demux->feed_lock);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0; /* drop and wait for next packets */
}
@@ -2055,7 +2540,7 @@
pes_header, buf,
&ts_payload_offset,
&bytes_avail)) {
- spin_unlock(&mpq_demux->feed_lock);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0;
}
@@ -2063,7 +2548,7 @@
pes_header, buf,
&ts_payload_offset,
&bytes_avail)) {
- spin_unlock(&mpq_demux->feed_lock);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0;
}
@@ -2072,7 +2557,7 @@
* then we are now at the PES payload data
*/
if (bytes_avail == 0) {
- spin_unlock(&mpq_demux->feed_lock);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0;
}
@@ -2125,7 +2610,7 @@
* or equivalent is found. Otherwise the data is dropped.
*/
if (!(feed_data->found_sequence_header_pattern)) {
- spin_unlock(&mpq_demux->feed_lock);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0;
}
@@ -2231,9 +2716,6 @@
feed->indexing_params.standard,
feed_data->last_framing_match_type);
if (is_video_frame == 1) {
- struct timespec curr_time, delta_time;
- u64 delta_time_ms;
-
mpq_dmx_write_pts_dts(feed_data,
&(meta_data.info.framing.pts_dts_info));
mpq_dmx_save_pts_dts(feed_data);
@@ -2248,33 +2730,7 @@
0, /* current write buffer handle */
&packet.raw_data_handle);
- curr_time = current_kernel_time();
- if (likely(mpq_demux->decoder_out_count)) {
- /* calculate time-delta between frame */
- delta_time = timespec_sub(curr_time,
- mpq_demux->decoder_out_last_time);
-
- delta_time_ms =
- ((u64)delta_time.tv_sec * MSEC_PER_SEC)
- + delta_time.tv_nsec / NSEC_PER_MSEC;
-
- mpq_demux->decoder_out_interval_sum +=
- (u32)delta_time_ms;
-
- mpq_demux->
- decoder_out_interval_average =
- mpq_demux->decoder_out_interval_sum /
- mpq_demux->decoder_out_count;
-
- if (delta_time_ms >
- mpq_demux->decoder_out_interval_max)
- mpq_demux->
- decoder_out_interval_max =
- delta_time_ms;
- }
-
- mpq_demux->decoder_out_last_time = curr_time;
- mpq_demux->decoder_out_count++;
+ mpq_dmx_update_decoder_stat(mpq_demux);
/*
* writing meta-data that includes
@@ -2328,7 +2784,7 @@
}
}
- spin_unlock(&mpq_demux->feed_lock);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0;
}
@@ -2343,23 +2799,32 @@
struct mpq_streambuffer *stream_buffer;
struct pes_packet_header *pes_header;
struct mpq_demux *mpq_demux;
+ struct mpq_feed *mpq_feed;
int discontinuity_indicator = 0;
struct dmx_data_ready data;
mpq_demux = feed->demux->priv;
+ mpq_feed = feed->priv;
+ feed_data = &mpq_feed->video_info;
- spin_lock(&mpq_demux->feed_lock);
-
- feed_data = feed->priv;
- if (unlikely(feed_data == NULL)) {
- spin_unlock(&mpq_demux->feed_lock);
+ /*
+ * spin-lock is taken to protect against manipulation of video
+ * output buffer by the API (terminate video feed, re-use of video
+ * buffers). Mutex on the video-feed cannot be held here
+ * since SW demux holds a spin-lock while calling write_to_decoder
+ */
+ spin_lock(&feed_data->video_buffer_lock);
+ stream_buffer = feed_data->video_buffer;
+ if (stream_buffer == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: video_buffer released\n",
+ __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0;
}
ts_header = (const struct ts_packet_header *)buf;
- stream_buffer = feed_data->video_buffer;
-
pes_header = &feed_data->pes_header;
/* Make sure this TS packet has a payload and not scrambled */
@@ -2368,7 +2833,7 @@
(ts_header->adaptation_field_control == 2) ||
(ts_header->transport_scrambling_control)) {
/* continue to next packet */
- spin_unlock(&mpq_demux->feed_lock);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0;
}
@@ -2402,6 +2867,8 @@
meta_data.packet_type = DMX_PES_PACKET;
+ mpq_dmx_update_decoder_stat(mpq_demux);
+
if (mpq_streambuffer_pkt_write(
stream_buffer,
&packet,
@@ -2436,10 +2903,6 @@
}
/* Reset PES info */
- feed_data->pes_payload_address =
- (u32)stream_buffer->raw_data.data +
- stream_buffer->raw_data.pwrite;
-
feed->peslen = 0;
feed_data->pes_header_offset = 0;
feed_data->pes_header_left_bytes =
@@ -2454,7 +2917,7 @@
* otherwise the data is dropped
*/
if (!feed->pusi_seen) {
- spin_unlock(&mpq_demux->feed_lock);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0; /* drop and wait for next packets */
}
@@ -2481,7 +2944,7 @@
pes_header, buf,
&ts_payload_offset,
&bytes_avail)) {
- spin_unlock(&mpq_demux->feed_lock);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0;
}
@@ -2489,7 +2952,7 @@
pes_header, buf,
&ts_payload_offset,
&bytes_avail)) {
- spin_unlock(&mpq_demux->feed_lock);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0;
}
@@ -2498,7 +2961,7 @@
* then we are now at the PES payload data
*/
if (bytes_avail == 0) {
- spin_unlock(&mpq_demux->feed_lock);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0;
}
@@ -2529,7 +2992,7 @@
feed->peslen += bytes_avail;
}
- spin_unlock(&mpq_demux->feed_lock);
+ spin_unlock(&feed_data->video_buffer_lock);
return 0;
}
@@ -2540,6 +3003,7 @@
struct mpq_demux *mpq_demux = feed->demux->priv;
struct mpq_video_feed_info *feed_data;
struct mpq_streambuffer *video_buff;
+ struct mpq_feed *mpq_feed;
if (!mpq_dmx_is_video_feed(feed)) {
MPQ_DVB_ERR_PRINT(
@@ -2549,20 +3013,13 @@
return -EINVAL;
}
- spin_lock(&mpq_demux->feed_lock);
+ mutex_lock(&mpq_demux->mutex);
- if (feed->priv == NULL) {
- MPQ_DVB_ERR_PRINT(
- "%s: invalid feed, feed->priv is NULL\n",
- __func__);
- spin_unlock(&mpq_demux->feed_lock);
- return -EINVAL;
- }
-
- feed_data = feed->priv;
+ mpq_feed = feed->priv;
+ feed_data = &mpq_feed->video_info;
video_buff = feed_data->video_buffer;
if (!video_buff) {
- spin_unlock(&mpq_demux->feed_lock);
+ mutex_unlock(&mpq_demux->mutex);
return -EINVAL;
}
@@ -2592,7 +3049,7 @@
&dmx_buffer_status->read_offset,
&dmx_buffer_status->write_offset);
- spin_unlock(&mpq_demux->feed_lock);
+ mutex_unlock(&mpq_demux->mutex);
return 0;
}
@@ -2609,17 +3066,21 @@
}
EXPORT_SYMBOL(mpq_dmx_process_video_packet);
-int mpq_dmx_process_pcr_packet(
- struct dvb_demux_feed *feed,
- const u8 *buf)
+/*
+ * Extract the PCR field and discontinuity indicator from a TS packet buffer
+ * @buf: TSP buffer
+ * @pcr: returned PCR value
+ * @dci: returned discontinuity indicator
+ * Returns 1 if PCR was extracted, 0 otherwise.
+ */
+static int mpq_dmx_extract_pcr_and_dci(const u8 *buf, u64 *pcr, int *dci)
{
- u64 pcr;
- u64 stc;
- struct dmx_data_ready data;
- struct mpq_demux *mpq_demux = feed->demux->priv;
const struct ts_packet_header *ts_header;
const struct ts_adaptation_field *adaptation_field;
+ if (buf == NULL || pcr == NULL || dci == NULL)
+ return 0;
+
ts_header = (const struct ts_packet_header *)buf;
/* Make sure this TS packet has a adaptation field */
@@ -2636,16 +3097,32 @@
(!adaptation_field->PCR_flag))
return 0; /* 0 adaptation field or no PCR */
- pcr = ((u64)adaptation_field->program_clock_reference_base_1) << 25;
- pcr += ((u64)adaptation_field->program_clock_reference_base_2) << 17;
- pcr += ((u64)adaptation_field->program_clock_reference_base_3) << 9;
- pcr += ((u64)adaptation_field->program_clock_reference_base_4) << 1;
- pcr += adaptation_field->program_clock_reference_base_5;
- pcr *= 300;
- pcr +=
- (((u64)adaptation_field->program_clock_reference_ext_1) << 8) +
+ *pcr = ((u64)adaptation_field->program_clock_reference_base_1) << 25;
+ *pcr += ((u64)adaptation_field->program_clock_reference_base_2) << 17;
+ *pcr += ((u64)adaptation_field->program_clock_reference_base_3) << 9;
+ *pcr += ((u64)adaptation_field->program_clock_reference_base_4) << 1;
+ *pcr += adaptation_field->program_clock_reference_base_5;
+ *pcr *= 300;
+ *pcr += (((u64)adaptation_field->program_clock_reference_ext_1) << 8) +
adaptation_field->program_clock_reference_ext_2;
+ *dci = adaptation_field->discontinuity_indicator;
+
+ return 1;
+}
+
+int mpq_dmx_process_pcr_packet(
+ struct dvb_demux_feed *feed,
+ const u8 *buf)
+{
+ u64 stc;
+ struct dmx_data_ready data;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
+
+ if (0 == mpq_dmx_extract_pcr_and_dci(buf, &data.pcr.pcr,
+ &data.pcr.disc_indicator_set))
+ return 0;
+
/*
* When we play from front-end, we configure HW
* to output the extra timestamp, if we are playing
@@ -2663,12 +3140,1333 @@
}
data.data_length = 0;
- data.pcr.pcr = pcr;
data.pcr.stc = stc;
- data.pcr.disc_indicator_set = adaptation_field->discontinuity_indicator;
data.status = DMX_OK_PCR;
feed->data_ready_cb.ts(&feed->feed.ts, &data);
return 0;
}
EXPORT_SYMBOL(mpq_dmx_process_pcr_packet);
+
+int mpq_dmx_set_secure_mode(struct dmx_demux *demux,
+ struct dmx_secure_mode *sec_mode)
+{
+ struct dvb_demux *dvb_demux;
+ struct mpq_demux *mpq_demux;
+ struct mpq_demux_keyladder_info *key_info;
+ int found = 0;
+ int ret;
+
+ if ((demux == NULL) || (demux->priv == NULL))
+ return -EINVAL;
+
+ dvb_demux = demux->priv;
+ mpq_demux = dvb_demux->priv;
+
+ if (mpq_demux == NULL)
+ return -EINVAL;
+
+ mutex_lock(&mpq_demux->mutex);
+
+ /* Lookup pid in the keyladder list */
+ list_for_each_entry(key_info, &mpq_demux->keyladder_list, list) {
+ if (key_info->pid == sec_mode->pid) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!sec_mode->is_secured) {
+ /* Key info needs to be removed */
+ if (found) {
+ key_info->refs--;
+ if (0 == key_info->refs) {
+ list_del(&key_info->list);
+ vfree(key_info);
+ mpq_demux->secure_mode_count--;
+ }
+ }
+ } else {
+ if (!found) {
+ key_info = vzalloc(sizeof(*key_info));
+ if (key_info == NULL)
+ return -ENOMEM;
+ key_info->pid = sec_mode->pid;
+ key_info->keyladder_id = sec_mode->key_ladder_id;
+ list_add(&key_info->list, &mpq_demux->keyladder_list);
+ mpq_demux->secure_mode_count++;
+ } else {
+ key_info->keyladder_id = sec_mode->key_ladder_id;
+ }
+
+ /*
+ * If secure demux is active, set the KL now,
+ * otherwise it will be set when secure-demux is started
+ * (when filtering starts).
+ */
+ if (mpq_demux->sdmx_session_handle !=
+ SDMX_INVALID_SESSION_HANDLE) {
+ ret = sdmx_set_kl_ind(mpq_demux->sdmx_session_handle,
+ sec_mode->pid,
+ sec_mode->key_ladder_id);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to set keyladder, ret=%d\n",
+ __func__, ret);
+ }
+ }
+
+ key_info->refs++;
+ }
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: Setting pid=%d, key_id=%d, is_secured=%d, count=%d\n",
+ __func__, sec_mode->pid, sec_mode->key_ladder_id,
+ sec_mode->is_secured, mpq_demux->secure_mode_count);
+
+ mutex_unlock(&mpq_demux->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpq_dmx_set_secure_mode);
+
+int mpq_sdmx_open_session(struct mpq_demux *mpq_demux)
+{
+ enum sdmx_status ret = SDMX_SUCCESS;
+ enum sdmx_proc_mode proc_mode;
+ enum sdmx_pkt_format pkt_format;
+
+ MPQ_DVB_DBG_PRINT("%s: ref_count %d\n",
+ __func__, mpq_demux->sdmx_session_ref_count);
+
+ if (mpq_demux->sdmx_session_ref_count) {
+ /* session is already open */
+ mpq_demux->sdmx_session_ref_count++;
+ return ret;
+ }
+
+ proc_mode = (mpq_demux->demux.playback_mode == DMX_PB_MODE_PUSH) ?
+ SDMX_PUSH_MODE : SDMX_PULL_MODE;
+ MPQ_DVB_DBG_PRINT(
+ "%s: Proc mode = %s\n",
+ __func__, SDMX_PUSH_MODE == proc_mode ? "Push" : "Pull");
+
+ if (mpq_demux->source < DMX_SOURCE_DVR0) {
+ pkt_format = SDMX_192_BYTE_PKT;
+ } else if (DMX_TSP_FORMAT_188 == mpq_demux->demux.tsp_format) {
+ pkt_format = SDMX_188_BYTE_PKT;
+ } else if (DMX_TSP_FORMAT_192_TAIL == mpq_demux->demux.tsp_format) {
+ pkt_format = SDMX_192_BYTE_PKT;
+ } else {
+ MPQ_DVB_ERR_PRINT("%s: invalid tsp format\n", __func__);
+ return -EINVAL;
+ }
+
+ MPQ_DVB_DBG_PRINT("%s: (%s) source, packet format: %d\n",
+ __func__,
+ (mpq_demux->source < DMX_SOURCE_DVR0) ?
+ "frontend" : "DVR", pkt_format);
+
+ /* open session and set configuration */
+ ret = sdmx_open_session(&mpq_demux->sdmx_session_handle);
+ if (ret != SDMX_SUCCESS) {
+ MPQ_DVB_ERR_PRINT("%s: Could not open session. ret=%d\n",
+ __func__ , ret);
+ return ret;
+ }
+
+ MPQ_DVB_DBG_PRINT("%s: new session_handle = %d\n",
+ __func__ , mpq_demux->sdmx_session_handle);
+
+ ret = sdmx_set_session_cfg(mpq_demux->sdmx_session_handle,
+ proc_mode,
+ SDMX_PKT_ENC_MODE,
+ pkt_format,
+ mpq_sdmx_scramble_odd,
+ mpq_sdmx_scramble_even);
+ if (ret != SDMX_SUCCESS) {
+ MPQ_DVB_ERR_PRINT("%s: Could not set session config. ret=%d\n",
+ __func__, ret);
+ sdmx_close_session(mpq_demux->sdmx_session_handle);
+ mpq_demux->sdmx_session_handle = SDMX_INVALID_SESSION_HANDLE;
+ return -EINVAL;
+ }
+
+ mpq_demux->sdmx_process_count = 0;
+ mpq_demux->sdmx_process_time_sum = 0;
+ mpq_demux->sdmx_process_time_average = 0;
+ mpq_demux->sdmx_process_time_max = 0;
+ mpq_demux->sdmx_process_packets_sum = 0;
+ mpq_demux->sdmx_process_packets_average = 0;
+ mpq_demux->sdmx_process_packets_min = 0;
+
+ mpq_demux->sdmx_session_ref_count++;
+ return ret;
+}
+EXPORT_SYMBOL(mpq_sdmx_open_session);
+
+int mpq_sdmx_close_session(struct mpq_demux *mpq_demux)
+{
+ int ret = 0;
+ enum sdmx_status status;
+
+ MPQ_DVB_DBG_PRINT("%s: session_handle = %d, ref_count %d\n",
+ __func__,
+ mpq_demux->sdmx_session_handle,
+ mpq_demux->sdmx_session_ref_count);
+
+ if (!mpq_demux->sdmx_session_ref_count)
+ return -EINVAL;
+
+ if (mpq_demux->sdmx_session_ref_count == 1) {
+ status = sdmx_close_session(mpq_demux->sdmx_session_handle);
+ if (status != SDMX_SUCCESS) {
+ MPQ_DVB_ERR_PRINT("%s: sdmx_close_session failed %d\n",
+ __func__, status);
+ return -EINVAL;
+ }
+ mpq_demux->sdmx_session_handle = SDMX_INVALID_SESSION_HANDLE;
+ }
+
+ mpq_demux->sdmx_session_ref_count--;
+
+ return ret;
+}
+EXPORT_SYMBOL(mpq_sdmx_close_session);
+
+static int mpq_sdmx_init_data_buffer(struct mpq_demux *mpq_demux,
+ struct mpq_feed *feed, u32 *num_buffers,
+ struct sdmx_buff_descr *buf_desc, enum sdmx_buf_mode *buf_mode)
+{
+ struct dvb_demux_feed *dvbdmx_feed = feed->dvb_demux_feed;
+ struct dvb_ringbuffer *buffer;
+ struct mpq_video_feed_info *feed_data = &feed->video_info;
+ ion_phys_addr_t addr;
+ struct ion_handle *sdmx_buff;
+ int ret;
+ int i;
+
+ *buf_mode = SDMX_RING_BUF;
+
+ if (mpq_dmx_is_video_feed(feed->dvb_demux_feed)) {
+ if (feed_data->buffer_desc.decoder_buffers_num > 1)
+ *buf_mode = SDMX_LINEAR_GROUP_BUF;
+ *num_buffers = feed_data->buffer_desc.decoder_buffers_num;
+
+ for (i = 0; i < *num_buffers; i++) {
+ ret = ion_phys(mpq_demux->ion_client,
+ feed_data->buffer_desc.ion_handle[i],
+ &addr, &buf_desc[i].size);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to get physical buffer address, ret=%d\n",
+ __func__, ret);
+ goto end;
+ }
+ buf_desc[i].base_addr = (void *)addr;
+ buf_desc[i].size = feed_data->buffer_desc.desc[i].size;
+ }
+ } else {
+ *num_buffers = 1;
+ if (mpq_dmx_is_sec_feed(dvbdmx_feed) ||
+ mpq_dmx_is_pcr_feed(dvbdmx_feed)) {
+ buffer = &feed->sdmx_buf;
+ sdmx_buff = feed->sdmx_buf_handle;
+ } else {
+ buffer = (struct dvb_ringbuffer *)
+ dvbdmx_feed->feed.ts.buffer.ringbuff;
+ sdmx_buff = dvbdmx_feed->feed.ts.buffer.priv_handle;
+ }
+
+ if (sdmx_buff == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Invalid buffer allocation\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ ret = ion_phys(mpq_demux->ion_client, sdmx_buff, &addr,
+ &buf_desc[0].size);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to get physical buffer address, ret=%d\n",
+ __func__, ret);
+ goto end;
+ } else {
+ buf_desc[0].size = buffer->size;
+ buf_desc[0].base_addr = (void *)addr;
+ }
+
+ }
+ return 0;
+
+end:
+ return ret;
+}
+
+
+static int mpq_sdmx_filter_setup(struct mpq_demux *mpq_demux,
+ struct dvb_demux_feed *dvbdmx_feed)
+{
+ int ret = 0;
+ struct mpq_feed *feed;
+ struct mpq_feed *main_rec_feed;
+ struct sdmx_buff_descr metadata_buff_desc;
+ struct sdmx_buff_descr data_buff_desc[DMX_MAX_DECODER_BUFFER_NUM];
+ u32 data_buf_num = DMX_MAX_DECODER_BUFFER_NUM;
+ enum sdmx_buf_mode buf_mode;
+ struct mpq_demux_keyladder_info *key_info;
+
+ feed = dvbdmx_feed->priv;
+
+ if (mpq_dmx_is_sec_feed(dvbdmx_feed)) {
+ feed->filter_type = SDMX_SECTION_FILTER;
+ MPQ_DVB_DBG_PRINT("%s: SDMX_SECTION_FILTER\n", __func__);
+ } else if (mpq_dmx_is_pcr_feed(dvbdmx_feed)) {
+ feed->filter_type = SDMX_PCR_FILTER;
+ MPQ_DVB_DBG_PRINT("%s: SDMX_PCR_FILTER\n", __func__);
+ } else if (mpq_dmx_is_video_feed(dvbdmx_feed)) {
+ feed->filter_type = SDMX_SEPARATED_PES_FILTER;
+ MPQ_DVB_DBG_PRINT("%s: SDMX_SEPARATED_PES_FILTER\n", __func__);
+ } else if (mpq_dmx_is_rec_feed(dvbdmx_feed)) {
+ feed->filter_type = SDMX_RAW_FILTER;
+ MPQ_DVB_DBG_PRINT("%s: SDMX_RAW_FILTER\n", __func__);
+ } else {
+ feed->filter_type = SDMX_PES_FILTER;
+ MPQ_DVB_DBG_PRINT("%s: SDMX_PES_FILTER\n", __func__);
+ }
+
+ /*
+ * Recording feed sdmx filter handle lookup:
+ * In case this is a recording filter with multiple feeds,
+ * this feed is either the first feed of a new recording filter,
+ * or it is another feed of an existing filter for which a filter was
+ * already opened with sdmx. In such case, we need to look up in the
+ * feed pool for a allocated feed with same output buffer (meaning they
+ * belong to the same filter) and to use the already allocated sdmx
+ * filter handle.
+ */
+ if (feed->filter_type == SDMX_RAW_FILTER)
+ main_rec_feed = mpq_sdmx_lookup_feed(dvbdmx_feed);
+ else
+ main_rec_feed = NULL;
+
+ /*
+ * If this PID is not part of existing recording filter,
+ * configure a new filter to SDMX.
+ */
+ if (!main_rec_feed) {
+ feed->secondary_feed = 0;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: Adding new sdmx filter, pid %d\n",
+ __func__, dvbdmx_feed->pid);
+
+ /* Meta-data initialization,
+ * Recording filters do no need meta-data buffers.
+ */
+ if (mpq_dmx_is_rec_feed(dvbdmx_feed)) {
+ metadata_buff_desc.base_addr = 0;
+ metadata_buff_desc.size = 0;
+ } else {
+ ret = mpq_sdmx_init_metadata_buffer(mpq_demux, feed,
+ &metadata_buff_desc);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Failed to initialize metadata buffer. ret=%d\n",
+ __func__, ret);
+ goto end;
+ }
+ }
+
+ ret = mpq_sdmx_init_data_buffer(mpq_demux, feed, &data_buf_num,
+ data_buff_desc, &buf_mode);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Failed to initialize data buffer. ret=%d\n",
+ __func__, ret);
+ mpq_sdmx_terminate_metadata_buffer(feed);
+ goto end;
+ }
+ ret = sdmx_add_filter(mpq_demux->sdmx_session_handle,
+ dvbdmx_feed->pid,
+ feed->filter_type,
+ &metadata_buff_desc,
+ buf_mode,
+ data_buf_num,
+ data_buff_desc,
+ &feed->sdmx_filter_handle);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: SDMX_add_filter failed. ret = %d\n",
+ __func__, ret);
+ ret = -ENODEV;
+ mpq_sdmx_terminate_metadata_buffer(feed);
+ goto end;
+ }
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: feed=0x%p, filter pid=%d, handle=%d, data buffer(s)=%d, size=%d\n",
+ __func__, feed, dvbdmx_feed->pid,
+ feed->sdmx_filter_handle,
+ data_buf_num, data_buff_desc[0].size);
+
+ mpq_demux->sdmx_filter_count++;
+ } else {
+ MPQ_DVB_DBG_PRINT(
+ "%s: Adding RAW pid to sdmx, pid %d\n",
+ __func__, dvbdmx_feed->pid);
+
+ feed->secondary_feed = 1;
+ feed->sdmx_filter_handle = main_rec_feed->sdmx_filter_handle;
+ ret = sdmx_add_raw_pid(mpq_demux->sdmx_session_handle,
+ feed->sdmx_filter_handle, dvbdmx_feed->pid);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to add raw pid, ret=%d\n",
+ __func__, ret);
+ ret = -ENODEV;
+ goto end;
+ }
+ }
+
+ /*
+ * If pid has a key ladder id associated, we need to
+ * set it to SDMX.
+ */
+ list_for_each_entry(key_info, &mpq_demux->keyladder_list, list) {
+ if (key_info->pid == dvbdmx_feed->pid) {
+ ret = sdmx_set_kl_ind(
+ mpq_demux->sdmx_session_handle,
+ key_info->pid,
+ key_info->keyladder_id);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: FAILED to set key ladder, ret=%d\n",
+ __func__, ret);
+ ret = -ENODEV;
+ goto end;
+ }
+ }
+ }
+
+end:
+ return ret;
+}
+
+int mpq_dmx_init_mpq_feed(struct dvb_demux_feed *feed)
+{
+ int ret = 0;
+ struct mpq_demux *mpq_demux = feed->demux->priv;
+ struct mpq_feed *mpq_feed = feed->priv;
+
+ mutex_lock(&mpq_demux->mutex);
+
+ if (mpq_dmx_is_video_feed(feed)) {
+ ret = mpq_dmx_init_video_feed(mpq_feed);
+
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_dmx_init_video_feed failed, ret=%d\n",
+ __func__, ret);
+ goto init_mpq_feed_failed;
+ }
+ }
+
+ if (!mpq_sdmx_is_loaded()) {
+ /* nothing more to do */
+ mpq_feed->sdmx_buf_handle = NULL;
+ mpq_feed->metadata_buf_handle = NULL;
+ mpq_feed->sdmx_filter_handle = SDMX_INVALID_FILTER_HANDLE;
+ mpq_demux->sdmx_session_handle = SDMX_INVALID_SESSION_HANDLE;
+ mutex_unlock(&mpq_demux->mutex);
+ return ret;
+ }
+
+ /* Further initializations for secure demux */
+ ret = mpq_sdmx_open_session(mpq_demux);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_sdmx_open_session failed, ret=%d\n",
+ __func__, ret);
+
+ ret = -ENODEV;
+ goto init_mpq_feed_failed_free_video;
+ }
+
+ /* PCR and sections have internal buffer for SDMX */
+ if (mpq_dmx_is_pcr_feed(feed))
+ ret = mpq_sdmx_alloc_data_buf(mpq_feed,
+ SDMX_PCR_BUFFER_SIZE);
+ else if (mpq_dmx_is_sec_feed(feed))
+ ret = mpq_sdmx_alloc_data_buf(mpq_feed,
+ SDMX_SECTION_BUFFER_SIZE);
+ else
+ ret = 0;
+
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: init buffer failed, ret=%d\n",
+ __func__, ret);
+ goto init_mpq_feed_failed_free_sdmx;
+ }
+
+ ret = mpq_sdmx_filter_setup(mpq_demux, feed);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_sdmx_filter_setup failed, ret=%d\n",
+ __func__, ret);
+ goto init_mpq_feed_failed_free_data_buff;
+ }
+
+ mutex_unlock(&mpq_demux->mutex);
+ return 0;
+
+init_mpq_feed_failed_free_data_buff:
+ mpq_sdmx_free_data_buf(mpq_feed);
+init_mpq_feed_failed_free_sdmx:
+ mpq_sdmx_close_session(mpq_demux);
+init_mpq_feed_failed_free_video:
+ if (mpq_dmx_is_video_feed(feed))
+ mpq_dmx_terminate_video_feed(mpq_feed);
+init_mpq_feed_failed:
+ mutex_unlock(&mpq_demux->mutex);
+ return ret;
+}
+EXPORT_SYMBOL(mpq_dmx_init_mpq_feed);
+
+static void mpq_sdmx_prepare_filter_status(struct mpq_demux *mpq_demux,
+ struct sdmx_filter_status *filter_sts,
+ struct mpq_feed *mpq_feed)
+{
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ struct mpq_video_feed_info *feed_data;
+ struct mpq_streambuffer *sbuff;
+
+ filter_sts->filter_handle = mpq_feed->sdmx_filter_handle;
+ filter_sts->metadata_fill_count =
+ dvb_ringbuffer_avail(&mpq_feed->metadata_buf);
+ filter_sts->metadata_write_offset = mpq_feed->metadata_buf.pwrite;
+ filter_sts->error_indicators = 0;
+ filter_sts->status_indicators = 0;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: Filter meta-data buffer status: fill count = %d, write_offset = %d\n",
+ __func__, filter_sts->metadata_fill_count,
+ filter_sts->metadata_write_offset);
+
+ if (!mpq_dmx_is_video_feed(feed)) {
+ struct dvb_ringbuffer *buffer;
+
+ if (mpq_dmx_is_sec_feed(feed) ||
+ mpq_dmx_is_pcr_feed(feed)) {
+ buffer = (struct dvb_ringbuffer *)
+ &mpq_feed->sdmx_buf;
+ } else {
+ buffer = (struct dvb_ringbuffer *)
+ feed->feed.ts.buffer.ringbuff;
+ }
+
+ filter_sts->data_fill_count = dvb_ringbuffer_avail(buffer);
+ filter_sts->data_write_offset = buffer->pwrite;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: Filter buffers status: fill count = %d, write_offset = %d\n",
+ __func__, filter_sts->data_fill_count,
+ filter_sts->data_write_offset);
+
+ return;
+ }
+
+ /* Video feed - decoder buffers */
+ feed_data = &mpq_feed->video_info;
+
+ spin_lock(&mpq_feed->video_info.video_buffer_lock);
+ sbuff = feed_data->video_buffer;
+ if (sbuff == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: video_buffer released\n",
+ __func__);
+ spin_unlock(&feed_data->video_buffer_lock);
+ return;
+ }
+
+ if (feed_data->buffer_desc.decoder_buffers_num > 1) {
+ /* linear mode */
+ filter_sts->data_fill_count = sbuff->pending_buffers_count;
+ filter_sts->data_write_offset =
+ sbuff->raw_data.pwrite /
+ sizeof(struct mpq_streambuffer_buffer_desc);
+ } else {
+ /* ring buffer mode */
+ filter_sts->data_fill_count =
+ mpq_streambuffer_data_avail(sbuff);
+ mpq_streambuffer_get_data_rw_offset(sbuff, NULL,
+ &filter_sts->data_write_offset);
+
+ }
+
+ spin_unlock(&mpq_feed->video_info.video_buffer_lock);
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: Decoder buffers filter status: fill count = %d, write_offset = %d\n",
+ __func__, filter_sts->data_fill_count,
+ filter_sts->data_write_offset);
+}
+
+
+static int mpq_sdmx_section_filtering(struct mpq_feed *mpq_feed,
+ struct dvb_demux_filter *f,
+ struct sdmx_metadata_header *header)
+{
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ int ret;
+ u8 neq = 0;
+ u8 xor;
+ u8 tmp;
+ int i;
+
+ for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
+ tmp = DVB_RINGBUFFER_PEEK(&mpq_feed->sdmx_buf, i);
+ xor = f->filter.filter_value[i] ^ tmp;
+
+ if (f->maskandmode[i] & xor)
+ return 0;
+
+ neq |= f->maskandnotmode[i] & xor;
+ }
+
+ if (f->doneq && !neq)
+ return 0;
+
+ if (feed->demux->playback_mode == DMX_PB_MODE_PULL) {
+ int was_locked;
+
+ if (mutex_is_locked(&mpq_feed->mpq_demux->mutex)) {
+ mutex_unlock(&mpq_feed->mpq_demux->mutex);
+ was_locked = 1;
+ } else {
+ was_locked = 0;
+ }
+
+ ret = feed->demux->buffer_ctrl.sec(&f->filter,
+ header->payload_length);
+
+ if (was_locked)
+ mutex_lock(&mpq_feed->mpq_demux->mutex);
+
+ if (ret) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: buffer_ctrl.sec aborted\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ if (mpq_feed->sdmx_buf.pread + header->payload_length <
+ mpq_feed->sdmx_buf.size) {
+ feed->cb.sec(&mpq_feed->sdmx_buf.data[mpq_feed->sdmx_buf.pread],
+ header->payload_length,
+ NULL, 0, &f->filter, DMX_OK);
+ } else {
+ int split = mpq_feed->sdmx_buf.size - mpq_feed->sdmx_buf.pread;
+ feed->cb.sec(&mpq_feed->sdmx_buf.data[mpq_feed->sdmx_buf.pread],
+ split,
+ &mpq_feed->sdmx_buf.data[0],
+ header->payload_length - split,
+ &f->filter, DMX_OK);
+ }
+
+ return 0;
+}
+
+static int mpq_sdmx_check_ts_stall(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed,
+ struct sdmx_filter_status *sts,
+ size_t req,
+ int events_only)
+{
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ int ret;
+ int was_locked;
+
+ /*
+ * For PULL mode need to verify there is enough space for the dmxdev
+ * event. Also, if data buffer is full we want to stall until some
+ * data is removed from it to prevent calling the sdmx when it cannot
+ * output data to the still full buffer.
+ */
+ if (mpq_demux->demux.playback_mode == DMX_PB_MODE_PULL) {
+ MPQ_DVB_DBG_PRINT("%s: Stalling for events and %d bytes\n",
+ __func__, req);
+
+ if (mutex_is_locked(&mpq_demux->mutex)) {
+ mutex_unlock(&mpq_demux->mutex);
+ was_locked = 1;
+ } else {
+ was_locked = 0;
+ }
+
+ ret = mpq_demux->demux.buffer_ctrl.ts(&feed->feed.ts, req);
+ MPQ_DVB_DBG_PRINT("%s: stall result = %d\n",
+ __func__, ret);
+
+ if (was_locked)
+ mutex_lock(&mpq_demux->mutex);
+
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Handle filter results for filters with no extra meta-data */
+static void mpq_sdmx_pes_filter_results(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed,
+ struct sdmx_filter_status *sts)
+{
+ int ret;
+ struct sdmx_metadata_header header;
+ struct dmx_data_ready data_event;
+ struct dmx_data_ready pes_event;
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ struct dvb_ringbuffer *buf = (struct dvb_ringbuffer *)
+ feed->feed.ts.buffer.ringbuff;
+
+ if ((!sts->metadata_fill_count) && (!sts->data_fill_count))
+ goto pes_filter_check_overflow;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: Meta: fill=%u, write=%u. Data: fill=%u, write=%u\n",
+ __func__, sts->metadata_fill_count, sts->metadata_write_offset,
+ sts->data_fill_count, sts->data_write_offset);
+
+ mpq_feed->metadata_buf.pwrite = sts->metadata_write_offset;
+
+ if ((0 == sts->metadata_fill_count) &&
+ (sts->error_indicators & SDMX_FILTER_ERR_D_BUF_FULL)) {
+ ssize_t free = dvb_ringbuffer_free(buf);
+ ret = 0;
+ if ((free + SZ_2K) < MAX_PES_LENGTH)
+ ret = mpq_sdmx_check_ts_stall(mpq_demux, mpq_feed, sts,
+ free + SZ_2K, 0);
+ else
+ MPQ_DVB_ERR_PRINT(
+ "%s: Cannot stall when free space bigger than max PES size\n",
+ __func__);
+ if (ret) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_sdmx_check_ts_stall aborted\n",
+ __func__);
+ return;
+ }
+ }
+
+ while (sts->metadata_fill_count) {
+ if (dvb_ringbuffer_avail(&mpq_feed->metadata_buf) <
+ sizeof(header)) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: metadata_fill_count is %d but actual buffer has less than %d bytes\n",
+ __func__,
+ sts->metadata_fill_count,
+ sizeof(header));
+ break;
+ }
+
+ dvb_ringbuffer_read(&mpq_feed->metadata_buf, (u8 *) &header,
+ sizeof(header));
+ MPQ_DVB_DBG_PRINT(
+ "%s: metadata header: start=%u, length=%u\n",
+ __func__, header.payload_start, header.payload_length);
+ sts->metadata_fill_count -= sizeof(header);
+
+ /* Notify new data in buffer */
+ data_event.status = DMX_OK;
+ data_event.data_length = header.payload_length;
+ ret = mpq_sdmx_check_ts_stall(mpq_demux, mpq_feed, sts,
+ data_event.data_length, 0);
+ if (ret) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_sdmx_check_ts_stall aborted\n",
+ __func__);
+ return;
+ }
+
+ feed->data_ready_cb.ts(&feed->feed.ts, &data_event);
+
+ /* Notify new complete PES */
+ pes_event.status = DMX_OK_PES_END;
+ pes_event.pes_end.actual_length = header.payload_length;
+ pes_event.pes_end.start_gap = 0;
+ pes_event.data_length = 0;
+
+ /* Parse error indicators - TODO: these should be per filter */
+ if (sts->error_indicators & SDMX_FILTER_ERR_INVALID_PES_LEN)
+ pes_event.pes_end.pes_length_mismatch = 1;
+ if (sts->error_indicators & SDMX_FILTER_ERR_CONT_CNT_INVALID)
+ pes_event.pes_end.disc_indicator_set = 0;
+ /* TODO: report these when SDMX returns them */
+ pes_event.pes_end.stc = 0;
+ pes_event.pes_end.tei_counter = 0;
+ pes_event.pes_end.cont_err_counter = 0;
+ pes_event.pes_end.ts_packets_num = 0;
+
+ ret = mpq_sdmx_check_ts_stall(mpq_demux, mpq_feed, sts, 0, 1);
+ if (ret) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_sdmx_check_ts_stall aborted\n",
+ __func__);
+ return;
+ }
+ feed->data_ready_cb.ts(&feed->feed.ts, &pes_event);
+ }
+
+pes_filter_check_overflow:
+ if ((mpq_demux->demux.playback_mode == DMX_PB_MODE_PUSH) &&
+ (sts->error_indicators & SDMX_FILTER_ERR_D_BUF_FULL)) {
+ MPQ_DVB_ERR_PRINT("%s: DMX_OVERRUN_ERROR\n", __func__);
+ data_event.status = DMX_OVERRUN_ERROR;
+ data_event.data_length = 0;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data_event);
+ }
+}
+
+static void mpq_sdmx_section_filter_results(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed,
+ struct sdmx_filter_status *sts)
+{
+ struct sdmx_metadata_header header;
+ struct dmx_data_ready event;
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ struct dvb_demux_filter *f;
+ struct dmx_section_feed *sec = &feed->feed.sec;
+
+ /* Parse error indicators */
+ if (sts->error_indicators & SDMX_FILTER_ERR_SEC_VERIF_CRC32_FAIL) {
+ MPQ_DVB_DBG_PRINT("%s: Notify CRC err event\n", __func__);
+ event.status = DMX_CRC_ERROR;
+ event.data_length = 0;
+ feed->data_ready_cb.sec(&feed->filter->filter, &event);
+ }
+
+ if (sts->error_indicators & SDMX_FILTER_ERR_D_BUF_FULL)
+ MPQ_DVB_ERR_PRINT("%s: internal section buffer overflowed!\n",
+ __func__);
+
+ if ((!sts->metadata_fill_count) && (!sts->data_fill_count))
+ return;
+
+ mpq_feed->metadata_buf.pwrite = sts->metadata_write_offset;
+ mpq_feed->sdmx_buf.pwrite = sts->data_write_offset;
+
+ while (sts->metadata_fill_count) {
+ dvb_ringbuffer_read(&mpq_feed->metadata_buf, (u8 *) &header,
+ sizeof(header));
+ sts->metadata_fill_count -= sizeof(header);
+ MPQ_DVB_DBG_PRINT(
+ "%s: metadata header: start=%u, length=%u\n",
+ __func__, header.payload_start, header.payload_length);
+
+ f = feed->filter;
+ do {
+ if (mpq_sdmx_section_filtering(mpq_feed, f, &header))
+ return;
+ } while ((f = f->next) && sec->is_filtering);
+
+ DVB_RINGBUFFER_SKIP(&mpq_feed->sdmx_buf, header.payload_length);
+ }
+}
+
+static void mpq_sdmx_decoder_filter_results(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed,
+ struct sdmx_filter_status *sts)
+{
+ struct sdmx_metadata_header header;
+ int pes_header_offset;
+ struct ts_packet_header *ts_header;
+ struct ts_adaptation_field *ts_adapt;
+ struct pes_packet_header *pes_header;
+ u8 metadata_buf[MAX_SDMX_METADATA_LENGTH];
+ struct mpq_streambuffer *sbuf;
+ int ret;
+ int pes_cnt = 0;
+ struct dmx_data_ready data_event;
+
+ if ((!sts->metadata_fill_count) && (!sts->data_fill_count))
+ goto decoder_filter_check_overflow;
+
+ /* Update meta data buffer write pointer */
+ mpq_feed->metadata_buf.pwrite = sts->metadata_write_offset;
+
+ if ((mpq_demux->demux.playback_mode == DMX_PB_MODE_PULL) &&
+ (sts->error_indicators & SDMX_FILTER_ERR_D_LIN_BUFS_FULL)) {
+ MPQ_DVB_DBG_PRINT("%s: Decoder stall...\n", __func__);
+
+ ret = mpq_dmx_decoder_fullness_wait(
+ mpq_feed->dvb_demux_feed, 0);
+ if (ret) {
+ /* we reach here if demuxing was aborted */
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_dmx_decoder_fullness_wait aborted\n",
+ __func__);
+ return;
+ }
+ }
+
+ while (sts->metadata_fill_count) {
+ struct mpq_streambuffer_packet_header packet;
+ struct mpq_adapter_video_meta_data meta_data;
+
+ pes_cnt++;
+ /* Read header & metadata */
+ dvb_ringbuffer_read(&mpq_feed->metadata_buf, (u8 *)&header,
+ sizeof(header));
+ sts->metadata_fill_count -= sizeof(header);
+ MPQ_DVB_DBG_PRINT(
+ "%s: metadata header: start=%u, length=%u, metadata=%u\n",
+ __func__, header.payload_start, header.payload_length,
+ header.metadata_length);
+
+ /* Read actual metadata */
+ if (header.metadata_length < MAX_SDMX_METADATA_LENGTH)
+ dvb_ringbuffer_read(&mpq_feed->metadata_buf,
+ metadata_buf,
+ header.metadata_length);
+ else
+ MPQ_DVB_ERR_PRINT(
+ "%s: meta-data size=%d is too big for meta-data buffer=%d\n",
+ __func__, header.metadata_length,
+ MAX_SDMX_METADATA_LENGTH);
+ sts->metadata_fill_count -= header.metadata_length;
+
+ ts_header = (struct ts_packet_header *)&metadata_buf[0];
+ if (1 == ts_header->adaptation_field_control) {
+ ts_adapt = NULL;
+ pes_header_offset = sizeof(*ts_header);
+ } else {
+ ts_adapt = (struct ts_adaptation_field *)
+ &metadata_buf[sizeof(*ts_header)];
+ pes_header_offset = sizeof(*ts_header) + 1 +
+ ts_adapt->adaptation_field_length;
+ }
+ pes_header = (struct pes_packet_header *)
+ &metadata_buf[pes_header_offset];
+ meta_data.packet_type = DMX_PES_PACKET;
+ if (pes_header->pts_dts_flag & 0x2) {
+ meta_data.info.pes.pts_dts_info.pts_exist = 1;
+ meta_data.info.pes.pts_dts_info.pts =
+ ((u64)pes_header->pts_1 << 30) |
+ ((u64)pes_header->pts_2 << 22) |
+ ((u64)pes_header->pts_3 << 15) |
+ ((u64)pes_header->pts_4 << 7) |
+ (u64)pes_header->pts_5;
+ } else {
+ meta_data.info.pes.pts_dts_info.pts_exist = 0;
+ }
+
+ if (pes_header->pts_dts_flag & 0x1) {
+ meta_data.info.pes.pts_dts_info.dts_exist = 1;
+ meta_data.info.pes.pts_dts_info.dts =
+ ((u64)pes_header->dts_1 << 30) |
+ ((u64)pes_header->dts_2 << 22) |
+ ((u64)pes_header->dts_3 << 15) |
+ ((u64)pes_header->dts_4 << 7) |
+ (u64)pes_header->dts_5;
+ } else {
+ meta_data.info.pes.pts_dts_info.dts_exist = 0;
+ }
+
+ spin_lock(&mpq_feed->video_info.video_buffer_lock);
+ sbuf = mpq_feed->video_info.video_buffer;
+ if (sbuf == NULL) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: video_buffer released\n",
+ __func__);
+ spin_unlock(&mpq_feed->video_info.video_buffer_lock);
+ return;
+ }
+
+ packet.raw_data_len = header.payload_length;
+ packet.user_data_len = sizeof(meta_data);
+ mpq_streambuffer_get_buffer_handle(sbuf, 0,
+ &packet.raw_data_handle);
+ mpq_streambuffer_get_data_rw_offset(sbuf,
+ NULL, &packet.raw_data_offset);
+ ret = mpq_streambuffer_data_write_deposit(sbuf,
+ header.payload_length);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_streambuffer_data_write_deposit failed. ret=%d\n",
+ __func__, ret);
+ }
+ mpq_dmx_update_decoder_stat(mpq_demux);
+ mpq_streambuffer_pkt_write(sbuf, &packet, (u8 *)&meta_data);
+
+ if (generate_es_events) {
+ struct dmx_data_ready data;
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ mpq_dmx_prepare_es_event_data(
+ &packet, &meta_data, &mpq_feed->video_info,
+ sbuf, &data);
+ MPQ_DVB_DBG_PRINT("%s: Notify ES Event\n", __func__);
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ }
+
+ spin_unlock(&mpq_feed->video_info.video_buffer_lock);
+ }
+
+decoder_filter_check_overflow:
+ if ((mpq_demux->demux.playback_mode == DMX_PB_MODE_PUSH) &&
+ (sts->error_indicators & SDMX_FILTER_ERR_D_LIN_BUFS_FULL)) {
+ MPQ_DVB_ERR_PRINT("%s: DMX_OVERRUN_ERROR\n", __func__);
+ data_event.status = DMX_OVERRUN_ERROR;
+ data_event.data_length = 0;
+ mpq_feed->dvb_demux_feed->data_ready_cb.ts(
+ &mpq_feed->dvb_demux_feed->feed.ts, &data_event);
+ }
+}
+
+static void mpq_sdmx_pcr_filter_results(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed,
+ struct sdmx_filter_status *sts)
+{
+ int ret;
+ struct sdmx_metadata_header header;
+ struct dmx_data_ready data;
+ struct dvb_ringbuffer *rbuff = &mpq_feed->sdmx_buf;
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ u8 buf[TS_PACKET_HEADER_LENGTH + MAX_TSP_ADAPTATION_LENGTH +
+ TIMESTAMP_LEN];
+ size_t stc_len = 0;
+
+ if (sts->error_indicators & SDMX_FILTER_ERR_D_BUF_FULL)
+ MPQ_DVB_ERR_PRINT("%s: internal PCR buffer overflowed!\n",
+ __func__);
+
+ /* MPQ_TODO: Parse rest of error indicators ? */
+
+ if ((!sts->metadata_fill_count) && (!sts->data_fill_count))
+ return;
+
+ if (DMX_TSP_FORMAT_192_TAIL == mpq_demux->demux.tsp_format)
+ stc_len = 4;
+
+ mpq_feed->metadata_buf.pwrite = sts->metadata_write_offset;
+ rbuff->pwrite = sts->data_write_offset;
+
+ while (sts->metadata_fill_count) {
+ dvb_ringbuffer_read(&mpq_feed->metadata_buf, (u8 *) &header,
+ sizeof(header));
+ MPQ_DVB_DBG_PRINT(
+ "%s: metadata header: start=%u, length=%u\n",
+ __func__, header.payload_start, header.payload_length);
+ sts->metadata_fill_count -= sizeof(header);
+
+ dvb_ringbuffer_read(rbuff, buf, header.payload_length);
+
+ if (mpq_dmx_extract_pcr_and_dci(buf, &data.pcr.pcr,
+ &data.pcr.disc_indicator_set)) {
+
+ if (stc_len) {
+ data.pcr.stc =
+ buf[header.payload_length-2] << 16;
+ data.pcr.stc +=
+ buf[header.payload_length-3] << 8;
+ data.pcr.stc += buf[header.payload_length-4];
+ /* convert from 105.47 KHZ to 27MHz */
+ data.pcr.stc *= 256;
+ } else {
+ data.pcr.stc = 0;
+ }
+
+ data.data_length = 0;
+ data.status = DMX_OK_PCR;
+ ret = mpq_sdmx_check_ts_stall(
+ mpq_demux, mpq_feed, sts, 0, 1);
+ if (ret) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_sdmx_check_ts_stall aborted\n",
+ __func__);
+ return;
+ }
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ }
+ }
+}
+
+static void mpq_sdmx_raw_filter_results(struct mpq_demux *mpq_demux,
+ struct mpq_feed *mpq_feed,
+ struct sdmx_filter_status *sts)
+{
+ int ret;
+ ssize_t new_data;
+ struct dmx_data_ready data_event;
+ struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
+ struct dvb_ringbuffer *buf = (struct dvb_ringbuffer *)
+ feed->feed.ts.buffer.ringbuff;
+
+ if ((!sts->metadata_fill_count) && (!sts->data_fill_count))
+ goto raw_filter_check_overflow;
+
+ new_data = sts->data_write_offset -
+ buf->pwrite;
+ if (new_data < 0)
+ new_data += buf->size;
+
+ ret = mpq_sdmx_check_ts_stall(mpq_demux, mpq_feed, sts,
+ new_data + feed->demux->ts_packet_size, 0);
+ if (ret) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_sdmx_check_ts_stall aborted\n",
+ __func__);
+ return;
+ }
+
+ data_event.status = DMX_OK;
+ data_event.data_length = new_data;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data_event);
+ MPQ_DVB_DBG_PRINT("%s: Callback DMX_OK, size=%d\n",
+ __func__, data_event.data_length);
+
+raw_filter_check_overflow:
+ if ((mpq_demux->demux.playback_mode == DMX_PB_MODE_PUSH) &&
+ (sts->error_indicators & SDMX_FILTER_ERR_D_BUF_FULL)) {
+ MPQ_DVB_DBG_PRINT("%s: DMX_OVERRUN_ERROR\n", __func__);
+ data_event.status = DMX_OVERRUN_ERROR;
+ data_event.data_length = 0;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data_event);
+ }
+}
+
+static void mpq_sdmx_process_results(struct mpq_demux *mpq_demux)
+{
+ int i;
+ int j;
+ struct sdmx_filter_status *sts;
+ struct mpq_feed *mpq_feed;
+
+ for (i = 0; i < mpq_demux->sdmx_filter_count; i++) {
+ /*
+ * MPQ_TODO: review lookup optimization
+ * Can have the related mpq_feed index already associated with
+ * the filter status.
+ */
+ sts = &mpq_demux->filters_status[i];
+ MPQ_DVB_DBG_PRINT(
+ "%s: Filter: handle=%d, status=0x%x, errors=0x%x\n",
+ __func__, sts->filter_handle, sts->status_indicators,
+ sts->error_indicators);
+ MPQ_DVB_DBG_PRINT("%s: Metadata fill count=%d (write=%d)\n",
+ __func__, sts->metadata_fill_count,
+ sts->metadata_write_offset);
+ MPQ_DVB_DBG_PRINT("%s: Data fill count=%d (write=%d)\n",
+ __func__, sts->data_fill_count, sts->data_write_offset);
+
+ for (j = 0; j < MPQ_MAX_DMX_FILES; j++) {
+ mpq_feed = &mpq_demux->feeds[j];
+ if ((mpq_feed->dvb_demux_feed->state == DMX_STATE_GO) &&
+ (sts->filter_handle ==
+ mpq_feed->sdmx_filter_handle) &&
+ (!mpq_feed->secondary_feed))
+ break;
+ }
+
+ if (j == MPQ_MAX_DMX_FILES)
+ continue;
+
+ if (sts->error_indicators & SDMX_FILTER_ERR_MD_BUF_FULL)
+ MPQ_DVB_ERR_PRINT(
+ "%s: meta-data buff for pid %d overflowed!\n",
+ __func__, mpq_feed->dvb_demux_feed->pid);
+
+ switch (mpq_feed->filter_type) {
+ case SDMX_PCR_FILTER:
+ mpq_sdmx_pcr_filter_results(mpq_demux, mpq_feed, sts);
+ break;
+ case SDMX_PES_FILTER:
+ mpq_sdmx_pes_filter_results(mpq_demux, mpq_feed,
+ sts);
+ break;
+ case SDMX_SEPARATED_PES_FILTER:
+ mpq_sdmx_decoder_filter_results(mpq_demux, mpq_feed,
+ sts);
+ break;
+ case SDMX_SECTION_FILTER:
+ mpq_sdmx_section_filter_results(mpq_demux, mpq_feed,
+ sts);
+ break;
+ case SDMX_RAW_FILTER:
+ mpq_sdmx_raw_filter_results(mpq_demux, mpq_feed, sts);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+int mpq_sdmx_process(struct mpq_demux *mpq_demux,
+ struct sdmx_buff_descr *input,
+ u32 fill_count,
+ u32 read_offset)
+{
+ struct sdmx_filter_status *sts;
+ struct mpq_feed *mpq_feed;
+ u8 flags = 0; /* MPQ_TODO: EOS handling */
+ u32 errors;
+ u32 status;
+ u32 prev_read_offset;
+ u32 prev_fill_count;
+ enum sdmx_status sdmx_res;
+ int i;
+ int filter_index = 0;
+ int bytes_read;
+ struct timespec process_start_time;
+ struct timespec process_end_time;
+
+ mutex_lock(&mpq_demux->mutex);
+
+ /*
+ * All active filters may get totally closed and therefore
+ * sdmx session may get terminated, in such case nothing to process
+ */
+ if (mpq_demux->sdmx_session_handle == SDMX_INVALID_SESSION_HANDLE) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: sdmx filters aborted, filter-count %d, session %d\n",
+ __func__, mpq_demux->sdmx_filter_count,
+ mpq_demux->sdmx_session_handle);
+ mutex_unlock(&mpq_demux->mutex);
+ return 0;
+ }
+
+ /* Build up to date filter status array */
+ for (i = 0; i < MPQ_MAX_DMX_FILES; i++) {
+ mpq_feed = &mpq_demux->feeds[i];
+ if ((mpq_feed->sdmx_filter_handle != SDMX_INVALID_FILTER_HANDLE)
+ && (mpq_feed->dvb_demux_feed->state == DMX_STATE_GO)
+ && (!mpq_feed->secondary_feed)) {
+ sts = &mpq_demux->filters_status[filter_index];
+ mpq_sdmx_prepare_filter_status(mpq_demux, sts,
+ mpq_feed);
+ filter_index++;
+ }
+ }
+
+ /* Sanity check */
+ if (filter_index != mpq_demux->sdmx_filter_count) {
+ mutex_unlock(&mpq_demux->mutex);
+ MPQ_DVB_ERR_PRINT(
+ "%s: Updated %d SDMX filters status but should be %d\n",
+ __func__, filter_index, mpq_demux->sdmx_filter_count);
+ return -ERESTART;
+ }
+
+ MPQ_DVB_DBG_PRINT(
+ "\n\n%s: Before SDMX_process: input read_offset=%u, fill count=%u\n",
+ __func__, read_offset, fill_count);
+
+ process_start_time = current_kernel_time();
+
+ prev_read_offset = read_offset;
+ prev_fill_count = fill_count;
+ sdmx_res = sdmx_process(mpq_demux->sdmx_session_handle, flags, input,
+ &fill_count, &read_offset, &errors, &status,
+ mpq_demux->sdmx_filter_count, mpq_demux->filters_status);
+
+ process_end_time = current_kernel_time();
+ mpq_dmx_update_sdmx_stat(mpq_demux, prev_fill_count,
+ &process_start_time, &process_end_time);
+
+ bytes_read = prev_fill_count - fill_count;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: SDMX result=%d, input_fill_count=%u, read_offset=%u, read %d bytes from input, status=0x%X, errors=0x%X\n",
+ __func__, sdmx_res, fill_count, read_offset, bytes_read,
+ status, errors);
+
+ if ((sdmx_res == SDMX_SUCCESS) ||
+ (sdmx_res == SDMX_STATUS_STALLED_IN_PULL_MODE)) {
+ if (sdmx_res == SDMX_STATUS_STALLED_IN_PULL_MODE)
+ MPQ_DVB_DBG_PRINT("%s: SDMX stalled for PULL mode\n",
+ __func__);
+
+ mpq_sdmx_process_results(mpq_demux);
+ } else {
+ MPQ_DVB_ERR_PRINT(
+ "%s: SDMX Process returned %d\n",
+ __func__, sdmx_res);
+ }
+
+ mutex_unlock(&mpq_demux->mutex);
+
+ return bytes_read;
+}
+EXPORT_SYMBOL(mpq_sdmx_process);
+
+static int mpq_sdmx_write(struct mpq_demux *mpq_demux,
+ struct ion_handle *input_handle,
+ const char *buf,
+ size_t count)
+{
+ struct sdmx_buff_descr buf_desc;
+ struct dvb_ringbuffer *rbuf = (struct dvb_ringbuffer *)
+ mpq_demux->demux.dmx.dvr_input.ringbuff;
+ ion_phys_addr_t phys_addr;
+ u32 fill_count;
+ u32 read_offset;
+ size_t len;
+ int ret;
+
+ if (mpq_demux == NULL || input_handle == NULL) {
+ MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = ion_phys(mpq_demux->ion_client, input_handle, &phys_addr, &len);
+ if (ret) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Failed to obtain physical address of input buffer. ret = %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ buf_desc.base_addr = (void *)phys_addr;
+ buf_desc.size = rbuf->size;
+ read_offset = rbuf->pread;
+ fill_count = dvb_ringbuffer_avail(rbuf);
+
+ return mpq_sdmx_process(mpq_demux, &buf_desc, fill_count, read_offset);
+}
+
+int mpq_dmx_write(struct dmx_demux *demux, const char *buf, size_t count)
+{
+ struct dvb_demux *dvb_demux;
+ struct mpq_demux *mpq_demux;
+
+ if (demux == NULL)
+ return -EINVAL;
+
+ dvb_demux = demux->priv;
+ mpq_demux = dvb_demux->priv;
+
+ if (mpq_sdmx_is_loaded()) {
+ /* route through secure demux */
+ return mpq_sdmx_write(mpq_demux,
+ demux->dvr_input.priv_handle,
+ buf,
+ count);
+ } else {
+ /* route through sw filter */
+ dvb_dmx_swfilter_format(dvb_demux, buf, count,
+ dvb_demux->tsp_format);
+ if (signal_pending(current))
+ return -EINTR;
+ return count;
+ }
+}
+EXPORT_SYMBOL(mpq_dmx_write);
+
+int mpq_sdmx_is_loaded(void)
+{
+ return mpq_bypass_sdmx ? 0 : mpq_dmx_info.secure_demux_app_loaded;
+}
+EXPORT_SYMBOL(mpq_sdmx_is_loaded);
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
index c0d5412..2ab342f 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -21,12 +21,11 @@
#include "dvb_demux.h"
#include "dvb_frontend.h"
#include "mpq_adapter.h"
-
+#include "mpq_sdmx.h"
/* Max number open() request can be done on demux device */
#define MPQ_MAX_DMX_FILES 128
-
/**
* TSIF alias name length
*/
@@ -35,80 +34,21 @@
#define MPQ_MAX_FOUND_PATTERNS 5
/**
- * struct mpq_demux - mpq demux information
- * @demux: The dvb_demux instance used by mpq_demux
- * @dmxdev: The dmxdev instance used by mpq_demux
- * @fe_memory: Handle of front-end memory source to mpq_demux
- * @source: The current source connected to the demux
- * @is_initialized: Indicates whether this demux device was
- * initialized or not.
- * @ion_client: ION demux client used to allocate memory from ION.
- * @feed_lock: Lock used to protect against private feed data
- * @hw_notification_interval: Notification interval in msec,
- * exposed in debugfs.
- * @hw_notification_min_interval: Minimum notification internal in msec,
- * exposed in debugfs.
- * @hw_notification_count: Notification count, exposed in debugfs.
- * @hw_notification_size: Notification size in bytes, exposed in debugfs.
- * @hw_notification_min_size: Minimum notification size in bytes,
- * exposed in debugfs.
- * @decoder_drop_count: Accumulated number of bytes dropped due to decoder
- * buffer fullness, exposed in debugfs.
- * @decoder_out_count: Counter incremeneted for each video frame output by
- * demux, exposed in debugfs.
- * @decoder_out_interval_sum: Sum of intervals (msec) holding the time between
- * two successive video frames output, exposed in debugfs.
- * @decoder_out_interval_average: Average interval (msec) between two
- * successive video frames output, exposed in debugfs.
- * @decoder_out_interval_max: Max interval (msec) between two
- * successive video frames output, exposed in debugfs.
- * @decoder_ts_errors: Counter for number of decoder packets with TEI bit
- * set, exposed in debugfs.
- * @decoder_out_last_time: Time of last video frame output.
- * @last_notification_time: Time of last HW notification.
+ * Key ladder information table, manages association of pid with a key ladder
+ * index.
+ * @pid: pid
+ * @keyladder_id: key ladder index associated with the pid
+ * @list: list item pointer
+ * @refs: reference count of how many filters use this association.
*/
-struct mpq_demux {
- struct dvb_demux demux;
- struct dmxdev dmxdev;
- struct dmx_frontend fe_memory;
- dmx_source_t source;
- int is_initialized;
- struct ion_client *ion_client;
- spinlock_t feed_lock;
-
- /* debug-fs */
- u32 hw_notification_interval;
- u32 hw_notification_min_interval;
- u32 hw_notification_count;
- u32 hw_notification_size;
- u32 hw_notification_min_size;
- u32 decoder_drop_count;
- u32 decoder_out_count;
- u32 decoder_out_interval_sum;
- u32 decoder_out_interval_average;
- u32 decoder_out_interval_max;
- u32 decoder_ts_errors;
- struct timespec decoder_out_last_time;
- struct timespec last_notification_time;
+struct mpq_demux_keyladder_info {
+ u16 pid;
+ u32 keyladder_id;
+ struct list_head list;
+ u16 refs;
};
/**
- * mpq_dmx_init - initialization and registration function of
- * single MPQ demux device
- *
- * @adapter: The adapter to register mpq_demux to
- * @mpq_demux: The mpq demux to initialize
- *
- * Every HW pluging need to provide implementation of such
- * function that will be called for each demux device on the
- * module initialization. The function mpq_demux_plugin_init
- * should be called during the HW plugin module initialization.
- */
-typedef int (*mpq_dmx_init)(
- struct dvb_adapter *mpq_adapter,
- struct mpq_demux *demux);
-
-/**
* struct ts_packet_header - Transport packet header
* as defined in MPEG2 transport stream standard.
*/
@@ -307,9 +247,12 @@
/*
* mpq_video_feed_info - private data used for video feed.
*
- * @plugin_data: Underlying plugin's own private data.
* @video_buffer: Holds the streamer buffer shared with
* the decoder for feeds having the data going to the decoder.
+ * @video_buffer_lock: Lock protecting against video output buffer.
+ * The lock protects against API calls to manipulate the output buffer
+ * (initialize, free, re-use buffers) and dvb-sw demux parsing the video
+ * data through mpq_dmx_process_video_packet().
* @buffer_desc: Holds decoder buffer(s) information used for stream buffer.
* @pes_header: Used for feeds that output data to decoder,
* holds PES header of current processed PES.
@@ -319,8 +262,6 @@
* pes header.
* @fullness_wait_cancel: Flag used to signal to abort waiting for
* decoder's fullness.
- * @pes_payload_address: Used for feeds that output data to decoder,
- * holds current PES payload start address.
* @stream_interface: The ID of the video stream interface registered
* with this stream buffer.
* @patterns: pointer to the framing patterns to look for.
@@ -361,13 +302,12 @@
* a new elementary stream data event.
*/
struct mpq_video_feed_info {
- void *plugin_data;
struct mpq_streambuffer *video_buffer;
+ spinlock_t video_buffer_lock;
struct mpq_decoder_buffers_desc buffer_desc;
struct pes_packet_header pes_header;
u32 pes_header_left_bytes;
u32 pes_header_offset;
- u32 pes_payload_address;
int fullness_wait_cancel;
enum mpq_adapter_stream_if stream_interface;
const struct mpq_framing_pattern_lookup_params *patterns;
@@ -393,6 +333,145 @@
};
/**
+ * mpq feed object - mpq common plugin feed information
+ *
+ * @dvb_demux_feed: Back pointer to dvb demux level feed object
+ * @mpq_demux: Pointer to common mpq demux object
+ * @plugin_priv: Plugin specific private data
+ * @sdmx_filter_handle: Secure demux filter handle. Recording feed may share
+ * same filter handle
+ * @secondary_feed: Specifies if this feed shares filter handle with
+ * other feeds
+ * @metadata_buf: Ring buffer object for managing the metadata buffer
+ * @metadata_buf_handle: Allocation handle for the metadata buffer
+ * @sdmx_buf: Ring buffer object for intermediate output data from the sdmx
+ * @sdmx_buf_handle: Allocation handle for the sdmx intermediate data buffer
+ * @video_info: Video feed specific information
+ */
+struct mpq_feed {
+ struct dvb_demux_feed *dvb_demux_feed;
+ struct mpq_demux *mpq_demux;
+ void *plugin_priv;
+
+ /* Secure demux related */
+ int sdmx_filter_handle;
+ int secondary_feed;
+ enum sdmx_filter filter_type;
+ struct dvb_ringbuffer metadata_buf;
+ struct ion_handle *metadata_buf_handle;
+
+ struct dvb_ringbuffer sdmx_buf;
+ struct ion_handle *sdmx_buf_handle;
+
+ struct mpq_video_feed_info video_info;
+};
+
+/**
+ * struct mpq_demux - mpq demux information
+ * @demux: The dvb_demux instance used by mpq_demux
+ * @dmxdev: The dmxdev instance used by mpq_demux
+ * @fe_memory: Handle of front-end memory source to mpq_demux
+ * @source: The current source connected to the demux
+ * @is_initialized: Indicates whether this demux device was
+ * initialized or not.
+ * @ion_client: ION demux client used to allocate memory from ION.
+ * @mutex: Lock used to protect against private feed data
+ * @feeds: mpq common feed object pool
+ * @filters_status: Array holding buffers status for each secure demux filter.
+ * Used before each call to sdmx_process() to build up to date state.
+ * @keyladder_list: List head of key ladder table
+ * @secure_mode_count: Number of filters set with secure mode
+ * @sdmx_session_handle: Secure demux open session handle
+ * @sdmx_filter_count: Number of active secure demux filters
+ * @plugin_priv: Underlying plugin's own private data
+ * @hw_notification_interval: Notification interval in msec,
+ * exposed in debugfs.
+ * @hw_notification_min_interval: Minimum notification internal in msec,
+ * exposed in debugfs.
+ * @hw_notification_count: Notification count, exposed in debugfs.
+ * @hw_notification_size: Notification size in bytes, exposed in debugfs.
+ * @hw_notification_min_size: Minimum notification size in bytes,
+ * exposed in debugfs.
+ * @decoder_drop_count: Accumulated number of bytes dropped due to decoder
+ * buffer fullness, exposed in debugfs.
+ * @decoder_out_count: Counter incremeneted for each video frame output by
+ * demux, exposed in debugfs.
+ * @decoder_out_interval_sum: Sum of intervals (msec) holding the time between
+ * two successive video frames output, exposed in debugfs.
+ * @decoder_out_interval_average: Average interval (msec) between two
+ * successive video frames output, exposed in debugfs.
+ * @decoder_out_interval_max: Max interval (msec) between two
+ * successive video frames output, exposed in debugfs.
+ * @decoder_ts_errors: Counter for number of decoder packets with TEI bit
+ * set, exposed in debugfs.
+ * @sdmx_process_count: Total number of times sdmx_process is called.
+ * @sdmx_process_time_sum: Total time sdmx_process takes.
+ * @sdmx_process_time_average: Average time sdmx_process takes.
+ * @sdmx_process_time_max: Max time sdmx_process takes.
+ * @sdmx_process_packets_sum: Total packets number sdmx_process handled.
+ * @sdmx_process_packets_average: Average packets number sdmx_process handled.
+ * @sdmx_process_packets_min: Minimum packets number sdmx_process handled.
+ * @decoder_out_last_time: Time of last video frame output.
+ * @last_notification_time: Time of last HW notification.
+ */
+struct mpq_demux {
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend fe_memory;
+ dmx_source_t source;
+ int is_initialized;
+ struct ion_client *ion_client;
+ struct mutex mutex;
+ struct mpq_feed feeds[MPQ_MAX_DMX_FILES];
+ struct sdmx_filter_status filters_status[MPQ_MAX_DMX_FILES];
+ struct list_head keyladder_list; /* struct mpq_demux_keyladder_info */
+ int secure_mode_count;
+ int sdmx_session_handle;
+ int sdmx_session_ref_count;
+ int sdmx_filter_count;
+ void *plugin_priv;
+
+ /* debug-fs */
+ u32 hw_notification_interval;
+ u32 hw_notification_min_interval;
+ u32 hw_notification_count;
+ u32 hw_notification_size;
+ u32 hw_notification_min_size;
+ u32 decoder_drop_count;
+ u32 decoder_out_count;
+ u32 decoder_out_interval_sum;
+ u32 decoder_out_interval_average;
+ u32 decoder_out_interval_max;
+ u32 decoder_ts_errors;
+ u32 sdmx_process_count;
+ u32 sdmx_process_time_sum;
+ u32 sdmx_process_time_average;
+ u32 sdmx_process_time_max;
+ u32 sdmx_process_packets_sum;
+ u32 sdmx_process_packets_average;
+ u32 sdmx_process_packets_min;
+
+ struct timespec decoder_out_last_time;
+ struct timespec last_notification_time;
+};
+
+/**
+ * mpq_dmx_init - initialization and registration function of
+ * single MPQ demux device
+ *
+ * @adapter: The adapter to register mpq_demux to
+ * @mpq_demux: The mpq demux to initialize
+ *
+ * Every HW pluging need to provide implementation of such
+ * function that will be called for each demux device on the
+ * module initialization. The function mpq_demux_plugin_init
+ * should be called during the HW plugin module initialization.
+ */
+typedef int (*mpq_dmx_init)(
+ struct dvb_adapter *mpq_adapter,
+ struct mpq_demux *demux);
+
+/**
* mpq_demux_plugin_init - Initialize demux devices and register
* them to the dvb adapter.
*
@@ -454,36 +533,7 @@
* The function unmaps the buffer from kernel memory only if the buffer
* was not allocated with secure flag.
*/
-int mpq_dmx_unmap_buffer(struct dmx_demux *demux,
- void *priv_handle);
-
-/**
- * mpq_dmx_init_video_feed - Initializes video feed
- * used to pass data to decoder directly.
- *
- * @feed: The feed used for the video TS packets
- *
- * Return error code.
- *
- * If the underlying plugin wishes to perform SW PES assmebly
- * for the video data and stream it to the decoder, it should
- * call this function when video feed is initialized before
- * using mpq_dmx_process_video_packet.
- *
- * The function allocates mpq_video_feed_info and saves in
- * feed->priv.
- */
-int mpq_dmx_init_video_feed(struct dvb_demux_feed *feed);
-
-/**
- * mpq_dmx_terminate_video_feed - Free private data of
- * video feed allocated in mpq_dmx_init_video_feed
- *
- * @feed: The feed used for the video TS packets
- *
- * Return error code.
- */
-int mpq_dmx_terminate_video_feed(struct dvb_demux_feed *feed);
+int mpq_dmx_unmap_buffer(struct dmx_demux *demux, void *priv_handle);
/**
* mpq_dmx_decoder_fullness_init - Initialize waiting
@@ -634,6 +684,36 @@
}
/**
+ * mpq_dmx_is_sec_feed - Returns whether this is a section feed
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return 1 if feed is a section feed, 0 otherwise.
+ */
+static inline int mpq_dmx_is_sec_feed(struct dvb_demux_feed *feed)
+{
+ return (feed->type == DMX_TYPE_SEC);
+}
+
+/**
+ * mpq_dmx_is_rec_feed - Returns whether this is a recording feed
+ *
+ * @feed: The feed to be checked.
+ *
+ * Return 1 if feed is recording feed, 0 otherwise.
+ */
+static inline int mpq_dmx_is_rec_feed(struct dvb_demux_feed *feed)
+{
+ if (feed->type != DMX_TYPE_TS)
+ return 0;
+
+ if (feed->ts_type & (TS_DECODER | TS_PAYLOAD_ONLY))
+ return 0;
+
+ return 1;
+}
+
+/**
* mpq_dmx_init_hw_statistics -
* Extend dvb-demux debugfs with HW statistics.
*
@@ -641,7 +721,6 @@
*/
void mpq_dmx_init_hw_statistics(struct mpq_demux *mpq_demux);
-
/**
* mpq_dmx_update_hw_statistics -
* Update dvb-demux debugfs with HW notification statistics.
@@ -650,5 +729,96 @@
*/
void mpq_dmx_update_hw_statistics(struct mpq_demux *mpq_demux);
+/**
+ * mpq_dmx_set_secure_mode - Handles set secure mode command from demux device
+ *
+ * @demux: demux interface
+ * @sec_mode: Secure mode details (key ladder info)
+ *
+ * Return error code
+ */
+int mpq_dmx_set_secure_mode(struct dmx_demux *demux,
+ struct dmx_secure_mode *sec_mode);
+
+/**
+ * mpq_sdmx_open_session - Handle the details of opening a new secure demux
+ * session for the specified mpq demux instance. Multiple calls to this
+ * is allowed, reference counting is managed to open it only when needed.
+ *
+ * @mpq_demux: mpq demux instance
+ *
+ * Return error code
+ */
+int mpq_sdmx_open_session(struct mpq_demux *mpq_demux);
+
+/**
+ * mpq_sdmx_close_session - Closes secure demux session. The session
+ * is closed only if reference counter of the session reaches 0.
+ *
+ * @mpq_demux: mpq demux instance
+ *
+ * Return error code
+ */
+int mpq_sdmx_close_session(struct mpq_demux *mpq_demux);
+
+/**
+ * mpq_dmx_init_mpq_feed - Initialize an mpq feed object
+ * The function allocates mpq_feed object and saves in the dvb_demux_feed
+ * priv field.
+ *
+ * @feed: A dvb demux level feed parent object
+ *
+ * Return error code
+ */
+int mpq_dmx_init_mpq_feed(struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_terminate_feed - Destroy an mpq feed object
+ *
+ * @feed: A dvb demux level feed parent object
+ *
+ * Return error code
+ */
+int mpq_dmx_terminate_feed(struct dvb_demux_feed *feed);
+
+/**
+ * mpq_dmx_write - demux write() function implementation.
+ *
+ * A wrapper function used for writing new data into the demux via DVR.
+ * It checks where new data should actually go, the secure demux or the normal
+ * dvb demux software demux.
+ *
+ * @demux: demux interface
+ * @buf: input buffer
+ * @count: number of data bytes in input buffer
+ *
+ * Return number of bytes processed or error code
+ */
+int mpq_dmx_write(struct dmx_demux *demux, const char *buf, size_t count);
+
+/**
+ * mpq_sdmx_process - Perform demuxing process on the specified input buffer
+ * in the secure demux instance
+ *
+ * @mpq_demux: mpq demux instance
+ * @input: input buffer descriptor
+ * @fill_count: number of data bytes in input buffer that can be read
+ * @read_offset: offset in buffer for reading
+ *
+ * Return number of bytes read or error code
+ */
+int mpq_sdmx_process(struct mpq_demux *mpq_demux,
+ struct sdmx_buff_descr *input,
+ u32 fill_count,
+ u32 read_offset);
+
+/**
+ * mpq_sdmx_loaded - Returns 1 if secure demux application is loaded,
+ * 0 otherwise. This function should be used to determine whether or not
+ * processing should take place in the SDMX.
+ */
+int mpq_sdmx_is_loaded(void);
+
+
#endif /* _MPQ_DMX_PLUGIN_COMMON_H */
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
index 3ad3d21..50d18b9 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -440,24 +440,17 @@
*/
feed->pusi_seen = 0;
- /*
- * For video PES, data is tunneled to the decoder,
- * initialize tunneling and pes parsing.
- */
- if (mpq_dmx_is_video_feed(feed)) {
- ret = mpq_dmx_init_video_feed(feed);
+ ret = mpq_dmx_init_mpq_feed(feed);
+ if (ret < 0) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_dmx_init_mpq_feed failed(%d)\n",
+ __func__,
+ ret);
- if (ret < 0) {
- MPQ_DVB_DBG_PRINT(
- "%s: mpq_dmx_init_video_feed failed(%d)\n",
- __func__,
- ret);
+ if (mpq_demux->source < DMX_SOURCE_DVR0)
+ mpq_tsif_dmx_stop(mpq_demux);
- if (mpq_demux->source < DMX_SOURCE_DVR0)
- mpq_tsif_dmx_stop(mpq_demux);
-
- return ret;
- }
+ return ret;
}
return ret;
@@ -489,12 +482,7 @@
return -EINVAL;
}
- /*
- * For video PES, data is tunneled to the decoder,
- * terminate tunnel and pes parsing.
- */
- if (mpq_dmx_is_video_feed(feed))
- mpq_dmx_terminate_video_feed(feed);
+ mpq_dmx_terminate_feed(feed);
if (mpq_demux->source < DMX_SOURCE_DVR0) {
/* Source from TSIF, need to configure TSIF hardware */
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
index 0eee8f3..781db4d 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012-2013, 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
@@ -13,6 +13,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kthread.h>
+#include <linux/vmalloc.h>
#include <mach/msm_tspp.h>
#include "mpq_dvb_debug.h"
#include "mpq_dmx_plugin_common.h"
@@ -116,6 +117,13 @@
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 TSPP filters along with
* how many feeds are opened on same PID.
*/
@@ -222,6 +230,51 @@
}
/**
+ * Demux TS packets from TSPP by secure-demux.
+ * The fucntion 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 start_addr = 0;
+ int i;
+
+ while ((tspp_data_desc = tspp_get_buffer(0, channel_id)) != NULL) {
+ if (0 == aggregate_count)
+ start_addr = tspp_data_desc->phys_base;
+ 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 +=
+ tspp_data_desc->size / TSPP_RAW_TTS_SIZE;
+ }
+
+ if (!aggregate_count)
+ return;
+
+ MPQ_DVB_DBG_PRINT(
+ "%s: Processing %d descriptors: %d bytes at start address 0x%x\n",
+ __func__, aggregate_count, aggregate_len, start_addr);
+ input.base_addr = (void *)start_addr;
+ input.size = aggregate_len;
+ mpq_sdmx_process(mpq_demux, &input, aggregate_len, 0);
+
+ 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
@@ -270,27 +323,40 @@
mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
mpq_demux->hw_notification_size = 0;
- /*
- * Go through all filled descriptors
- * and perform demuxing on them
- */
- while ((tspp_data_desc = tspp_get_buffer(0, channel_id))
- != NULL) {
- notif_size = tspp_data_desc->size / TSPP_RAW_TTS_SIZE;
- mpq_demux->hw_notification_size += notif_size;
+ if (MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC != allocation_mode &&
+ mpq_sdmx_is_loaded())
+ pr_err_once(
+ "%s: TSPP Allocation mode does not support secure demux.\n",
+ __func__);
- for (j = 0; j < notif_size; j++)
- dvb_dmx_swfilter_packet(
- &mpq_demux->demux,
- ((u8 *)tspp_data_desc->virt_base) +
- j * TSPP_RAW_TTS_SIZE,
- ((u8 *)tspp_data_desc->virt_base) +
- j * TSPP_RAW_TTS_SIZE + TSPP_RAW_SIZE);
+ if (MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC == allocation_mode &&
+ mpq_sdmx_is_loaded()) {
+ mpq_dmx_tspp_aggregated_process(tsif, channel_id);
+ } else {
/*
- * Notify TSPP that the buffer
- * is no longer needed
+ * Go through all filled descriptors
+ * and perform demuxing on them
*/
- tspp_release_buffer(0, channel_id, tspp_data_desc->id);
+ while ((tspp_data_desc = tspp_get_buffer(0, channel_id))
+ != NULL) {
+ notif_size = tspp_data_desc->size /
+ TSPP_RAW_TTS_SIZE;
+ mpq_demux->hw_notification_size += notif_size;
+
+ for (j = 0; j < notif_size; j++)
+ dvb_dmx_swfilter_packet(
+ &mpq_demux->demux,
+ ((u8 *)tspp_data_desc->virt_base) +
+ j * TSPP_RAW_TTS_SIZE,
+ ((u8 *)tspp_data_desc->virt_base) +
+ j * TSPP_RAW_TTS_SIZE + TSPP_RAW_SIZE);
+ /*
+ * Notify TSPP that the buffer
+ * is no longer needed
+ */
+ tspp_release_buffer(0, channel_id,
+ tspp_data_desc->id);
+ }
}
if (mpq_demux->hw_notification_size &&
@@ -523,8 +589,8 @@
(void *)tsif,
tspp_channel_timeout);
- /* register allocater and provide allocation function
- * that allocates from continous memory so that we can have
+ /* 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.
*/
@@ -739,7 +805,7 @@
struct mpq_demux *mpq_demux = feed->demux->priv;
MPQ_DVB_DBG_PRINT(
- "%s(%d) executed\n",
+ "%s(pid=%d) executed\n",
__func__,
feed->pid);
@@ -770,24 +836,16 @@
*/
feed->pusi_seen = 0;
- /*
- * For video PES, data is tunneled to the decoder,
- * initialize tunneling and pes parsing.
- */
- if (mpq_dmx_is_video_feed(feed)) {
- ret = mpq_dmx_init_video_feed(feed);
+ 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);
- if (ret < 0) {
- MPQ_DVB_ERR_PRINT(
- "%s: mpq_dmx_init_video_feed failed(%d)\n",
- __func__,
- ret);
-
- if (mpq_demux->source < DMX_SOURCE_DVR0)
- mpq_tspp_dmx_remove_channel(feed);
-
- return ret;
- }
+ return ret;
}
return 0;
@@ -797,18 +855,12 @@
{
int ret = 0;
struct mpq_demux *mpq_demux = feed->demux->priv;
-
MPQ_DVB_DBG_PRINT(
"%s(%d) executed\n",
__func__,
feed->pid);
- /*
- * For video PES, data is tunneled to the decoder,
- * terminate tunnel and pes parsing.
- */
- if (mpq_dmx_is_video_feed(feed))
- mpq_dmx_terminate_video_feed(feed);
+ mpq_dmx_terminate_feed(feed);
if (mpq_demux->source < DMX_SOURCE_DVR0) {
/* source from TSPP, need to configure tspp pipe */
@@ -986,7 +1038,8 @@
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->set_secure_mode = mpq_dmx_set_secure_mode;
+ 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",
@@ -1018,6 +1071,20 @@
mpq_dmx_tspp_info.tsif[i].buffer_count =
TSPP_BUFFER_COUNT(tspp_out_buffer_size);
+ mpq_dmx_tspp_info.tsif[i].aggregate_ids =
+ vzalloc(mpq_dmx_tspp_info.tsif[i].buffer_count *
+ sizeof(int));
+ if (NULL == mpq_dmx_tspp_info.tsif[i].aggregate_ids) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: Failed to allocate memory for buffer descriptors aggregation\n",
+ __func__);
+ for (j = 0; j < i; j++) {
+ kthread_stop(mpq_dmx_tspp_info.tsif[j].thread);
+ vfree(mpq_dmx_tspp_info.tsif[j].aggregate_ids);
+ mutex_destroy(&mpq_dmx_tspp_info.tsif[j].mutex);
+ }
+ return -ENOMEM;
+ }
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;
@@ -1042,8 +1109,11 @@
mpq_dmx_tspp_info.tsif[i].name);
if (IS_ERR(mpq_dmx_tspp_info.tsif[i].thread)) {
+ vfree(mpq_dmx_tspp_info.tsif[i].aggregate_ids);
+
for (j = 0; j < i; j++) {
kthread_stop(mpq_dmx_tspp_info.tsif[j].thread);
+ vfree(mpq_dmx_tspp_info.tsif[j].aggregate_ids);
mutex_destroy(&mpq_dmx_tspp_info.tsif[j].mutex);
}
@@ -1067,6 +1137,7 @@
for (i = 0; i < TSIF_COUNT; i++) {
kthread_stop(mpq_dmx_tspp_info.tsif[i].thread);
+ vfree(mpq_dmx_tspp_info.tsif[i].aggregate_ids);
mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex);
}
}
@@ -1098,6 +1169,8 @@
MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC)
mpq_dmx_channel_mem_free(i);
}
+ if (mpq_dmx_tspp_info.tsif[i].aggregate_ids)
+ vfree(mpq_dmx_tspp_info.tsif[i].aggregate_ids);
mutex_unlock(&mpq_dmx_tspp_info.tsif[i].mutex);
kthread_stop(mpq_dmx_tspp_info.tsif[i].thread);
diff --git a/drivers/media/dvb/mpq/demux/mpq_sdmx.c b/drivers/media/dvb/mpq/demux/mpq_sdmx.c
new file mode 100644
index 0000000..0f91930
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_sdmx.c
@@ -0,0 +1,928 @@
+/* Copyright (c) 2013, 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/slab.h>
+#include <linux/mutex.h>
+#include "qseecom_kernel.h"
+#include "mpq_sdmx.h"
+
+static struct qseecom_handle *sdmx_qseecom_handles[SDMX_MAX_SESSIONS];
+static struct mutex sdmx_lock[SDMX_MAX_SESSIONS];
+
+#define QSEECOM_ALIGN_SIZE 0x40
+#define QSEECOM_ALIGN_MASK (QSEECOM_ALIGN_SIZE - 1)
+#define QSEECOM_ALIGN(x) \
+ ((x + QSEECOM_ALIGN_SIZE) & (~QSEECOM_ALIGN_MASK))
+
+enum sdmx_cmd_id {
+ SDMX_OPEN_SESSION_CMD,
+ SDMX_CLOSE_SESSION_CMD,
+ SDMX_SET_SESSION_CFG_CMD,
+ SDMX_ADD_FILTER_CMD,
+ SDMX_REMOVE_FILTER_CMD,
+ SDMX_SET_KL_IDX_CMD,
+ SDMX_ADD_RAW_PID_CMD,
+ SDMX_REMOVE_RAW_PID_CMD,
+ SDMX_PROCESS_CMD,
+ SDMX_GET_DBG_COUNTERS_CMD,
+ SDMX_RESET_DBG_COUNTERS_CMD,
+ SDMX_GET_VERSION_CMD
+};
+
+struct sdmx_proc_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ u8 flags;
+ struct sdmx_buff_descr in_buf_descr;
+ u32 inp_fill_cnt;
+ u32 in_rd_offset;
+ u32 num_filters;
+ struct sdmx_filter_status filters_status[];
+};
+
+struct sdmx_proc_rsp {
+ enum sdmx_status ret;
+ u32 inp_fill_cnt;
+ u32 in_rd_offset;
+ u32 err_indicators;
+ u32 status_indicators;
+};
+
+struct sdmx_open_ses_req {
+ enum sdmx_cmd_id cmd_id;
+};
+
+struct sdmx_open_ses_rsp {
+ enum sdmx_status ret;
+ u32 session_handle;
+};
+
+struct sdmx_close_ses_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+};
+
+struct sdmx_close_ses_rsp {
+ enum sdmx_status ret;
+};
+
+struct sdmx_ses_cfg_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ enum sdmx_proc_mode process_mode;
+ enum sdmx_inp_mode input_mode;
+ enum sdmx_pkt_format packet_len;
+ u8 odd_scramble_bits;
+ u8 even_scramble_bits;
+};
+
+struct sdmx_ses_cfg_rsp {
+ enum sdmx_status ret;
+};
+
+struct sdmx_set_kl_ind_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ u32 pid;
+ u32 kl_index;
+};
+
+struct sdmx_set_kl_ind_rsp {
+ enum sdmx_status ret;
+};
+
+struct sdmx_add_filt_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ u32 pid;
+ enum sdmx_filter filter_type;
+ struct sdmx_buff_descr meta_data_buf;
+ enum sdmx_buf_mode buffer_mode;
+ u32 num_data_bufs;
+ struct sdmx_buff_descr data_bufs[];
+};
+
+struct sdmx_add_filt_rsp {
+ enum sdmx_status ret;
+ u32 filter_handle;
+};
+
+struct sdmx_rem_filt_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ u32 filter_handle;
+};
+
+struct sdmx_rem_filt_rsp {
+ enum sdmx_status ret;
+};
+
+struct sdmx_add_raw_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ u32 filter_handle;
+ u32 pid;
+};
+
+struct sdmx_add_raw_rsp {
+ enum sdmx_status ret;
+};
+
+struct sdmx_rem_raw_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ u32 filter_handle;
+ u32 pid;
+};
+
+struct sdmx_rem_raw_rsp {
+ enum sdmx_status ret;
+};
+
+struct sdmx_get_counters_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+ u32 num_filters;
+};
+
+struct sdmx_get_counters_rsp {
+ enum sdmx_status ret;
+ struct sdmx_session_dbg_counters session_counters;
+ u32 num_filters;
+ struct sdmx_filter_dbg_counters filter_counters[];
+};
+
+struct sdmx_rst_counters_req {
+ enum sdmx_cmd_id cmd_id;
+ u32 session_handle;
+};
+
+struct sdmx_rst_counters_rsp {
+ enum sdmx_status ret;
+};
+
+struct sdmx_get_version_req {
+ enum sdmx_cmd_id cmd_id;
+};
+
+struct sdmx_get_version_rsp {
+ enum sdmx_status ret;
+ int32_t version;
+};
+
+static void get_cmd_rsp_buffers(int handle_index,
+ void **cmd,
+ int *cmd_len,
+ void **rsp,
+ int *rsp_len)
+{
+ *cmd = sdmx_qseecom_handles[handle_index]->sbuf;
+
+ if (*cmd_len & QSEECOM_ALIGN_MASK)
+ *cmd_len = QSEECOM_ALIGN(*cmd_len);
+
+ *rsp = sdmx_qseecom_handles[handle_index]->sbuf + *cmd_len;
+
+ if (*rsp_len & QSEECOM_ALIGN_MASK)
+ *rsp_len = QSEECOM_ALIGN(*rsp_len);
+
+}
+
+/*
+ * Returns version of secure-demux app.
+ *
+ * @session_handle: Returned instance handle. Must not be NULL.
+ * Return error code
+ */
+int sdmx_get_version(int session_handle, int32_t *version)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_get_version_req *cmd;
+ struct sdmx_get_version_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS) ||
+ (version == NULL))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_get_version_req);
+ rsp_len = sizeof(struct sdmx_get_version_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_GET_VERSION_CMD;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+ *version = rsp->version;
+
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+
+}
+EXPORT_SYMBOL(sdmx_get_version);
+
+/*
+ * Initializes a new secure demux instance and returns a handle of the instance.
+ *
+ * @session_handle: handle of a secure demux instance to get its version.
+ * Return the version if successfull or an error code.
+ */
+int sdmx_open_session(int *session_handle)
+{
+ int res, cmd_len, rsp_len;
+ enum sdmx_status ret, version_ret;
+ struct sdmx_open_ses_req *cmd;
+ struct sdmx_open_ses_rsp *rsp;
+ struct qseecom_handle *qseecom_handle = NULL;
+ int32_t version;
+
+ /* Input validation */
+ if (session_handle == NULL)
+ return SDMX_STATUS_GENERAL_FAILURE;
+
+ /* Start the TZ app */
+ res = qseecom_start_app(&qseecom_handle, "securemm", 4096);
+
+ if (res < 0)
+ return SDMX_STATUS_GENERAL_FAILURE;
+
+ cmd_len = sizeof(struct sdmx_open_ses_req);
+ rsp_len = sizeof(struct sdmx_open_ses_rsp);
+
+ /* Get command and response buffers */
+ cmd = (struct sdmx_open_ses_req *)qseecom_handle->sbuf;
+
+ if (cmd_len & QSEECOM_ALIGN_MASK)
+ cmd_len = QSEECOM_ALIGN(cmd_len);
+
+ rsp = (struct sdmx_open_ses_rsp *)qseecom_handle->sbuf + cmd_len;
+
+ if (rsp_len & QSEECOM_ALIGN_MASK)
+ rsp_len = QSEECOM_ALIGN(rsp_len);
+
+ /* Will be later overridden by SDMX response */
+ *session_handle = SDMX_INVALID_SESSION_HANDLE;
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_OPEN_SESSION_CMD;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(qseecom_handle, (void *)cmd, cmd_len,
+ (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ qseecom_shutdown_app(&qseecom_handle);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ /* Parse response struct */
+ *session_handle = rsp->session_handle;
+
+ /* Initialize handle and mutex */
+ sdmx_qseecom_handles[*session_handle] = qseecom_handle;
+ mutex_init(&sdmx_lock[*session_handle]);
+ ret = rsp->ret;
+
+ /* Get and print the app version */
+ version_ret = sdmx_get_version(*session_handle, &version);
+ if (SDMX_SUCCESS == version_ret)
+ pr_info("TZ SDMX version is %x.%x\n", version >> 8,
+ version & 0xFF);
+ else
+ pr_err("Error reading TZ SDMX version\n");
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_open_session);
+
+/*
+ * Closes a secure demux instance.
+ *
+ * @session_handle: handle of a secure demux instance to close.
+ * Return error code
+ */
+int sdmx_close_session(int session_handle)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_close_ses_req *cmd;
+ struct sdmx_close_ses_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_close_ses_req);
+ rsp_len = sizeof(struct sdmx_close_ses_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_CLOSE_SESSION_CMD;
+ cmd->session_handle = session_handle;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+
+ /* Shutdown the TZ app (or at least free the current handle) */
+ res = qseecom_shutdown_app(&sdmx_qseecom_handles[session_handle]);
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ sdmx_qseecom_handles[session_handle] = NULL;
+
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_close_session);
+
+/*
+ * Configures an open secure demux instance.
+ *
+ * @session_handle: secure demux instance
+ * @proc_mode: Defines secure demux's behavior in case of output
+ * buffer overflow.
+ * @inp_mode: Defines the input encryption settings.
+ * @pkt_format: TS packet length in input buffer.
+ * @odd_scramble_bits: Value of the scramble bits indicating the ODD key.
+ * @even_scramble_bits: Value of the scramble bits indicating the EVEN key.
+ * Return error code
+ */
+int sdmx_set_session_cfg(int session_handle,
+ enum sdmx_proc_mode proc_mode,
+ enum sdmx_inp_mode inp_mode,
+ enum sdmx_pkt_format pkt_format,
+ u8 odd_scramble_bits,
+ u8 even_scramble_bits)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_ses_cfg_req *cmd;
+ struct sdmx_ses_cfg_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_ses_cfg_req);
+ rsp_len = sizeof(struct sdmx_ses_cfg_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_SET_SESSION_CFG_CMD;
+ cmd->session_handle = session_handle;
+ cmd->process_mode = proc_mode;
+ cmd->input_mode = inp_mode;
+ cmd->packet_len = pkt_format;
+ cmd->odd_scramble_bits = odd_scramble_bits;
+ cmd->even_scramble_bits = even_scramble_bits;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_set_session_cfg);
+
+/*
+ * Creates a new secure demux filter and returns a filter handle
+ *
+ * @session_handle: secure demux instance
+ * @pid: pid to filter
+ * @filter_type: type of filtering
+ * @meta_data_buf: meta data buffer descriptor
+ * @data_buf_mode: data buffer mode (ring/linear)
+ * @num_data_bufs: number of data buffers (use 1 for a ring buffer)
+ * @data_bufs: data buffers descriptors array
+ * @filter_handle: returned filter handle
+ *
+ * Return error code
+ */
+int sdmx_add_filter(int session_handle,
+ u16 pid,
+ enum sdmx_filter filterype,
+ struct sdmx_buff_descr *meta_data_buf,
+ enum sdmx_buf_mode d_buf_mode,
+ u32 num_data_bufs,
+ struct sdmx_buff_descr *data_bufs,
+ int *filter_handle)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_add_filt_req *cmd;
+ struct sdmx_add_filt_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS) ||
+ (filter_handle == NULL))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_add_filt_req)
+ + num_data_bufs * sizeof(struct sdmx_buff_descr);
+ rsp_len = sizeof(struct sdmx_add_filt_rsp);
+
+ /* Will be later overridden by SDMX response */
+ *filter_handle = SDMX_INVALID_FILTER_HANDLE;
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_ADD_FILTER_CMD;
+ cmd->session_handle = session_handle;
+ cmd->pid = (u32)pid;
+ cmd->filter_type = filterype;
+ if (meta_data_buf != NULL)
+ memcpy(&(cmd->meta_data_buf), meta_data_buf,
+ sizeof(struct sdmx_buff_descr));
+ else
+ memset(&(cmd->meta_data_buf), 0,
+ sizeof(struct sdmx_buff_descr));
+
+ cmd->buffer_mode = d_buf_mode;
+ cmd->num_data_bufs = num_data_bufs;
+ memcpy(cmd->data_bufs, data_bufs,
+ num_data_bufs * sizeof(struct sdmx_buff_descr));
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ /* Parse response struct */
+ *filter_handle = rsp->filter_handle;
+ ret = rsp->ret;
+
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_add_filter);
+
+/*
+ * Removes a secure demux filter
+ *
+ * @session_handle: secure demux instance
+ * @filter_handle: filter handle to remove
+ *
+ * Return error code
+ */
+int sdmx_remove_filter(int session_handle, int filter_handle)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_rem_filt_req *cmd;
+ struct sdmx_rem_filt_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_rem_filt_req);
+ rsp_len = sizeof(struct sdmx_rem_filt_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_REMOVE_FILTER_CMD;
+ cmd->session_handle = session_handle;
+ cmd->filter_handle = filter_handle;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_remove_filter);
+
+/*
+ * Associates a key ladder index for the specified pid
+ *
+ * @session_handle: secure demux instance
+ * @pid: pid
+ * @key_ladder_index: key ladder index to associate to the pid
+ *
+ * Return error code
+ *
+ * Note: if pid already has some key ladder index associated, it will be
+ * overridden.
+ */
+int sdmx_set_kl_ind(int session_handle, u16 pid, u32 key_ladder_index)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_set_kl_ind_req *cmd;
+ struct sdmx_set_kl_ind_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_set_kl_ind_req);
+ rsp_len = sizeof(struct sdmx_set_kl_ind_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_SET_KL_IDX_CMD;
+ cmd->session_handle = session_handle;
+ cmd->pid = (u32)pid;
+ cmd->kl_index = key_ladder_index;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_set_kl_ind);
+
+/*
+ * Adds the specified pid to an existing raw (recording) filter
+ *
+ * @session_handle: secure demux instance
+ * @filter_handle: raw filter handle
+ * @pid: pid
+ *
+ * Return error code
+ */
+int sdmx_add_raw_pid(int session_handle, int filter_handle, u16 pid)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_add_raw_req *cmd;
+ struct sdmx_add_raw_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_add_raw_req);
+ rsp_len = sizeof(struct sdmx_add_raw_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_ADD_RAW_PID_CMD;
+ cmd->session_handle = session_handle;
+ cmd->filter_handle = filter_handle;
+ cmd->pid = (u32)pid;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_add_raw_pid);
+
+/*
+ * Removes the specified pid from a raw (recording) filter
+ *
+ * @session_handle: secure demux instance
+ * @filter_handle: raw filter handle
+ * @pid: pid
+ *
+ * Return error code
+ */
+int sdmx_remove_raw_pid(int session_handle, int filter_handle, u16 pid)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_rem_raw_req *cmd;
+ struct sdmx_rem_raw_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_rem_raw_req);
+ rsp_len = sizeof(struct sdmx_rem_raw_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_REMOVE_RAW_PID_CMD;
+ cmd->session_handle = session_handle;
+ cmd->filter_handle = filter_handle;
+ cmd->pid = (u32)pid;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_remove_raw_pid);
+
+/*
+ * Call secure demux to perform processing on the specified input buffer
+ *
+ * @session_handle: secure demux instance
+ * @flags: input flags. Currently only EOS marking is supported.
+ * @input_buf_desc: input buffer descriptor
+ * @input_fill_count: number of bytes available in input buffer
+ * @input_read_offset: offset inside input buffer where data starts
+ * @error_indicators: returned general error indicators
+ * @status_indicators: returned general status indicators
+ * @num_filters: number of filters in filter status array
+ * @filter_status: filter status descriptor array
+ *
+ * Return error code
+ */
+int sdmx_process(int session_handle, u8 flags,
+ struct sdmx_buff_descr *input_buf_desc,
+ u32 *input_fill_count,
+ u32 *input_read_offset,
+ u32 *error_indicators,
+ u32 *status_indicators,
+ u32 num_filters,
+ struct sdmx_filter_status *filter_status)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_proc_req *cmd;
+ struct sdmx_proc_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS) ||
+ (input_buf_desc == NULL) ||
+ (input_fill_count == NULL) || (input_read_offset == NULL) ||
+ (error_indicators == NULL) || (status_indicators == NULL) ||
+ (filter_status == NULL))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_proc_req)
+ + num_filters * sizeof(struct sdmx_filter_status);
+ rsp_len = sizeof(struct sdmx_proc_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_PROCESS_CMD;
+ cmd->session_handle = session_handle;
+ cmd->flags = flags;
+ cmd->in_buf_descr.base_addr = input_buf_desc->base_addr;
+ cmd->in_buf_descr.size = input_buf_desc->size;
+ cmd->inp_fill_cnt = *input_fill_count;
+ cmd->in_rd_offset = *input_read_offset;
+ cmd->num_filters = num_filters;
+ memcpy(cmd->filters_status, filter_status,
+ num_filters * sizeof(struct sdmx_filter_status));
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ /* Parse response struct */
+ *input_fill_count = rsp->inp_fill_cnt;
+ *input_read_offset = rsp->in_rd_offset;
+ *error_indicators = rsp->err_indicators;
+ *status_indicators = rsp->status_indicators;
+ memcpy(filter_status, cmd->filters_status,
+ num_filters * sizeof(struct sdmx_filter_status));
+ ret = rsp->ret;
+
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_process);
+
+/*
+ * Returns session-level & filter-level debug counters
+ *
+ * @session_handle: secure demux instance
+ * @session_counters: returned session-level debug counters
+ * @num_filters: returned number of filters reported in filter_counters
+ * @filter_counters: returned filter-level debug counters array
+ *
+ * Return error code
+ */
+int sdmx_get_dbg_counters(int session_handle,
+ struct sdmx_session_dbg_counters *session_counters,
+ u32 *num_filters,
+ struct sdmx_filter_dbg_counters *filter_counters)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_get_counters_req *cmd;
+ struct sdmx_get_counters_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS) ||
+ (session_counters == NULL) || (num_filters == NULL) ||
+ (filter_counters == NULL))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_get_counters_req);
+ rsp_len = sizeof(struct sdmx_get_counters_rsp)
+ + *num_filters * sizeof(struct sdmx_filter_dbg_counters);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_GET_DBG_COUNTERS_CMD;
+ cmd->session_handle = session_handle;
+ cmd->num_filters = *num_filters;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ /* Parse response struct */
+ *session_counters = rsp->session_counters;
+ *num_filters = rsp->num_filters;
+ memcpy(filter_counters, rsp->filter_counters,
+ *num_filters * sizeof(struct sdmx_filter_dbg_counters));
+ ret = rsp->ret;
+
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_get_dbg_counters);
+
+/*
+ * Reset debug counters
+ *
+ * @session_handle: secure demux instance
+ *
+ * Return error code
+ */
+int sdmx_reset_dbg_counters(int session_handle)
+{
+ int res, cmd_len, rsp_len;
+ struct sdmx_rst_counters_req *cmd;
+ struct sdmx_rst_counters_rsp *rsp;
+ enum sdmx_status ret;
+
+ if ((session_handle < 0) || (session_handle >= SDMX_MAX_SESSIONS))
+ return SDMX_STATUS_INVALID_INPUT_PARAMS;
+
+ cmd_len = sizeof(struct sdmx_rst_counters_req);
+ rsp_len = sizeof(struct sdmx_rst_counters_rsp);
+
+ /* Lock shared memory */
+ mutex_lock(&sdmx_lock[session_handle]);
+
+ /* Get command and response buffers */
+ get_cmd_rsp_buffers(session_handle, (void **)&cmd, &cmd_len,
+ (void **)&rsp, &rsp_len);
+
+ /* Populate command struct */
+ cmd->cmd_id = SDMX_RESET_DBG_COUNTERS_CMD;
+ cmd->session_handle = session_handle;
+
+ /* Issue QSEECom command */
+ res = qseecom_send_command(sdmx_qseecom_handles[session_handle],
+ (void *)cmd, cmd_len, (void *)rsp, rsp_len);
+
+ if (res < 0) {
+ mutex_unlock(&sdmx_lock[session_handle]);
+ return SDMX_STATUS_GENERAL_FAILURE;
+ }
+
+ ret = rsp->ret;
+
+ mutex_unlock(&sdmx_lock[session_handle]);
+
+ return ret;
+}
+EXPORT_SYMBOL(sdmx_reset_dbg_counters);
diff --git a/drivers/media/dvb/mpq/demux/mpq_sdmx.h b/drivers/media/dvb/mpq/demux/mpq_sdmx.h
new file mode 100644
index 0000000..ffb9046
--- /dev/null
+++ b/drivers/media/dvb/mpq/demux/mpq_sdmx.h
@@ -0,0 +1,232 @@
+/* Copyright (c) 2013, 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.
+ */
+
+#ifndef _MPQ_SDMX_H
+#define _MPQ_SDMX_H
+
+#include <linux/types.h>
+
+/* Constant declarations */
+#define SDMX_MAX_SESSIONS (4)
+#define SDMX_LOOPBACK_PID (0x2000)
+
+/* Filter-level error indicators */
+#define SDMX_FILTER_SUCCESS (0)
+#define SDMX_FILTER_ERR_MD_BUF_FULL BIT(0)
+#define SDMX_FILTER_ERR_D_BUF_FULL BIT(1)
+#define SDMX_FILTER_ERR_D_LIN_BUFS_FULL BIT(2)
+#define SDMX_FILTER_ERR_INVALID_SCRAMBLE_BITS BIT(3)
+#define SDMX_FILTER_ERR_KL_IND_NOT_SET BIT(4)
+#define SDMX_FILTER_ERR_CAS_DECRYPT_ERROR BIT(5)
+#define SDMX_FILTER_ERR_SEC_VERIF_GENERAL_FAIL BIT(6)
+#define SDMX_FILTER_ERR_SEC_VERIF_CRC32_FAIL BIT(7)
+#define SDMX_FILTER_ERR_SEC_INTERNAL_MALLOC_FAIL BIT(8)
+#define SDMX_FILTER_ERR_SEC_LEN_INVALID BIT(9)
+#define SDMX_FILTER_ERR_SEC_PUSI_PTR_INVALID BIT(10)
+#define SDMX_FILTER_ERR_TS_SYNC_BYTE_INVALID BIT(11)
+#define SDMX_FILTER_ERR_TS_TRANSPORT_ERR BIT(12)
+#define SDMX_FILTER_ERR_CONT_CNT_INVALID BIT(13)
+#define SDMX_FILTER_ERR_CONT_CNT_DUPLICATE BIT(14)
+#define SDMX_FILTER_ERR_INVALID_PES_HDR BIT(15)
+#define SDMX_FILTER_ERR_INVALID_PES_LEN BIT(16)
+#define SDMX_FILTER_ERR_INVALID_PES_ENCRYPTION BIT(17)
+#define SDMX_FILTER_ERR_SECURITY_FAULT BIT(18)
+#define SDMX_FILTER_ERR_IN_NS_BUFFER BIT(19)
+
+/* Filter-level status indicators */
+#define SDMX_FILTER_STATUS_EOS BIT(0)
+
+#define SDMX_INVALID_SESSION_HANDLE (-1)
+#define SDMX_INVALID_FILTER_HANDLE (-1)
+
+/* Input flags */
+#define SDMX_INPUT_FLAG_EOS BIT(0)
+
+
+enum sdmx_buf_mode {
+ SDMX_RING_BUF,
+ SDMX_LINEAR_GROUP_BUF,
+};
+
+enum sdmx_proc_mode {
+ SDMX_PUSH_MODE,
+ SDMX_PULL_MODE,
+};
+
+enum sdmx_inp_mode {
+ SDMX_PKT_ENC_MODE,
+ SDMX_BULK_ENC_MODE,
+ SDMX_CLEAR_MODE,
+};
+
+enum sdmx_pkt_format {
+ SDMX_188_BYTE_PKT = 188,
+ SDMX_192_BYTE_PKT = 192,
+ SDMX_195_BYTE_PKT = 195,
+};
+
+enum sdmx_status {
+ SDMX_SUCCESS = 0,
+ SDMX_STATUS_GENERAL_FAILURE = -1,
+ SDMX_STATUS_MAX_OPEN_SESSIONS_REACHED = -2,
+ SDMX_STATUS_INVALID_SESSION_HANDLE = -3,
+ SDMX_STATUS_INVALID_INPUT_PARAMS = -4,
+ SDMX_STATUS_UNSUPPORTED_MODE = -5,
+ SDMX_STATUS_INVALID_PID = -6,
+ SDMX_STATUS_OUT_OF_MEM = -7,
+ SDMX_STATUS_FILTER_EXISTS = -8,
+ SDMX_STATUS_INVALID_FILTER_HANDLE = -9,
+ SDMX_STATUS_MAX_RAW_PIDS_REACHED = -10,
+ SDMX_STATUS_SINGLE_PID_RAW_FILTER = -11,
+ SDMX_STATUS_INP_BUF_INVALID_PARAMS = -12,
+ SDMX_STATUS_INVALID_FILTER_CFG = -13,
+ SDMX_STATUS_ILLEGAL_WR_PTR_CHANGE = -14,
+ SDMX_STATUS_STALLED_IN_PULL_MODE = -15,
+ SDMX_STATUS_SECURITY_FAULT = -16,
+ SDMX_STATUS_NS_BUFFER_ERROR = -17,
+};
+
+enum sdmx_filter {
+ SDMX_PES_FILTER, /* Other PES */
+ SDMX_SEPARATED_PES_FILTER, /* Separated PES (for decoder) */
+ SDMX_SECTION_FILTER, /* Section */
+ SDMX_PCR_FILTER, /* PCR */
+ SDMX_RAW_FILTER, /* Recording */
+};
+
+struct sdmx_session_dbg_counters {
+ /* Total number of TS-packets input to SDMX. */
+ u32 ts_pkt_in;
+
+ /* Total number of TS-packets filtered out by SDMX. */
+ u32 ts_pkt_out;
+};
+
+struct sdmx_filter_dbg_counters {
+ int filter_handle;
+
+ /* Number of TS-packets filtered. */
+ u32 ts_pkt_count;
+
+ /* Number of TS-packets with adaptation field only (no payload). */
+ u32 ts_pkt_no_payload;
+
+ /* Number of TS-packets with the discontinuity indicator set. */
+ u32 ts_pkt_discont;
+
+ /* Number of duplicate TS-packets detected. */
+ u32 ts_pkt_dup;
+
+ /* Number of packets not decrypted because the key wasn't ready. */
+ u32 ts_pkt_key_not_ready;
+};
+
+struct sdmx_buff_descr {
+ /* Physical address where buffer starts */
+ void *base_addr;
+
+ /* Total size of buffer */
+ u32 size;
+};
+
+/*
+ * Data payload residing in the data buffers is described using this meta-data
+ * header. The meta data header specifies where the payload is located in the
+ * data buffer and how big it is.
+ * The meta data header optionally carries additional relevant meta data
+ * immediately following the meta-data header.
+ */
+struct sdmx_metadata_header {
+ /*
+ * Payload start offset inside data buffer. In case data is managed
+ * as a linear buffer group, this specifies buffer index.
+ */
+ u32 payload_start;
+
+ /* Payload length */
+ u32 payload_length;
+
+ /* Total metadata length (including this header, plus optional
+ * additional metadata.
+ */
+ u32 metadata_length;
+};
+
+
+struct sdmx_filter_status {
+ /* Secure demux filter handle */
+ int filter_handle;
+
+ /*
+ * Number of pending bytes in filter's output data buffer.
+ * For linear buffer mode, this is number of buffers pending.
+ */
+ u32 data_fill_count;
+
+ /*
+ * Offset in data buffer for next data payload to be written.
+ * For linear buffer mode, this is a buffer index.
+ */
+ u32 data_write_offset;
+
+ /* Number of pending bytes in filter's output meta data buffer */
+ u32 metadata_fill_count;
+
+ /* Offset in meta data buffer for next metadata header to be written */
+ u32 metadata_write_offset;
+
+ /* Errors (bitmap) reported by secure demux for this filter */
+ u32 error_indicators;
+
+ /* General status (bitmap) reported by secure demux for this filter */
+ u32 status_indicators;
+};
+
+int sdmx_open_session(int *session_handle);
+
+int sdmx_close_session(int session_handle);
+
+int sdmx_get_version(int session_handle, int32_t *version);
+
+int sdmx_set_session_cfg(int session_handle, enum sdmx_proc_mode proc_mode,
+ enum sdmx_inp_mode inp_mode, enum sdmx_pkt_format pkt_format,
+ u8 odd_scramble_bits, u8 even_scramble_bits);
+
+int sdmx_add_filter(int session_handle, u16 pid, enum sdmx_filter filter_type,
+ struct sdmx_buff_descr *meta_data_buf, enum sdmx_buf_mode data_buf_mode,
+ u32 num_data_bufs, struct sdmx_buff_descr *data_bufs,
+ int *filter_handle);
+
+int sdmx_remove_filter(int session_handle, int filter_handle);
+
+int sdmx_set_kl_ind(int session_handle, u16 pid, u32 key_ladder_index);
+
+int sdmx_add_raw_pid(int session_handle, int filter_handle, u16 pid);
+
+int sdmx_remove_raw_pid(int session_handle, int filter_handle, u16 pid);
+
+int sdmx_process(int session_handle, u8 flags,
+ struct sdmx_buff_descr *input_buf_desc,
+ u32 *input_fill_count, u32 *input_read_offset,
+ u32 *error_indicators,
+ u32 *status_indicators,
+ u32 num_filters,
+ struct sdmx_filter_status *filter_status);
+
+int sdmx_get_dbg_counters(int session_handle,
+ struct sdmx_session_dbg_counters *session_counters,
+ u32 *num_filters,
+ struct sdmx_filter_dbg_counters *filter_counters);
+
+int sdmx_reset_dbg_counters(int session_handle);
+
+#endif /* _MPQ_SDMX_H */
diff --git a/include/linux/dvb/dmx.h b/include/linux/dvb/dmx.h
index 53bbd5e..2cea256 100644
--- a/include/linux/dvb/dmx.h
+++ b/include/linux/dvb/dmx.h
@@ -39,8 +39,7 @@
/* Min recording chunk upon which event is generated */
#define DMX_REC_BUFF_CHUNK_MIN_SIZE (100*188)
-/* Decoder buffers are usually large ~1MB, 10 should suffice */
-#define DMX_MAX_DECODER_BUFFER_NUM (10)
+#define DMX_MAX_DECODER_BUFFER_NUM (32)
typedef enum
{
@@ -574,6 +573,20 @@
int handles[DMX_MAX_DECODER_BUFFER_NUM];
};
+struct dmx_secure_mode {
+ /*
+ * Specifies whether secure mode should be set or not for the filter's
+ * pid. Note that DMX_OUT_TSDEMUX_TAP filters can have more than 1 pid
+ */
+ int is_secured;
+
+ /* PID to associate with key ladder id */
+ __u16 pid;
+
+ /* key ladder information to associate with the specified pid */
+ __u32 key_ladder_id;
+};
+
#define DMX_START _IO('o', 41)
#define DMX_STOP _IO('o', 42)
#define DMX_SET_FILTER _IOW('o', 43, struct dmx_sct_filter_params)
@@ -597,5 +610,7 @@
#define DMX_SET_BUFFER _IOW('o', 62, struct dmx_buffer)
#define DMX_SET_DECODER_BUFFER _IOW('o', 63, struct dmx_decoder_buffers)
#define DMX_REUSE_DECODER_BUFFER _IO('o', 64)
+#define DMX_SET_SECURE_MODE _IOW('o', 65, struct dmx_secure_mode)
+
#endif /*_DVBDMX_H_*/