media: dvb: Add events API for demux

user-space programs using demux require to be notified
on several kinds of events such new PES event,
new section event, new recording-chunk event and
other error events. demux API was extended to meet
this requirement

Change-Id: I768b6acde346139e194a3e6637b6fc0fc9648446
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 e22bc64..9ad79e9 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -63,15 +63,52 @@
  */
 
 enum dmx_success {
-  DMX_OK = 0, /* Received Ok */
-  DMX_LENGTH_ERROR, /* Incorrect length */
-  DMX_OVERRUN_ERROR, /* Receiver ring buffer overrun */
-  DMX_CRC_ERROR, /* Incorrect CRC */
-  DMX_FRAME_ERROR, /* Frame alignment error */
-  DMX_FIFO_ERROR, /* Receiver FIFO overrun */
-  DMX_MISSED_ERROR /* Receiver missed packet */
+	DMX_OK = 0, /* Received Ok */
+	DMX_OK_PES_END, /* Received ok, data reached end of PES packet */
+	DMX_OK_PCR, /* Received OK, data with new PCR/STC pair */
+	DMX_LENGTH_ERROR, /* Incorrect length */
+	DMX_OVERRUN_ERROR, /* Receiver ring buffer overrun */
+	DMX_CRC_ERROR, /* Incorrect CRC */
+	DMX_FRAME_ERROR, /* Frame alignment error */
+	DMX_FIFO_ERROR, /* Receiver FIFO overrun */
+	DMX_MISSED_ERROR /* Receiver missed packet */
 } ;
 
+
+/*
+ * struct dmx_data_ready: Parameters for event notification callback.
+ * Event notification notifies demux device that data is written
+ * and available in the device's output buffer or provides
+ * notification on errors and other events. In the latter case
+ * data_length is zero.
+ */
+struct dmx_data_ready {
+	enum dmx_success status;
+
+	/*
+	 * data_length may be 0 in case of DMX_OK_PES_END
+	 * and in non-DMX_OK_XXX events. In DMX_OK_PES_END,
+	 * data_length is for data comming after the end of PES.
+	 */
+	int data_length;
+
+	union {
+		struct {
+			int start_gap;
+			int actual_length;
+			int disc_indicator_set;
+			int pes_length_mismatch;
+			u64 stc;
+		} pes_end;
+
+		struct {
+			u64 pcr;
+			u64 stc;
+			int disc_indicator_set;
+		} pcr;
+	};
+};
+
 /*--------------------------------------------------------------------------*/
 /* TS packet reception */
 /*--------------------------------------------------------------------------*/
@@ -123,6 +160,10 @@
 #define DMX_TS_PES_SUBTITLE DMX_TS_PES_SUBTITLE0
 #define DMX_TS_PES_PCR      DMX_TS_PES_PCR0
 
+struct dmx_ts_feed;
+typedef int (*dmx_ts_data_ready_cb)(
+		struct dmx_ts_feed *source,
+		struct dmx_data_ready *dmx_data_ready);
 
 struct dmx_ts_feed {
 	int is_filtering; /* Set to non-zero when filtering in progress */
@@ -141,6 +182,8 @@
 	int (*get_decoder_buff_status)(
 			struct dmx_ts_feed *feed,
 			struct dmx_buffer_status *dmx_buffer_status);
+	int (*data_ready_cb)(struct dmx_ts_feed *feed,
+			dmx_ts_data_ready_cb callback);
 };
 
 /*--------------------------------------------------------------------------*/
@@ -155,6 +198,11 @@
 	void* priv; /* Pointer to private data of the API client */
 };
 
+struct dmx_section_feed;
+typedef int (*dmx_section_data_ready_cb)(
+		struct dmx_section_filter *source,
+		struct dmx_data_ready *dmx_data_ready);
+
 struct dmx_section_feed {
 	int is_filtering; /* Set to non-zero when filtering in progress */
 	struct dmx_demux* parent; /* Back-pointer */
@@ -177,6 +225,8 @@
 			       struct dmx_section_filter* filter);
 	int (*start_filtering) (struct dmx_section_feed* feed);
 	int (*stop_filtering) (struct dmx_section_feed* feed);
+	int (*data_ready_cb)(struct dmx_section_feed *feed,
+			dmx_section_data_ready_cb callback);
 };
 
 /*--------------------------------------------------------------------------*/
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index 8e5127a..433e796 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -60,9 +60,302 @@
 	return dvb_ringbuffer_write(buf, src, len);
 }
 
+static inline u32 dvb_dmxdev_advance_event_idx(u32 index)
+{
+	index++;
+	if (index >= DMX_EVENT_QUEUE_SIZE)
+		index = 0;
+
+	return index;
+}
+
+static inline void dvb_dmxdev_flush_events(struct dmxdev_events_queue *events)
+{
+	events->read_index = 0;
+	events->write_index = 0;
+	events->notified_index = 0;
+	events->bytes_read_no_event = 0;
+	events->current_event_data_size = 0;
+}
+
+static inline void dvb_dmxdev_flush_output(struct dvb_ringbuffer *buffer,
+					struct dmxdev_events_queue *events)
+{
+	dvb_dmxdev_flush_events(events);
+	dvb_ringbuffer_flush(buffer);
+}
+
+static int dvb_dmxdev_update_pes_event(struct dmx_filter_event *event,
+					int bytes_read)
+{
+	int start_delta;
+
+	if (event->params.pes.total_length <= bytes_read)
+		return event->params.pes.total_length;
+
+	/*
+	 * only part of the data relevant to this event was read.
+	 * Update the event's information to reflect the new state.
+	 */
+	event->params.pes.total_length -= bytes_read;
+
+	start_delta = event->params.pes.start_offset -
+		event->params.pes.base_offset;
+
+	if (bytes_read <= start_delta) {
+		event->params.pes.base_offset +=
+			bytes_read;
+	} else {
+		start_delta =
+			bytes_read - start_delta;
+
+		event->params.pes.start_offset += start_delta;
+		event->params.pes.actual_length -= start_delta;
+
+		event->params.pes.base_offset =
+			event->params.pes.start_offset;
+	}
+
+	return 0;
+}
+
+static int dvb_dmxdev_update_section_event(struct dmx_filter_event *event,
+					int bytes_read)
+{
+	int start_delta;
+
+	if (event->params.section.total_length <= bytes_read)
+		return event->params.section.total_length;
+
+	/*
+	 * only part of the data relevant to this event was read.
+	 * Update the event's information to reflect the new state.
+	 */
+
+	event->params.section.total_length -= bytes_read;
+
+	start_delta = event->params.section.start_offset -
+		event->params.section.base_offset;
+
+	if (bytes_read <= start_delta) {
+		event->params.section.base_offset +=
+			bytes_read;
+	} else {
+		start_delta =
+			bytes_read - start_delta;
+
+		event->params.section.start_offset += start_delta;
+		event->params.section.actual_length -= start_delta;
+
+		event->params.section.base_offset =
+			event->params.section.start_offset;
+	}
+
+	return 0;
+}
+
+static int dvb_dmxdev_update_rec_event(struct dmx_filter_event *event,
+					int bytes_read)
+{
+	if (event->params.recording_chunk.size <= bytes_read)
+		return event->params.recording_chunk.size;
+
+	/*
+	 * only part of the data relevant to this event was read.
+	 * Update the event's information to reflect the new state.
+	 */
+	event->params.recording_chunk.size -= bytes_read;
+	event->params.recording_chunk.offset += bytes_read;
+
+	return 0;
+}
+
+static int dvb_dmxdev_add_event(struct dmxdev_events_queue *events,
+					struct dmx_filter_event *event)
+{
+	int res;
+	int new_write_index;
+	int data_event;
+
+	/* Check if we are adding an event that user already read its data */
+	if (events->bytes_read_no_event) {
+		data_event = 1;
+
+		if (event->type == DMX_EVENT_NEW_PES)
+			res = dvb_dmxdev_update_pes_event(event,
+						events->bytes_read_no_event);
+		else if (event->type == DMX_EVENT_NEW_SECTION)
+			res = dvb_dmxdev_update_section_event(event,
+						events->bytes_read_no_event);
+		else if (event->type == DMX_EVENT_NEW_REC_CHUNK)
+			res = dvb_dmxdev_update_rec_event(event,
+						events->bytes_read_no_event);
+		else
+			data_event = 0;
+
+		if (data_event) {
+			if (res) {
+				/*
+				 * Data relevent to this event was fully
+				 * consumed already, discard event.
+				 */
+				events->bytes_read_no_event -= res;
+				return 0;
+			}
+			events->bytes_read_no_event = 0;
+		} else {
+			/*
+			 * data was read beyond the non-data event,
+			 * making it not relevant anymore
+			 */
+			return 0;
+		}
+	}
+
+	new_write_index = dvb_dmxdev_advance_event_idx(events->write_index);
+	if (new_write_index == events->read_index) {
+		printk(KERN_ERR "dmxdev: events overflow\n");
+		return -EOVERFLOW;
+	}
+
+	events->queue[events->write_index] = *event;
+	events->write_index = new_write_index;
+
+	return 0;
+}
+
+static int dvb_dmxdev_remove_event(struct dmxdev_events_queue *events,
+					struct dmx_filter_event *event)
+{
+	if (events->notified_index == events->write_index)
+		return -ENODATA;
+
+	*event = events->queue[events->notified_index];
+
+	events->notified_index =
+		dvb_dmxdev_advance_event_idx(events->notified_index);
+
+	return 0;
+}
+
+static int dvb_dmxdev_update_events(struct dmxdev_events_queue *events,
+					int bytes_read)
+{
+	struct dmx_filter_event *event;
+	int res;
+	int data_event;
+
+	/*
+	 * Go through all events that were notified and
+	 * remove them from the events queue if their respective
+	 * data was read.
+	 */
+	while ((events->read_index != events->notified_index) &&
+		   (bytes_read)) {
+		event = events->queue + events->read_index;
+
+		data_event = 1;
+
+		if (event->type == DMX_EVENT_NEW_PES)
+			res = dvb_dmxdev_update_pes_event(event, bytes_read);
+		else if (event->type == DMX_EVENT_NEW_SECTION)
+			res = dvb_dmxdev_update_section_event(event,
+								bytes_read);
+		else if (event->type == DMX_EVENT_NEW_REC_CHUNK)
+			res = dvb_dmxdev_update_rec_event(event, bytes_read);
+		else
+			data_event = 0;
+
+		if (data_event) {
+			if (res) {
+				/*
+				 * Data relevent to this event was
+				 * fully consumed, remove it from the queue.
+				 */
+				bytes_read -= res;
+				events->read_index =
+					dvb_dmxdev_advance_event_idx(
+						events->read_index);
+			} else {
+				bytes_read = 0;
+			}
+		} else {
+			/*
+			 * non-data event was already notified,
+			 * no need to keep it
+			 */
+			events->read_index = dvb_dmxdev_advance_event_idx(
+						events->read_index);
+		}
+	}
+
+	if (!bytes_read)
+		return 0;
+
+	/*
+	 * If we reached here it means:
+	 * bytes_read != 0
+	 * events->read_index == events->notified_index
+	 * Check if there are pending events in the queue
+	 * which the user didn't read while their relevant data
+	 * was read.
+	 */
+	while ((events->notified_index != events->write_index) &&
+		   (bytes_read)) {
+		event = events->queue + events->notified_index;
+
+		data_event = 1;
+
+		if (event->type == DMX_EVENT_NEW_PES)
+			res = dvb_dmxdev_update_pes_event(event, bytes_read);
+		else if (event->type == DMX_EVENT_NEW_SECTION)
+			res = dvb_dmxdev_update_section_event(event,
+								bytes_read);
+		else if (event->type == DMX_EVENT_NEW_REC_CHUNK)
+			res = dvb_dmxdev_update_rec_event(event, bytes_read);
+		else
+			data_event = 0;
+
+		if (data_event) {
+			if (res) {
+				/*
+				 * Data relevent to this event was
+				 * fully consumed, remove it from the queue.
+				 */
+				bytes_read -= res;
+				events->notified_index =
+					dvb_dmxdev_advance_event_idx(
+						events->notified_index);
+			} else {
+				bytes_read = 0;
+			}
+		} else {
+			if (bytes_read)
+				/*
+				 * data was read beyond the non-data event,
+				 * making it not relevant anymore
+				 */
+				events->notified_index =
+					dvb_dmxdev_advance_event_idx(
+						events->notified_index);
+		}
+
+		events->read_index = events->notified_index;
+	}
+
+	/*
+	 * Check if data was read without having a respective
+	 * event in the events-queue
+	 */
+	if (bytes_read)
+		events->bytes_read_no_event += bytes_read;
+
+	return 0;
+}
+
 static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src,
-				      int non_blocking, char __user *buf,
-				      size_t count, loff_t *ppos)
+					int non_blocking, char __user *buf,
+					size_t count, loff_t *ppos)
 {
 	size_t todo;
 	ssize_t avail;
@@ -73,7 +366,7 @@
 
 	if (src->error) {
 		ret = src->error;
-		dvb_ringbuffer_flush(src);
+		src->error = 0;
 		return ret;
 	}
 
@@ -94,7 +387,7 @@
 
 		if (src->error) {
 			ret = src->error;
-			dvb_ringbuffer_flush(src);
+			src->error = 0;
 			break;
 		}
 
@@ -166,6 +459,8 @@
 			return -ENOMEM;
 		}
 		dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);
+		dvb_dmxdev_flush_events(&dmxdev->dvr_output_events);
+
 		dvbdev->readers--;
 	} else if (!dvbdev->writers) {
 		dmxdev->dvr_in_exit = 0;
@@ -412,15 +707,24 @@
 static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count,
 			    loff_t *ppos)
 {
+	ssize_t res;
 	struct dvb_device *dvbdev = file->private_data;
 	struct dmxdev *dmxdev = dvbdev->priv;
 
 	if (dmxdev->exit)
 		return -ENODEV;
 
-	return dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,
+	res = dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,
 				      file->f_flags & O_NONBLOCK,
 				      buf, count, ppos);
+
+	if (res > 0) {
+		spin_lock_irq(&dmxdev->lock);
+		dvb_dmxdev_update_events(&dmxdev->dvr_output_events, res);
+		spin_unlock_irq(&dmxdev->lock);
+	}
+
+	return res;
 }
 
 static void dvr_input_work_func(struct work_struct *worker)
@@ -556,6 +860,27 @@
 	return 0;
 }
 
+static int dvb_dvr_get_event(struct dmxdev *dmxdev,
+				unsigned int f_flags,
+				struct dmx_filter_event *event)
+{
+	int res;
+
+	if (!((f_flags & O_ACCMODE) == O_RDONLY))
+		return -EINVAL;
+
+	spin_lock_irq(&dmxdev->lock);
+
+	res = dvb_dmxdev_remove_event(&dmxdev->dvr_output_events, event);
+
+	if (event->type == DMX_EVENT_BUFFER_OVERFLOW)
+		dmxdev->dvr_buffer.error = 0;
+
+	spin_unlock_irq(&dmxdev->lock);
+
+	return res;
+}
+
 static int dvb_dvr_get_buffer_status(struct dmxdev *dmxdev,
 				unsigned int f_flags,
 				struct dmx_buffer_status *dmx_buffer_status)
@@ -574,8 +899,7 @@
 	spin_lock_irq(lock);
 
 	dmx_buffer_status->error = buf->error;
-	if (buf->error)
-		dvb_ringbuffer_flush(buf);
+	buf->error = 0;
 
 	dmx_buffer_status->fullness = dvb_ringbuffer_avail(buf);
 	dmx_buffer_status->free_bytes = dvb_ringbuffer_free(buf);
@@ -594,7 +918,7 @@
 {
 	ssize_t buff_fullness;
 
-	if (!(f_flags & O_ACCMODE) == O_RDONLY)
+	if (!((f_flags & O_ACCMODE) == O_RDONLY))
 		return -EINVAL;
 
 	if (!bytes_count)
@@ -606,6 +930,11 @@
 		return -EINVAL;
 
 	DVB_RINGBUFFER_SKIP(&dmxdev->dvr_buffer, bytes_count);
+
+	spin_lock_irq(&dmxdev->lock);
+	dvb_dmxdev_update_events(&dmxdev->dvr_output_events, bytes_count);
+	spin_unlock_irq(&dmxdev->lock);
+
 	wake_up_all(&dmxdev->dvr_buffer.queue);
 	return 0;
 }
@@ -854,40 +1183,27 @@
 		struct dmxdev_feed *feed;
 		int ret;
 
-		/*
-		 * Ask for status of decoder's buffer from underlying HW.
-		 * In case of PCR/STC extraction, the filter's ring-buffer
-		 * is used to gather the PCR/STC data and not using
-		 * an internal decoder buffer.
-		 */
-		if (!(dmxdevfilter->dev->capabilities &
-			DMXDEV_CAP_PCR_EXTRACTION) ||
-			((dmxdevfilter->params.pes.pes_type != DMX_PES_PCR0) &&
-			(dmxdevfilter->params.pes.pes_type != DMX_PES_PCR1) &&
-			(dmxdevfilter->params.pes.pes_type != DMX_PES_PCR2) &&
-			(dmxdevfilter->params.pes.pes_type != DMX_PES_PCR3))) {
-			list_for_each_entry(feed, &dmxdevfilter->feed.ts,
-								next) {
-				if (feed->ts->get_decoder_buff_status)
-					ret = feed->ts->get_decoder_buff_status(
-							feed->ts,
-							dmx_buffer_status);
-				else
-					ret = -ENODEV;
+		/* Ask for status of decoder's buffer from underlying HW */
+		list_for_each_entry(feed, &dmxdevfilter->feed.ts,
+							next) {
+			if (feed->ts->get_decoder_buff_status)
+				ret = feed->ts->get_decoder_buff_status(
+						feed->ts,
+						dmx_buffer_status);
+			else
+				ret = -ENODEV;
 
-				/*
-				 * There should not be more than one ts feed
-				 * in the list as this is DECODER feed.
-				 */
-				spin_unlock_irq(&dmxdevfilter->dev->lock);
-				return ret;
-			}
+			/*
+			 * There should not be more than one ts feed
+			 * in the list as this is DECODER feed.
+			 */
+			spin_unlock_irq(&dmxdevfilter->dev->lock);
+			return ret;
 		}
 	}
 
 	dmx_buffer_status->error = buf->error;
-	if (buf->error)
-		dvb_ringbuffer_flush(buf);
+	buf->error = 0;
 
 	dmx_buffer_status->fullness = dvb_ringbuffer_avail(buf);
 	dmx_buffer_status->free_bytes = dvb_ringbuffer_free(buf);
@@ -918,11 +1234,33 @@
 
 	DVB_RINGBUFFER_SKIP(&dmxdevfilter->buffer, bytes_count);
 
+	spin_lock_irq(&dmxdevfilter->dev->lock);
+	dvb_dmxdev_update_events(&dmxdevfilter->events, bytes_count);
+	spin_unlock_irq(&dmxdevfilter->dev->lock);
+
 	wake_up_all(&dmxdevfilter->buffer.queue);
 
 	return 0;
 }
 
+static int dvb_dmxdev_get_event(struct dmxdev_filter *dmxdevfilter,
+					struct dmx_filter_event *event)
+{
+	int res;
+
+	spin_lock_irq(&dmxdevfilter->dev->lock);
+
+	res = dvb_dmxdev_remove_event(&dmxdevfilter->events, event);
+
+	if (event->type == DMX_EVENT_BUFFER_OVERFLOW)
+		dmxdevfilter->buffer.error = 0;
+
+	spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+	return res;
+
+}
+
 static void dvb_dmxdev_filter_timeout(unsigned long data)
 {
 	struct dmxdev_filter *dmxdevfilter = (struct dmxdev_filter *)data;
@@ -954,6 +1292,7 @@
 				       enum dmx_success success)
 {
 	struct dmxdev_filter *dmxdevfilter = filter->priv;
+	struct dmx_filter_event event;
 	int ret;
 
 	if (dmxdevfilter->buffer.error) {
@@ -965,20 +1304,57 @@
 		spin_unlock(&dmxdevfilter->dev->lock);
 		return 0;
 	}
+
+	if ((buffer1_len + buffer2_len) == 0) {
+		if (DMX_CRC_ERROR == success) {
+			/* Section was dropped due to CRC error */
+			event.type = DMX_EVENT_SECTION_CRC_ERROR;
+			dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+
+			spin_unlock(&dmxdevfilter->dev->lock);
+			wake_up_all(&dmxdevfilter->buffer.queue);
+		} else {
+			spin_unlock(&dmxdevfilter->dev->lock);
+		}
+
+		return 0;
+	}
+
+	event.params.section.base_offset = dmxdevfilter->buffer.pwrite;
+	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) {
+	if (ret == buffer1_len)
 		ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer2,
 					      buffer2_len);
-	}
+
 	if (ret < 0) {
-		dvb_ringbuffer_flush(&dmxdevfilter->buffer);
+		dvb_dmxdev_flush_output(&dmxdevfilter->buffer,
+			&dmxdevfilter->events);
 		dmxdevfilter->buffer.error = ret;
+
+		event.type = DMX_EVENT_BUFFER_OVERFLOW;
+	} else {
+		event.type = DMX_EVENT_NEW_SECTION;
+		event.params.section.total_length =
+			buffer1_len + buffer2_len;
+		event.params.section.actual_length =
+			event.params.section.total_length;
+
+		if (success == DMX_MISSED_ERROR)
+			event.params.section.flags =
+					DMX_FILTER_CC_ERROR;
+		else
+			event.params.section.flags = 0;
 	}
+
+	dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+
 	if (dmxdevfilter->params.sec.flags & DMX_ONESHOT)
 		dmxdevfilter->state = DMXDEV_STATE_DONE;
 	spin_unlock(&dmxdevfilter->dev->lock);
@@ -993,46 +1369,298 @@
 {
 	struct dmxdev_filter *dmxdevfilter = feed->priv;
 	struct dvb_ringbuffer *buffer;
+	struct dmxdev_events_queue *events;
+	struct dmx_filter_event event;
 	int ret;
 
 	spin_lock(&dmxdevfilter->dev->lock);
 	if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) {
-		if ((dmxdevfilter->dev->capabilities &
-			DMXDEV_CAP_PCR_EXTRACTION) &&
-			((dmxdevfilter->params.pes.pes_type == DMX_PES_PCR0) ||
-			(dmxdevfilter->params.pes.pes_type == DMX_PES_PCR1) ||
-			(dmxdevfilter->params.pes.pes_type == DMX_PES_PCR2) ||
-			(dmxdevfilter->params.pes.pes_type == DMX_PES_PCR3))) {
-			/*
-			 * Support for reporting PCR and STC pairs to user.
-			 * Reported data should have the following format:
-			 * <8 bit flags><64 bits of STC> <64bits of PCR>
-			 * STC and PCR values are in 27MHz.
-			 * The current flags that are defined:
-			 * 0x00000001: discontinuity_indicator
-			 */
-			buffer = &dmxdevfilter->buffer;
-		} else {
-			spin_unlock(&dmxdevfilter->dev->lock);
-			return 0;
-		}
-	} else if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
-	    || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
+		spin_unlock(&dmxdevfilter->dev->lock);
+		return 0;
+	}
+
+	if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
+	    || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP) {
 		buffer = &dmxdevfilter->buffer;
-	else
+		events = &dmxdevfilter->events;
+	} else {
 		buffer = &dmxdevfilter->dev->dvr_buffer;
+		events = &dmxdevfilter->dev->dvr_output_events;
+	}
 
 	if (buffer->error) {
 		spin_unlock(&dmxdevfilter->dev->lock);
 		wake_up_all(&buffer->queue);
 		return 0;
 	}
-	ret = dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
-	if (ret == buffer1_len)
-		ret = dvb_dmxdev_buffer_write(buffer, buffer2, buffer2_len);
-	if (ret < 0) {
-		dvb_ringbuffer_flush(buffer);
-		buffer->error = ret;
+
+	if (dmxdevfilter->state != DMXDEV_STATE_GO) {
+		spin_unlock(&dmxdevfilter->dev->lock);
+		return 0;
+	}
+
+	if (dmxdevfilter->params.pes.output == DMX_OUT_TAP) {
+		if ((success == DMX_OK) &&
+			(!events->current_event_data_size)) {
+			events->current_event_start_offset = buffer->pwrite;
+		} else if (success == DMX_OK_PES_END) {
+			event.type = DMX_EVENT_NEW_PES;
+
+			event.params.pes.actual_length =
+				events->current_event_data_size;
+			event.params.pes.total_length =
+				events->current_event_data_size;
+
+			event.params.pes.base_offset =
+				events->current_event_start_offset;
+			event.params.pes.start_offset =
+				events->current_event_start_offset;
+
+			event.params.pes.flags = 0;
+			event.params.pes.stc = 0;
+
+			dvb_dmxdev_add_event(events, &event);
+			events->current_event_data_size = 0;
+		}
+	} else {
+		if (!events->current_event_data_size) {
+			events->current_event_start_offset =
+				buffer->pwrite;
+		}
+	}
+
+	if (buffer1_len + buffer2_len) {
+		ret = dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
+		if (ret == buffer1_len)
+			ret = dvb_dmxdev_buffer_write(buffer, buffer2,
+								buffer2_len);
+		if (ret < 0) {
+			dvb_dmxdev_flush_output(buffer, events);
+			buffer->error = ret;
+
+			event.type = DMX_EVENT_BUFFER_OVERFLOW;
+			dvb_dmxdev_add_event(events, &event);
+		} else {
+			events->current_event_data_size +=
+				(buffer1_len + buffer2_len);
+
+			if (((dmxdevfilter->params.pes.output ==
+				DMX_OUT_TS_TAP) ||
+				(dmxdevfilter->params.pes.output ==
+				DMX_OUT_TSDEMUX_TAP)) &&
+				(events->current_event_data_size >=
+				dmxdevfilter->params.pes.rec_chunk_size)) {
+
+				event.type = DMX_EVENT_NEW_REC_CHUNK;
+				event.params.recording_chunk.offset =
+					events->current_event_start_offset;
+
+				event.params.recording_chunk.size =
+					events->current_event_data_size;
+
+				dvb_dmxdev_add_event(events, &event);
+				events->current_event_data_size = 0;
+			}
+		}
+	}
+
+	spin_unlock(&dmxdevfilter->dev->lock);
+	wake_up_all(&buffer->queue);
+	return 0;
+}
+
+static int dvb_dmxdev_section_event_cb(struct dmx_section_filter *filter,
+			struct dmx_data_ready *dmx_data_ready)
+{
+	int res;
+	struct dmxdev_filter *dmxdevfilter = filter->priv;
+	struct dmx_filter_event event;
+	int free;
+
+	if (dmxdevfilter->buffer.error) {
+		wake_up_all(&dmxdevfilter->buffer.queue);
+		return 0;
+	}
+
+	spin_lock(&dmxdevfilter->dev->lock);
+
+	if (dmxdevfilter->state != DMXDEV_STATE_GO) {
+		spin_unlock(&dmxdevfilter->dev->lock);
+		return 0;
+	}
+
+	if (dmx_data_ready->data_length == 0) {
+		if (DMX_CRC_ERROR == dmx_data_ready->status) {
+			/* Section was dropped due to CRC error */
+			event.type = DMX_EVENT_SECTION_CRC_ERROR;
+			dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+
+			spin_unlock(&dmxdevfilter->dev->lock);
+			wake_up_all(&dmxdevfilter->buffer.queue);
+		} else {
+			spin_unlock(&dmxdevfilter->dev->lock);
+		}
+		return 0;
+	}
+
+	free = dvb_ringbuffer_free(&dmxdevfilter->buffer);
+
+	if ((DMX_OVERRUN_ERROR == dmx_data_ready->status) ||
+		(dmx_data_ready->data_length > free)) {
+		dvb_dmxdev_flush_output(&dmxdevfilter->buffer,
+				&dmxdevfilter->events);
+
+		dprintk("dmxdev: buffer overflow\n");
+
+		dmxdevfilter->buffer.error = -EOVERFLOW;
+
+		event.type = DMX_EVENT_BUFFER_OVERFLOW;
+		dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+		spin_unlock(&dmxdevfilter->dev->lock);
+		wake_up_all(&dmxdevfilter->buffer.queue);
+		return 0;
+	}
+
+	event.type = DMX_EVENT_NEW_SECTION;
+	event.params.section.base_offset = dmxdevfilter->buffer.pwrite;
+	event.params.section.start_offset = dmxdevfilter->buffer.pwrite;
+	event.params.section.total_length = dmx_data_ready->data_length;
+	event.params.section.actual_length = dmx_data_ready->data_length;
+
+	if (dmx_data_ready->status == DMX_MISSED_ERROR)
+		event.params.section.flags = DMX_FILTER_CC_ERROR;
+	else
+		event.params.section.flags = 0;
+
+	res = dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+	DVB_RINGBUFFER_PUSH(&dmxdevfilter->buffer, dmx_data_ready->data_length);
+
+	spin_unlock(&dmxdevfilter->dev->lock);
+	wake_up_all(&dmxdevfilter->buffer.queue);
+
+	return res;
+}
+
+static int dvb_dmxdev_ts_event_cb(struct dmx_ts_feed *feed,
+			struct dmx_data_ready *dmx_data_ready)
+{
+	struct dmxdev_filter *dmxdevfilter = feed->priv;
+	struct dvb_ringbuffer *buffer;
+	struct dmxdev_events_queue *events;
+	struct dmx_filter_event event;
+	int free;
+
+	spin_lock(&dmxdevfilter->dev->lock);
+
+	if (dmxdevfilter->state != DMXDEV_STATE_GO) {
+		spin_unlock(&dmxdevfilter->dev->lock);
+		return 0;
+	}
+
+	if (dmxdevfilter->params.pes.output != DMX_OUT_TS_TAP) {
+		buffer = &dmxdevfilter->buffer;
+		events = &dmxdevfilter->events;
+	} else {
+		buffer = &dmxdevfilter->dev->dvr_buffer;
+		events = &dmxdevfilter->dev->dvr_output_events;
+	}
+
+	if (dmx_data_ready->status == DMX_OK_PCR) {
+		event.type = DMX_EVENT_NEW_PCR;
+		event.params.pcr.pcr = dmx_data_ready->pcr.pcr;
+		event.params.pcr.stc = dmx_data_ready->pcr.stc;
+		if (dmx_data_ready->pcr.disc_indicator_set)
+			event.params.pcr.flags =
+				DMX_FILTER_DISCONTINUITY_INDEICATOR;
+		else
+			event.params.pcr.flags = 0;
+
+		dvb_dmxdev_add_event(events, &event);
+		spin_unlock(&dmxdevfilter->dev->lock);
+		wake_up_all(&buffer->queue);
+		return 0;
+	}
+
+	if ((dmxdevfilter->params.pes.output == DMX_OUT_DECODER) ||
+		(buffer->error)) {
+		spin_unlock(&dmxdevfilter->dev->lock);
+		wake_up_all(&buffer->queue);
+		return 0;
+	}
+
+	free = dvb_ringbuffer_free(&dmxdevfilter->buffer);
+
+	if ((DMX_OVERRUN_ERROR == dmx_data_ready->status) ||
+		(dmx_data_ready->data_length > free)) {
+		dvb_dmxdev_flush_output(buffer, events);
+
+		dprintk("dmxdev: buffer overflow\n");
+
+		buffer->error = -EOVERFLOW;
+
+		event.type = DMX_EVENT_BUFFER_OVERFLOW;
+		dvb_dmxdev_add_event(&dmxdevfilter->events, &event);
+
+		spin_unlock(&dmxdevfilter->dev->lock);
+		return 0;
+	}
+
+	if (dmxdevfilter->params.pes.output == DMX_OUT_TAP) {
+		if ((dmx_data_ready->status == DMX_OK) &&
+			(!events->current_event_data_size)) {
+			events->current_event_start_offset =
+				dmxdevfilter->buffer.pwrite;
+		} else if (dmx_data_ready->status == DMX_OK_PES_END) {
+			event.type = DMX_EVENT_NEW_PES;
+
+			event.params.pes.base_offset =
+				events->current_event_start_offset;
+			event.params.pes.start_offset =
+				events->current_event_start_offset +
+				dmx_data_ready->pes_end.start_gap;
+
+			event.params.pes.actual_length =
+				dmx_data_ready->pes_end.actual_length;
+			event.params.pes.total_length =
+				events->current_event_data_size;
+
+			event.params.pes.flags = 0;
+			if (dmx_data_ready->pes_end.disc_indicator_set)
+				event.params.pes.flags |=
+					DMX_FILTER_DISCONTINUITY_INDEICATOR;
+			if (dmx_data_ready->pes_end.pes_length_mismatch)
+				event.params.pes.flags |=
+					DMX_FILTER_PES_LENGTH_ERROR;
+
+			event.params.pes.stc = dmx_data_ready->pes_end.stc;
+			dvb_dmxdev_add_event(events, &event);
+
+			events->current_event_data_size = 0;
+		}
+	} else {
+		if (!events->current_event_data_size)
+			events->current_event_start_offset =
+					dmxdevfilter->buffer.pwrite;
+	}
+
+	events->current_event_data_size += dmx_data_ready->data_length;
+	DVB_RINGBUFFER_PUSH(&dmxdevfilter->buffer, dmx_data_ready->data_length);
+
+	if ((dmxdevfilter->params.pes.output == DMX_OUT_TS_TAP) ||
+		(dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)) {
+		if (events->current_event_data_size >=
+			dmxdevfilter->params.pes.rec_chunk_size) {
+			event.type = DMX_EVENT_NEW_REC_CHUNK;
+			event.params.recording_chunk.offset =
+				events->current_event_start_offset;
+
+			event.params.recording_chunk.size =
+				events->current_event_data_size;
+
+			dvb_dmxdev_add_event(events, &event);
+
+			events->current_event_data_size = 0;
+		 }
 	}
 	spin_unlock(&dmxdevfilter->dev->lock);
 	wake_up_all(&buffer->queue);
@@ -1144,7 +1772,10 @@
 		return -EINVAL;
 	}
 
-	dvb_ringbuffer_flush(&dmxdevfilter->buffer);
+	spin_lock_irq(&dmxdevfilter->dev->lock);
+	dvb_dmxdev_flush_output(&dmxdevfilter->buffer, &dmxdevfilter->events);
+	spin_unlock_irq(&dmxdevfilter->dev->lock);
+
 	wake_up_all(&dmxdevfilter->buffer.queue);
 
 	return 0;
@@ -1213,6 +1844,15 @@
 	tsfeed = feed->ts;
 	tsfeed->priv = filter;
 
+	if (tsfeed->data_ready_cb) {
+		ret = tsfeed->data_ready_cb(tsfeed, dvb_dmxdev_ts_event_cb);
+
+		if (ret < 0) {
+			dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+			return ret;
+		}
+	}
+
 	ret = tsfeed->set(tsfeed, feed->pid,
 					ts_type, ts_pes,
 					filter->pes_buffer_size, timeout);
@@ -1270,7 +1910,9 @@
 		spin_unlock_irq(&filter->dev->lock);
 	}
 
-	dvb_ringbuffer_flush(&filter->buffer);
+	spin_lock_irq(&filter->dev->lock);
+	dvb_dmxdev_flush_output(&filter->buffer, &filter->events);
+	spin_unlock_irq(&filter->dev->lock);
 
 	switch (filter->type) {
 	case DMXDEV_TYPE_SEC:
@@ -1295,14 +1937,27 @@
 		/* if no feed found, try to allocate new one */
 		if (!*secfeed) {
 			ret = dmxdev->demux->allocate_section_feed(dmxdev->demux,
-								   secfeed,
-								   dvb_dmxdev_section_callback);
+						secfeed,
+						dvb_dmxdev_section_callback);
 			if (ret < 0) {
 				printk("DVB (%s): could not alloc feed\n",
 				       __func__);
 				return ret;
 			}
 
+			if ((*secfeed)->data_ready_cb) {
+				ret = (*secfeed)->data_ready_cb(
+						*secfeed,
+						dvb_dmxdev_section_event_cb);
+
+				if (ret < 0) {
+					printk(KERN_ERR "DVB (%s): could not set event cb\n",
+				       __func__);
+					dvb_dmxdev_feed_restart(filter);
+					return ret;
+				}
+			}
+
 			ret = (*secfeed)->set(*secfeed, para->pid, 32768,
 					      (para->flags & DMX_CHECK_CRC) ? 1 : 0);
 			if (ret < 0) {
@@ -1348,6 +2003,16 @@
 		break;
 	}
 	case DMXDEV_TYPE_PES:
+		if (filter->params.pes.rec_chunk_size <
+			DMX_REC_BUFF_CHUNK_MIN_SIZE)
+			filter->params.pes.rec_chunk_size =
+				DMX_REC_BUFF_CHUNK_MIN_SIZE;
+
+		if (filter->params.pes.rec_chunk_size >=
+			filter->buffer.size)
+			filter->params.pes.rec_chunk_size =
+				filter->buffer.size >> 2;
+
 		list_for_each_entry(feed, &filter->feed.ts, next) {
 			ret = dvb_dmxdev_start_feed(dmxdev, filter, feed);
 			if (ret < 0) {
@@ -1391,6 +2056,8 @@
 	file->private_data = dmxdevfilter;
 
 	dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192);
+	dvb_dmxdev_flush_events(&dmxdevfilter->events);
+
 	dmxdevfilter->type = DMXDEV_TYPE_NONE;
 	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
 	init_timer(&dmxdevfilter->timer);
@@ -1607,8 +2274,14 @@
 		ret = dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos);
 	else
 		ret = dvb_dmxdev_buffer_read(&dmxdevfilter->buffer,
-					     file->f_flags & O_NONBLOCK,
-					     buf, count, ppos);
+					    file->f_flags & O_NONBLOCK,
+					    buf, count, ppos);
+
+	if (ret > 0) {
+		spin_lock_irq(&dmxdevfilter->dev->lock);
+		dvb_dmxdev_update_events(&dmxdevfilter->events, ret);
+		spin_unlock_irq(&dmxdevfilter->dev->lock);
+	}
 
 	mutex_unlock(&dmxdevfilter->mutex);
 	return ret;
@@ -1764,6 +2437,15 @@
 				*(enum dmx_playback_mode_t *)parg);
 		break;
 
+	case DMX_GET_EVENT:
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret = dvb_dmxdev_get_event(dmxdevfilter, parg);
+		mutex_unlock(&dmxdevfilter->mutex);
+		break;
+
 	case DMX_GET_STC:
 		if (!dmxdev->demux->get_stc) {
 			ret = -EINVAL;
@@ -1823,10 +2505,15 @@
 		return 0;
 
 	if (dmxdevfilter->buffer.error)
-		mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+		mask |= (POLLIN | POLLRDNORM | POLLERR);
 
 	if (!dvb_ringbuffer_empty(&dmxdevfilter->buffer))
-		mask |= (POLLIN | POLLRDNORM | POLLPRI);
+		mask |= (POLLIN | POLLRDNORM);
+
+	if (dmxdevfilter->events.notified_index !=
+		dmxdevfilter->events.write_index) {
+		mask |= POLLPRI;
+	}
 
 	return mask;
 }
@@ -1952,6 +2639,10 @@
 		ret = dvb_dvr_feed_data(dmxdev, file->f_flags, arg);
 		break;
 
+	case DMX_GET_EVENT:
+		ret = dvb_dvr_get_event(dmxdev, file->f_flags, parg);
+		break;
+
 	default:
 		ret = -EINVAL;
 		break;
@@ -1978,10 +2669,14 @@
 		poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
 
 		if (dmxdev->dvr_buffer.error)
-			mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+			mask |= (POLLIN | POLLRDNORM | POLLERR);
 
 		if (!dvb_ringbuffer_empty(&dmxdev->dvr_buffer))
-			mask |= (POLLIN | POLLRDNORM | POLLPRI);
+			mask |= (POLLIN | POLLRDNORM);
+
+		if (dmxdev->dvr_output_events.notified_index !=
+			dmxdev->dvr_output_events.write_index)
+			mask |= POLLPRI;
 	} else {
 		poll_wait(file, &dmxdev->dvr_input_buffer.queue, wait);
 		if (dmxdev->dvr_input_buffer.error)
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
index 4c52e84..6fa7054 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -62,6 +62,35 @@
 	struct list_head next;
 };
 
+struct dmxdev_events_queue {
+#define DMX_EVENT_QUEUE_SIZE	500 /* number of events */
+	/*
+	 * indices used to manage events queue.
+	 * read_index advanced when relevent data is read
+	 * from the buffer.
+	 * notified_index is the index from which next events
+	 * are returned.
+	 * read_index <= notified_index <= write_index
+	 *
+	 * If user reads the data without getting the respective
+	 * event first, the read/notified indices are updated
+	 * automatically to reflect the actual data that exist
+	 * in the buffer.
+	 */
+	u32 read_index;
+	u32 write_index;
+	u32 notified_index;
+
+	/* Bytes read by user without having respective event in the queue */
+	u32 bytes_read_no_event;
+
+	/* internal tracking of PES and recording events */
+	u32 current_event_data_size;
+	u32 current_event_start_offset;
+
+	struct dmx_filter_event queue[DMX_EVENT_QUEUE_SIZE];
+};
+
 struct dmxdev_filter {
 	union {
 		struct dmx_section_filter *sec;
@@ -78,6 +107,8 @@
 		struct dmx_pes_filter_params pes;
 	} params;
 
+	struct dmxdev_events_queue events;
+
 	enum dmxdev_type type;
 	enum dmxdev_state state;
 	struct dmxdev *dev;
@@ -88,6 +119,8 @@
 	/* relevent for decoder PES */
 	unsigned long pes_buffer_size;
 
+	u32 rec_chunk_size;
+
 	/* only for sections */
 	struct timer_list timer;
 	int todo;
@@ -105,10 +138,9 @@
 
 	int filternum;
 	int capabilities;
-#define DMXDEV_CAP_DUPLEX			0x1
-#define DMXDEV_CAP_PULL_MODE		0x2
-#define DMXDEV_CAP_PCR_EXTRACTION	0x4
-#define DMXDEV_CAP_INDEXING		0x8
+#define DMXDEV_CAP_DUPLEX	0x1
+#define DMXDEV_CAP_PULL_MODE	0x2
+#define DMXDEV_CAP_INDEXING	0x4
 
 	enum dmx_playback_mode_t playback_mode;
 	dmx_source_t source;
@@ -120,6 +152,8 @@
 	struct dmx_frontend *dvr_orig_fe;
 
 	struct dvb_ringbuffer dvr_buffer;
+	struct dmxdev_events_queue dvr_output_events;
+
 	struct dvb_ringbuffer dvr_input_buffer;
 	struct workqueue_struct *dvr_input_workqueue;
 
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index bc72fee..0be6a22 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -145,8 +145,15 @@
 
 	/* PUSI ? */
 	if (buf[1] & 0x40) {
-		feed->peslen = 0xfffa;
+		if (feed->pusi_seen)
+			/* We had seen PUSI before, this means
+			 * that previous PES can be closed now.
+			 */
+			feed->cb.ts(NULL, 0, NULL, 0,
+						&feed->feed.ts, DMX_OK_PES_END);
+
 		feed->pusi_seen = 1;
+		feed->peslen = 0;
 	}
 
 	if (feed->pusi_seen == 0)
@@ -204,6 +211,11 @@
 			if (dvb_demux_performancecheck)
 				demux->total_crc_time +=
 					dvb_dmx_calc_time_delta(pre_crc_time);
+
+			/* Notify on CRC error */
+			feed->cb.sec(NULL, 0, NULL, 0,
+				&f->filter, DMX_CRC_ERROR);
+
 			return -1;
 		}
 
@@ -1053,6 +1065,25 @@
 	return ret;
 }
 
+static int dmx_ts_feed_data_ready_cb(struct dmx_ts_feed *feed,
+				dmx_ts_data_ready_cb callback)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+	mutex_lock(&dvbdmx->mutex);
+
+	if (dvbdmxfeed->state == DMX_STATE_GO) {
+		mutex_unlock(&dvbdmx->mutex);
+		return -EINVAL;
+	}
+
+	dvbdmxfeed->data_ready_cb.ts = callback;
+
+	mutex_unlock(&dvbdmx->mutex);
+	return 0;
+}
+
 static int dmx_ts_set_indexing_params(
 	struct dmx_ts_feed *ts_feed,
 	struct dmx_indexing_video_params *params)
@@ -1084,7 +1115,7 @@
 	feed->cb.ts = callback;
 	feed->demux = demux;
 	feed->pid = 0xffff;
-	feed->peslen = 0xfffa;
+	feed->peslen = 0;
 	feed->buffer = NULL;
 	memset(&feed->indexing_params, 0,
 			sizeof(struct dmx_indexing_video_params));
@@ -1105,6 +1136,7 @@
 	(*ts_feed)->set = dmx_ts_feed_set;
 	(*ts_feed)->set_indexing_params = dmx_ts_set_indexing_params;
 	(*ts_feed)->get_decoder_buff_status = dmx_ts_feed_decoder_buff_status;
+	(*ts_feed)->data_ready_cb = dmx_ts_feed_data_ready_cb;
 
 	if (!(feed->filter = dvb_dmx_filter_alloc(demux))) {
 		feed->state = DMX_STATE_FREE;
@@ -1312,6 +1344,26 @@
 	return ret;
 }
 
+
+static int dmx_section_feed_data_ready_cb(struct dmx_section_feed *feed,
+				dmx_section_data_ready_cb callback)
+{
+	struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+	struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+	mutex_lock(&dvbdmx->mutex);
+
+	if (dvbdmxfeed->state == DMX_STATE_GO) {
+		mutex_unlock(&dvbdmx->mutex);
+		return -EINVAL;
+	}
+
+	dvbdmxfeed->data_ready_cb.sec = callback;
+
+	mutex_unlock(&dvbdmx->mutex);
+	return 0;
+}
+
 static int dmx_section_feed_release_filter(struct dmx_section_feed *feed,
 					   struct dmx_section_filter *filter)
 {
@@ -1381,6 +1433,7 @@
 	(*feed)->start_filtering = dmx_section_feed_start_filtering;
 	(*feed)->stop_filtering = dmx_section_feed_stop_filtering;
 	(*feed)->release_filter = dmx_section_feed_release_filter;
+	(*feed)->data_ready_cb = dmx_section_feed_data_ready_cb;
 
 	mutex_unlock(&dvbdmx->mutex);
 	return 0;
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index ebe34ad..3970a6c 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -78,6 +78,11 @@
 		dmx_section_cb sec;
 	} cb;
 
+	union {
+		dmx_ts_data_ready_cb ts;
+		dmx_section_data_ready_cb sec;
+	} data_ready_cb;
+
 	struct dvb_demux *demux;
 	void *priv;
 	int type;
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
index 8b591a6..4093fa5 100644
--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
@@ -111,6 +111,10 @@
 #define DVB_RINGBUFFER_SKIP(rbuf,num)	\
 			(rbuf)->pread=((rbuf)->pread+(num))%(rbuf)->size
 
+/* advance write ptr by <num> bytes */
+#define DVB_RINGBUFFER_PUSH(rbuf, num)	\
+			((rbuf)->pwrite = (((rbuf)->pwrite+(num))%(rbuf)->size))
+
 /*
 ** read <len> bytes from ring buffer into <buf>
 ** <usermem> specifies whether <buf> resides in user space
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 7223377..e7e9baf 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
@@ -33,16 +33,6 @@
 	   sizeof(struct mpq_streambuffer_packet_header) + \
 	   sizeof(struct mpq_adapter_video_meta_data)))
 
-/*
- * PCR/STC information length saved in ring-buffer.
- * PCR / STC are saved in ring-buffer in the following form:
- * <8 bit flags><64 bits of STC> <64bits of PCR>
- * STC and PCR values are in 27MHz.
- * The current flags that are defined:
- * 0x00000001: discontinuity_indicator
- */
-#define PCR_STC_LEN					17
-
 
 /* Number of demux devices, has default of linux configuration */
 static int mpq_demux_device_num = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
@@ -1906,10 +1896,9 @@
 			struct dvb_demux_feed *feed,
 			const u8 *buf)
 {
-	int i;
 	u64 pcr;
 	u64 stc;
-	u8 output[PCR_STC_LEN];
+	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;
@@ -1960,17 +1949,13 @@
 	stc += buf[188];
 	stc *= 256; /* convert from 105.47 KHZ to 27MHz */
 
-	output[0] = adaptation_field->discontinuity_indicator;
+	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);
 
-	for (i = 1; i <= 8; i++)
-		output[i] = (stc >> ((8-i) << 3)) & 0xFF;
-
-	for (i = 9; i <= 16; i++)
-		output[i] = (pcr >> ((16-i) << 3)) & 0xFF;
-
-	feed->cb.ts(output, PCR_STC_LEN,
-				NULL, 0,
-				&feed->feed.ts, DMX_OK);
 	return 0;
 }
 EXPORT_SYMBOL(mpq_dmx_process_pcr_packet);
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 e7d6b74..bd1ecfe 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
@@ -641,7 +641,6 @@
 	mpq_demux->dmxdev.capabilities =
 		DMXDEV_CAP_DUPLEX |
 		DMXDEV_CAP_PULL_MODE |
-		DMXDEV_CAP_PCR_EXTRACTION |
 		DMXDEV_CAP_INDEXING;
 
 	mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
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 f374d91..fd94e80 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
@@ -752,7 +752,6 @@
 	mpq_demux->dmxdev.capabilities =
 		DMXDEV_CAP_DUPLEX |
 		DMXDEV_CAP_PULL_MODE |
-		DMXDEV_CAP_PCR_EXTRACTION |
 		DMXDEV_CAP_INDEXING;
 
 	mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c
index 74b7c22..e4858fa 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c
@@ -139,7 +139,6 @@
 	mpq_demux->dmxdev.capabilities =
 		DMXDEV_CAP_DUPLEX |
 		DMXDEV_CAP_PULL_MODE |
-		DMXDEV_CAP_PCR_EXTRACTION |
 		DMXDEV_CAP_INDEXING;
 
 	mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
diff --git a/include/linux/dvb/dmx.h b/include/linux/dvb/dmx.h
index e0058d3..d19dfa5 100644
--- a/include/linux/dvb/dmx.h
+++ b/include/linux/dvb/dmx.h
@@ -36,6 +36,9 @@
 
 #define DMX_FILTER_SIZE 16
 
+/* Min recording chunk upon which event is generated */
+#define DMX_REC_BUFF_CHUNK_MIN_SIZE		(100*188)
+
 typedef enum
 {
 	DMX_OUT_DECODER, /* Streaming directly to decoder. */
@@ -135,7 +138,6 @@
 	enum dmx_indexing_video_profile profile;
 };
 
-
 struct dmx_pes_filter_params
 {
 	__u16          pid;
@@ -144,6 +146,18 @@
 	dmx_pes_type_t pes_type;
 	__u32          flags;
 
+	/*
+	 * The following configures when the event
+	 * DMX_EVENT_NEW_REC_CHUNK will be triggered.
+	 * When new recorded data is received with size
+	 * equal or larger than this value a new event
+	 * will be triggered. This is relevent when
+	 * output is DMX_OUT_TS_TAP or DMX_OUT_TSDEMUX_TAP,
+	 * size must be at least DMX_REC_BUFF_CHUNK_MIN_SIZE
+	 * and smaller than buffer size.
+	 */
+	__u32          rec_chunk_size;
+
 	struct dmx_indexing_video_params video_params;
 };
 
@@ -170,6 +184,129 @@
 	int error;
 };
 
+/* Events associated with each demux filter */
+enum dmx_event {
+	/* New PES packet is ready to be consumed */
+	DMX_EVENT_NEW_PES,
+
+	/* New section is ready to be consumed */
+	DMX_EVENT_NEW_SECTION,
+
+	/* New recording chunk is ready to be consumed */
+	DMX_EVENT_NEW_REC_CHUNK,
+
+	/* New PCR value is ready */
+	DMX_EVENT_NEW_PCR,
+
+	/* Overflow */
+	DMX_EVENT_BUFFER_OVERFLOW,
+
+	/* Section was dropped due to CRC error */
+	DMX_EVENT_SECTION_CRC_ERROR,
+
+	/* End-of-stream, no more data from this filter */
+	DMX_EVENT_EOS
+};
+
+/* Flags passed in filter events */
+
+/* Continuity counter error was detected */
+#define DMX_FILTER_CC_ERROR			0x01
+
+/* Discontinuity indicator was set */
+#define DMX_FILTER_DISCONTINUITY_INDEICATOR	0x02
+
+/* PES legnth in PES header is not correct */
+#define DMX_FILTER_PES_LENGTH_ERROR		0x04
+
+
+/* PES info associated with DMX_EVENT_NEW_PES event */
+struct dmx_pes_event_info {
+	/* Offset at which PES information starts */
+	__u32 base_offset;
+
+	/*
+	 * Start offset at which PES data
+	 * from the stream starts.
+	 * Equal to base_offset if PES data
+	 * starts from the beginning.
+	 */
+	__u32 start_offset;
+
+	/* Total length holding the PES information */
+	__u32 total_length;
+
+	/* Actual length holding the PES data */
+	__u32 actual_length;
+
+	/* Local receiver timestamp in 27MHz */
+	__u64 stc;
+
+	/* Flags passed in filter events */
+	__u32 flags;
+};
+
+/* Section info associated with DMX_EVENT_NEW_SECTION event */
+struct dmx_section_event_info {
+	/* Offset at which section information starts */
+	__u32 base_offset;
+
+	/*
+	 * Start offset at which section data
+	 * from the stream starts.
+	 * Equal to base_offset if section data
+	 * starts from the beginning.
+	 */
+	__u32 start_offset;
+
+	/* Total length holding the section information */
+	__u32 total_length;
+
+	/* Actual length holding the section data */
+	__u32 actual_length;
+
+	/* Flags passed in filter events */
+	__u32 flags;
+};
+
+/* Recording info associated with DMX_EVENT_NEW_REC_CHUNK event */
+struct dmx_rec_chunk_event_info {
+	/* Offset at which recording chunk starts */
+	__u32 offset;
+
+	/* Size of recording chunk in bytes */
+	__u32 size;
+};
+
+/* PCR info associated with DMX_EVENT_NEW_PCR event */
+struct dmx_pcr_event_info {
+	/* Local timestamp in 27MHz
+	 * when PCR packet was received
+	 */
+	__u64 stc;
+
+	/* PCR value in 27MHz */
+	__u64 pcr;
+
+	/* Flags passed in filter events */
+	__u32 flags;
+};
+
+/*
+ * Filter's event returned through DMX_GET_EVENT.
+ * poll with POLLPRI would block until events are available.
+ */
+struct dmx_filter_event {
+	enum dmx_event type;
+
+	union {
+		struct dmx_pes_event_info pes;
+		struct dmx_section_event_info section;
+		struct dmx_rec_chunk_event_info recording_chunk;
+		struct dmx_pcr_event_info pcr;
+	} params;
+};
+
 typedef struct dmx_caps {
 	__u32 caps;
 
@@ -292,5 +429,6 @@
 #define DMX_RELEASE_DATA		 _IO('o', 57)
 #define DMX_FEED_DATA			 _IO('o', 58)
 #define DMX_SET_PLAYBACK_MODE	 _IOW('o', 59, enum dmx_playback_mode_t)
+#define DMX_GET_EVENT			 _IOR('o', 60, struct dmx_filter_event)
 
 #endif /*_DVBDMX_H_*/