Current V4L2 API assumes that each video buffer contains exactly one,
contiguous memory buffer for video data. Even in case of planar video
formats, e.g. YCbCr with each component residing in a separate area
of memory, it is specified that each of those planes immediately follows
the previous one in memory.

There exist hardware video devices that handle, or even require, each of
the planes to reside in a separate, arbitrary memory area. Some even
require different planes to be placed in different, physical memory banks.

This patch introduces a backward-compatible extension of V4L2 API, which
allows passing additional, per-plane info in the video buffer structure.

Signed-off-by: Pawel Osciak <p.osc...@samsung.com>
Reviewed-by: Marek Szyprowski <m.szyprow...@samsung.com>
Reviewed-by: Kyungmin Park <kyungmin.p...@samsung.com>
---
 drivers/media/video/v4l2-ioctl.c |   97 ++++++++++++++++++++++++++-----------
 include/linux/videodev2.h        |   33 ++++++++++++-
 2 files changed, 99 insertions(+), 31 deletions(-)

diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index 4b11257..b89b73f 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -172,6 +172,8 @@ static const char *v4l2_memory_names[] = {
        [V4L2_MEMORY_MMAP]    = "mmap",
        [V4L2_MEMORY_USERPTR] = "userptr",
        [V4L2_MEMORY_OVERLAY] = "overlay",
+       [V4L2_MEMORY_MULTI_USERPTR]     = "multi-userptr",
+       [V4L2_MEMORY_MULTI_MMAP]        = "multi-mmap",
 };
 
 #define prt_names(a, arr) ((((a) >= 0) && ((a) < ARRAY_SIZE(arr))) ? \
@@ -1975,7 +1977,7 @@ static unsigned long cmd_input_size(unsigned int cmd)
        switch (cmd) {
                CMDINSIZE(ENUM_FMT,             fmtdesc,        type);
                CMDINSIZE(G_FMT,                format,         type);
-               CMDINSIZE(QUERYBUF,             buffer,         type);
+               CMDINSIZE(QUERYBUF,             buffer,         length);
                CMDINSIZE(G_PARM,               streamparm,     type);
                CMDINSIZE(ENUMSTD,              standard,       index);
                CMDINSIZE(ENUMINPUT,            input,          index);
@@ -2000,6 +2002,46 @@ static unsigned long cmd_input_size(unsigned int cmd)
        }
 }
 
+static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
+                           void * __user *user_ptr, void ***kernel_ptr)
+{
+       int ret = 0;
+
+       switch(cmd) {
+       case VIDIOC_QUERYBUF:
+       case VIDIOC_QBUF:
+       case VIDIOC_DQBUF: {
+               struct v4l2_buffer *buf = parg;
+
+               if ((buf->memory == V4L2_MEMORY_MULTI_USERPTR
+                    || buf->memory == V4L2_MEMORY_MULTI_MMAP)) {
+                       *user_ptr = (void __user *)buf->m.planes;
+                       *kernel_ptr = (void **)&buf->m.planes;
+                       *array_size = sizeof(struct v4l2_plane) * buf->length;
+                       ret = 1;
+               }
+               break;
+       }
+
+       case VIDIOC_S_EXT_CTRLS:
+       case VIDIOC_G_EXT_CTRLS:
+       case VIDIOC_TRY_EXT_CTRLS: {
+               struct v4l2_ext_controls *ctrls = parg;
+
+               if (ctrls->count != 0) {
+                       *user_ptr = (void __user *)ctrls->controls;
+                       *kernel_ptr = (void **)&ctrls->controls;
+                       *array_size = sizeof(struct v4l2_ext_control)
+                                   * ctrls->count;
+                       ret = 1;
+               }
+               break;
+       }
+       }
+
+       return ret;
+}
+
 long video_ioctl2(struct file *file,
               unsigned int cmd, unsigned long arg)
 {
@@ -2007,15 +2049,16 @@ long video_ioctl2(struct file *file,
        void    *mbuf = NULL;
        void    *parg = NULL;
        long    err  = -EINVAL;
-       int     is_ext_ctrl;
-       size_t  ctrls_size = 0;
+       int     has_array_args;
+       size_t  array_size = 0;
        void __user *user_ptr = NULL;
+       void    **kernel_ptr = NULL;
 
 #ifdef __OLD_VIDIOC_
        cmd = video_fix_command(cmd);
 #endif
-       is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS ||
-                      cmd == VIDIOC_TRY_EXT_CTRLS);
+       /*is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS 
||
+                      cmd == VIDIOC_TRY_EXT_CTRLS);*/
 
        /*  Copy arguments into temp kernel buffer  */
        if (_IOC_DIR(cmd) != _IOC_NONE) {
@@ -2045,43 +2088,39 @@ long video_ioctl2(struct file *file,
                }
        }
 
-       if (is_ext_ctrl) {
-               struct v4l2_ext_controls *p = parg;
+       has_array_args = check_array_args(cmd, parg, &array_size,
+                                         &user_ptr, &kernel_ptr);
 
-               /* In case of an error, tell the caller that it wasn't
-                  a specific control that caused it. */
-               p->error_idx = p->count;
-               user_ptr = (void __user *)p->controls;
-               if (p->count) {
-                       ctrls_size = sizeof(struct v4l2_ext_control) * p->count;
-                       /* Note: v4l2_ext_controls fits in sbuf[] so mbuf is 
still NULL. */
-                       mbuf = kmalloc(ctrls_size, GFP_KERNEL);
-                       err = -ENOMEM;
-                       if (NULL == mbuf)
-                               goto out_ext_ctrl;
-                       err = -EFAULT;
-                       if (copy_from_user(mbuf, user_ptr, ctrls_size))
-                               goto out_ext_ctrl;
-                       p->controls = mbuf;
-               }
+       if (has_array_args) {
+               /* When adding new types of array args, make sure that the
+                * parent argument to ioctl, which contains the array, fits into
+                * sbuf (so that mbuf will still remain unused up to here).
+                */
+               mbuf = kmalloc(array_size, GFP_KERNEL);
+               err = -ENOMEM;
+               if (NULL == mbuf)
+                       goto out_array_args;
+               err = -EFAULT;
+               if (copy_from_user(mbuf, user_ptr, array_size))
+                       goto out_array_args;
+               *kernel_ptr = mbuf;
        }
 
        /* Handles IOCTL */
        err = __video_do_ioctl(file, cmd, parg);
        if (err == -ENOIOCTLCMD)
                err = -EINVAL;
-       if (is_ext_ctrl) {
-               struct v4l2_ext_controls *p = parg;
 
-               p->controls = (void *)user_ptr;
-               if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, 
ctrls_size))
+       if (has_array_args) {
+               *kernel_ptr = user_ptr;
+               if (copy_to_user(user_ptr, mbuf, array_size))
                        err = -EFAULT;
-               goto out_ext_ctrl;
+               goto out_array_args;
        }
        if (err < 0)
                goto out;
 
-out_ext_ctrl:
+out_array_args:
        /*  Copy results into user buffer  */
        switch (_IOC_DIR(cmd)) {
        case _IOC_READ:
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index d4962a7..bf3f33d 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -70,6 +70,7 @@
  * Moved from videodev.h
  */
 #define VIDEO_MAX_FRAME               32
+#define VIDEO_MAX_PLANES               3
 
 #ifndef __KERNEL__
 
@@ -180,6 +181,10 @@ enum v4l2_memory {
        V4L2_MEMORY_MMAP             = 1,
        V4L2_MEMORY_USERPTR          = 2,
        V4L2_MEMORY_OVERLAY          = 3,
+
+       /* Discontiguous buffer types */
+       V4L2_MEMORY_MULTI_USERPTR    = 4,
+       V4L2_MEMORY_MULTI_MMAP       = 5,
 };
 
 /* see also http://vektor.theorem.ca/graphics/ycbcr/ */
@@ -519,6 +524,29 @@ struct v4l2_requestbuffers {
        __u32                   reserved[2];
 };
 
+/* struct v4l2_plane - a multi-plane buffer plane.
+ *
+ * Multi-plane buffers consist of two or more planes, e.g. an YCbCr buffer
+ * with two planes has one plane for Y, and another for interleaved CbCr
+ * components. Each plane can reside in a separate memory buffer, or in
+ * a completely separate memory chip even (e.g. in embedded devices).
+ */
+struct v4l2_plane {
+       __u32                   bytesused;
+
+       union {
+               __u32           offset;
+               unsigned long   userptr;
+       } m;
+       __u32                   length;
+       __u32                   reserved[5];
+};
+
+/* struct v4l2_buffer - a video buffer (frame)
+ * @length:    size of the buffer (not its payload) for single-plane buffers,
+ *             number of planes (and number of elements in planes array)
+ *             for multi-plane
+ */
 struct v4l2_buffer {
        __u32                   index;
        enum v4l2_buf_type      type;
@@ -532,8 +560,9 @@ struct v4l2_buffer {
        /* memory location */
        enum v4l2_memory        memory;
        union {
-               __u32           offset;
-               unsigned long   userptr;
+               __u32                   offset;
+               unsigned long           userptr;
+               struct v4l2_plane       *planes;
        } m;
        __u32                   length;
        __u32                   input;
-- 
1.7.0.31.g1df487

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to