static int mpq_sw_dmx_start_filtering(struct dvb_demux_feed *feed) { int ret = -EINVAL; struct mpq_demux *mpq_demux = feed->demux->priv; MPQ_DVB_DBG_PRINT("%s(pid=%d) executed\n", __func__, feed->pid); if (mpq_demux == NULL) { MPQ_DVB_ERR_PRINT("%s: invalid mpq_demux handle\n", __func__); goto out; } if (mpq_demux->source < DMX_SOURCE_DVR0) { MPQ_DVB_ERR_PRINT("%s: only DVR source is supported (%d)\n", __func__, mpq_demux->source); goto out; } /* * Always feed sections/PES starting from a new one and * do not partial transfer data from older one */ feed->pusi_seen = 0; ret = mpq_dmx_init_mpq_feed(feed); if (ret) MPQ_DVB_ERR_PRINT("%s: mpq_dmx_init_mpq_feed failed(%d)\n", __func__, ret); out: return ret; }
static int mpq_tspp_dmx_start_filtering(struct dvb_demux_feed *feed) { int ret; struct mpq_demux *mpq_demux = feed->demux->priv; MPQ_DVB_DBG_PRINT( "%s(pid=%d) executed\n", __func__, feed->pid); if (mpq_demux == NULL) { MPQ_DVB_ERR_PRINT( "%s: invalid mpq_demux handle\n", __func__); return -EINVAL; } if (mpq_demux->source < DMX_SOURCE_DVR0) { /* source from TSPP, need to configure tspp pipe */ ret = mpq_tspp_dmx_add_channel(feed); if (ret < 0) { MPQ_DVB_DBG_PRINT( "%s: mpq_tspp_dmx_add_channel failed(%d)\n", __func__, ret); return ret; } } /* * Always feed sections/PES starting from a new one and * do not partial transfer data from older one */ feed->pusi_seen = 0; ret = mpq_dmx_init_mpq_feed(feed); if (ret) { MPQ_DVB_ERR_PRINT( "%s: mpq_dmx_init_mpq_feed failed(%d)\n", __func__, ret); if (mpq_demux->source < DMX_SOURCE_DVR0) mpq_tspp_dmx_remove_channel(feed); return ret; } return 0; }
int mpq_streambuffer_data_write_deposit( struct mpq_streambuffer *sbuff, size_t len) { if (NULL == sbuff) return -EINVAL; if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len)) return -ENOSPC; if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == sbuff->mode) { DVB_RINGBUFFER_PUSH(&sbuff->raw_data, len); wake_up_all(&sbuff->raw_data.queue); } else { /* Linear buffer group */ struct mpq_streambuffer_buffer_desc *desc; desc = (struct mpq_streambuffer_buffer_desc *) &sbuff->raw_data.data[sbuff->raw_data.pwrite]; if ((sbuff->pending_buffers_count == sbuff->buffers_num) || ((desc->size - desc->write_ptr) < len)) { MPQ_DVB_ERR_PRINT( "%s: No space available!\n", __func__); return -ENOSPC; } desc->write_ptr += len; } return 0; }
/** * Stop filtering according to feed parameter. * * @feed: the feed we are working on. * * Return error code. */ static int mpq_tsif_dmx_stop_filtering(struct dvb_demux_feed *feed) { int ret = 0; struct mpq_demux *mpq_demux = feed->demux->priv; MPQ_DVB_DBG_PRINT( "%s(%d) executed\n", __func__, feed->pid); if (mpq_demux == NULL) { MPQ_DVB_ERR_PRINT( "%s: invalid mpq_demux handle\n", __func__); return -EINVAL; } mpq_dmx_terminate_feed(feed); if (mpq_demux->source < DMX_SOURCE_DVR0) { /* Source from TSIF, need to configure TSIF hardware */ ret = mpq_tsif_dmx_stop(mpq_demux); } return ret; }
/** * Returns demux capabilities of TSPPv2 plugin * * @demux: demux device * @caps: Returned capbabilities * * Return error code */ static int mpq_tspp_dmx_get_caps(struct dmx_demux *demux, struct dmx_caps *caps) { struct dvb_demux *dvb_demux = demux->priv; if ((dvb_demux == NULL) || (caps == NULL)) { MPQ_DVB_ERR_PRINT( "%s: invalid parameters\n", __func__); return -EINVAL; } caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_INDEXING | DMX_CAP_VIDEO_DECODER_DATA | DMX_CAP_TS_INSERTION | DMX_CAP_SECURED_INPUT_PLAYBACK; caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES; caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES; caps->num_pid_filters = TSPP_MAX_PID_FILTER_NUM; caps->num_section_filters = dvb_demux->filternum; caps->num_section_filters_per_pid = dvb_demux->filternum; caps->section_filter_length = DMX_FILTER_SIZE; caps->num_demod_inputs = TSIF_COUNT; caps->num_memory_inputs = BAM_INPUT_COUNT; caps->max_bitrate = 320; caps->demod_input_max_bitrate = 96; caps->memory_input_max_bitrate = 80; caps->num_cipher_ops = DMX_MAX_CIPHER_OPERATIONS_COUNT; /* TSIF reports 7 bytes STC at unit of 27MHz */ caps->max_stc = 0x00FFFFFFFFFFFFFF; return 0; }
/** * Returns demux capabilities of TSPPv2 plugin * * @demux: demux device * @caps: Returned capbabilities * * Return error code */ static int mpq_tspp_dmx_get_caps(struct dmx_demux *demux, struct dmx_caps *caps) { struct dvb_demux *dvb_demux = demux->priv; if ((dvb_demux == NULL) || (caps == NULL)) { MPQ_DVB_ERR_PRINT( "%s: invalid parameters\n", __func__); return -EINVAL; } caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_INDEXING | DMX_CAP_VIDEO_DECODER_DATA; caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES; caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES; caps->num_pid_filters = TSPP_MAX_PID_FILTER_NUM; caps->num_section_filters = dvb_demux->filternum; caps->num_section_filters_per_pid = dvb_demux->filternum; caps->section_filter_length = DMX_FILTER_SIZE; caps->num_demod_inputs = TSIF_COUNT; caps->num_memory_inputs = BAM_INPUT_COUNT; caps->max_bitrate = 320; caps->demod_input_max_bitrate = 96; caps->memory_input_max_bitrate = 80; return 0; }
static int mpq_tspp_dmx_start_filtering(struct dvb_demux_feed *feed) { int ret; struct mpq_demux *mpq_demux = feed->demux->priv; MPQ_DVB_DBG_PRINT( "%s(pid=%d) executed\n", __func__, feed->pid); if (mpq_demux == NULL) { MPQ_DVB_ERR_PRINT( "%s: invalid mpq_demux handle\n", __func__); return -EINVAL; } if (mpq_demux->source < DMX_SOURCE_DVR0) { ret = mpq_tspp_dmx_add_channel(feed); if (ret < 0) { MPQ_DVB_DBG_PRINT( "%s: mpq_tspp_dmx_add_channel failed(%d)\n", __func__, ret); return ret; } } feed->pusi_seen = 0; ret = mpq_dmx_init_mpq_feed(feed); if (ret) { MPQ_DVB_ERR_PRINT( "%s: mpq_dmx_init_mpq_feed failed(%d)\n", __func__, ret); if (mpq_demux->source < DMX_SOURCE_DVR0) mpq_tspp_dmx_remove_channel(feed); return ret; } return 0; }
static void mpq_dmx_tspp_aggregated_process(int tsif, int channel_id) { const struct tspp_data_descriptor *tspp_data_desc; struct mpq_demux *mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux; struct sdmx_buff_descr input; size_t aggregate_len = 0; size_t aggregate_count = 0; phys_addr_t buff_start_addr_phys; phys_addr_t buff_current_addr_phys = 0; u32 notif_size; int i; while ((tspp_data_desc = tspp_get_buffer(0, channel_id)) != NULL) { if (0 == aggregate_count) buff_current_addr_phys = tspp_data_desc->phys_base; notif_size = tspp_data_desc->size / TSPP_RAW_TTS_SIZE; mpq_dmx_tspp_info.tsif[tsif].aggregate_ids[aggregate_count] = tspp_data_desc->id; aggregate_len += tspp_data_desc->size; aggregate_count++; mpq_demux->hw_notification_size += notif_size; if (mpq_demux->num_active_feeds > mpq_demux->num_secure_feeds) mpq_dmx_tspp_swfilter_desc(mpq_demux, tspp_data_desc); } if (!aggregate_count) return; buff_start_addr_phys = mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_phys_base; if (buff_start_addr_phys > 0xFFFFFFFF) MPQ_DVB_ERR_PRINT( "%s: WARNNING - physical address %pa is larger than 32bits!\n", __func__, &buff_start_addr_phys); input.base_addr = (void *)(u32)buff_start_addr_phys; input.size = mpq_dmx_tspp_info.tsif[tsif].buffer_count * TSPP_DESCRIPTOR_SIZE; if (mpq_sdmx_is_loaded() && mpq_demux->sdmx_filter_count) { MPQ_DVB_DBG_PRINT( "%s: SDMX Processing %d descriptors: %d bytes at start address 0x%x, read offset %d\n", __func__, aggregate_count, aggregate_len, (unsigned int)input.base_addr, (int)(buff_current_addr_phys - buff_start_addr_phys)); mpq_sdmx_process(mpq_demux, &input, aggregate_len, buff_current_addr_phys - buff_start_addr_phys, TSPP_RAW_TTS_SIZE); } for (i = 0; i < aggregate_count; i++) tspp_release_buffer(0, channel_id, mpq_dmx_tspp_info.tsif[tsif].aggregate_ids[i]); }
static int mpq_tspp_add_all_user_filters(int channel_id, enum tspp_source source) { struct tspp_filter tspp_filter; int tsif = TSPP_GET_TSIF_NUM(channel_id); int slot; u16 added_count = 0; u16 total_filters_count = 0; MPQ_DVB_DBG_PRINT("%s: executed\n", __func__); tspp_filter.mode = TSPP_MODE_RAW; tspp_filter.source = source; tspp_filter.decrypt = 0; for (slot = 0; slot < TSPP_MAX_PID_FILTER_NUM; slot++) { if (mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid == -1) continue; total_filters_count++; if (added_count > TSPP_MAX_HW_PID_FILTER_NUM) continue; tspp_filter.priority = mpq_tspp_allocate_hw_filter_index(tsif); if (mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid == TSPP_PASS_THROUGH_PID) { tspp_filter.pid = 0; tspp_filter.mask = 0; } else { tspp_filter.pid = mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid; tspp_filter.mask = TSPP_PID_MASK; } MPQ_DVB_DBG_PRINT( "%s: adding HW filter, PID = %d, mask = 0x%X, index = %d\n", __func__, tspp_filter.pid, tspp_filter.mask, tspp_filter.priority); if (!tspp_add_filter(0, channel_id, &tspp_filter)) { mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index = tspp_filter.priority; added_count++; } else { MPQ_DVB_ERR_PRINT("%s: tspp_add_filter failed\n", __func__); } } if ((added_count != TSPP_MAX_HW_PID_FILTER_NUM) || (added_count != total_filters_count)) return -EINVAL; return 0; }
/** * Allocate memory for channel output of specific TSIF. * * @tsif: The TSIF id to which memory should be allocated. * * Return error status */ static int mpq_dmx_channel_mem_alloc(int tsif) { int result; size_t len; MPQ_DVB_DBG_PRINT("%s(%d)\n", __func__, tsif); mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle = ion_alloc(mpq_dmx_tspp_info.ion_client, (mpq_dmx_tspp_info.tsif[tsif].buffer_count * TSPP_DESCRIPTOR_SIZE), SZ_4K, ION_HEAP(tspp_out_ion_heap), 0); /* non-cached */ if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle)) { MPQ_DVB_ERR_PRINT("%s: ion_alloc() failed\n", __func__); mpq_dmx_channel_mem_free(tsif); return -ENOMEM; } /* save virtual base address of heap */ mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_virt_base = ion_map_kernel(mpq_dmx_tspp_info.ion_client, mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle); if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[tsif]. ch_mem_heap_virt_base)) { MPQ_DVB_ERR_PRINT("%s: ion_map_kernel() failed\n", __func__); mpq_dmx_channel_mem_free(tsif); return -ENOMEM; } /* save physical base address of heap */ result = ion_phys(mpq_dmx_tspp_info.ion_client, mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle, &(mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_phys_base), &len); if (result < 0) { MPQ_DVB_ERR_PRINT("%s: ion_phys() failed\n", __func__); mpq_dmx_channel_mem_free(tsif); return -ENOMEM; } return 0; }
static int mpq_tspp_add_null_blocking_filters(int channel_id, enum tspp_source source) { struct tspp_filter tspp_filter; int ret = 0; int i, j; u16 full_pid_mask = 0x1FFF; u8 mask_shift; u8 pid_shift; int tsif = TSPP_GET_TSIF_NUM(channel_id); MPQ_DVB_DBG_PRINT("%s: executed, channel id = %d, source = %d\n", __func__, channel_id, source); tspp_filter.mode = TSPP_MODE_RAW; tspp_filter.source = source; tspp_filter.decrypt = 0; for (i = 0; i < TSPP_BLOCK_NULLS_FILTERS_NUM; i++) { tspp_filter.priority = mpq_tspp_allocate_hw_filter_index(tsif); if (tspp_filter.priority != i) { MPQ_DVB_ERR_PRINT( "%s: got unexpected HW index %d, expected %d\n", __func__, tspp_filter.priority, i); ret = -1; break; } mask_shift = (TSPP_BLOCK_NULLS_FILTERS_NUM - 1 - i); pid_shift = (TSPP_BLOCK_NULLS_FILTERS_NUM - i); tspp_filter.mask = ((full_pid_mask >> mask_shift) << mask_shift); tspp_filter.pid = ((full_pid_mask >> pid_shift) << pid_shift); if (tspp_add_filter(0, channel_id, &tspp_filter)) { ret = -1; break; } } if (ret) { for (j = 0; j < i; j++) { tspp_filter.priority = j; mpq_tspp_release_hw_filter_index(tsif, j); tspp_remove_filter(0, channel_id, &tspp_filter); } } else { MPQ_DVB_DBG_PRINT( "%s: NULL blocking filters added successfully\n", __func__); } return ret; }
ssize_t mpq_streambuffer_data_write( struct mpq_streambuffer *sbuff, const u8 *buf, size_t len) { int res; if ((NULL == sbuff) || (NULL == buf)) return -EINVAL; if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == sbuff->mode) { if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len)) return -ENOSPC; /* * Secure buffers are not permitted to be mapped into kernel * memory, and so buffer base address may be NULL */ if (NULL == sbuff->raw_data.data) return -EPERM; res = dvb_ringbuffer_write(&sbuff->raw_data, buf, len); wake_up_all(&sbuff->raw_data.queue); } else { /* Linear buffer group */ struct mpq_streambuffer_buffer_desc *desc; desc = (struct mpq_streambuffer_buffer_desc *) &sbuff->raw_data.data[sbuff->raw_data.pwrite]; /* * Secure buffers are not permitted to be mapped into kernel * memory, and so buffer base address may be NULL */ if (NULL == desc->base) return -EPERM; if ((sbuff->pending_buffers_count == sbuff->buffers_num) || ((desc->size - desc->write_ptr) < len)) { MPQ_DVB_ERR_PRINT( "%s: No space available! %d pending buffers out of %d total buffers. write_ptr=%d, size=%d\n", __func__, sbuff->pending_buffers_count, sbuff->buffers_num, desc->write_ptr, desc->size); return -ENOSPC; } 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; } return res; }
static int mpq_sw_dmx_stop_filtering(struct dvb_demux_feed *feed) { int ret; MPQ_DVB_DBG_PRINT("%s(%d) executed\n", __func__, feed->pid); ret = mpq_dmx_terminate_feed(feed); if (ret) MPQ_DVB_ERR_PRINT("%s: mpq_dmx_terminate_feed failed(%d)\n", __func__, ret); return ret; }
static int mpq_sw_dmx_set_source(struct dmx_demux *demux, const dmx_source_t *src) { int ret = -EINVAL; if (demux == NULL || demux->priv == NULL || src == NULL) { MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__); goto out; } if (*src >= DMX_SOURCE_DVR0 && *src <= DMX_SOURCE_DVR3) { ret = mpq_dmx_set_source(demux, src); if (ret) MPQ_DVB_ERR_PRINT( "%s: mpq_dmx_set_source(%d) failed, ret=%d\n", __func__, *src, ret); } else { MPQ_DVB_ERR_PRINT("%s: not a DVR source\n", __func__); } out: return ret; }
/** * Stop TSIF operation and detach from TSIF driver. * * @mpq_demux: the mpq_demux we are working on. * * Return error code. */ static int mpq_tsif_dmx_stop(struct mpq_demux *mpq_demux) { int tsif; struct tsif_driver_info *tsif_driver; MPQ_DVB_DBG_PRINT("%s executed\n", __func__); /* determine the TSIF we are reading from */ if (mpq_demux->source == DMX_SOURCE_FRONT0) { tsif = 0; } else if (mpq_demux->source == DMX_SOURCE_FRONT1) { tsif = 1; } else { /* invalid source */ MPQ_DVB_ERR_PRINT( "%s: invalid input source (%d)\n", __func__, mpq_demux->source); return -EINVAL; } if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex)) return -ERESTARTSYS; mpq_dmx_tsif_info.tsif[tsif].ref_count--; if (mpq_dmx_tsif_info.tsif[tsif].ref_count == 0) { tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver); tsif_stop(tsif_driver->tsif_handler); tsif_detach(tsif_driver->tsif_handler); tsif_driver->tsif_handler = NULL; tsif_driver->data_buffer = NULL; tsif_driver->buffer_size = 0; atomic_set(&mpq_dmx_tsif_info.tsif[tsif].data_cnt, 0); mpq_dmx_tsif_info.tsif[tsif].mpq_demux = NULL; } mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex); return 0; }
static int mpq_tspp_remove_null_blocking_filters(int channel_id, enum tspp_source source) { struct tspp_filter tspp_filter; int tsif = TSPP_GET_TSIF_NUM(channel_id); int ret = 0; int i; MPQ_DVB_DBG_PRINT("%s: executed, channel id = %d, source = %d\n", __func__, channel_id, source); for (i = 0; i < TSPP_BLOCK_NULLS_FILTERS_NUM; i++) { tspp_filter.priority = i; if (tspp_remove_filter(0, channel_id, &tspp_filter)) { MPQ_DVB_ERR_PRINT("%s: failed to remove filter %d\n", __func__, i); ret = -1; } mpq_tspp_release_hw_filter_index(tsif, i); } return ret; }
static int mpq_sw_dmx_get_caps(struct dmx_demux *demux, struct dmx_caps *caps) { struct dvb_demux *dvb_demux = demux->priv; if (dvb_demux == NULL || caps == NULL) { MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__); return -EINVAL; } caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_DECODER_DATA | DMX_CAP_TS_INSERTION | DMX_CAP_VIDEO_INDEXING | DMX_CAP_AUTO_BUFFER_FLUSH; caps->recording_max_video_pids_indexed = 0; caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES; caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES; caps->num_pid_filters = MPQ_MAX_DMX_FILES; caps->num_section_filters = dvb_demux->filternum; caps->num_section_filters_per_pid = dvb_demux->filternum; caps->section_filter_length = DMX_FILTER_SIZE; caps->num_demod_inputs = 0; caps->num_memory_inputs = CONFIG_DVB_MPQ_NUM_DMX_DEVICES; caps->max_bitrate = 192; caps->demod_input_max_bitrate = 96; caps->memory_input_max_bitrate = 96; caps->num_cipher_ops = 1; /* No STC support */ caps->max_stc = 0; /* Buffer requirements */ caps->section.flags = DMX_BUFFER_EXTERNAL_SUPPORT | DMX_BUFFER_INTERNAL_SUPPORT | DMX_BUFFER_CACHED; caps->section.max_buffer_num = 1; caps->section.max_size = 0xFFFFFFFF; caps->section.size_alignment = 0; caps->pes.flags = DMX_BUFFER_EXTERNAL_SUPPORT | DMX_BUFFER_INTERNAL_SUPPORT | DMX_BUFFER_CACHED; caps->pes.max_buffer_num = 1; caps->pes.max_size = 0xFFFFFFFF; caps->pes.size_alignment = 0; caps->recording_188_tsp.flags = DMX_BUFFER_EXTERNAL_SUPPORT | DMX_BUFFER_INTERNAL_SUPPORT | DMX_BUFFER_CACHED; caps->recording_188_tsp.max_buffer_num = 1; caps->recording_188_tsp.max_size = 0xFFFFFFFF; caps->recording_188_tsp.size_alignment = 0; caps->recording_192_tsp.flags = DMX_BUFFER_EXTERNAL_SUPPORT | DMX_BUFFER_INTERNAL_SUPPORT | DMX_BUFFER_CACHED; caps->recording_192_tsp.max_buffer_num = 1; caps->recording_192_tsp.max_size = 0xFFFFFFFF; caps->recording_192_tsp.size_alignment = 0; caps->playback_188_tsp.flags = DMX_BUFFER_EXTERNAL_SUPPORT | DMX_BUFFER_INTERNAL_SUPPORT | DMX_BUFFER_CACHED; caps->playback_188_tsp.max_buffer_num = 1; caps->playback_188_tsp.max_size = 0xFFFFFFFF; caps->playback_188_tsp.size_alignment = 188; caps->playback_192_tsp.flags = DMX_BUFFER_EXTERNAL_SUPPORT | DMX_BUFFER_INTERNAL_SUPPORT | DMX_BUFFER_CACHED; caps->playback_192_tsp.max_buffer_num = 1; caps->playback_192_tsp.max_size = 0xFFFFFFFF; caps->playback_192_tsp.size_alignment = 192; caps->decoder.flags = DMX_BUFFER_SECURED_IF_DECRYPTED | DMX_BUFFER_EXTERNAL_SUPPORT | DMX_BUFFER_INTERNAL_SUPPORT | DMX_BUFFER_LINEAR_GROUP_SUPPORT | DMX_BUFFER_CACHED; caps->decoder.max_buffer_num = DMX_MAX_DECODER_BUFFER_NUM; caps->decoder.max_size = 0xFFFFFFFF; caps->decoder.size_alignment = SZ_4K; return 0; }
static int mpq_tspp_dmx_init( struct dvb_adapter *mpq_adapter, struct mpq_demux *mpq_demux) { int result; MPQ_DVB_DBG_PRINT("%s executed\n", __func__); mpq_dmx_tspp_info.ion_client = mpq_demux->ion_client; /* Set the kernel-demux object capabilities */ mpq_demux->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_PES_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING | DMX_CRC_CHECKING | DMX_TS_DESCRAMBLING; /* Set dvb-demux "virtual" function pointers */ mpq_demux->demux.priv = (void *)mpq_demux; mpq_demux->demux.filternum = TSPP_MAX_SECTION_FILTER_NUM; mpq_demux->demux.feednum = MPQ_MAX_DMX_FILES; mpq_demux->demux.start_feed = mpq_tspp_dmx_start_filtering; mpq_demux->demux.stop_feed = mpq_tspp_dmx_stop_filtering; mpq_demux->demux.write_to_decoder = mpq_tspp_dmx_write_to_decoder; mpq_demux->demux.decoder_fullness_init = mpq_dmx_decoder_fullness_init; mpq_demux->demux.decoder_fullness_wait = mpq_dmx_decoder_fullness_wait; mpq_demux->demux.decoder_fullness_abort = mpq_dmx_decoder_fullness_abort; mpq_demux->demux.decoder_buffer_status = mpq_dmx_decoder_buffer_status; mpq_demux->demux.reuse_decoder_buffer = mpq_dmx_reuse_decoder_buffer; mpq_demux->demux.set_secure_mode = mpq_dmx_set_secure_mode; /* Initialize dvb_demux object */ result = dvb_dmx_init(&mpq_demux->demux); if (result < 0) { MPQ_DVB_ERR_PRINT("%s: dvb_dmx_init failed\n", __func__); goto init_failed; } /* Now initailize the dmx-dev object */ mpq_demux->dmxdev.filternum = MPQ_MAX_DMX_FILES; mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx; mpq_demux->dmxdev.capabilities = DMXDEV_CAP_DUPLEX | DMXDEV_CAP_PULL_MODE | DMXDEV_CAP_INDEXING; mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source; mpq_demux->dmxdev.demux->get_caps = mpq_tspp_dmx_get_caps; mpq_demux->dmxdev.demux->map_buffer = mpq_dmx_map_buffer; mpq_demux->dmxdev.demux->unmap_buffer = mpq_dmx_unmap_buffer; mpq_demux->dmxdev.demux->write = mpq_dmx_write; result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter); if (result < 0) { MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed (errno=%d)\n", __func__, result); goto init_failed_dmx_release; } /* Extend dvb-demux debugfs with TSPP statistics. */ mpq_dmx_init_hw_statistics(mpq_demux); return 0; init_failed_dmx_release: dvb_dmx_release(&mpq_demux->demux); init_failed: return result; }
/** * Returns demux capabilities of TSPPv1 plugin * * @demux: demux device * @caps: Returned capbabilities * * Return error code */ static int mpq_tspp_dmx_get_caps(struct dmx_demux *demux, struct dmx_caps *caps) { struct dvb_demux *dvb_demux = demux->priv; if ((dvb_demux == NULL) || (caps == NULL)) { MPQ_DVB_ERR_PRINT( "%s: invalid parameters\n", __func__); return -EINVAL; } caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_DECODER_DATA; caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES; caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES; caps->num_pid_filters = TSPP_MAX_PID_FILTER_NUM; caps->num_section_filters = dvb_demux->filternum; caps->num_section_filters_per_pid = dvb_demux->filternum; caps->section_filter_length = DMX_FILTER_SIZE; caps->num_demod_inputs = TSIF_COUNT; caps->num_memory_inputs = CONFIG_DVB_MPQ_NUM_DMX_DEVICES; caps->max_bitrate = 144; caps->demod_input_max_bitrate = 72; caps->memory_input_max_bitrate = 72; /* Buffer requirements */ caps->section.flags = DMX_BUFFER_EXTERNAL_SUPPORT | DMX_BUFFER_INTERNAL_SUPPORT; caps->section.max_buffer_num = 1; caps->section.max_size = 0xFFFFFFFF; caps->section.size_alignment = 0; caps->pes.flags = DMX_BUFFER_EXTERNAL_SUPPORT | DMX_BUFFER_INTERNAL_SUPPORT; caps->pes.max_buffer_num = 1; caps->pes.max_size = 0xFFFFFFFF; caps->pes.size_alignment = 0; caps->recording_188_tsp.flags = DMX_BUFFER_EXTERNAL_SUPPORT | DMX_BUFFER_INTERNAL_SUPPORT; caps->recording_188_tsp.max_buffer_num = 1; caps->recording_188_tsp.max_size = 0xFFFFFFFF; caps->recording_188_tsp.size_alignment = 0; caps->recording_192_tsp.flags = DMX_BUFFER_EXTERNAL_SUPPORT | DMX_BUFFER_INTERNAL_SUPPORT; caps->recording_192_tsp.max_buffer_num = 1; caps->recording_192_tsp.max_size = 0xFFFFFFFF; caps->recording_192_tsp.size_alignment = 0; caps->playback_188_tsp.flags = DMX_BUFFER_EXTERNAL_SUPPORT | DMX_BUFFER_INTERNAL_SUPPORT; caps->playback_188_tsp.max_buffer_num = 1; caps->playback_188_tsp.max_size = 0xFFFFFFFF; caps->playback_188_tsp.size_alignment = 0; caps->playback_192_tsp.flags = DMX_BUFFER_EXTERNAL_SUPPORT | DMX_BUFFER_INTERNAL_SUPPORT; caps->playback_192_tsp.max_buffer_num = 1; caps->playback_192_tsp.max_size = 0xFFFFFFFF; caps->playback_192_tsp.size_alignment = 0; caps->decoder.flags = DMX_BUFFER_CONTIGUOUS_MEM | DMX_BUFFER_SECURED_IF_DECRYPTED | DMX_BUFFER_EXTERNAL_SUPPORT | DMX_BUFFER_INTERNAL_SUPPORT | DMX_BUFFER_LINEAR_GROUP_SUPPORT; caps->decoder.max_buffer_num = DMX_MAX_DECODER_BUFFER_NUM; caps->decoder.max_size = 0xFFFFFFFF; caps->decoder.size_alignment = SZ_4K; return 0; }
/** * Removes filter from TSPP. * * @feed: The feed to remove * * Return error status * * The function checks if this is the only PID allocated within * the channel, if so, the channel is closed as well. */ static int mpq_tspp_dmx_remove_channel(struct dvb_demux_feed *feed) { int tsif; int ret; int channel_id; atomic_t *data_cnt; int *channel_ref_count; struct tspp_filter tspp_filter; struct mpq_demux *mpq_demux = feed->demux->priv; /* determine the TSIF we are reading from */ if (mpq_demux->source == DMX_SOURCE_FRONT0) { tsif = 0; } else if (mpq_demux->source == DMX_SOURCE_FRONT1) { tsif = 1; } else { /* invalid source */ MPQ_DVB_ERR_PRINT( "%s: invalid input source (%d)\n", __func__, mpq_demux->source); return -EINVAL; } if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex)) return -ERESTARTSYS; channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL); channel_ref_count = &mpq_dmx_tspp_info.tsif[tsif].channel_ref; data_cnt = &mpq_dmx_tspp_info.tsif[tsif].data_cnt; /* check if required TSPP pipe is already allocated or not */ if (*channel_ref_count == 0) { /* invalid feed provided as the channel is not allocated */ MPQ_DVB_ERR_PRINT( "%s: invalid feed (%d)\n", __func__, channel_id); ret = -EINVAL; goto remove_channel_failed; } tspp_filter.priority = mpq_tspp_get_filter_slot(tsif, feed->pid); if (tspp_filter.priority < 0) { /* invalid feed provided as it has no filter allocated */ MPQ_DVB_ERR_PRINT( "%s: mpq_tspp_get_filter_slot failed (%d,%d)\n", __func__, feed->pid, tsif); ret = -EINVAL; goto remove_channel_failed; } mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].ref_count--; if (mpq_dmx_tspp_info.tsif[tsif]. filters[tspp_filter.priority].ref_count) { /* * there are still references to this pid, do not * remove the filter yet */ mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex); return 0; } ret = tspp_remove_filter(0, channel_id, &tspp_filter); if (ret < 0) { /* invalid feed provided as it has no filter allocated */ MPQ_DVB_ERR_PRINT( "%s: tspp_remove_filter failed (%d,%d)\n", __func__, channel_id, tspp_filter.priority); goto remove_channel_failed_restore_count; } mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].pid = -1; (*channel_ref_count)--; if (*channel_ref_count == 0) { /* channel is not used any more, release it */ tspp_unregister_notification(0, channel_id); tspp_close_channel(0, channel_id); tspp_close_stream(0, channel_id); atomic_set(data_cnt, 0); if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) mpq_dmx_channel_mem_free(tsif); } mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex); return 0; remove_channel_failed_restore_count: mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].ref_count++; remove_channel_failed: mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex); return ret; }
/** * Configure TSPP channel to filter the PID of new feed. * * @feed: The feed to configure the channel with * * Return error status * * The function checks if the new PID can be added to an already * allocated channel, if not, a new channel is allocated and configured. */ static int mpq_tspp_dmx_add_channel(struct dvb_demux_feed *feed) { struct mpq_demux *mpq_demux = feed->demux->priv; struct tspp_select_source tspp_source; struct tspp_filter tspp_filter; int tsif; int ret; int channel_id; int *channel_ref_count; u32 buffer_size; tspp_source.clk_inverse = clock_inv; tspp_source.data_inverse = 0; tspp_source.sync_inverse = 0; tspp_source.enable_inverse = 0; switch (tsif_mode) { case 1: tspp_source.mode = TSPP_TSIF_MODE_1; break; case 2: tspp_source.mode = TSPP_TSIF_MODE_2; break; default: tspp_source.mode = TSPP_TSIF_MODE_LOOPBACK; break; } /* determine the TSIF we are reading from */ if (mpq_demux->source == DMX_SOURCE_FRONT0) { tsif = 0; tspp_source.source = TSPP_SOURCE_TSIF0; } else if (mpq_demux->source == DMX_SOURCE_FRONT1) { tsif = 1; tspp_source.source = TSPP_SOURCE_TSIF1; } else { /* invalid source */ MPQ_DVB_ERR_PRINT( "%s: invalid input source (%d)\n", __func__, mpq_demux->source); return -EINVAL; } if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex)) return -ERESTARTSYS; /* * It is possible that this PID was already requested before. * Can happen if we play and record same PES or PCR * piggypacked on video packet. */ ret = mpq_tspp_get_filter_slot(tsif, feed->pid); if (ret >= 0) { /* PID already configured */ mpq_dmx_tspp_info.tsif[tsif].filters[ret].ref_count++; mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex); return 0; } channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL); channel_ref_count = &mpq_dmx_tspp_info.tsif[tsif].channel_ref; buffer_size = TSPP_DESCRIPTOR_SIZE; /* check if required TSPP pipe is already allocated or not */ if (*channel_ref_count == 0) { if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) { ret = mpq_dmx_channel_mem_alloc(tsif); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: mpq_dmx_channel_mem_alloc(%d) failed (%d)\n", __func__, channel_id, ret); goto add_channel_failed; } } ret = tspp_open_channel(0, channel_id); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: tspp_open_channel(%d) failed (%d)\n", __func__, channel_id, ret); goto add_channel_failed; } /* set TSPP source */ ret = tspp_open_stream(0, channel_id, &tspp_source); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: tspp_select_source(%d,%d) failed (%d)\n", __func__, channel_id, tspp_source.source, ret); goto add_channel_close_ch; } /* register notification on TS packets */ tspp_register_notification(0, channel_id, mpq_tspp_callback, (void *)tsif, tspp_channel_timeout); /* register allocator and provide allocation function * that allocates from contiguous memory so that we can have * big notification size, smallest descriptor, and still provide * TZ with single big buffer based on notification size. */ if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) { ret = tspp_allocate_buffers(0, channel_id, mpq_dmx_tspp_info.tsif[tsif].buffer_count, buffer_size, tspp_notification_size, tspp_mem_allocator, tspp_mem_free, NULL); } else { ret = tspp_allocate_buffers(0, channel_id, mpq_dmx_tspp_info.tsif[tsif].buffer_count, buffer_size, tspp_notification_size, NULL, NULL, NULL); } if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: tspp_allocate_buffers(%d) failed (%d)\n", __func__, channel_id, ret); goto add_channel_unregister_notif; } mpq_dmx_tspp_info.tsif[tsif].mpq_demux = mpq_demux; } /* add new PID to the existing pipe */ ret = mpq_tspp_get_free_filter_slot(tsif, channel_id); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: mpq_allocate_filter_slot(%d, %d) failed\n", __func__, tsif, channel_id); goto add_channel_unregister_notif; } mpq_dmx_tspp_info.tsif[tsif].filters[ret].pid = feed->pid; mpq_dmx_tspp_info.tsif[tsif].filters[ret].ref_count++; tspp_filter.priority = ret; if (feed->pid == TSPP_PASS_THROUGH_PID) { /* pass all pids */ tspp_filter.pid = 0; tspp_filter.mask = 0; } else { tspp_filter.pid = feed->pid; tspp_filter.mask = TSPP_PID_MASK; } /* * Include TTS in RAW packets, if you change this to * TSPP_MODE_RAW_NO_SUFFIX you must also change TSPP_RAW_TTS_SIZE * accordingly. */ tspp_filter.mode = TSPP_MODE_RAW; tspp_filter.source = tspp_source.source; tspp_filter.decrypt = 0; ret = tspp_add_filter(0, channel_id, &tspp_filter); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: tspp_add_filter(%d) failed (%d)\n", __func__, channel_id, ret); goto add_channel_free_filter_slot; } (*channel_ref_count)++; mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex); return 0; add_channel_free_filter_slot: mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].pid = -1; mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].ref_count--; add_channel_unregister_notif: tspp_unregister_notification(0, channel_id); add_channel_close_ch: tspp_close_channel(0, channel_id); add_channel_failed: if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) mpq_dmx_channel_mem_free(tsif); mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex); return ret; }
static int mpq_tspp_dmx_add_channel(struct dvb_demux_feed *feed) { struct mpq_demux *mpq_demux = feed->demux->priv; struct tspp_select_source tspp_source; struct tspp_filter tspp_filter; int tsif; int ret = 0; int slot; int channel_id; int *channel_ref_count; u32 buffer_size; int restore_user_filters = 0; int remove_accept_all_filter = 0; int remove_null_blocking_filters = 0; tspp_source.clk_inverse = clock_inv; tspp_source.data_inverse = 0; tspp_source.sync_inverse = 0; tspp_source.enable_inverse = 0; MPQ_DVB_DBG_PRINT("%s: executed, PID = %d\n", __func__, feed->pid); switch (tsif_mode) { case 1: tspp_source.mode = TSPP_TSIF_MODE_1; break; case 2: tspp_source.mode = TSPP_TSIF_MODE_2; break; default: tspp_source.mode = TSPP_TSIF_MODE_LOOPBACK; break; } if (mpq_demux->source == DMX_SOURCE_FRONT0) { tsif = 0; tspp_source.source = TSPP_SOURCE_TSIF0; } else if (mpq_demux->source == DMX_SOURCE_FRONT1) { tsif = 1; tspp_source.source = TSPP_SOURCE_TSIF1; } else { MPQ_DVB_ERR_PRINT( "%s: invalid input source (%d)\n", __func__, mpq_demux->source); return -EINVAL; } if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex)) return -ERESTARTSYS; slot = mpq_tspp_get_filter_slot(tsif, feed->pid); if (slot >= 0) { mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count++; mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex); return 0; } channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL); channel_ref_count = &mpq_dmx_tspp_info.tsif[tsif].channel_ref; buffer_size = TSPP_DESCRIPTOR_SIZE; if (*channel_ref_count == 0) { if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) { ret = mpq_dmx_channel_mem_alloc(tsif); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: mpq_dmx_channel_mem_alloc(%d) failed (%d)\n", __func__, channel_id, ret); goto add_channel_failed; } } ret = tspp_open_channel(0, channel_id); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: tspp_open_channel(%d) failed (%d)\n", __func__, channel_id, ret); goto add_channel_failed; } ret = tspp_open_stream(0, channel_id, &tspp_source); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: tspp_select_source(%d,%d) failed (%d)\n", __func__, channel_id, tspp_source.source, ret); goto add_channel_close_ch; } tspp_register_notification(0, channel_id, mpq_tspp_callback, (void *)tsif, tspp_channel_timeout); if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) { ret = tspp_allocate_buffers(0, channel_id, mpq_dmx_tspp_info.tsif[tsif].buffer_count, buffer_size, tspp_notification_size, tspp_mem_allocator, tspp_mem_free, NULL); } else { ret = tspp_allocate_buffers(0, channel_id, mpq_dmx_tspp_info.tsif[tsif].buffer_count, buffer_size, tspp_notification_size, NULL, NULL, NULL); } if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: tspp_allocate_buffers(%d) failed (%d)\n", __func__, channel_id, ret); goto add_channel_unregister_notif; } mpq_dmx_tspp_info.tsif[tsif].mpq_demux = mpq_demux; } slot = mpq_tspp_get_free_filter_slot(tsif); if (slot < 0) { MPQ_DVB_ERR_PRINT( "%s: mpq_tspp_get_free_filter_slot(%d) failed\n", __func__, tsif); goto add_channel_unregister_notif; } if (feed->pid == TSPP_PASS_THROUGH_PID) mpq_dmx_tspp_info.tsif[tsif].pass_all_flag = 1; else if (feed->pid == TSPP_NULL_PACKETS_PID) mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag = 1; mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid = feed->pid; mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count++; tspp_filter.priority = -1; if (mpq_dmx_tspp_info.tsif[tsif].current_filter_count < TSPP_MAX_HW_PID_FILTER_NUM) { tspp_filter.priority = mpq_tspp_allocate_hw_filter_index(tsif); if (tspp_filter.priority < 0) goto add_channel_free_filter_slot; if (feed->pid == TSPP_PASS_THROUGH_PID) { tspp_filter.pid = 0; tspp_filter.mask = 0; } else { tspp_filter.pid = feed->pid; tspp_filter.mask = TSPP_PID_MASK; } tspp_filter.mode = TSPP_MODE_RAW; tspp_filter.source = tspp_source.source; tspp_filter.decrypt = 0; ret = tspp_add_filter(0, channel_id, &tspp_filter); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: tspp_add_filter(%d) failed (%d)\n", __func__, channel_id, ret); goto add_channel_free_filter_slot; } mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index = tspp_filter.priority; MPQ_DVB_DBG_PRINT( "%s: HW filtering mode: added TSPP HW filter, PID = %d, mask = 0x%X, index = %d\n", __func__, tspp_filter.pid, tspp_filter.mask, tspp_filter.priority); } else if (mpq_dmx_tspp_info.tsif[tsif].current_filter_count == TSPP_MAX_HW_PID_FILTER_NUM) { ret = mpq_tspp_add_accept_all_filter(channel_id, tspp_source.source); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: mpq_tspp_add_accept_all_filter(%d, %d) failed\n", __func__, channel_id, tspp_source.source); goto add_channel_free_filter_slot; } ret = mpq_tspp_remove_all_user_filters(channel_id, tspp_source.source); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: mpq_tspp_remove_all_user_filters(%d, %d) failed\n", __func__, channel_id, tspp_source.source); restore_user_filters = 1; remove_accept_all_filter = 1; goto add_channel_free_filter_slot; } ret = mpq_tspp_add_null_blocking_filters(channel_id, tspp_source.source); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: mpq_tspp_add_null_blocking_filters(%d, %d) failed\n", __func__, channel_id, tspp_source.source); restore_user_filters = 1; remove_accept_all_filter = 1; goto add_channel_free_filter_slot; } if ((mpq_dmx_tspp_info.tsif[tsif].pass_all_flag == 0) && (mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag == 0)) { ret = mpq_tspp_remove_accept_all_filter(channel_id, tspp_source.source); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: mpq_tspp_remove_accept_all_filter(%d, %d) failed\n", __func__, channel_id, tspp_source.source); remove_null_blocking_filters = 1; restore_user_filters = 1; remove_accept_all_filter = 1; goto add_channel_free_filter_slot; } } } else { if (mpq_dmx_tspp_info.tsif[tsif].pass_all_flag || mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag) { ret = mpq_tspp_add_accept_all_filter(channel_id, tspp_source.source); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: mpq_tspp_add_accept_all_filter(%d, %d) failed\n", __func__, channel_id, tspp_source.source); goto add_channel_free_filter_slot; } } } (*channel_ref_count)++; mpq_dmx_tspp_info.tsif[tsif].current_filter_count++; MPQ_DVB_DBG_PRINT("%s: success, current_filter_count = %d\n", __func__, mpq_dmx_tspp_info.tsif[tsif].current_filter_count); mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex); return 0; add_channel_free_filter_slot: mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid = -1; mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count--; if (tspp_filter.priority >= 0) { mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index = -1; mpq_tspp_release_hw_filter_index(tsif, tspp_filter.priority); } if (remove_null_blocking_filters) mpq_tspp_remove_null_blocking_filters(channel_id, tspp_source.source); if (restore_user_filters) mpq_tspp_add_all_user_filters(channel_id, tspp_source.source); if (remove_accept_all_filter) mpq_tspp_remove_accept_all_filter(channel_id, tspp_source.source); if (feed->pid == TSPP_PASS_THROUGH_PID) mpq_dmx_tspp_info.tsif[tsif].pass_all_flag = 0; else if (feed->pid == TSPP_NULL_PACKETS_PID) mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag = 0; add_channel_unregister_notif: if (*channel_ref_count == 0) { tspp_unregister_notification(0, channel_id); tspp_close_stream(0, channel_id); } add_channel_close_ch: if (*channel_ref_count == 0) tspp_close_channel(0, channel_id); add_channel_failed: if (*channel_ref_count == 0) if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) mpq_dmx_channel_mem_free(tsif); mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex); return ret; }
/** * Demux thread function handling data from specific TSIF. * * @arg: TSIF number */ static int mpq_dmx_tspp_thread(void *arg) { int tsif = (int)arg; struct mpq_demux *mpq_demux; const struct tspp_data_descriptor *tspp_data_desc; atomic_t *data_cnt; u32 notif_size; int channel_id; int ref_count; int ret; int j; do { ret = wait_event_interruptible( mpq_dmx_tspp_info.tsif[tsif].wait_queue, atomic_read(&mpq_dmx_tspp_info.tsif[tsif].data_cnt) || kthread_should_stop()); if ((ret < 0) || kthread_should_stop()) { MPQ_DVB_ERR_PRINT("%s: exit\n", __func__); break; } /* Lock against the TSPP filters data-structure */ if (mutex_lock_interruptible( &mpq_dmx_tspp_info.tsif[tsif].mutex)) return -ERESTARTSYS; channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL); ref_count = mpq_dmx_tspp_info.tsif[tsif].channel_ref; data_cnt = &mpq_dmx_tspp_info.tsif[tsif].data_cnt; /* Make sure channel is still active */ if (ref_count == 0) { mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex); continue; } atomic_dec(data_cnt); mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux; mpq_demux->hw_notification_size = 0; if (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__); if (MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC == allocation_mode && mpq_sdmx_is_loaded()) { mpq_dmx_tspp_aggregated_process(tsif, channel_id); } else { /* * 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; 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 && (mpq_demux->hw_notification_size < mpq_demux->hw_notification_min_size)) mpq_demux->hw_notification_min_size = mpq_demux->hw_notification_size; mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex); } while (1); return 0; }
static int mpq_tsif_dmx_start(struct mpq_demux *mpq_demux) { int ret = 0; int tsif; struct tsif_driver_info *tsif_driver; MPQ_DVB_DBG_PRINT("%s executed\n", __func__); if (mpq_demux->source == DMX_SOURCE_FRONT0) { tsif = 0; } else if (mpq_demux->source == DMX_SOURCE_FRONT1) { tsif = 1; } else { MPQ_DVB_ERR_PRINT( "%s: invalid input source (%d)\n", __func__, mpq_demux->source); return -EINVAL; } if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex)) return -ERESTARTSYS; if (mpq_dmx_tsif_info.tsif[tsif].ref_count == 0) { tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver); tsif_driver->tsif_handler = tsif_attach(tsif, mpq_tsif_callback, (void *)tsif); if (IS_ERR_OR_NULL(tsif_driver->tsif_handler)) { tsif_driver->tsif_handler = NULL; mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex); MPQ_DVB_DBG_PRINT("%s: tsif_attach(%d) failed\n", __func__, tsif); return -ENODEV; } ret = tsif_set_clk_inverse(tsif_driver->tsif_handler, clock_inv); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: tsif_set_clk_inverse (%d) failed\n", __func__, clock_inv); } ret = tsif_set_mode(tsif_driver->tsif_handler, tsif_mode); if (ret < 0) { MPQ_DVB_ERR_PRINT("%s: tsif_set_mode (%d) failed\n", __func__, tsif_mode); } ret = tsif_set_buf_config(tsif_driver->tsif_handler, threshold, DMX_TSIF_CHUNKS_IN_BUF); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: tsif_set_buf_config (%d, %d) failed\n", __func__, threshold, DMX_TSIF_CHUNKS_IN_BUF); MPQ_DVB_ERR_PRINT("Using default TSIF driver values\n"); } ret = tsif_start(tsif_driver->tsif_handler); if (ret < 0) { mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex); MPQ_DVB_ERR_PRINT("%s: tsif_start failed\n", __func__); return ret; } tsif_get_info(tsif_driver->tsif_handler, &(tsif_driver->data_buffer), &(tsif_driver->buffer_size)); mpq_dmx_tsif_info.tsif[tsif].mpq_demux = mpq_demux; } mpq_dmx_tsif_info.tsif[tsif].ref_count++; mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex); return ret; }
static int mpq_tsif_dmx_init( struct dvb_adapter *mpq_adapter, struct mpq_demux *mpq_demux) { int result; MPQ_DVB_DBG_PRINT("%s executed\n", __func__); mpq_demux->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_PES_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING | DMX_CRC_CHECKING | DMX_TS_DESCRAMBLING; mpq_demux->demux.priv = (void *)mpq_demux; mpq_demux->demux.filternum = DMX_TSIF_MAX_SECTION_FILTER_NUM; mpq_demux->demux.feednum = MPQ_MAX_DMX_FILES; mpq_demux->demux.start_feed = mpq_tsif_dmx_start_filtering; mpq_demux->demux.stop_feed = mpq_tsif_dmx_stop_filtering; mpq_demux->demux.write_to_decoder = mpq_tsif_dmx_write_to_decoder; mpq_demux->demux.decoder_fullness_init = mpq_dmx_decoder_fullness_init; mpq_demux->demux.decoder_fullness_wait = mpq_dmx_decoder_fullness_wait; mpq_demux->demux.decoder_fullness_abort = mpq_dmx_decoder_fullness_abort; mpq_demux->demux.decoder_buffer_status = mpq_dmx_decoder_buffer_status; mpq_demux->demux.reuse_decoder_buffer = mpq_dmx_reuse_decoder_buffer; mpq_demux->demux.set_secure_mode = NULL; mpq_demux->demux.oob_command = mpq_dmx_oob_command; mpq_demux->demux.convert_ts = mpq_dmx_convert_tts; result = dvb_dmx_init(&mpq_demux->demux); if (result < 0) { MPQ_DVB_ERR_PRINT("%s: dvb_dmx_init failed\n", __func__); goto init_failed; } mpq_demux->dmxdev.filternum = MPQ_MAX_DMX_FILES; mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx; mpq_demux->dmxdev.capabilities = DMXDEV_CAP_DUPLEX; mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source; mpq_demux->dmxdev.demux->get_stc = mpq_tsif_dmx_get_stc; mpq_demux->dmxdev.demux->get_caps = mpq_tsif_dmx_get_caps; mpq_demux->dmxdev.demux->map_buffer = mpq_dmx_map_buffer; mpq_demux->dmxdev.demux->unmap_buffer = mpq_dmx_unmap_buffer; result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter); if (result < 0) { MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed (errno=%d)\n", __func__, result); goto init_failed_dmx_release; } mpq_dmx_init_debugfs_entries(mpq_demux); return 0; init_failed_dmx_release: dvb_dmx_release(&mpq_demux->demux); init_failed: return result; }
/** * Attach to TSIF driver and start TSIF operation. * * @mpq_demux: the mpq_demux we are working on. * * Return error code. */ static int mpq_tsif_dmx_start(struct mpq_demux *mpq_demux) { int ret = 0; int tsif; struct tsif_driver_info *tsif_driver; MPQ_DVB_DBG_PRINT("%s executed\n", __func__); /* determine the TSIF we are reading from */ if (mpq_demux->source == DMX_SOURCE_FRONT0) { tsif = 0; } else if (mpq_demux->source == DMX_SOURCE_FRONT1) { tsif = 1; } else { /* invalid source */ MPQ_DVB_ERR_PRINT( "%s: invalid input source (%d)\n", __func__, mpq_demux->source); return -EINVAL; } if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex)) return -ERESTARTSYS; if (mpq_dmx_tsif_info.tsif[tsif].ref_count == 0) { tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver); /* Attach to TSIF driver */ tsif_driver->tsif_handler = tsif_attach(tsif, mpq_tsif_callback, (void *)tsif); if (IS_ERR_OR_NULL(tsif_driver->tsif_handler)) { tsif_driver->tsif_handler = NULL; mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex); MPQ_DVB_DBG_PRINT("%s: tsif_attach(%d) failed\n", __func__, tsif); return -ENODEV; } ret = tsif_set_clk_inverse(tsif_driver->tsif_handler, clock_inv); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: tsif_set_clk_inverse (%d) failed\n", __func__, clock_inv); } /* Set TSIF driver mode */ ret = tsif_set_mode(tsif_driver->tsif_handler, tsif_mode); if (ret < 0) { MPQ_DVB_ERR_PRINT("%s: tsif_set_mode (%d) failed\n", __func__, tsif_mode); } /* Set TSIF buffer configuration */ ret = tsif_set_buf_config(tsif_driver->tsif_handler, threshold, DMX_TSIF_CHUNKS_IN_BUF); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: tsif_set_buf_config (%d, %d) failed\n", __func__, threshold, DMX_TSIF_CHUNKS_IN_BUF); MPQ_DVB_ERR_PRINT("Using default TSIF driver values\n"); } /* Start TSIF driver */ ret = tsif_start(tsif_driver->tsif_handler); if (ret < 0) { mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex); MPQ_DVB_ERR_PRINT("%s: tsif_start failed\n", __func__); return ret; } /* * Get data buffer information from TSIF driver * (must be called after tsif_start) */ tsif_get_info(tsif_driver->tsif_handler, &(tsif_driver->data_buffer), &(tsif_driver->buffer_size)); /* save pointer to the mpq_demux we are working on */ mpq_dmx_tsif_info.tsif[tsif].mpq_demux = mpq_demux; } mpq_dmx_tsif_info.tsif[tsif].ref_count++; mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex); return ret; }
static int mpq_sw_dmx_init(struct dvb_adapter *mpq_adapter, struct mpq_demux *mpq_demux) { int ret; struct dvb_demux *dvb_demux = &mpq_demux->demux; /* Set the kernel-demux object capabilities */ mpq_demux->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_PES_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING | DMX_CRC_CHECKING | DMX_TS_DESCRAMBLING; mpq_demux->decoder_alloc_flags = ION_FLAG_CACHED; /* Set dvb-demux "virtual" function pointers */ dvb_demux->priv = (void *)mpq_demux; dvb_demux->filternum = MPQ_MAX_DMX_FILES; dvb_demux->feednum = MPQ_MAX_DMX_FILES; dvb_demux->start_feed = mpq_sw_dmx_start_filtering; dvb_demux->stop_feed = mpq_sw_dmx_stop_filtering; dvb_demux->write_to_decoder = mpq_sw_dmx_write_to_decoder; dvb_demux->decoder_fullness_init = mpq_dmx_decoder_fullness_init; dvb_demux->decoder_fullness_wait = mpq_dmx_decoder_fullness_wait; dvb_demux->decoder_fullness_abort = mpq_dmx_decoder_fullness_abort; dvb_demux->decoder_buffer_status = mpq_dmx_decoder_buffer_status; dvb_demux->reuse_decoder_buffer = mpq_dmx_reuse_decoder_buffer; dvb_demux->set_cipher_op = mpq_dmx_set_cipher_ops; dvb_demux->oob_command = mpq_dmx_oob_command; dvb_demux->convert_ts = mpq_dmx_convert_tts; dvb_demux->flush_decoder_buffer = NULL; /* Initialize dvb_demux object */ ret = dvb_dmx_init(dvb_demux); if (ret) { MPQ_DVB_ERR_PRINT("%s: dvb_dmx_init failed, ret=%d\n", __func__, ret); goto init_failed; } /* Now initialize the dmx-dev object */ mpq_demux->dmxdev.filternum = MPQ_MAX_DMX_FILES; mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx; mpq_demux->dmxdev.capabilities = DMXDEV_CAP_DUPLEX; mpq_demux->dmxdev.demux->set_source = mpq_sw_dmx_set_source; mpq_demux->dmxdev.demux->get_stc = NULL; mpq_demux->dmxdev.demux->get_caps = mpq_sw_dmx_get_caps; mpq_demux->dmxdev.demux->map_buffer = mpq_dmx_map_buffer; mpq_demux->dmxdev.demux->unmap_buffer = mpq_dmx_unmap_buffer; mpq_demux->dmxdev.demux->write = mpq_dmx_write; ret = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter); if (ret) { MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed, ret=%d\n", __func__, ret); goto init_failed_dmx_release; } /* Extend dvb-demux debugfs with mpq demux statistics. */ mpq_dmx_init_debugfs_entries(mpq_demux); return 0; init_failed_dmx_release: dvb_dmx_release(dvb_demux); init_failed: return ret; }
/** * Module initialization function. * * Return error code */ static int __init mpq_dmx_tsif_plugin_init(void) { int i; int ret; MPQ_DVB_DBG_PRINT("%s executed\n", __func__); /* check module parameters validity */ if (threshold < 1) { MPQ_DVB_ERR_PRINT( "%s: invalid threshold parameter, using %d instead\n", __func__, DMX_TSIF_PACKETS_IN_CHUNK_DEF); threshold = DMX_TSIF_PACKETS_IN_CHUNK_DEF; } if ((tsif_mode < 1) || (tsif_mode > 3)) { MPQ_DVB_ERR_PRINT( "%s: invalid mode parameter, using %d instead\n", __func__, DMX_TSIF_DRIVER_MODE_DEF); tsif_mode = DMX_TSIF_DRIVER_MODE_DEF; } for (i = 0; i < TSIF_COUNT; i++) { snprintf(mpq_dmx_tsif_info.tsif[i].name, TSIF_NAME_LENGTH, "dmx_tsif%d", i); atomic_set(&mpq_dmx_tsif_info.tsif[i].data_cnt, 0); init_waitqueue_head(&mpq_dmx_tsif_info.tsif[i].wait_queue); mpq_dmx_tsif_info.tsif[i].thread = kthread_run( mpq_dmx_tsif_thread, (void *)i, mpq_dmx_tsif_info.tsif[i].name); if (IS_ERR(mpq_dmx_tsif_info.tsif[i].thread)) { int j; for (j = 0; j < i; j++) { kthread_stop(mpq_dmx_tsif_info.tsif[j].thread); mutex_destroy(&mpq_dmx_tsif_info.tsif[j].mutex); } MPQ_DVB_ERR_PRINT( "%s: kthread_run failed\n", __func__); return -ENOMEM; } mutex_init(&mpq_dmx_tsif_info.tsif[i].mutex); mpq_dmx_tsif_info.tsif[i].tsif_driver.tsif_handler = NULL; mpq_dmx_tsif_info.tsif[i].ref_count = 0; } ret = mpq_dmx_plugin_init(mpq_tsif_dmx_init); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: mpq_dmx_plugin_init failed (errno=%d)\n", __func__, ret); for (i = 0; i < TSIF_COUNT; i++) { kthread_stop(mpq_dmx_tsif_info.tsif[i].thread); mutex_destroy(&mpq_dmx_tsif_info.tsif[i].mutex); } } return ret; }
static int __init mpq_dmx_tspp_plugin_init(void) { int i; int j; int ret; MPQ_DVB_DBG_PRINT("%s executed\n", __func__); for (i = 0; i < TSIF_COUNT; i++) { mpq_dmx_tspp_info.tsif[i].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; mpq_dmx_tspp_info.tsif[i].ch_mem_heap_virt_base = NULL; mpq_dmx_tspp_info.tsif[i].ch_mem_heap_phys_base = 0; atomic_set(&mpq_dmx_tspp_info.tsif[i].data_cnt, 0); for (j = 0; j < TSPP_MAX_PID_FILTER_NUM; j++) { mpq_dmx_tspp_info.tsif[i].filters[j].pid = -1; mpq_dmx_tspp_info.tsif[i].filters[j].ref_count = 0; } snprintf(mpq_dmx_tspp_info.tsif[i].name, TSIF_NAME_LENGTH, "dmx_tsif%d", i); init_waitqueue_head(&mpq_dmx_tspp_info.tsif[i].wait_queue); mpq_dmx_tspp_info.tsif[i].thread = kthread_run( mpq_dmx_tspp_thread, (void *)i, 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); } MPQ_DVB_ERR_PRINT( "%s: kthread_run failed\n", __func__); return -ENOMEM; } mutex_init(&mpq_dmx_tspp_info.tsif[i].mutex); } ret = mpq_dmx_plugin_init(mpq_tspp_dmx_init); if (ret < 0) { MPQ_DVB_ERR_PRINT( "%s: mpq_dmx_plugin_init failed (errno=%d)\n", __func__, ret); for (i = 0; i < TSIF_COUNT; i++) { kthread_stop(mpq_dmx_tspp_info.tsif[i].thread); vfree(mpq_dmx_tspp_info.tsif[i].aggregate_ids); mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex); } } return ret; }
static int mpq_dmx_tspp_thread(void *arg) { int tsif = (int)arg; struct mpq_demux *mpq_demux; const struct tspp_data_descriptor *tspp_data_desc; atomic_t *data_cnt; u32 notif_size; int channel_id; int ref_count; int ret; do { ret = wait_event_interruptible( mpq_dmx_tspp_info.tsif[tsif].wait_queue, atomic_read(&mpq_dmx_tspp_info.tsif[tsif].data_cnt) || kthread_should_stop()); if ((ret < 0) || kthread_should_stop()) { MPQ_DVB_ERR_PRINT("%s: exit\n", __func__); break; } if (mutex_lock_interruptible( &mpq_dmx_tspp_info.tsif[tsif].mutex)) return -ERESTARTSYS; channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL); ref_count = mpq_dmx_tspp_info.tsif[tsif].channel_ref; data_cnt = &mpq_dmx_tspp_info.tsif[tsif].data_cnt; if (ref_count == 0) { mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex); continue; } atomic_dec(data_cnt); mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux; mpq_demux->hw_notification_size = 0; if (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__); if (MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC == allocation_mode && mpq_sdmx_is_loaded()) { mpq_dmx_tspp_aggregated_process(tsif, channel_id); } else { 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; mpq_dmx_tspp_swfilter_desc(mpq_demux, tspp_data_desc); tspp_release_buffer(0, channel_id, tspp_data_desc->id); } } if (mpq_demux->hw_notification_size && (mpq_demux->hw_notification_size < mpq_demux->hw_notification_min_size)) mpq_demux->hw_notification_min_size = mpq_demux->hw_notification_size; mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex); } while (1); return 0; }