summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Verkuil2006-06-18 18:43:28 +0200
committerMauro Carvalho Chehab2006-06-25 07:05:21 +0200
commit0597691456cf3b08974d9949e4caa830e1d3c520 (patch)
treee70e6b89eb1d5dad01bd5f58ef715a72efbfba13
parentV4L/DVB (4188): Add new MPEG control/ioctl definitions to videodev2.h (diff)
downloadkernel-qcow2-linux-0597691456cf3b08974d9949e4caa830e1d3c520.tar.gz
kernel-qcow2-linux-0597691456cf3b08974d9949e4caa830e1d3c520.tar.xz
kernel-qcow2-linux-0597691456cf3b08974d9949e4caa830e1d3c520.zip
V4L/DVB (4189): Add videodev support for VIDIOC_S/G/TRY_EXT_CTRLS.
videodev.c copies the control list specified in struct v4l2_ext_controls to kernel space. Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
-rw-r--r--drivers/media/video/videodev.c105
-rw-r--r--include/media/v4l2-dev.h6
2 files changed, 109 insertions, 2 deletions
diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c
index 43152297e6d5..763e178555d0 100644
--- a/drivers/media/video/videodev.c
+++ b/drivers/media/video/videodev.c
@@ -190,10 +190,15 @@ video_usercopy(struct inode *inode, struct file *file,
void *mbuf = NULL;
void *parg = NULL;
int err = -EINVAL;
+ int is_ext_ctrl;
+ size_t ctrls_size = 0;
+ void __user *user_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 */
switch (_IOC_DIR(cmd)) {
@@ -215,19 +220,47 @@ video_usercopy(struct inode *inode, struct file *file,
err = -EFAULT;
if (_IOC_DIR(cmd) & _IOC_WRITE)
- if (copy_from_user(parg, (void __user *)arg,
- _IOC_SIZE(cmd)))
+ if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
goto out;
break;
}
+ if (is_ext_ctrl) {
+ struct v4l2_ext_controls *p = parg;
+
+ /* 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;
+ }
+ }
/* call driver */
err = func(inode, 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))
+ err = -EFAULT;
+ goto out_ext_ctrl;
+ }
if (err < 0)
goto out;
+out_ext_ctrl:
/* Copy results into user buffer */
switch (_IOC_DIR(cmd))
{
@@ -993,6 +1026,39 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
ret=vfd->vidioc_s_ctrl(file, fh, p);
break;
}
+ case VIDIOC_G_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *p = arg;
+
+ if (vfd->vidioc_g_ext_ctrls) {
+ dbgarg(cmd, "count=%d\n", p->count);
+
+ ret=vfd->vidioc_g_ext_ctrls(file, fh, p);
+ }
+ break;
+ }
+ case VIDIOC_S_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *p = arg;
+
+ if (vfd->vidioc_s_ext_ctrls) {
+ dbgarg(cmd, "count=%d\n", p->count);
+
+ 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) {
+ dbgarg(cmd, "count=%d\n", p->count);
+
+ ret=vfd->vidioc_try_ext_ctrls(file, fh, p);
+ }
+ break;
+ }
case VIDIOC_QUERYMENU:
{
struct v4l2_querymenu *p=arg;
@@ -1326,10 +1392,15 @@ int video_ioctl2 (struct inode *inode, struct file *file,
void *mbuf = NULL;
void *parg = NULL;
int err = -EINVAL;
+ int is_ext_ctrl;
+ size_t ctrls_size = 0;
+ void __user *user_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 */
switch (_IOC_DIR(cmd)) {
@@ -1356,13 +1427,43 @@ int video_ioctl2 (struct inode *inode, struct file *file,
break;
}
+ if (is_ext_ctrl) {
+ struct v4l2_ext_controls *p = parg;
+
+ /* 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;
+ }
+ }
+
/* Handles IOCTL */
err = __video_do_ioctl(inode, 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))
+ err = -EFAULT;
+ goto out_ext_ctrl;
+ }
if (err < 0)
goto out;
+out_ext_ctrl:
/* Copy results into user buffer */
switch (_IOC_DIR(cmd))
{
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index dec6b24e4c42..a1b473190e65 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -232,6 +232,12 @@ struct video_device
struct v4l2_control *a);
int (*vidioc_s_ctrl) (struct file *file, void *fh,
struct v4l2_control *a);
+ int (*vidioc_g_ext_ctrls) (struct file *file, void *fh,
+ struct v4l2_ext_controls *a);
+ int (*vidioc_s_ext_ctrls) (struct file *file, void *fh,
+ struct v4l2_ext_controls *a);
+ int (*vidioc_try_ext_ctrls) (struct file *file, void *fh,
+ struct v4l2_ext_controls *a);
int (*vidioc_querymenu) (struct file *file, void *fh,
struct v4l2_querymenu *a);