V4L/DVB (4189): Add videodev support for VIDIOC_S/G/TRY_EXT_CTRLS.
[linux-2.6.git] / drivers / media / video / videodev.c
index 43152297e6d5631fa6a544c67d9fd430c43d7a4d..763e178555d08b2d8ae27f87f31d8d2c894dc4bb 100644 (file)
@@ -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))
        {