[media] vb2: add thread support
authorHans Verkuil <hans.verkuil@cisco.com>
Mon, 14 Apr 2014 10:33:00 +0000 (07:33 -0300)
committerMauro Carvalho Chehab <m.chehab@samsung.com>
Wed, 16 Apr 2014 21:57:25 +0000 (18:57 -0300)
In order to implement vb2 DVB support you need to be able to start
a kernel thread that queues and dequeues buffers, calling a callback
function for every buffer. This patch adds support for that.

It's based on drivers/media/v4l2-core/videobuf-dvb.c, but with all the DVB
specific stuff stripped out, thus making it much more generic.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
drivers/media/v4l2-core/videobuf2-core.c
include/media/videobuf2-core.h

index 2075bac748c0a02917f7fcf5bc8d56c4bc89ce71..3f0cdb150dfd85a5b89a3b97f64f5e2dc63d4886 100644 (file)
@@ -6,6 +6,9 @@
  * Author: Pawel Osciak <pawel@osciak.com>
  *        Marek Szyprowski <m.szyprowski@samsung.com>
  *
+ * The vb2_thread implementation was based on code from videobuf-dvb.c:
+ *     (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation.
@@ -18,6 +21,8 @@
 #include <linux/poll.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
 
 #include <media/v4l2-dev.h>
 #include <media/v4l2-fh.h>
@@ -3005,6 +3010,147 @@ size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
 }
 EXPORT_SYMBOL_GPL(vb2_write);
 
+struct vb2_threadio_data {
+       struct task_struct *thread;
+       vb2_thread_fnc fnc;
+       void *priv;
+       bool stop;
+};
+
+static int vb2_thread(void *data)
+{
+       struct vb2_queue *q = data;
+       struct vb2_threadio_data *threadio = q->threadio;
+       struct vb2_fileio_data *fileio = q->fileio;
+       bool set_timestamp = false;
+       int prequeue = 0;
+       int index = 0;
+       int ret = 0;
+
+       if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+               prequeue = q->num_buffers;
+               set_timestamp =
+                       (q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) ==
+                       V4L2_BUF_FLAG_TIMESTAMP_COPY;
+       }
+
+       set_freezable();
+
+       for (;;) {
+               struct vb2_buffer *vb;
+
+               /*
+                * Call vb2_dqbuf to get buffer back.
+                */
+               memset(&fileio->b, 0, sizeof(fileio->b));
+               fileio->b.type = q->type;
+               fileio->b.memory = q->memory;
+               if (prequeue) {
+                       fileio->b.index = index++;
+                       prequeue--;
+               } else {
+                       call_void_qop(q, wait_finish, q);
+                       ret = vb2_internal_dqbuf(q, &fileio->b, 0);
+                       call_void_qop(q, wait_prepare, q);
+                       dprintk(5, "file io: vb2_dqbuf result: %d\n", ret);
+               }
+               if (threadio->stop)
+                       break;
+               if (ret)
+                       break;
+               try_to_freeze();
+
+               vb = q->bufs[fileio->b.index];
+               if (!(fileio->b.flags & V4L2_BUF_FLAG_ERROR))
+                       ret = threadio->fnc(vb, threadio->priv);
+               if (ret)
+                       break;
+               call_void_qop(q, wait_finish, q);
+               if (set_timestamp)
+                       v4l2_get_timestamp(&fileio->b.timestamp);
+               ret = vb2_internal_qbuf(q, &fileio->b);
+               call_void_qop(q, wait_prepare, q);
+               if (ret)
+                       break;
+       }
+
+       /* Hmm, linux becomes *very* unhappy without this ... */
+       while (!kthread_should_stop()) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule();
+       }
+       return 0;
+}
+
+/*
+ * This function should not be used for anything else but the videobuf2-dvb
+ * support. If you think you have another good use-case for this, then please
+ * contact the linux-media mailinglist first.
+ */
+int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv,
+                    const char *thread_name)
+{
+       struct vb2_threadio_data *threadio;
+       int ret = 0;
+
+       if (q->threadio)
+               return -EBUSY;
+       if (vb2_is_busy(q))
+               return -EBUSY;
+       if (WARN_ON(q->fileio))
+               return -EBUSY;
+
+       threadio = kzalloc(sizeof(*threadio), GFP_KERNEL);
+       if (threadio == NULL)
+               return -ENOMEM;
+       threadio->fnc = fnc;
+       threadio->priv = priv;
+
+       ret = __vb2_init_fileio(q, !V4L2_TYPE_IS_OUTPUT(q->type));
+       dprintk(3, "file io: vb2_init_fileio result: %d\n", ret);
+       if (ret)
+               goto nomem;
+       q->threadio = threadio;
+       threadio->thread = kthread_run(vb2_thread, q, "vb2-%s", thread_name);
+       if (IS_ERR(threadio->thread)) {
+               ret = PTR_ERR(threadio->thread);
+               threadio->thread = NULL;
+               goto nothread;
+       }
+       return 0;
+
+nothread:
+       __vb2_cleanup_fileio(q);
+nomem:
+       kfree(threadio);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_thread_start);
+
+int vb2_thread_stop(struct vb2_queue *q)
+{
+       struct vb2_threadio_data *threadio = q->threadio;
+       struct vb2_fileio_data *fileio = q->fileio;
+       int err;
+
+       if (threadio == NULL)
+               return 0;
+       call_void_qop(q, wait_finish, q);
+       threadio->stop = true;
+       vb2_internal_streamoff(q, q->type);
+       call_void_qop(q, wait_prepare, q);
+       q->fileio = NULL;
+       fileio->req.count = 0;
+       vb2_reqbufs(q, &fileio->req);
+       kfree(fileio);
+       err = kthread_stop(threadio->thread);
+       threadio->thread = NULL;
+       kfree(threadio);
+       q->fileio = NULL;
+       q->threadio = NULL;
+       return err;
+}
+EXPORT_SYMBOL_GPL(vb2_thread_stop);
 
 /*
  * The following functions are not part of the vb2 core API, but are helper
index b1859f6953b2c7fe62dfd5af8107fc39e8e0fcaa..46e76096c22aa16bc6922247ebae861600c0b095 100644 (file)
@@ -20,6 +20,7 @@
 
 struct vb2_alloc_ctx;
 struct vb2_fileio_data;
+struct vb2_threadio_data;
 
 /**
  * struct vb2_mem_ops - memory handling/memory allocator operations
@@ -375,6 +376,7 @@ struct v4l2_fh;
  * @start_streaming_called: start_streaming() was called successfully and we
  *             started streaming.
  * @fileio:    file io emulator internal data, used only if emulator is active
+ * @threadio:  thread io internal data, used only if thread is active
  */
 struct vb2_queue {
        enum v4l2_buf_type              type;
@@ -411,6 +413,7 @@ struct vb2_queue {
        unsigned int                    start_streaming_called:1;
 
        struct vb2_fileio_data          *fileio;
+       struct vb2_threadio_data        *threadio;
 
 #ifdef CONFIG_VIDEO_ADV_DEBUG
        /*
@@ -461,6 +464,35 @@ size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
                loff_t *ppos, int nonblock);
 size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
                loff_t *ppos, int nonblock);
+/**
+ * vb2_thread_fnc - callback function for use with vb2_thread
+ *
+ * This is called whenever a buffer is dequeued in the thread.
+ */
+typedef int (*vb2_thread_fnc)(struct vb2_buffer *vb, void *priv);
+
+/**
+ * vb2_thread_start() - start a thread for the given queue.
+ * @q:         videobuf queue
+ * @fnc:       callback function
+ * @priv:      priv pointer passed to the callback function
+ * @thread_name:the name of the thread. This will be prefixed with "vb2-".
+ *
+ * This starts a thread that will queue and dequeue until an error occurs
+ * or @vb2_thread_stop is called.
+ *
+ * This function should not be used for anything else but the videobuf2-dvb
+ * support. If you think you have another good use-case for this, then please
+ * contact the linux-media mailinglist first.
+ */
+int vb2_thread_start(struct vb2_queue *q, vb2_thread_fnc fnc, void *priv,
+                    const char *thread_name);
+
+/**
+ * vb2_thread_stop() - stop the thread for the given queue.
+ * @q:         videobuf queue
+ */
+int vb2_thread_stop(struct vb2_queue *q);
 
 /**
  * vb2_is_streaming() - return streaming status of the queue