diff options
author | Pawel Osciak <p.osciak@samsung.com> | 2010-12-23 04:15:27 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-03-21 20:31:33 -0300 |
commit | d14e6d76ebf740fd0d0bd296933993a555938896 (patch) | |
tree | d6b922de6d8849e379eea61f7d56bc67ed645126 /drivers/media | |
parent | f8f3914cf922f5f9e1d60e9e10f6fb92742907ad (diff) |
[media] v4l: Add multi-planar ioctl handling code
Add multi-planar API core ioctl handling and conversion functions.
[mchehab@redhat.com: CondingStyle fixup]
Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/video/v4l2-ioctl.c | 453 |
1 files changed, 409 insertions, 44 deletions
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 1a9d90ab666..8360ed2d933 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -428,20 +428,33 @@ static void dbgbuf(unsigned int cmd, struct video_device *vfd, struct v4l2_buffer *p) { struct v4l2_timecode *tc = &p->timecode; + struct v4l2_plane *plane; + int i; dbgarg(cmd, "%02ld:%02d:%02d.%08ld index=%d, type=%s, " - "bytesused=%d, flags=0x%08d, " - "field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx, length=%d\n", + "flags=0x%08d, field=%0d, sequence=%d, memory=%s\n", p->timestamp.tv_sec / 3600, (int)(p->timestamp.tv_sec / 60) % 60, (int)(p->timestamp.tv_sec % 60), (long)p->timestamp.tv_usec, p->index, prt_names(p->type, v4l2_type_names), - p->bytesused, p->flags, - p->field, p->sequence, - prt_names(p->memory, v4l2_memory_names), - p->m.userptr, p->length); + p->flags, p->field, p->sequence, + prt_names(p->memory, v4l2_memory_names)); + + if (V4L2_TYPE_IS_MULTIPLANAR(p->type) && p->m.planes) { + for (i = 0; i < p->length; ++i) { + plane = &p->m.planes[i]; + dbgarg2("plane %d: bytesused=%d, data_offset=0x%08x " + "offset/userptr=0x%08lx, length=%d\n", + i, plane->bytesused, plane->data_offset, + plane->m.userptr, plane->length); + } + } else { + dbgarg2("bytesused=%d, offset/userptr=0x%08lx, length=%d\n", + p->bytesused, p->m.userptr, p->length); + } + dbgarg2("timecode=%02d:%02d:%02d type=%d, " "flags=0x%08d, frames=%d, userbits=0x%08x\n", tc->hours, tc->minutes, tc->seconds, @@ -469,6 +482,27 @@ static inline void v4l_print_pix_fmt(struct video_device *vfd, fmt->bytesperline, fmt->sizeimage, fmt->colorspace); }; +static inline void v4l_print_pix_fmt_mplane(struct video_device *vfd, + struct v4l2_pix_format_mplane *fmt) +{ + int i; + + dbgarg2("width=%d, height=%d, format=%c%c%c%c, field=%s, " + "colorspace=%d, num_planes=%d\n", + fmt->width, fmt->height, + (fmt->pixelformat & 0xff), + (fmt->pixelformat >> 8) & 0xff, + (fmt->pixelformat >> 16) & 0xff, + (fmt->pixelformat >> 24) & 0xff, + prt_names(fmt->field, v4l2_field_names), + fmt->colorspace, fmt->num_planes); + + for (i = 0; i < fmt->num_planes; ++i) + dbgarg2("plane %d: bytesperline=%d sizeimage=%d\n", i, + fmt->plane_fmt[i].bytesperline, + fmt->plane_fmt[i].sizeimage); +} + static inline void v4l_print_ext_ctrls(unsigned int cmd, struct video_device *vfd, struct v4l2_ext_controls *c, int show_vals) { @@ -522,7 +556,12 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type) switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (ops->vidioc_g_fmt_vid_cap) + if (ops->vidioc_g_fmt_vid_cap || + ops->vidioc_g_fmt_vid_cap_mplane) + return 0; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (ops->vidioc_g_fmt_vid_cap_mplane) return 0; break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: @@ -530,7 +569,12 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type) return 0; break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (ops->vidioc_g_fmt_vid_out) + if (ops->vidioc_g_fmt_vid_out || + ops->vidioc_g_fmt_vid_out_mplane) + return 0; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (ops->vidioc_g_fmt_vid_out_mplane) return 0; break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: @@ -561,12 +605,70 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type) return -EINVAL; } +/** + * fmt_sp_to_mp() - Convert a single-plane format to its multi-planar 1-plane + * equivalent + */ +static int fmt_sp_to_mp(const struct v4l2_format *f_sp, + struct v4l2_format *f_mp) +{ + struct v4l2_pix_format_mplane *pix_mp = &f_mp->fmt.pix_mp; + const struct v4l2_pix_format *pix = &f_sp->fmt.pix; + + if (f_sp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + f_mp->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + else if (f_sp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + f_mp->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + else + return -EINVAL; + + pix_mp->width = pix->width; + pix_mp->height = pix->height; + pix_mp->pixelformat = pix->pixelformat; + pix_mp->field = pix->field; + pix_mp->colorspace = pix->colorspace; + pix_mp->num_planes = 1; + pix_mp->plane_fmt[0].sizeimage = pix->sizeimage; + pix_mp->plane_fmt[0].bytesperline = pix->bytesperline; + + return 0; +} + +/** + * fmt_mp_to_sp() - Convert a multi-planar 1-plane format to its single-planar + * equivalent + */ +static int fmt_mp_to_sp(const struct v4l2_format *f_mp, + struct v4l2_format *f_sp) +{ + const struct v4l2_pix_format_mplane *pix_mp = &f_mp->fmt.pix_mp; + struct v4l2_pix_format *pix = &f_sp->fmt.pix; + + if (f_mp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + f_sp->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + else if (f_mp->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + f_sp->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + else + return -EINVAL; + + pix->width = pix_mp->width; + pix->height = pix_mp->height; + pix->pixelformat = pix_mp->pixelformat; + pix->field = pix_mp->field; + pix->colorspace = pix_mp->colorspace; + pix->sizeimage = pix_mp->plane_fmt[0].sizeimage; + pix->bytesperline = pix_mp->plane_fmt[0].bytesperline; + + return 0; +} + static long __video_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vfd = video_devdata(file); const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; void *fh = file->private_data; + struct v4l2_format f_copy; long ret = -EINVAL; if (ops == NULL) { @@ -635,6 +737,11 @@ static long __video_do_ioctl(struct file *file, if (ops->vidioc_enum_fmt_vid_cap) ret = ops->vidioc_enum_fmt_vid_cap(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (ops->vidioc_enum_fmt_vid_cap_mplane) + ret = ops->vidioc_enum_fmt_vid_cap_mplane(file, + fh, f); + break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: if (ops->vidioc_enum_fmt_vid_overlay) ret = ops->vidioc_enum_fmt_vid_overlay(file, @@ -644,6 +751,11 @@ static long __video_do_ioctl(struct file *file, if (ops->vidioc_enum_fmt_vid_out) ret = ops->vidioc_enum_fmt_vid_out(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (ops->vidioc_enum_fmt_vid_out_mplane) + ret = ops->vidioc_enum_fmt_vid_out_mplane(file, + fh, f); + break; case V4L2_BUF_TYPE_PRIVATE: if (ops->vidioc_enum_fmt_type_private) ret = ops->vidioc_enum_fmt_type_private(file, @@ -672,22 +784,90 @@ static long __video_do_ioctl(struct file *file, switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (ops->vidioc_g_fmt_vid_cap) + if (ops->vidioc_g_fmt_vid_cap) { ret = ops->vidioc_g_fmt_vid_cap(file, fh, f); + } else if (ops->vidioc_g_fmt_vid_cap_mplane) { + if (fmt_sp_to_mp(f, &f_copy)) + break; + ret = ops->vidioc_g_fmt_vid_cap_mplane(file, fh, + &f_copy); + if (ret) + break; + + /* Driver is currently in multi-planar format, + * we can't return it in single-planar API*/ + if (f_copy.fmt.pix_mp.num_planes > 1) { + ret = -EBUSY; + break; + } + + ret = fmt_mp_to_sp(&f_copy, f); + } if (!ret) v4l_print_pix_fmt(vfd, &f->fmt.pix); break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + if (ops->vidioc_g_fmt_vid_cap_mplane) { + ret = ops->vidioc_g_fmt_vid_cap_mplane(file, + fh, f); + } else if (ops->vidioc_g_fmt_vid_cap) { + if (fmt_mp_to_sp(f, &f_copy)) + break; + ret = ops->vidioc_g_fmt_vid_cap(file, + fh, &f_copy); + if (ret) + break; + + ret = fmt_sp_to_mp(&f_copy, f); + } + if (!ret) + v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: if (ops->vidioc_g_fmt_vid_overlay) ret = ops->vidioc_g_fmt_vid_overlay(file, fh, f); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (ops->vidioc_g_fmt_vid_out) + if (ops->vidioc_g_fmt_vid_out) { ret = ops->vidioc_g_fmt_vid_out(file, fh, f); + } else if (ops->vidioc_g_fmt_vid_out_mplane) { + if (fmt_sp_to_mp(f, &f_copy)) + break; + ret = ops->vidioc_g_fmt_vid_out_mplane(file, fh, + &f_copy); + if (ret) + break; + + /* Driver is currently in multi-planar format, + * we can't return it in single-planar API*/ + if (f_copy.fmt.pix_mp.num_planes > 1) { + ret = -EBUSY; + break; + } + + ret = fmt_mp_to_sp(&f_copy, f); + } if (!ret) v4l_print_pix_fmt(vfd, &f->fmt.pix); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + if (ops->vidioc_g_fmt_vid_out_mplane) { + ret = ops->vidioc_g_fmt_vid_out_mplane(file, + fh, f); + } else if (ops->vidioc_g_fmt_vid_out) { + if (fmt_mp_to_sp(f, &f_copy)) + break; + ret = ops->vidioc_g_fmt_vid_out(file, + fh, &f_copy); + if (ret) + break; + + ret = fmt_sp_to_mp(&f_copy, f); + } + if (!ret) + v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: if (ops->vidioc_g_fmt_vid_out_overlay) ret = ops->vidioc_g_fmt_vid_out_overlay(file, @@ -731,8 +911,44 @@ static long __video_do_ioctl(struct file *file, case V4L2_BUF_TYPE_VIDEO_CAPTURE: CLEAR_AFTER_FIELD(f, fmt.pix); v4l_print_pix_fmt(vfd, &f->fmt.pix); - if (ops->vidioc_s_fmt_vid_cap) + if (ops->vidioc_s_fmt_vid_cap) { ret = ops->vidioc_s_fmt_vid_cap(file, fh, f); + } else if (ops->vidioc_s_fmt_vid_cap_mplane) { + if (fmt_sp_to_mp(f, &f_copy)) + break; + ret = ops->vidioc_s_fmt_vid_cap_mplane(file, fh, + &f_copy); + if (ret) + break; + + if (f_copy.fmt.pix_mp.num_planes > 1) { + /* Drivers shouldn't adjust from 1-plane + * to more than 1-plane formats */ + ret = -EBUSY; + WARN_ON(1); + break; + } + + ret = fmt_mp_to_sp(&f_copy, f); + } + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + CLEAR_AFTER_FIELD(f, fmt.pix_mp); + v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + if (ops->vidioc_s_fmt_vid_cap_mplane) { + ret = ops->vidioc_s_fmt_vid_cap_mplane(file, + fh, f); + } else if (ops->vidioc_s_fmt_vid_cap && + f->fmt.pix_mp.num_planes == 1) { + if (fmt_mp_to_sp(f, &f_copy)) + break; + ret = ops->vidioc_s_fmt_vid_cap(file, + fh, &f_copy); + if (ret) + break; + + ret = fmt_sp_to_mp(&f_copy, f); + } break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: CLEAR_AFTER_FIELD(f, fmt.win); @@ -743,8 +959,44 @@ static long __video_do_ioctl(struct file *file, case V4L2_BUF_TYPE_VIDEO_OUTPUT: CLEAR_AFTER_FIELD(f, fmt.pix); v4l_print_pix_fmt(vfd, &f->fmt.pix); - if (ops->vidioc_s_fmt_vid_out) + if (ops->vidioc_s_fmt_vid_out) { ret = ops->vidioc_s_fmt_vid_out(file, fh, f); + } else if (ops->vidioc_s_fmt_vid_out_mplane) { + if (fmt_sp_to_mp(f, &f_copy)) + break; + ret = ops->vidioc_s_fmt_vid_out_mplane(file, fh, + &f_copy); + if (ret) + break; + + if (f_copy.fmt.pix_mp.num_planes > 1) { + /* Drivers shouldn't adjust from 1-plane + * to more than 1-plane formats */ + ret = -EBUSY; + WARN_ON(1); + break; + } + + ret = fmt_mp_to_sp(&f_copy, f); + } + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + CLEAR_AFTER_FIELD(f, fmt.pix_mp); + v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + if (ops->vidioc_s_fmt_vid_out_mplane) { + ret = ops->vidioc_s_fmt_vid_out_mplane(file, + fh, f); + } else if (ops->vidioc_s_fmt_vid_out && + f->fmt.pix_mp.num_planes == 1) { + if (fmt_mp_to_sp(f, &f_copy)) + break; + ret = ops->vidioc_s_fmt_vid_out(file, + fh, &f_copy); + if (ret) + break; + + ret = fmt_mp_to_sp(&f_copy, f); + } break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: CLEAR_AFTER_FIELD(f, fmt.win); @@ -793,11 +1045,47 @@ static long __video_do_ioctl(struct file *file, switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: CLEAR_AFTER_FIELD(f, fmt.pix); - if (ops->vidioc_try_fmt_vid_cap) + if (ops->vidioc_try_fmt_vid_cap) { ret = ops->vidioc_try_fmt_vid_cap(file, fh, f); + } else if (ops->vidioc_try_fmt_vid_cap_mplane) { + if (fmt_sp_to_mp(f, &f_copy)) + break; + ret = ops->vidioc_try_fmt_vid_cap_mplane(file, + fh, &f_copy); + if (ret) + break; + + if (f_copy.fmt.pix_mp.num_planes > 1) { + /* Drivers shouldn't adjust from 1-plane + * to more than 1-plane formats */ + ret = -EBUSY; + WARN_ON(1); + break; + } + ret = fmt_mp_to_sp(&f_copy, f); + } if (!ret) v4l_print_pix_fmt(vfd, &f->fmt.pix); break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + CLEAR_AFTER_FIELD(f, fmt.pix_mp); + if (ops->vidioc_try_fmt_vid_cap_mplane) { + ret = ops->vidioc_try_fmt_vid_cap_mplane(file, + fh, f); + } else if (ops->vidioc_try_fmt_vid_cap && + f->fmt.pix_mp.num_planes == 1) { + if (fmt_mp_to_sp(f, &f_copy)) + break; + ret = ops->vidioc_try_fmt_vid_cap(file, + fh, &f_copy); + if (ret) + break; + + ret = fmt_sp_to_mp(&f_copy, f); + } + if (!ret) + v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: CLEAR_AFTER_FIELD(f, fmt.win); if (ops->vidioc_try_fmt_vid_overlay) @@ -806,11 +1094,47 @@ static long __video_do_ioctl(struct file *file, break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: CLEAR_AFTER_FIELD(f, fmt.pix); - if (ops->vidioc_try_fmt_vid_out) + if (ops->vidioc_try_fmt_vid_out) { ret = ops->vidioc_try_fmt_vid_out(file, fh, f); + } else if (ops->vidioc_try_fmt_vid_out_mplane) { + if (fmt_sp_to_mp(f, &f_copy)) + break; + ret = ops->vidioc_try_fmt_vid_out_mplane(file, + fh, &f_copy); + if (ret) + break; + + if (f_copy.fmt.pix_mp.num_planes > 1) { + /* Drivers shouldn't adjust from 1-plane + * to more than 1-plane formats */ + ret = -EBUSY; + WARN_ON(1); + break; + } + ret = fmt_mp_to_sp(&f_copy, f); + } if (!ret) v4l_print_pix_fmt(vfd, &f->fmt.pix); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + CLEAR_AFTER_FIELD(f, fmt.pix_mp); + if (ops->vidioc_try_fmt_vid_out_mplane) { + ret = ops->vidioc_try_fmt_vid_out_mplane(file, + fh, f); + } else if (ops->vidioc_try_fmt_vid_out && + f->fmt.pix_mp.num_planes == 1) { + if (fmt_mp_to_sp(f, &f_copy)) + break; + ret = ops->vidioc_try_fmt_vid_out(file, + fh, &f_copy); + if (ret) + break; + + ret = fmt_sp_to_mp(&f_copy, f); + } + if (!ret) + v4l_print_pix_fmt_mplane(vfd, &f->fmt.pix_mp); + break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: CLEAR_AFTER_FIELD(f, fmt.win); if (ops->vidioc_try_fmt_vid_out_overlay) @@ -1975,7 +2299,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 +2324,49 @@ 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 (V4L2_TYPE_IS_MULTIPLANAR(buf->type) && buf->length > 0) { + if (buf->length > VIDEO_MAX_PLANES) { + ret = -EINVAL; + break; + } + *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,16 +2374,14 @@ long video_ioctl2(struct file *file, void *mbuf = NULL; void *parg = (void *)arg; long err = -EINVAL; - int is_ext_ctrl; - size_t ctrls_size = 0; + bool 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); - /* Copy arguments into temp kernel buffer */ if (_IOC_DIR(cmd) != _IOC_NONE) { if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { @@ -2045,43 +2410,43 @@ long video_ioctl2(struct file *file, } } - if (is_ext_ctrl) { - struct v4l2_ext_controls *p = parg; + err = check_array_args(cmd, parg, &array_size, &user_ptr, &kernel_ptr); + if (err < 0) + goto out; + has_array_args = err; - /* 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 pointer to 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: |