From: Mike Isely <isely@pobox.com>
Date: Mon, 4 Feb 2008 03:00:19 +0000 (-0300)
Subject: V4L/DVB (7682): pvrusb2-dvb: finish up stream & buffer handling
X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=bb8ce9d9143c0fe2b5cdf65fa41250446805a4be;p=GitHub%2FLineageOS%2FG12%2Fandroid_kernel_amlogic_linux-4.9.git

V4L/DVB (7682): pvrusb2-dvb: finish up stream & buffer handling

Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
---

diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/drivers/media/video/pvrusb2/pvrusb2-dvb.c
index c85477709e6e..bde85c973058 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-dvb.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.c
@@ -19,13 +19,198 @@
  */
 
 #include <linux/kthread.h>
+#include <linux/freezer.h>
 #include "dvbdev.h"
 #include "pvrusb2-hdw-internal.h"
 #include "pvrusb2-hdw.h"
+#include "pvrusb2-io.h"
 #include "pvrusb2-dvb.h"
 
 DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
 
+#define BUFFER_COUNT 32
+#define BUFFER_SIZE PAGE_ALIGN(0x4000)
+
+struct pvr2_dvb_fh {
+	struct pvr2_channel	channel;
+	struct pvr2_stream	*stream;
+	struct pvr2_dvb_adapter	*adap;
+	wait_queue_head_t	wait_data;
+	char			*buffer_storage[BUFFER_COUNT];
+};
+
+static void pvr2_dvb_notify(struct pvr2_dvb_fh *fhp)
+{
+	wake_up(&fhp->wait_data);
+}
+
+static int pvr2_dvb_fh_init(struct pvr2_dvb_fh *fh,
+			    struct pvr2_dvb_adapter *adap)
+{
+	struct pvr2_context *pvr = adap->pvr;
+	unsigned int idx;
+	int ret;
+	struct pvr2_buffer *bp;
+
+	init_waitqueue_head(&fh->wait_data);
+
+	fh->adap = adap;
+
+	pvr2_channel_init(&fh->channel, adap->pvr);
+
+	ret = pvr2_channel_claim_stream(&fh->channel, &pvr->video_stream);
+	/* somebody else already has the stream */
+	if (ret != 0)
+		return ret;
+
+	fh->stream = pvr->video_stream.stream;
+
+	for (idx = 0; idx < BUFFER_COUNT; idx++) {
+		fh->buffer_storage[idx] = kmalloc(BUFFER_SIZE, GFP_KERNEL);
+		if (!(fh->buffer_storage[idx]))
+			break;
+	}
+
+	if (idx < BUFFER_COUNT) {
+		/* An allocation appears to have failed */
+		ret = -ENOMEM;
+		goto cleanup;
+	}
+
+	pvr2_stream_set_callback(pvr->video_stream.stream,
+				 (pvr2_stream_callback) pvr2_dvb_notify, fh);
+
+	ret = pvr2_stream_set_buffer_count(fh->stream, BUFFER_COUNT);
+	if (ret < 0)
+		return ret;
+
+	for (idx = 0; idx < BUFFER_COUNT; idx++) {
+		bp = pvr2_stream_get_buffer(fh->stream, idx);
+		pvr2_buffer_set_buffer(bp,
+				       fh->buffer_storage[idx],
+				       BUFFER_SIZE);
+	}
+
+	ret = pvr2_hdw_set_streaming(fh->channel.hdw, 1);
+	if (ret < 0)
+		goto cleanup;
+
+	while ((bp = pvr2_stream_get_idle_buffer(fh->stream)) != 0) {
+		ret = pvr2_buffer_queue(bp);
+		if (ret < 0)
+			goto cleanup;
+	}
+
+	return ret;
+
+cleanup:
+	if (fh->stream)
+		pvr2_stream_kill(fh->stream);
+
+	for (idx = 0; idx < BUFFER_COUNT; idx++) {
+		if (!(fh->buffer_storage[idx]))
+			continue;
+
+		kfree(fh->buffer_storage[idx]);
+	}
+	pvr2_channel_done(&fh->channel);
+
+	return ret;
+}
+
+static void pvr2_dvb_fh_done(struct pvr2_dvb_fh *fh)
+{
+	unsigned int idx;
+
+	pvr2_hdw_set_streaming(fh->channel.hdw, 0);
+
+	pvr2_stream_kill(fh->stream);
+
+//	pvr2_channel_claim_stream(&fh->channel, NULL);
+
+	for (idx = 0; idx < BUFFER_COUNT; idx++) {
+		if (!(fh->buffer_storage[idx]))
+			continue;
+
+		kfree(fh->buffer_storage[idx]);
+	}
+
+	pvr2_channel_done(&fh->channel);
+}
+
+static int pvr2_dvb_feed_thread(void *data)
+{
+	struct pvr2_dvb_adapter *adap = data;
+	struct pvr2_dvb_fh fh;
+	int ret;
+	unsigned int count;
+	struct pvr2_buffer *bp;
+
+	printk(KERN_DEBUG "dvb thread started\n");
+	set_freezable();
+
+	memset(&fh, 0, sizeof(fh));
+
+	ret = pvr2_dvb_fh_init(&fh, adap);
+	if (ret != 0)
+		return ret;
+
+	for (;;) {
+		if ((0 == adap->feedcount) || (kthread_should_stop()))
+			break;
+
+		/* Not sure about this... */
+		try_to_freeze();
+
+		bp = pvr2_stream_get_ready_buffer(fh.stream);
+		if (bp != NULL) {
+			count = pvr2_buffer_get_count(bp);
+			if (count) {
+				dvb_dmx_swfilter(
+					&adap->demux,
+					fh.buffer_storage[
+						pvr2_buffer_get_id(bp)],
+					count);
+			} else {
+				ret = pvr2_buffer_get_status(bp);
+				if (ret < 0)
+					break;
+			}
+			ret = pvr2_buffer_queue(bp);
+			if (ret < 0)
+				break;
+
+			/* Since we know we did something to a buffer,
+			   just go back and try again.  No point in
+			   blocking unless we really ran out of
+			   buffers to process. */
+			continue;
+		}
+
+
+		/* Wait until more buffers become available. */
+		ret = wait_event_interruptible(
+			fh.wait_data,
+			pvr2_stream_get_ready_count(fh.stream) > 0);
+		if (ret < 0)
+			break;
+	}
+
+	pvr2_dvb_fh_done(&fh);
+
+	/* If we get here and ret is < 0, then an error has occurred.
+	   Probably would be a good idea to communicate that to DVB core... */
+
+	printk(KERN_DEBUG "dvb thread stopped\n");
+
+	/* from videobuf-dvb.c: */
+	while (!kthread_should_stop()) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule();
+	}
+	return 0;
+}
+
 static int pvr2_dvb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
 {
 	struct pvr2_dvb_adapter *adap = dvbdmxfeed->demux->priv;
@@ -52,6 +237,8 @@ static int pvr2_dvb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
 
 		printk(KERN_DEBUG "start feeding\n");
 
+		adap->thread = kthread_run(pvr2_dvb_feed_thread,
+					   adap, "pvrusb2-dvb");
 		if (IS_ERR(adap->thread)) {
 			ret = PTR_ERR(adap->thread);
 			adap->thread = NULL;