Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d14e6d76 authored by Pawel Osciak's avatar Pawel Osciak Committed by Mauro Carvalho Chehab
Browse files

[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: default avatarPawel Osciak <p.osciak@samsung.com>
Signed-off-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
Reviewed-by: default avatarMarek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: default avatarMarek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: default avatarHans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent f8f3914c
Loading
Loading
Loading
Loading
+409 −44
Original line number Original line Diff line number Diff line
@@ -428,20 +428,33 @@ static void dbgbuf(unsigned int cmd, struct video_device *vfd,
					struct v4l2_buffer *p)
					struct v4l2_buffer *p)
{
{
	struct v4l2_timecode *tc = &p->timecode;
	struct v4l2_timecode *tc = &p->timecode;
	struct v4l2_plane *plane;
	int i;


	dbgarg(cmd, "%02ld:%02d:%02d.%08ld index=%d, type=%s, "
	dbgarg(cmd, "%02ld:%02d:%02d.%08ld index=%d, type=%s, "
		"bytesused=%d, flags=0x%08d, "
		"flags=0x%08d, field=%0d, sequence=%d, memory=%s\n",
		"field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx, length=%d\n",
			p->timestamp.tv_sec / 3600,
			p->timestamp.tv_sec / 3600,
			(int)(p->timestamp.tv_sec / 60) % 60,
			(int)(p->timestamp.tv_sec / 60) % 60,
			(int)(p->timestamp.tv_sec % 60),
			(int)(p->timestamp.tv_sec % 60),
			(long)p->timestamp.tv_usec,
			(long)p->timestamp.tv_usec,
			p->index,
			p->index,
			prt_names(p->type, v4l2_type_names),
			prt_names(p->type, v4l2_type_names),
			p->bytesused, p->flags,
			p->flags, p->field, p->sequence,
			p->field, p->sequence,
			prt_names(p->memory, v4l2_memory_names));
			prt_names(p->memory, v4l2_memory_names),

			p->m.userptr, p->length);
	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, "
	dbgarg2("timecode=%02d:%02d:%02d type=%d, "
		"flags=0x%08d, frames=%d, userbits=0x%08x\n",
		"flags=0x%08d, frames=%d, userbits=0x%08x\n",
			tc->hours, tc->minutes, tc->seconds,
			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);
		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,
static inline void v4l_print_ext_ctrls(unsigned int cmd,
	struct video_device *vfd, struct v4l2_ext_controls *c, int show_vals)
	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) {
	switch (type) {
	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
	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;
			return 0;
		break;
		break;
	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
	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;
			return 0;
		break;
		break;
	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
	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;
			return 0;
		break;
		break;
	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
	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;
	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,
static long __video_do_ioctl(struct file *file,
		unsigned int cmd, void *arg)
		unsigned int cmd, void *arg)
{
{
	struct video_device *vfd = video_devdata(file);
	struct video_device *vfd = video_devdata(file);
	const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
	const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
	void *fh = file->private_data;
	void *fh = file->private_data;
	struct v4l2_format f_copy;
	long ret = -EINVAL;
	long ret = -EINVAL;


	if (ops == NULL) {
	if (ops == NULL) {
@@ -635,6 +737,11 @@ static long __video_do_ioctl(struct file *file,
			if (ops->vidioc_enum_fmt_vid_cap)
			if (ops->vidioc_enum_fmt_vid_cap)
				ret = ops->vidioc_enum_fmt_vid_cap(file, fh, f);
				ret = ops->vidioc_enum_fmt_vid_cap(file, fh, f);
			break;
			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:
		case V4L2_BUF_TYPE_VIDEO_OVERLAY:
			if (ops->vidioc_enum_fmt_vid_overlay)
			if (ops->vidioc_enum_fmt_vid_overlay)
				ret = ops->vidioc_enum_fmt_vid_overlay(file,
				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)
			if (ops->vidioc_enum_fmt_vid_out)
				ret = ops->vidioc_enum_fmt_vid_out(file, fh, f);
				ret = ops->vidioc_enum_fmt_vid_out(file, fh, f);
			break;
			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:
		case V4L2_BUF_TYPE_PRIVATE:
			if (ops->vidioc_enum_fmt_type_private)
			if (ops->vidioc_enum_fmt_type_private)
				ret = ops->vidioc_enum_fmt_type_private(file,
				ret = ops->vidioc_enum_fmt_type_private(file,
@@ -672,22 +784,90 @@ static long __video_do_ioctl(struct file *file,


		switch (f->type) {
		switch (f->type) {
		case V4L2_BUF_TYPE_VIDEO_CAPTURE:
		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);
				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)
			if (!ret)
				v4l_print_pix_fmt(vfd, &f->fmt.pix);
				v4l_print_pix_fmt(vfd, &f->fmt.pix);
			break;
			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:
		case V4L2_BUF_TYPE_VIDEO_OVERLAY:
			if (ops->vidioc_g_fmt_vid_overlay)
			if (ops->vidioc_g_fmt_vid_overlay)
				ret = ops->vidioc_g_fmt_vid_overlay(file,
				ret = ops->vidioc_g_fmt_vid_overlay(file,
								    fh, f);
								    fh, f);
			break;
			break;
		case V4L2_BUF_TYPE_VIDEO_OUTPUT:
		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);
				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)
			if (!ret)
				v4l_print_pix_fmt(vfd, &f->fmt.pix);
				v4l_print_pix_fmt(vfd, &f->fmt.pix);
			break;
			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:
		case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
			if (ops->vidioc_g_fmt_vid_out_overlay)
			if (ops->vidioc_g_fmt_vid_out_overlay)
				ret = ops->vidioc_g_fmt_vid_out_overlay(file,
				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:
		case V4L2_BUF_TYPE_VIDEO_CAPTURE:
			CLEAR_AFTER_FIELD(f, fmt.pix);
			CLEAR_AFTER_FIELD(f, fmt.pix);
			v4l_print_pix_fmt(vfd, &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);
				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;
			break;
		case V4L2_BUF_TYPE_VIDEO_OVERLAY:
		case V4L2_BUF_TYPE_VIDEO_OVERLAY:
			CLEAR_AFTER_FIELD(f, fmt.win);
			CLEAR_AFTER_FIELD(f, fmt.win);
@@ -743,8 +959,44 @@ static long __video_do_ioctl(struct file *file,
		case V4L2_BUF_TYPE_VIDEO_OUTPUT:
		case V4L2_BUF_TYPE_VIDEO_OUTPUT:
			CLEAR_AFTER_FIELD(f, fmt.pix);
			CLEAR_AFTER_FIELD(f, fmt.pix);
			v4l_print_pix_fmt(vfd, &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);
				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;
			break;
		case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
		case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
			CLEAR_AFTER_FIELD(f, fmt.win);
			CLEAR_AFTER_FIELD(f, fmt.win);
@@ -793,11 +1045,47 @@ static long __video_do_ioctl(struct file *file,
		switch (f->type) {
		switch (f->type) {
		case V4L2_BUF_TYPE_VIDEO_CAPTURE:
		case V4L2_BUF_TYPE_VIDEO_CAPTURE:
			CLEAR_AFTER_FIELD(f, fmt.pix);
			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);
				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)
			if (!ret)
				v4l_print_pix_fmt(vfd, &f->fmt.pix);
				v4l_print_pix_fmt(vfd, &f->fmt.pix);
			break;
			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:
		case V4L2_BUF_TYPE_VIDEO_OVERLAY:
			CLEAR_AFTER_FIELD(f, fmt.win);
			CLEAR_AFTER_FIELD(f, fmt.win);
			if (ops->vidioc_try_fmt_vid_overlay)
			if (ops->vidioc_try_fmt_vid_overlay)
@@ -806,11 +1094,47 @@ static long __video_do_ioctl(struct file *file,
			break;
			break;
		case V4L2_BUF_TYPE_VIDEO_OUTPUT:
		case V4L2_BUF_TYPE_VIDEO_OUTPUT:
			CLEAR_AFTER_FIELD(f, fmt.pix);
			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);
				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)
			if (!ret)
				v4l_print_pix_fmt(vfd, &f->fmt.pix);
				v4l_print_pix_fmt(vfd, &f->fmt.pix);
			break;
			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:
		case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
			CLEAR_AFTER_FIELD(f, fmt.win);
			CLEAR_AFTER_FIELD(f, fmt.win);
			if (ops->vidioc_try_fmt_vid_out_overlay)
			if (ops->vidioc_try_fmt_vid_out_overlay)
@@ -1975,7 +2299,7 @@ static unsigned long cmd_input_size(unsigned int cmd)
	switch (cmd) {
	switch (cmd) {
		CMDINSIZE(ENUM_FMT,		fmtdesc,	type);
		CMDINSIZE(ENUM_FMT,		fmtdesc,	type);
		CMDINSIZE(G_FMT,		format,		type);
		CMDINSIZE(G_FMT,		format,		type);
		CMDINSIZE(QUERYBUF,		buffer,		type);
		CMDINSIZE(QUERYBUF,		buffer,		length);
		CMDINSIZE(G_PARM,		streamparm,	type);
		CMDINSIZE(G_PARM,		streamparm,	type);
		CMDINSIZE(ENUMSTD,		standard,	index);
		CMDINSIZE(ENUMSTD,		standard,	index);
		CMDINSIZE(ENUMINPUT,		input,		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,
long video_ioctl2(struct file *file,
	       unsigned int cmd, unsigned long arg)
	       unsigned int cmd, unsigned long arg)
{
{
@@ -2007,16 +2374,14 @@ long video_ioctl2(struct file *file,
	void    *mbuf = NULL;
	void    *mbuf = NULL;
	void	*parg = (void *)arg;
	void	*parg = (void *)arg;
	long	err  = -EINVAL;
	long	err  = -EINVAL;
	int     is_ext_ctrl;
	bool	has_array_args;
	size_t  ctrls_size = 0;
	size_t  array_size = 0;
	void __user *user_ptr = NULL;
	void __user *user_ptr = NULL;
	void	**kernel_ptr = NULL;


#ifdef __OLD_VIDIOC_
#ifdef __OLD_VIDIOC_
	cmd = video_fix_command(cmd);
	cmd = video_fix_command(cmd);
#endif
#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  */
	/*  Copy arguments into temp kernel buffer  */
	if (_IOC_DIR(cmd) != _IOC_NONE) {
	if (_IOC_DIR(cmd) != _IOC_NONE) {
		if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
		if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
@@ -2045,43 +2410,43 @@ long video_ioctl2(struct file *file,
		}
		}
	}
	}


	if (is_ext_ctrl) {
	err = check_array_args(cmd, parg, &array_size, &user_ptr, &kernel_ptr);
		struct v4l2_ext_controls *p = parg;
	if (err < 0)
		goto out;
	has_array_args = err;


		/* In case of an error, tell the caller that it wasn't
	if (has_array_args) {
		   a specific control that caused it. */
		/*
		p->error_idx = p->count;
		 * When adding new types of array args, make sure that the
		user_ptr = (void __user *)p->controls;
		 * parent argument to ioctl (which contains the pointer to the
		if (p->count) {
		 * array) fits into sbuf (so that mbuf will still remain
			ctrls_size = sizeof(struct v4l2_ext_control) * p->count;
		 * unused up to here).
			/* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */
		 */
			mbuf = kmalloc(ctrls_size, GFP_KERNEL);
		mbuf = kmalloc(array_size, GFP_KERNEL);
		err = -ENOMEM;
		err = -ENOMEM;
		if (NULL == mbuf)
		if (NULL == mbuf)
				goto out_ext_ctrl;
			goto out_array_args;
		err = -EFAULT;
		err = -EFAULT;
			if (copy_from_user(mbuf, user_ptr, ctrls_size))
		if (copy_from_user(mbuf, user_ptr, array_size))
				goto out_ext_ctrl;
			goto out_array_args;
			p->controls = mbuf;
		*kernel_ptr = mbuf;
		}
	}
	}


	/* Handles IOCTL */
	/* Handles IOCTL */
	err = __video_do_ioctl(file, cmd, parg);
	err = __video_do_ioctl(file, cmd, parg);
	if (err == -ENOIOCTLCMD)
	if (err == -ENOIOCTLCMD)
		err = -EINVAL;
		err = -EINVAL;
	if (is_ext_ctrl) {
		struct v4l2_ext_controls *p = parg;


		p->controls = (void *)user_ptr;
	if (has_array_args) {
		if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size))
		*kernel_ptr = user_ptr;
		if (copy_to_user(user_ptr, mbuf, array_size))
			err = -EFAULT;
			err = -EFAULT;
		goto out_ext_ctrl;
		goto out_array_args;
	}
	}
	if (err < 0)
	if (err < 0)
		goto out;
		goto out;


out_ext_ctrl:
out_array_args:
	/*  Copy results into user buffer  */
	/*  Copy results into user buffer  */
	switch (_IOC_DIR(cmd)) {
	switch (_IOC_DIR(cmd)) {
	case _IOC_READ:
	case _IOC_READ:
+16 −0
Original line number Original line Diff line number Diff line
@@ -37,6 +37,10 @@ struct v4l2_ioctl_ops {
					    struct v4l2_fmtdesc *f);
					    struct v4l2_fmtdesc *f);
	int (*vidioc_enum_fmt_vid_out)     (struct file *file, void *fh,
	int (*vidioc_enum_fmt_vid_out)     (struct file *file, void *fh,
					    struct v4l2_fmtdesc *f);
					    struct v4l2_fmtdesc *f);
	int (*vidioc_enum_fmt_vid_cap_mplane)(struct file *file, void *fh,
					      struct v4l2_fmtdesc *f);
	int (*vidioc_enum_fmt_vid_out_mplane)(struct file *file, void *fh,
					      struct v4l2_fmtdesc *f);
	int (*vidioc_enum_fmt_type_private)(struct file *file, void *fh,
	int (*vidioc_enum_fmt_type_private)(struct file *file, void *fh,
					    struct v4l2_fmtdesc *f);
					    struct v4l2_fmtdesc *f);


@@ -57,6 +61,10 @@ struct v4l2_ioctl_ops {
					struct v4l2_format *f);
					struct v4l2_format *f);
	int (*vidioc_g_fmt_sliced_vbi_out)(struct file *file, void *fh,
	int (*vidioc_g_fmt_sliced_vbi_out)(struct file *file, void *fh,
					struct v4l2_format *f);
					struct v4l2_format *f);
	int (*vidioc_g_fmt_vid_cap_mplane)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_g_fmt_vid_out_mplane)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_g_fmt_type_private)(struct file *file, void *fh,
	int (*vidioc_g_fmt_type_private)(struct file *file, void *fh,
					struct v4l2_format *f);
					struct v4l2_format *f);


@@ -77,6 +85,10 @@ struct v4l2_ioctl_ops {
					struct v4l2_format *f);
					struct v4l2_format *f);
	int (*vidioc_s_fmt_sliced_vbi_out)(struct file *file, void *fh,
	int (*vidioc_s_fmt_sliced_vbi_out)(struct file *file, void *fh,
					struct v4l2_format *f);
					struct v4l2_format *f);
	int (*vidioc_s_fmt_vid_cap_mplane)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_s_fmt_vid_out_mplane)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_s_fmt_type_private)(struct file *file, void *fh,
	int (*vidioc_s_fmt_type_private)(struct file *file, void *fh,
					struct v4l2_format *f);
					struct v4l2_format *f);


@@ -97,6 +109,10 @@ struct v4l2_ioctl_ops {
					  struct v4l2_format *f);
					  struct v4l2_format *f);
	int (*vidioc_try_fmt_sliced_vbi_out)(struct file *file, void *fh,
	int (*vidioc_try_fmt_sliced_vbi_out)(struct file *file, void *fh,
					  struct v4l2_format *f);
					  struct v4l2_format *f);
	int (*vidioc_try_fmt_vid_cap_mplane)(struct file *file, void *fh,
					     struct v4l2_format *f);
	int (*vidioc_try_fmt_vid_out_mplane)(struct file *file, void *fh,
					     struct v4l2_format *f);
	int (*vidioc_try_fmt_type_private)(struct file *file, void *fh,
	int (*vidioc_try_fmt_type_private)(struct file *file, void *fh,
					  struct v4l2_format *f);
					  struct v4l2_format *f);