Pawel Osciak wrote: > 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.osciak@xxxxxxxxxxx> > Reviewed-by: Marek Szyprowski <m.szyprowski@xxxxxxxxxxx> > Reviewed-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> > --- > 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);*/ Just drop it if not used anymore. > > /* 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; You probably need something similar to this at v4l2-compat-ioctl32.c. > } > > /* 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; -- Cheers, Mauro -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html