@@ -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:
@@ -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;