summary refs log tree commit diff
path: root/drivers/media/video/videodev.c
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil@xs4all.nl>2008-06-25 06:29:01 -0300
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-07-20 07:12:18 -0300
commitd8799b4699af008290e141804b40c5ebf3d7dc35 (patch)
treea41396cd14c12be748b83691d8bdb298755caf69 /drivers/media/video/videodev.c
parent36ecd4955a5ebee6135e2bcc997712fad4ac9b69 (diff)
downloadlinux-d8799b4699af008290e141804b40c5ebf3d7dc35.tar.gz
V4L/DVB (8112): videodev: improve extended control support in video_ioctl2()
- add sanity checks for the extended controls argument.
- if the driver only supports extended controls, then convert
  old-style controls to an extended control callback.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/videodev.c')
-rw-r--r--drivers/media/video/videodev.c85
1 files changed, 71 insertions, 14 deletions
diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c
index 91902d52d022..5bf2256fedeb 100644
--- a/drivers/media/video/videodev.c
+++ b/drivers/media/video/videodev.c
@@ -710,6 +710,29 @@ static inline void v4l_print_ext_ctrls(unsigned int cmd,
 	printk(KERN_CONT "\n");
 };
 
+static inline int check_ext_ctrls(struct v4l2_ext_controls *c)
+{
+	__u32 i;
+
+	/* zero the reserved fields */
+	c->reserved[0] = c->reserved[1] = 0;
+	for (i = 0; i < c->count; i++) {
+		c->controls[i].reserved2[0] = 0;
+		c->controls[i].reserved2[1] = 0;
+	}
+	/* V4L2_CID_PRIVATE_BASE cannot be used as control class
+	 * when using extended controls. */
+	if (c->ctrl_class == V4L2_CID_PRIVATE_BASE)
+		return 0;
+	/* Check that all controls are from the same control class. */
+	for (i = 0; i < c->count; i++) {
+		if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) {
+			c->error_idx = i;
+			return 0;
+		}
+	}
+	return 1;
+}
 
 static int check_fmt (struct video_device *vfd, enum v4l2_buf_type type)
 {
@@ -1392,10 +1415,24 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
 	{
 		struct v4l2_control *p = arg;
 
-		if (!vfd->vidioc_g_ctrl)
+		if (vfd->vidioc_g_ctrl)
+			ret = vfd->vidioc_g_ctrl(file, fh, p);
+		else if (vfd->vidioc_g_ext_ctrls) {
+			struct v4l2_ext_controls ctrls;
+			struct v4l2_ext_control ctrl;
+
+			ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+			ctrls.count = 1;
+			ctrls.controls = &ctrl;
+			ctrl.id = p->id;
+			ctrl.value = p->value;
+			if (check_ext_ctrls(&ctrls)) {
+				ret = vfd->vidioc_g_ext_ctrls(file, fh, &ctrls);
+				if (ret == 0)
+					p->value = ctrl.value;
+			}
+		} else
 			break;
-
-		ret = vfd->vidioc_g_ctrl(file, fh, p);
 		if (!ret)
 			dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
 		else
@@ -1405,21 +1442,39 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
 	case VIDIOC_S_CTRL:
 	{
 		struct v4l2_control *p = arg;
+		struct v4l2_ext_controls ctrls;
+		struct v4l2_ext_control ctrl;
 
-		if (!vfd->vidioc_s_ctrl)
+		if (!vfd->vidioc_s_ctrl && !vfd->vidioc_s_ext_ctrls)
 			break;
+
 		dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
 
-		ret = vfd->vidioc_s_ctrl(file, fh, p);
+		if (vfd->vidioc_s_ctrl) {
+			ret = vfd->vidioc_s_ctrl(file, fh, p);
+			break;
+		}
+		if (!vfd->vidioc_s_ext_ctrls)
+			break;
+
+		ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+		ctrls.count = 1;
+		ctrls.controls = &ctrl;
+		ctrl.id = p->id;
+		ctrl.value = p->value;
+		if (check_ext_ctrls(&ctrls))
+			ret = vfd->vidioc_s_ext_ctrls(file, fh, &ctrls);
 		break;
 	}
 	case VIDIOC_G_EXT_CTRLS:
 	{
 		struct v4l2_ext_controls *p = arg;
 
+		p->error_idx = p->count;
 		if (!vfd->vidioc_g_ext_ctrls)
 			break;
-		ret = vfd->vidioc_g_ext_ctrls(file, fh, p);
+		if (check_ext_ctrls(p))
+			ret = vfd->vidioc_g_ext_ctrls(file, fh, p);
 		v4l_print_ext_ctrls(cmd, vfd, p, !ret);
 		break;
 	}
@@ -1427,22 +1482,24 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
 	{
 		struct v4l2_ext_controls *p = arg;
 
-		if (vfd->vidioc_s_ext_ctrls) {
-			v4l_print_ext_ctrls(cmd, vfd, p, 1);
-
+		p->error_idx = p->count;
+		if (!vfd->vidioc_s_ext_ctrls)
+			break;
+		v4l_print_ext_ctrls(cmd, vfd, p, 1);
+		if (check_ext_ctrls(p))
 			ret = vfd->vidioc_s_ext_ctrls(file, fh, p);
-		}
 		break;
 	}
 	case VIDIOC_TRY_EXT_CTRLS:
 	{
 		struct v4l2_ext_controls *p = arg;
 
-		if (vfd->vidioc_try_ext_ctrls) {
-			v4l_print_ext_ctrls(cmd, vfd, p, 1);
-
+		p->error_idx = p->count;
+		if (!vfd->vidioc_try_ext_ctrls)
+			break;
+		v4l_print_ext_ctrls(cmd, vfd, p, 1);
+		if (check_ext_ctrls(p))
 			ret = vfd->vidioc_try_ext_ctrls(file, fh, p);
-		}
 		break;
 	}
 	case VIDIOC_QUERYMENU: