[media] uvcvideo: Implement compat_ioctl32 for custom ioctls
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Sun, 18 Dec 2011 23:45:39 +0000 (20:45 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 19 Mar 2012 17:07:01 +0000 (14:07 -0300)
Support 32-bit/64-bit compatibility for the the UVCIOC_ ioctls.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/uvc/uvc_v4l2.c

index f09ee8b2ea54060e0e5f8a1089573420dc569b55..ff2cdddf9bc629c6d264390e6a68fb9c9c2d581a 100644 (file)
@@ -11,6 +11,7 @@
  *
  */
 
+#include <linux/compat.h>
 #include <linux/kernel.h>
 #include <linux/version.h>
 #include <linux/list.h>
@@ -1030,6 +1031,207 @@ static long uvc_v4l2_ioctl(struct file *file,
        return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
 }
 
+#ifdef CONFIG_COMPAT
+struct uvc_xu_control_mapping32 {
+       __u32 id;
+       __u8 name[32];
+       __u8 entity[16];
+       __u8 selector;
+
+       __u8 size;
+       __u8 offset;
+       __u32 v4l2_type;
+       __u32 data_type;
+
+       compat_caddr_t menu_info;
+       __u32 menu_count;
+
+       __u32 reserved[4];
+};
+
+static int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp,
+                       const struct uvc_xu_control_mapping32 __user *up)
+{
+       struct uvc_menu_info __user *umenus;
+       struct uvc_menu_info __user *kmenus;
+       compat_caddr_t p;
+
+       if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+           __copy_from_user(kp, up, offsetof(typeof(*up), menu_info)) ||
+           __get_user(kp->menu_count, &up->menu_count))
+               return -EFAULT;
+
+       memset(kp->reserved, 0, sizeof(kp->reserved));
+
+       if (kp->menu_count == 0) {
+               kp->menu_info = NULL;
+               return 0;
+       }
+
+       if (__get_user(p, &up->menu_info))
+               return -EFAULT;
+       umenus = compat_ptr(p);
+       if (!access_ok(VERIFY_READ, umenus, kp->menu_count * sizeof(*umenus)))
+               return -EFAULT;
+
+       kmenus = compat_alloc_user_space(kp->menu_count * sizeof(*kmenus));
+       if (kmenus == NULL)
+               return -EFAULT;
+       kp->menu_info = kmenus;
+
+       if (copy_in_user(kmenus, umenus, kp->menu_count * sizeof(*umenus)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp,
+                       struct uvc_xu_control_mapping32 __user *up)
+{
+       struct uvc_menu_info __user *umenus;
+       struct uvc_menu_info __user *kmenus = kp->menu_info;
+       compat_caddr_t p;
+
+       if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
+           __copy_to_user(up, kp, offsetof(typeof(*up), menu_info)) ||
+           __put_user(kp->menu_count, &up->menu_count))
+               return -EFAULT;
+
+       __clear_user(up->reserved, sizeof(up->reserved));
+
+       if (kp->menu_count == 0)
+               return 0;
+
+       if (get_user(p, &up->menu_info))
+               return -EFAULT;
+       umenus = compat_ptr(p);
+       if (!access_ok(VERIFY_WRITE, umenus, kp->menu_count * sizeof(*umenus)))
+               return -EFAULT;
+
+       if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus)))
+               return -EFAULT;
+
+       return 0;
+}
+
+struct uvc_xu_control_query32 {
+       __u8 unit;
+       __u8 selector;
+       __u8 query;
+       __u16 size;
+       compat_caddr_t data;
+};
+
+static int uvc_v4l2_get_xu_query(struct uvc_xu_control_query *kp,
+                       const struct uvc_xu_control_query32 __user *up)
+{
+       u8 __user *udata;
+       u8 __user *kdata;
+       compat_caddr_t p;
+
+       if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+           __copy_from_user(kp, up, offsetof(typeof(*up), data)))
+               return -EFAULT;
+
+       if (kp->size == 0) {
+               kp->data = NULL;
+               return 0;
+       }
+
+       if (__get_user(p, &up->data))
+               return -EFAULT;
+       udata = compat_ptr(p);
+       if (!access_ok(VERIFY_READ, udata, kp->size))
+               return -EFAULT;
+
+       kdata = compat_alloc_user_space(kp->size);
+       if (kdata == NULL)
+               return -EFAULT;
+       kp->data = kdata;
+
+       if (copy_in_user(kdata, udata, kp->size))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp,
+                       struct uvc_xu_control_query32 __user *up)
+{
+       u8 __user *udata;
+       u8 __user *kdata = kp->data;
+       compat_caddr_t p;
+
+       if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
+           __copy_to_user(up, kp, offsetof(typeof(*up), data)))
+               return -EFAULT;
+
+       if (kp->size == 0)
+               return 0;
+
+       if (get_user(p, &up->data))
+               return -EFAULT;
+       udata = compat_ptr(p);
+       if (!access_ok(VERIFY_READ, udata, kp->size))
+               return -EFAULT;
+
+       if (copy_in_user(udata, kdata, kp->size))
+               return -EFAULT;
+
+       return 0;
+}
+
+#define UVCIOC_CTRL_MAP32      _IOWR('u', 0x20, struct uvc_xu_control_mapping32)
+#define UVCIOC_CTRL_QUERY32    _IOWR('u', 0x21, struct uvc_xu_control_query32)
+
+static long uvc_v4l2_compat_ioctl32(struct file *file,
+                    unsigned int cmd, unsigned long arg)
+{
+       union {
+               struct uvc_xu_control_mapping xmap;
+               struct uvc_xu_control_query xqry;
+       } karg;
+       void __user *up = compat_ptr(arg);
+       mm_segment_t old_fs;
+       long ret;
+
+       switch (cmd) {
+       case UVCIOC_CTRL_MAP32:
+               cmd = UVCIOC_CTRL_MAP;
+               ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
+               break;
+
+       case UVCIOC_CTRL_QUERY32:
+               cmd = UVCIOC_CTRL_QUERY;
+               ret = uvc_v4l2_get_xu_query(&karg.xqry, up);
+               break;
+
+       default:
+               return -ENOIOCTLCMD;
+       }
+
+       old_fs = get_fs();
+       set_fs(KERNEL_DS);
+       ret = uvc_v4l2_ioctl(file, cmd, (unsigned long)&karg);
+       set_fs(old_fs);
+
+       if (ret < 0)
+               return ret;
+
+       switch (cmd) {
+       case UVCIOC_CTRL_MAP:
+               ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up);
+               break;
+
+       case UVCIOC_CTRL_QUERY:
+               ret = uvc_v4l2_put_xu_query(&karg.xqry, up);
+               break;
+       }
+
+       return ret;
+}
+#endif
+
 static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
                    size_t count, loff_t *ppos)
 {
@@ -1076,6 +1278,9 @@ const struct v4l2_file_operations uvc_fops = {
        .open           = uvc_v4l2_open,
        .release        = uvc_v4l2_release,
        .unlocked_ioctl = uvc_v4l2_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl32 = uvc_v4l2_compat_ioctl32,
+#endif
        .read           = uvc_v4l2_read,
        .mmap           = uvc_v4l2_mmap,
        .poll           = uvc_v4l2_poll,