Merge branch 'stable-3.2' into pandora-3.2
[pandora-kernel.git] / drivers / media / video / v4l2-compat-ioctl32.c
index 2671959..b431b1f 100644 (file)
 #include <linux/videodev2.h>
 #include <linux/module.h>
 #include <media/v4l2-ioctl.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
 
 #ifdef CONFIG_COMPAT
 
+/* Use the same argument order as copy_in_user */
+#define assign_in_user(to, from)                                       \
+({                                                                     \
+       typeof(*from) __assign_tmp;                                     \
+                                                                       \
+       get_user(__assign_tmp, from) || put_user(__assign_tmp, to);     \
+})
+
 static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        long ret = -ENOIOCTLCMD;
@@ -33,117 +44,88 @@ static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
 struct v4l2_clip32 {
        struct v4l2_rect        c;
-       compat_caddr_t          next;
+       compat_caddr_t          next;
 };
 
 struct v4l2_window32 {
        struct v4l2_rect        w;
-       enum v4l2_field         field;
+       __u32                   field;  /* enum v4l2_field */
        __u32                   chromakey;
        compat_caddr_t          clips; /* actually struct v4l2_clip32 * */
        __u32                   clipcount;
        compat_caddr_t          bitmap;
+       __u8                    global_alpha;
 };
 
-static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
+static int get_v4l2_window32(struct v4l2_window __user *kp,
+                            struct v4l2_window32 __user *up,
+                            void __user *aux_buf, u32 aux_space)
 {
-       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_window32)) ||
-               copy_from_user(&kp->w, &up->w, sizeof(up->w)) ||
-               get_user(kp->field, &up->field) ||
-               get_user(kp->chromakey, &up->chromakey) ||
-               get_user(kp->clipcount, &up->clipcount))
-                       return -EFAULT;
-       if (kp->clipcount > 2048)
-               return -EINVAL;
-       if (kp->clipcount) {
-               struct v4l2_clip32 __user *uclips;
-               struct v4l2_clip __user *kclips;
-               int n = kp->clipcount;
-               compat_caddr_t p;
-
-               if (get_user(p, &up->clips))
-                       return -EFAULT;
-               uclips = compat_ptr(p);
-               kclips = compat_alloc_user_space(n * sizeof(struct v4l2_clip));
-               kp->clips = kclips;
-               while (--n >= 0) {
-                       if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
-                               return -EFAULT;
-                       if (put_user(n ? kclips + 1 : NULL, &kclips->next))
-                               return -EFAULT;
-                       uclips += 1;
-                       kclips += 1;
-               }
-       } else
-               kp->clips = NULL;
-       return 0;
-}
-
-static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
-{
-       if (copy_to_user(&up->w, &kp->w, sizeof(kp->w)) ||
-               put_user(kp->field, &up->field) ||
-               put_user(kp->chromakey, &up->chromakey) ||
-               put_user(kp->clipcount, &up->clipcount))
-                       return -EFAULT;
-       return 0;
-}
-
-static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
-{
-       if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format)))
-               return -EFAULT;
-       return 0;
-}
-
-static inline int get_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
-                               struct v4l2_pix_format_mplane __user *up)
-{
-       if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format_mplane)))
+       struct v4l2_clip32 __user *uclips;
+       struct v4l2_clip __user *kclips;
+       compat_caddr_t p;
+       u32 clipcount;
+
+       if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+           copy_in_user(&kp->w, &up->w, sizeof(up->w)) ||
+           assign_in_user(&kp->field, &up->field) ||
+           assign_in_user(&kp->chromakey, &up->chromakey) ||
+           assign_in_user(&kp->global_alpha, &up->global_alpha) ||
+           get_user(clipcount, &up->clipcount) ||
+           put_user(clipcount, &kp->clipcount))
                return -EFAULT;
-       return 0;
-}
+       if (clipcount > 2048)
+               return -EINVAL;
+       if (!clipcount)
+               return put_user(NULL, &kp->clips);
 
-static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
-{
-       if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format)))
+       if (get_user(p, &up->clips))
                return -EFAULT;
-       return 0;
-}
-
-static inline int put_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
-                               struct v4l2_pix_format_mplane __user *up)
-{
-       if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format_mplane)))
+       uclips = compat_ptr(p);
+       if (aux_space < clipcount * sizeof(*kclips))
                return -EFAULT;
-       return 0;
-}
-
-static inline int get_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
-{
-       if (copy_from_user(kp, up, sizeof(struct v4l2_vbi_format)))
+       kclips = aux_buf;
+       if (put_user(kclips, &kp->clips))
                return -EFAULT;
-       return 0;
-}
 
-static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
-{
-       if (copy_to_user(up, kp, sizeof(struct v4l2_vbi_format)))
-               return -EFAULT;
+       while (clipcount--) {
+               if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
+                       return -EFAULT;
+               if (put_user(clipcount ? kclips + 1 : NULL, &kclips->next))
+                       return -EFAULT;
+               uclips++;
+               kclips++;
+       }
        return 0;
 }
 
-static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
+static int put_v4l2_window32(struct v4l2_window __user *kp,
+                            struct v4l2_window32 __user *up)
 {
-       if (copy_from_user(kp, up, sizeof(struct v4l2_sliced_vbi_format)))
+       struct v4l2_clip __user *kclips = kp->clips;
+       struct v4l2_clip32 __user *uclips;
+       compat_caddr_t p;
+       u32 clipcount;
+
+       if (copy_in_user(&up->w, &kp->w, sizeof(kp->w)) ||
+           assign_in_user(&up->field, &kp->field) ||
+           assign_in_user(&up->chromakey, &kp->chromakey) ||
+           assign_in_user(&up->global_alpha, &kp->global_alpha) ||
+           get_user(clipcount, &kp->clipcount) ||
+           put_user(clipcount, &up->clipcount))
                return -EFAULT;
-       return 0;
-}
+       if (!clipcount)
+               return 0;
 
-static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
-{
-       if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format)))
+       if (get_user(p, &up->clips))
                return -EFAULT;
+       uclips = compat_ptr(p);
+       while (clipcount--) {
+               if (copy_in_user(&uclips->c, &kclips->c, sizeof(uclips->c)))
+                       return -EFAULT;
+               uclips++;
+               kclips++;
+       }
        return 0;
 }
 
@@ -176,128 +158,185 @@ struct v4l2_create_buffers32 {
        __u32                   reserved[8];
 };
 
-static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+static int __bufsize_v4l2_format(struct v4l2_format32 __user *up, u32 *size)
 {
-       if (get_user(kp->type, &up->type))
+       u32 type;
+
+       if (get_user(type, &up->type))
+               return -EFAULT;
+
+       switch (type) {
+       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: {
+               u32 clipcount;
+
+               if (get_user(clipcount, &up->fmt.win.clipcount))
+                       return -EFAULT;
+               if (clipcount > 2048)
+                       return -EINVAL;
+               *size = clipcount * sizeof(struct v4l2_clip);
+               return 0;
+       }
+       default:
+               *size = 0;
+               return 0;
+       }
+}
+
+static int bufsize_v4l2_format(struct v4l2_format32 __user *up, u32 *size)
+{
+       if (!access_ok(VERIFY_READ, up, sizeof(*up)))
+               return -EFAULT;
+       return __bufsize_v4l2_format(up, size);
+}
+
+static int __get_v4l2_format32(struct v4l2_format __user *kp,
+                              struct v4l2_format32 __user *up,
+                              void __user *aux_buf, u32 aux_space)
+{
+       u32 type;
+
+       if (get_user(type, &up->type) || put_user(type, &kp->type))
                return -EFAULT;
 
-       switch (kp->type) {
+       switch (type) {
        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
        case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
+               return copy_in_user(&kp->fmt.pix, &up->fmt.pix,
+                                   sizeof(kp->fmt.pix)) ? -EFAULT : 0;
        case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
        case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-               return get_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
-                                                 &up->fmt.pix_mp);
+               return copy_in_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
+                                   sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
        case V4L2_BUF_TYPE_VIDEO_OVERLAY:
        case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-               return get_v4l2_window32(&kp->fmt.win, &up->fmt.win);
+               return get_v4l2_window32(&kp->fmt.win, &up->fmt.win,
+                                        aux_buf, aux_space);
        case V4L2_BUF_TYPE_VBI_CAPTURE:
        case V4L2_BUF_TYPE_VBI_OUTPUT:
-               return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
+               return copy_in_user(&kp->fmt.vbi, &up->fmt.vbi,
+                                   sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
        case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
        case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-               return get_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
-       case V4L2_BUF_TYPE_PRIVATE:
-               if (copy_from_user(kp, up, sizeof(kp->fmt.raw_data)))
-                       return -EFAULT;
-               return 0;
+               return copy_in_user(&kp->fmt.sliced, &up->fmt.sliced,
+                                   sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
        default:
-               printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
-                                                               kp->type);
                return -EINVAL;
        }
 }
 
-static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+static int get_v4l2_format32(struct v4l2_format __user *kp,
+                            struct v4l2_format32 __user *up,
+                            void __user *aux_buf, u32 aux_space)
+{
+       if (!access_ok(VERIFY_READ, up, sizeof(*up)))
+               return -EFAULT;
+       return __get_v4l2_format32(kp, up, aux_buf, aux_space);
+}
+
+static int bufsize_v4l2_create(struct v4l2_create_buffers32 __user *up,
+                              u32 *size)
 {
-       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)))
+       if (!access_ok(VERIFY_READ, up, sizeof(*up)))
                return -EFAULT;
-       return __get_v4l2_format32(kp, up);
+       return __bufsize_v4l2_format(&up->format, size);
 }
 
-static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+static int get_v4l2_create32(struct v4l2_create_buffers __user *kp,
+                            struct v4l2_create_buffers32 __user *up,
+                            void __user *aux_buf, u32 aux_space)
 {
-       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) ||
-           copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format)))
+       if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+           copy_in_user(kp, up,
+                        offsetof(struct v4l2_create_buffers32, format)))
                return -EFAULT;
-       return __get_v4l2_format32(&kp->format, &up->format);
+       return __get_v4l2_format32(&kp->format, &up->format,
+                                  aux_buf, aux_space);
 }
 
-static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+static int __put_v4l2_format32(struct v4l2_format __user *kp,
+                              struct v4l2_format32 __user *up)
 {
-       switch (kp->type) {
+       u32 type;
+
+       if (get_user(type, &kp->type))
+               return -EFAULT;
+
+       switch (type) {
        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
        case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-               return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
+               return copy_in_user(&up->fmt.pix, &kp->fmt.pix,
+                                   sizeof(kp->fmt.pix)) ? -EFAULT : 0;
        case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
        case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-               return put_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
-                                                 &up->fmt.pix_mp);
+               return copy_in_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
+                                   sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
        case V4L2_BUF_TYPE_VIDEO_OVERLAY:
        case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
                return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
        case V4L2_BUF_TYPE_VBI_CAPTURE:
        case V4L2_BUF_TYPE_VBI_OUTPUT:
-               return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
+               return copy_in_user(&up->fmt.vbi, &kp->fmt.vbi,
+                                   sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
        case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
        case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-               return put_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
-       case V4L2_BUF_TYPE_PRIVATE:
-               if (copy_to_user(up, kp, sizeof(up->fmt.raw_data)))
-                       return -EFAULT;
-               return 0;
+               return copy_in_user(&up->fmt.sliced, &kp->fmt.sliced,
+                                   sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
        default:
-               printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
-                                                               kp->type);
                return -EINVAL;
        }
 }
 
-static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+static int put_v4l2_format32(struct v4l2_format __user *kp,
+                            struct v4l2_format32 __user *up)
 {
-       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
-               put_user(kp->type, &up->type))
+       if (!access_ok(VERIFY_WRITE, up, sizeof(*up)))
                return -EFAULT;
        return __put_v4l2_format32(kp, up);
 }
 
-static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+static int put_v4l2_create32(struct v4l2_create_buffers __user *kp,
+                            struct v4l2_create_buffers32 __user *up)
 {
-       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) ||
-           copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format.fmt)))
-                       return -EFAULT;
+       if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
+           copy_in_user(up, kp,
+                        offsetof(struct v4l2_create_buffers32, format)) ||
+           copy_in_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
+               return -EFAULT;
        return __put_v4l2_format32(&kp->format, &up->format);
 }
 
 struct v4l2_standard32 {
        __u32                index;
-       __u32                id[2]; /* __u64 would get the alignment wrong */
+       compat_u64           id;
        __u8                 name[24];
        struct v4l2_fract    frameperiod; /* Frames, not fields */
        __u32                framelines;
        __u32                reserved[4];
 };
 
-static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
+static int get_v4l2_standard32(struct v4l2_standard __user *kp,
+                              struct v4l2_standard32 __user *up)
 {
        /* other fields are not set by the user, nor used by the driver */
-       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_standard32)) ||
-               get_user(kp->index, &up->index))
+       if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+           assign_in_user(&kp->index, &up->index))
                return -EFAULT;
        return 0;
 }
 
-static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
+static int put_v4l2_standard32(struct v4l2_standard __user *kp,
+                              struct v4l2_standard32 __user *up)
 {
-       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) ||
-               put_user(kp->index, &up->index) ||
-               copy_to_user(up->id, &kp->id, sizeof(__u64)) ||
-               copy_to_user(up->name, kp->name, 24) ||
-               copy_to_user(&up->frameperiod, &kp->frameperiod, sizeof(kp->frameperiod)) ||
-               put_user(kp->framelines, &up->framelines) ||
-               copy_to_user(up->reserved, kp->reserved, 4 * sizeof(__u32)))
-                       return -EFAULT;
+       if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
+           assign_in_user(&up->index, &kp->index) ||
+           assign_in_user(&up->id, &kp->id) ||
+           copy_in_user(up->name, kp->name, sizeof(up->name)) ||
+           copy_in_user(&up->frameperiod, &kp->frameperiod,
+                        sizeof(up->frameperiod)) ||
+           assign_in_user(&up->framelines, &kp->framelines) ||
+           copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
+               return -EFAULT;
        return 0;
 }
 
@@ -334,191 +373,242 @@ struct v4l2_buffer32 {
        __u32                   reserved;
 };
 
-static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
-                               enum v4l2_memory memory)
+static int get_v4l2_plane32(struct v4l2_plane __user *up,
+                           struct v4l2_plane32 __user *up32,
+                           enum v4l2_memory memory)
 {
-       void __user *up_pln;
-       compat_long_t p;
+       compat_ulong_t p;
 
        if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
-               copy_in_user(&up->data_offset, &up32->data_offset,
-                               sizeof(__u32)))
+           copy_in_user(&up->data_offset, &up32->data_offset,
+                        sizeof(up->data_offset)))
                return -EFAULT;
 
-       if (memory == V4L2_MEMORY_USERPTR) {
-               if (get_user(p, &up32->m.userptr))
-                       return -EFAULT;
-               up_pln = compat_ptr(p);
-               if (put_user((unsigned long)up_pln, &up->m.userptr))
-                       return -EFAULT;
-       } else {
+       switch (memory) {
+       case V4L2_MEMORY_MMAP:
+       case V4L2_MEMORY_OVERLAY:
                if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
-                                       sizeof(__u32)))
+                                sizeof(up32->m.mem_offset)))
+                       return -EFAULT;
+               break;
+       case V4L2_MEMORY_USERPTR:
+               if (get_user(p, &up32->m.userptr) ||
+                   put_user((unsigned long)compat_ptr(p), &up->m.userptr))
                        return -EFAULT;
+               break;
        }
 
        return 0;
 }
 
-static int put_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
-                               enum v4l2_memory memory)
+static int put_v4l2_plane32(struct v4l2_plane __user *up,
+                           struct v4l2_plane32 __user *up32,
+                           enum v4l2_memory memory)
 {
+       unsigned long p;
+
        if (copy_in_user(up32, up, 2 * sizeof(__u32)) ||
-               copy_in_user(&up32->data_offset, &up->data_offset,
-                               sizeof(__u32)))
+           copy_in_user(&up32->data_offset, &up->data_offset,
+                        sizeof(up->data_offset)))
                return -EFAULT;
 
-       /* For MMAP, driver might've set up the offset, so copy it back.
-        * USERPTR stays the same (was userspace-provided), so no copying. */
-       if (memory == V4L2_MEMORY_MMAP)
+       switch (memory) {
+       case V4L2_MEMORY_MMAP:
+       case V4L2_MEMORY_OVERLAY:
                if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset,
-                                       sizeof(__u32)))
+                                sizeof(up->m.mem_offset)))
+                       return -EFAULT;
+               break;
+       case V4L2_MEMORY_USERPTR:
+               if (get_user(p, &up->m.userptr) ||
+                   put_user((compat_ulong_t)ptr_to_compat((__force void *)p),
+                            &up32->m.userptr))
                        return -EFAULT;
+               break;
+       }
 
        return 0;
 }
 
-static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
+static int bufsize_v4l2_buffer(struct v4l2_buffer32 __user *up, u32 *size)
 {
+       u32 type;
+       u32 length;
+
+       if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+           get_user(type, &up->type) ||
+           get_user(length, &up->length))
+               return -EFAULT;
+
+       if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
+               if (length > VIDEO_MAX_PLANES)
+                       return -EINVAL;
+
+               /*
+                * We don't really care if userspace decides to kill itself
+                * by passing a very big length value
+                */
+               *size = length * sizeof(struct v4l2_plane);
+       } else {
+               *size = 0;
+       }
+       return 0;
+}
+
+static int get_v4l2_buffer32(struct v4l2_buffer __user *kp,
+                            struct v4l2_buffer32 __user *up,
+                            void __user *aux_buf, u32 aux_space)
+{
+       u32 type;
+       u32 length;
+       enum v4l2_memory memory;
        struct v4l2_plane32 __user *uplane32;
        struct v4l2_plane __user *uplane;
        compat_caddr_t p;
-       int num_planes;
        int ret;
 
-       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) ||
-               get_user(kp->index, &up->index) ||
-               get_user(kp->type, &up->type) ||
-               get_user(kp->flags, &up->flags) ||
-               get_user(kp->memory, &up->memory) ||
-               get_user(kp->input, &up->input))
-                       return -EFAULT;
+       if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+           assign_in_user(&kp->index, &up->index) ||
+           get_user(type, &up->type) ||
+           put_user(type, &kp->type) ||
+           assign_in_user(&kp->flags, &up->flags) ||
+           get_user(memory, &up->memory) ||
+           put_user(memory, &kp->memory) ||
+           get_user(length, &up->length) ||
+           put_user(length, &kp->length))
+               return -EFAULT;
 
-       if (V4L2_TYPE_IS_OUTPUT(kp->type))
-               if (get_user(kp->bytesused, &up->bytesused) ||
-                       get_user(kp->field, &up->field) ||
-                       get_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
-                       get_user(kp->timestamp.tv_usec,
-                                       &up->timestamp.tv_usec))
+       if (V4L2_TYPE_IS_OUTPUT(type))
+               if (assign_in_user(&kp->bytesused, &up->bytesused) ||
+                   assign_in_user(&kp->field, &up->field) ||
+                   assign_in_user(&kp->timestamp.tv_sec,
+                                  &up->timestamp.tv_sec) ||
+                   assign_in_user(&kp->timestamp.tv_usec,
+                                  &up->timestamp.tv_usec))
                        return -EFAULT;
 
-       if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
-               if (get_user(kp->length, &up->length))
-                       return -EFAULT;
+       if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
+               u32 num_planes = length;
 
-               num_planes = kp->length;
                if (num_planes == 0) {
-                       kp->m.planes = NULL;
-                       /* num_planes == 0 is legal, e.g. when userspace doesn't
-                        * need planes array on DQBUF*/
-                       return 0;
+                       /*
+                        * num_planes == 0 is legal, e.g. when userspace doesn't
+                        * need planes array on DQBUF
+                        */
+                       return put_user(NULL, &kp->m.planes);
                }
+               if (num_planes > VIDEO_MAX_PLANES)
+                       return -EINVAL;
 
                if (get_user(p, &up->m.planes))
                        return -EFAULT;
 
                uplane32 = compat_ptr(p);
                if (!access_ok(VERIFY_READ, uplane32,
-                               num_planes * sizeof(struct v4l2_plane32)))
+                              num_planes * sizeof(*uplane32)))
+                       return -EFAULT;
+
+               /*
+                * We don't really care if userspace decides to kill itself
+                * by passing a very big num_planes value
+                */
+               if (aux_space < num_planes * sizeof(*uplane))
                        return -EFAULT;
 
-               /* We don't really care if userspace decides to kill itself
-                * by passing a very big num_planes value */
-               uplane = compat_alloc_user_space(num_planes *
-                                               sizeof(struct v4l2_plane));
-               kp->m.planes = uplane;
+               uplane = aux_buf;
+               if (put_user((__force struct v4l2_plane *)uplane,
+                            &kp->m.planes))
+                       return -EFAULT;
 
-               while (--num_planes >= 0) {
-                       ret = get_v4l2_plane32(uplane, uplane32, kp->memory);
+               while (num_planes--) {
+                       ret = get_v4l2_plane32(uplane, uplane32, memory);
                        if (ret)
                                return ret;
-                       ++uplane;
-                       ++uplane32;
+                       uplane++;
+                       uplane32++;
                }
        } else {
-               switch (kp->memory) {
+               switch (memory) {
                case V4L2_MEMORY_MMAP:
-                       if (get_user(kp->length, &up->length) ||
-                               get_user(kp->m.offset, &up->m.offset))
+               case V4L2_MEMORY_OVERLAY:
+                       if (assign_in_user(&kp->m.offset, &up->m.offset))
                                return -EFAULT;
                        break;
-               case V4L2_MEMORY_USERPTR:
-                       {
-                       compat_long_t tmp;
+               case V4L2_MEMORY_USERPTR: {
+                       compat_ulong_t userptr;
 
-                       if (get_user(kp->length, &up->length) ||
-                           get_user(tmp, &up->m.userptr))
-                               return -EFAULT;
-
-                       kp->m.userptr = (unsigned long)compat_ptr(tmp);
-                       }
-                       break;
-               case V4L2_MEMORY_OVERLAY:
-                       if (get_user(kp->m.offset, &up->m.offset))
+                       if (get_user(userptr, &up->m.userptr) ||
+                           put_user((unsigned long)compat_ptr(userptr),
+                                    &kp->m.userptr))
                                return -EFAULT;
                        break;
                }
+               }
        }
 
        return 0;
 }
 
-static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
+static int put_v4l2_buffer32(struct v4l2_buffer __user *kp,
+                            struct v4l2_buffer32 __user *up)
 {
+       u32 type;
+       u32 length;
+       enum v4l2_memory memory;
        struct v4l2_plane32 __user *uplane32;
        struct v4l2_plane __user *uplane;
        compat_caddr_t p;
-       int num_planes;
        int ret;
 
-       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_buffer32)) ||
-               put_user(kp->index, &up->index) ||
-               put_user(kp->type, &up->type) ||
-               put_user(kp->flags, &up->flags) ||
-               put_user(kp->memory, &up->memory) ||
-               put_user(kp->input, &up->input))
-                       return -EFAULT;
+       if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
+           assign_in_user(&up->index, &kp->index) ||
+           get_user(type, &kp->type) ||
+           put_user(type, &up->type) ||
+           assign_in_user(&up->flags, &kp->flags) ||
+           get_user(memory, &kp->memory) ||
+           put_user(memory, &up->memory))
+               return -EFAULT;
 
-       if (put_user(kp->bytesused, &up->bytesused) ||
-               put_user(kp->field, &up->field) ||
-               put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
-               put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) ||
-               copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) ||
-               put_user(kp->sequence, &up->sequence) ||
-               put_user(kp->reserved, &up->reserved))
-                       return -EFAULT;
+       if (assign_in_user(&up->bytesused, &kp->bytesused) ||
+           assign_in_user(&up->field, &kp->field) ||
+           assign_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) ||
+           assign_in_user(&up->timestamp.tv_usec, &kp->timestamp.tv_usec) ||
+           copy_in_user(&up->timecode, &kp->timecode, sizeof(kp->timecode)) ||
+           assign_in_user(&up->sequence, &kp->sequence) ||
+           assign_in_user(&up->input, &kp->input) ||
+           assign_in_user(&up->reserved, &kp->reserved) ||
+           get_user(length, &kp->length) ||
+           put_user(length, &up->length))
+               return -EFAULT;
+
+       if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
+               u32 num_planes = length;
 
-       if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
-               num_planes = kp->length;
                if (num_planes == 0)
                        return 0;
 
-               uplane = kp->m.planes;
+               if (get_user(uplane, ((__force struct v4l2_plane __user **)&kp->m.planes)))
+                       return -EFAULT;
                if (get_user(p, &up->m.planes))
                        return -EFAULT;
                uplane32 = compat_ptr(p);
 
-               while (--num_planes >= 0) {
-                       ret = put_v4l2_plane32(uplane, uplane32, kp->memory);
+               while (num_planes--) {
+                       ret = put_v4l2_plane32(uplane, uplane32, memory);
                        if (ret)
                                return ret;
                        ++uplane;
                        ++uplane32;
                }
        } else {
-               switch (kp->memory) {
+               switch (memory) {
                case V4L2_MEMORY_MMAP:
-                       if (put_user(kp->length, &up->length) ||
-                               put_user(kp->m.offset, &up->m.offset))
+               case V4L2_MEMORY_OVERLAY:
+                       if (assign_in_user(&up->m.offset, &kp->m.offset))
                                return -EFAULT;
                        break;
                case V4L2_MEMORY_USERPTR:
-                       if (put_user(kp->length, &up->length) ||
-                               put_user(kp->m.userptr, &up->m.userptr))
-                               return -EFAULT;
-                       break;
-               case V4L2_MEMORY_OVERLAY:
-                       if (put_user(kp->m.offset, &up->m.offset))
+                       if (assign_in_user(&up->m.userptr, &kp->m.userptr))
                                return -EFAULT;
                        break;
                }
@@ -530,34 +620,46 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
 struct v4l2_framebuffer32 {
        __u32                   capability;
        __u32                   flags;
-       compat_caddr_t          base;
-       struct v4l2_pix_format  fmt;
+       compat_caddr_t          base;
+       struct {
+               __u32           width;
+               __u32           height;
+               __u32           pixelformat;
+               __u32           field;
+               __u32           bytesperline;
+               __u32           sizeimage;
+               __u32           colorspace;
+               __u32           priv;
+       } fmt;
 };
 
-static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
+static int get_v4l2_framebuffer32(struct v4l2_framebuffer __user *kp,
+                                 struct v4l2_framebuffer32 __user *up)
 {
-       u32 tmp;
-
-       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) ||
-               get_user(tmp, &up->base) ||
-               get_user(kp->capability, &up->capability) ||
-               get_user(kp->flags, &up->flags))
-                       return -EFAULT;
-       kp->base = compat_ptr(tmp);
-       get_v4l2_pix_format(&kp->fmt, &up->fmt);
+       compat_caddr_t tmp;
+
+       if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+           get_user(tmp, &up->base) ||
+           put_user((__force void *)compat_ptr(tmp), &kp->base) ||
+           assign_in_user(&kp->capability, &up->capability) ||
+           assign_in_user(&kp->flags, &up->flags) ||
+           copy_in_user(&kp->fmt, &up->fmt, sizeof(kp->fmt)))
+               return -EFAULT;
        return 0;
 }
 
-static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
+static int put_v4l2_framebuffer32(struct v4l2_framebuffer __user *kp,
+                                 struct v4l2_framebuffer32 __user *up)
 {
-       u32 tmp = (u32)((unsigned long)kp->base);
-
-       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) ||
-               put_user(tmp, &up->base) ||
-               put_user(kp->capability, &up->capability) ||
-               put_user(kp->flags, &up->flags))
-                       return -EFAULT;
-       put_v4l2_pix_format(&kp->fmt, &up->fmt);
+       void *base;
+
+       if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
+           get_user(base, &kp->base) ||
+           put_user(ptr_to_compat(base), &up->base) ||
+           assign_in_user(&up->capability, &kp->capability) ||
+           assign_in_user(&up->flags, &kp->flags) ||
+           copy_in_user(&up->fmt, &kp->fmt, sizeof(kp->fmt)))
+               return -EFAULT;
        return 0;
 }
 
@@ -567,33 +669,38 @@ struct v4l2_input32 {
        __u32        type;              /*  Type of input */
        __u32        audioset;          /*  Associated audios (bitfield) */
        __u32        tuner;             /*  Associated tuner */
-       v4l2_std_id  std;
+       compat_u64   std;
        __u32        status;
-       __u32        reserved[4];
-} __attribute__ ((packed));
+       __u32        capabilities;
+       __u32        reserved[3];
+};
 
-/* The 64-bit v4l2_input struct has extra padding at the end of the struct.
-   Otherwise it is identical to the 32-bit version. */
-static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
+/*
+ * The 64-bit v4l2_input struct has extra padding at the end of the struct.
+ * Otherwise it is identical to the 32-bit version.
+ */
+static inline int get_v4l2_input32(struct v4l2_input __user *kp,
+                                  struct v4l2_input32 __user *up)
 {
-       if (copy_from_user(kp, up, sizeof(struct v4l2_input32)))
+       if (copy_in_user(kp, up, sizeof(*up)))
                return -EFAULT;
        return 0;
 }
 
-static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
+static inline int put_v4l2_input32(struct v4l2_input __user *kp,
+                                  struct v4l2_input32 __user *up)
 {
-       if (copy_to_user(up, kp, sizeof(struct v4l2_input32)))
+       if (copy_in_user(up, kp, sizeof(*up)))
                return -EFAULT;
        return 0;
 }
 
 struct v4l2_ext_controls32 {
-       __u32 ctrl_class;
-       __u32 count;
-       __u32 error_idx;
-       __u32 reserved[2];
-       compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */
+       __u32 ctrl_class;
+       __u32 count;
+       __u32 error_idx;
+       __u32 reserved[2];
+       compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */
 };
 
 struct v4l2_ext_control32 {
@@ -607,53 +714,88 @@ struct v4l2_ext_control32 {
        };
 } __attribute__ ((packed));
 
-/* The following function really belong in v4l2-common, but that causes
-   a circular dependency between modules. We need to think about this, but
-   for now this will do. */
-
-/* Return non-zero if this control is a pointer type. Currently only
-   type STRING is a pointer type. */
-static inline int ctrl_is_pointer(u32 id)
+/* Return true if this control is a pointer type. */
+static inline bool ctrl_is_pointer(struct file *file, u32 id)
 {
-       switch (id) {
-       case V4L2_CID_RDS_TX_PS_NAME:
-       case V4L2_CID_RDS_TX_RADIO_TEXT:
-               return 1;
-       default:
-               return 0;
+       struct video_device *vdev = video_devdata(file);
+       struct v4l2_fh *fh = NULL;
+       struct v4l2_ctrl_handler *hdl = NULL;
+
+       if (test_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags))
+               fh = file->private_data;
+
+       if (fh && fh->ctrl_handler)
+               hdl = fh->ctrl_handler;
+       else if (vdev->ctrl_handler)
+               hdl = vdev->ctrl_handler;
+
+       if (hdl) {
+               struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, id);
+
+               return ctrl && ctrl->type == V4L2_CTRL_TYPE_STRING;
        }
+       return false;
+}
+
+static int bufsize_v4l2_ext_controls(struct v4l2_ext_controls32 __user *up,
+                                    u32 *size)
+{
+       u32 count;
+
+       if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+           get_user(count, &up->count))
+               return -EFAULT;
+       if (count > V4L2_CID_MAX_CTRLS)
+               return -EINVAL;
+       *size = count * sizeof(struct v4l2_ext_control);
+       return 0;
 }
 
-static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
+static int get_v4l2_ext_controls32(struct file *file,
+                                  struct v4l2_ext_controls __user *kp,
+                                  struct v4l2_ext_controls32 __user *up,
+                                  void __user *aux_buf, u32 aux_space)
 {
        struct v4l2_ext_control32 __user *ucontrols;
        struct v4l2_ext_control __user *kcontrols;
-       int n;
+       u32 count;
+       u32 n;
        compat_caddr_t p;
 
-       if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) ||
-               get_user(kp->ctrl_class, &up->ctrl_class) ||
-               get_user(kp->count, &up->count) ||
-               get_user(kp->error_idx, &up->error_idx) ||
-               copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
-                       return -EFAULT;
-       n = kp->count;
-       if (n == 0) {
-               kp->controls = NULL;
-               return 0;
-       }
+       if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+           assign_in_user(&kp->ctrl_class, &up->ctrl_class) ||
+           get_user(count, &up->count) ||
+           put_user(count, &kp->count) ||
+           assign_in_user(&kp->error_idx, &up->error_idx) ||
+           copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
+               return -EFAULT;
+
+       if (count == 0)
+               return put_user(NULL, &kp->controls);
+       if (count > V4L2_CID_MAX_CTRLS)
+               return -EINVAL;
        if (get_user(p, &up->controls))
                return -EFAULT;
        ucontrols = compat_ptr(p);
-       if (!access_ok(VERIFY_READ, ucontrols,
-                       n * sizeof(struct v4l2_ext_control32)))
+       if (!access_ok(VERIFY_READ, ucontrols, count * sizeof(*ucontrols)))
                return -EFAULT;
-       kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control));
-       kp->controls = kcontrols;
-       while (--n >= 0) {
+       if (aux_space < count * sizeof(*kcontrols))
+               return -EFAULT;
+       kcontrols = aux_buf;
+       if (put_user((__force struct v4l2_ext_control *)kcontrols,
+                    &kp->controls))
+               return -EFAULT;
+
+       for (n = 0; n < count; n++) {
+               u32 id;
+
                if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols)))
                        return -EFAULT;
-               if (ctrl_is_pointer(kcontrols->id)) {
+
+               if (get_user(id, &kcontrols->id))
+                       return -EFAULT;
+
+               if (ctrl_is_pointer(file, id)) {
                        void __user *s;
 
                        if (get_user(p, &ucontrols->string))
@@ -668,39 +810,55 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
        return 0;
 }
 
-static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
+static int put_v4l2_ext_controls32(struct file *file,
+                                  struct v4l2_ext_controls __user *kp,
+                                  struct v4l2_ext_controls32 __user *up)
 {
        struct v4l2_ext_control32 __user *ucontrols;
-       struct v4l2_ext_control __user *kcontrols = kp->controls;
-       int n = kp->count;
+       struct v4l2_ext_control __user *kcontrols;
+       u32 count;
+       u32 n;
        compat_caddr_t p;
 
-       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) ||
-               put_user(kp->ctrl_class, &up->ctrl_class) ||
-               put_user(kp->count, &up->count) ||
-               put_user(kp->error_idx, &up->error_idx) ||
-               copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved)))
-                       return -EFAULT;
-       if (!kp->count)
-               return 0;
+       if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
+           assign_in_user(&up->ctrl_class, &kp->ctrl_class) ||
+           get_user(count, &kp->count) ||
+           put_user(count, &up->count) ||
+           assign_in_user(&up->error_idx, &kp->error_idx) ||
+           copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)) ||
+           get_user(kcontrols, &kp->controls))
+               return -EFAULT;
 
+       if (!count)
+               return 0;
        if (get_user(p, &up->controls))
                return -EFAULT;
        ucontrols = compat_ptr(p);
-       if (!access_ok(VERIFY_WRITE, ucontrols,
-                       n * sizeof(struct v4l2_ext_control32)))
+       if (!access_ok(VERIFY_WRITE, ucontrols, count * sizeof(*ucontrols)))
                return -EFAULT;
 
-       while (--n >= 0) {
-               unsigned size = sizeof(*ucontrols);
+       for (n = 0; n < count; n++) {
+               unsigned int size = sizeof(*ucontrols);
+               u32 id;
 
-               /* Do not modify the pointer when copying a pointer control.
-                  The contents of the pointer was changed, not the pointer
-                  itself. */
-               if (ctrl_is_pointer(kcontrols->id))
+               if (get_user(id, &kcontrols->id) ||
+                   put_user(id, &ucontrols->id) ||
+                   assign_in_user(&ucontrols->size, &kcontrols->size) ||
+                   copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2,
+                                sizeof(ucontrols->reserved2)))
+                       return -EFAULT;
+
+               /*
+                * Do not modify the pointer when copying a pointer control.
+                * The contents of the pointer was changed, not the pointer
+                * itself.
+                */
+               if (ctrl_is_pointer(file, id))
                        size -= sizeof(ucontrols->value64);
+
                if (copy_in_user(ucontrols, kcontrols, size))
                        return -EFAULT;
+
                ucontrols++;
                kcontrols++;
        }
@@ -710,6 +868,7 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
 struct v4l2_event32 {
        __u32                           type;
        union {
+               compat_s64              value64;
                __u8                    data[64];
        } u;
        __u32                           pending;
@@ -719,17 +878,19 @@ struct v4l2_event32 {
        __u32                           reserved[8];
 };
 
-static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *up)
+static int put_v4l2_event32(struct v4l2_event __user *kp,
+                           struct v4l2_event32 __user *up)
 {
-       if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_event32)) ||
-               put_user(kp->type, &up->type) ||
-               copy_to_user(&up->u, &kp->u, sizeof(kp->u)) ||
-               put_user(kp->pending, &up->pending) ||
-               put_user(kp->sequence, &up->sequence) ||
-               put_compat_timespec(&kp->timestamp, &up->timestamp) ||
-               put_user(kp->id, &up->id) ||
-               copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
-                       return -EFAULT;
+       if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
+           assign_in_user(&up->type, &kp->type) ||
+           copy_in_user(&up->u, &kp->u, sizeof(kp->u)) ||
+           assign_in_user(&up->pending, &kp->pending) ||
+           assign_in_user(&up->sequence, &kp->sequence) ||
+           assign_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) ||
+           assign_in_user(&up->timestamp.tv_nsec, &kp->timestamp.tv_nsec) ||
+           assign_in_user(&up->id, &kp->id) ||
+           copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
+               return -EFAULT;
        return 0;
 }
 
@@ -742,7 +903,7 @@ static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *u
 #define VIDIOC_DQBUF32         _IOWR('V', 17, struct v4l2_buffer32)
 #define VIDIOC_ENUMSTD32       _IOWR('V', 25, struct v4l2_standard32)
 #define VIDIOC_ENUMINPUT32     _IOWR('V', 26, struct v4l2_input32)
-#define VIDIOC_TRY_FMT32       _IOWR('V', 64, struct v4l2_format32)
+#define VIDIOC_TRY_FMT32       _IOWR('V', 64, struct v4l2_format32)
 #define VIDIOC_G_EXT_CTRLS32    _IOWR('V', 71, struct v4l2_ext_controls32)
 #define VIDIOC_S_EXT_CTRLS32    _IOWR('V', 72, struct v4l2_ext_controls32)
 #define VIDIOC_TRY_EXT_CTRLS32  _IOWR('V', 73, struct v4l2_ext_controls32)
@@ -758,21 +919,23 @@ static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *u
 #define VIDIOC_G_OUTPUT32      _IOR ('V', 46, s32)
 #define VIDIOC_S_OUTPUT32      _IOWR('V', 47, s32)
 
+static int alloc_userspace(unsigned int size, u32 aux_space,
+                          void __user **up_native)
+{
+       *up_native = compat_alloc_user_space(size + aux_space);
+       if (!*up_native)
+               return -ENOMEM;
+       if (clear_user(*up_native, size))
+               return -EFAULT;
+       return 0;
+}
+
 static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-       union {
-               struct v4l2_format v2f;
-               struct v4l2_buffer v2b;
-               struct v4l2_framebuffer v2fb;
-               struct v4l2_input v2i;
-               struct v4l2_standard v2s;
-               struct v4l2_ext_controls v2ecs;
-               struct v4l2_event v2ev;
-               struct v4l2_create_buffers v2crt;
-               unsigned long vx;
-               int vi;
-       } karg;
        void __user *up = compat_ptr(arg);
+       void __user *up_native = NULL;
+       void __user *aux_buf;
+       u32 aux_space;
        int compatible_arg = 1;
        long err = 0;
 
@@ -809,24 +972,44 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
        case VIDIOC_STREAMOFF:
        case VIDIOC_S_INPUT:
        case VIDIOC_S_OUTPUT:
-               err = get_user(karg.vi, (s32 __user *)up);
+               err = alloc_userspace(sizeof(unsigned int), 0, &up_native);
+               if (!err && assign_in_user((unsigned int __user *)up_native,
+                                          (compat_uint_t __user *)up))
+                       err = -EFAULT;
                compatible_arg = 0;
                break;
 
        case VIDIOC_G_INPUT:
        case VIDIOC_G_OUTPUT:
+               err = alloc_userspace(sizeof(unsigned int), 0, &up_native);
                compatible_arg = 0;
                break;
 
        case VIDIOC_G_FMT:
        case VIDIOC_S_FMT:
        case VIDIOC_TRY_FMT:
-               err = get_v4l2_format32(&karg.v2f, up);
+               err = bufsize_v4l2_format(up, &aux_space);
+               if (!err)
+                       err = alloc_userspace(sizeof(struct v4l2_format),
+                                             aux_space, &up_native);
+               if (!err) {
+                       aux_buf = up_native + sizeof(struct v4l2_format);
+                       err = get_v4l2_format32(up_native, up,
+                                               aux_buf, aux_space);
+               }
                compatible_arg = 0;
                break;
 
        case VIDIOC_CREATE_BUFS:
-               err = get_v4l2_create32(&karg.v2crt, up);
+               err = bufsize_v4l2_create(up, &aux_space);
+               if (!err)
+                       err = alloc_userspace(sizeof(struct v4l2_create_buffers),
+                                             aux_space, &up_native);
+               if (!err) {
+                       aux_buf = up_native + sizeof(struct v4l2_create_buffers);
+                       err = get_v4l2_create32(up_native, up,
+                                               aux_buf, aux_space);
+               }
                compatible_arg = 0;
                break;
 
@@ -834,36 +1017,63 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
        case VIDIOC_QUERYBUF:
        case VIDIOC_QBUF:
        case VIDIOC_DQBUF:
-               err = get_v4l2_buffer32(&karg.v2b, up);
+               err = bufsize_v4l2_buffer(up, &aux_space);
+               if (!err)
+                       err = alloc_userspace(sizeof(struct v4l2_buffer),
+                                             aux_space, &up_native);
+               if (!err) {
+                       aux_buf = up_native + sizeof(struct v4l2_buffer);
+                       err = get_v4l2_buffer32(up_native, up,
+                                               aux_buf, aux_space);
+               }
                compatible_arg = 0;
                break;
 
        case VIDIOC_S_FBUF:
-               err = get_v4l2_framebuffer32(&karg.v2fb, up);
+               err = alloc_userspace(sizeof(struct v4l2_framebuffer), 0,
+                                     &up_native);
+               if (!err)
+                       err = get_v4l2_framebuffer32(up_native, up);
                compatible_arg = 0;
                break;
 
        case VIDIOC_G_FBUF:
+               err = alloc_userspace(sizeof(struct v4l2_framebuffer), 0,
+                                     &up_native);
                compatible_arg = 0;
                break;
 
        case VIDIOC_ENUMSTD:
-               err = get_v4l2_standard32(&karg.v2s, up);
+               err = alloc_userspace(sizeof(struct v4l2_standard), 0,
+                                     &up_native);
+               if (!err)
+                       err = get_v4l2_standard32(up_native, up);
                compatible_arg = 0;
                break;
 
        case VIDIOC_ENUMINPUT:
-               err = get_v4l2_input32(&karg.v2i, up);
+               err = alloc_userspace(sizeof(struct v4l2_input), 0, &up_native);
+               if (!err)
+                       err = get_v4l2_input32(up_native, up);
                compatible_arg = 0;
                break;
 
        case VIDIOC_G_EXT_CTRLS:
        case VIDIOC_S_EXT_CTRLS:
        case VIDIOC_TRY_EXT_CTRLS:
-               err = get_v4l2_ext_controls32(&karg.v2ecs, up);
+               err = bufsize_v4l2_ext_controls(up, &aux_space);
+               if (!err)
+                       err = alloc_userspace(sizeof(struct v4l2_ext_controls),
+                                             aux_space, &up_native);
+               if (!err) {
+                       aux_buf = up_native + sizeof(struct v4l2_ext_controls);
+                       err = get_v4l2_ext_controls32(file, up_native, up,
+                                                     aux_buf, aux_space);
+               }
                compatible_arg = 0;
                break;
        case VIDIOC_DQEVENT:
+               err = alloc_userspace(sizeof(struct v4l2_event), 0, &up_native);
                compatible_arg = 0;
                break;
        }
@@ -872,22 +1082,22 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
 
        if (compatible_arg)
                err = native_ioctl(file, cmd, (unsigned long)up);
-       else {
-               mm_segment_t old_fs = get_fs();
+       else
+               err = native_ioctl(file, cmd, (unsigned long)up_native);
 
-               set_fs(KERNEL_DS);
-               err = native_ioctl(file, cmd, (unsigned long)&karg);
-               set_fs(old_fs);
-       }
+       if (err == -ENOTTY)
+               return err;
 
-       /* Special case: even after an error we need to put the
-          results back for these ioctls since the error_idx will
-          contain information on which control failed. */
+       /*
+        * Special case: even after an error we need to put the
+        * results back for these ioctls since the error_idx will
+        * contain information on which control failed.
+        */
        switch (cmd) {
        case VIDIOC_G_EXT_CTRLS:
        case VIDIOC_S_EXT_CTRLS:
        case VIDIOC_TRY_EXT_CTRLS:
-               if (put_v4l2_ext_controls32(&karg.v2ecs, up))
+               if (put_v4l2_ext_controls32(file, up_native, up))
                        err = -EFAULT;
                break;
        }
@@ -899,39 +1109,42 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
        case VIDIOC_S_OUTPUT:
        case VIDIOC_G_INPUT:
        case VIDIOC_G_OUTPUT:
-               err = put_user(((s32)karg.vi), (s32 __user *)up);
+               if (assign_in_user((compat_uint_t __user *)up,
+                                  ((unsigned int __user *)up_native)))
+                       err = -EFAULT;
                break;
 
        case VIDIOC_G_FBUF:
-               err = put_v4l2_framebuffer32(&karg.v2fb, up);
+               err = put_v4l2_framebuffer32(up_native, up);
                break;
 
        case VIDIOC_DQEVENT:
-               err = put_v4l2_event32(&karg.v2ev, up);
+               err = put_v4l2_event32(up_native, up);
                break;
 
        case VIDIOC_G_FMT:
        case VIDIOC_S_FMT:
        case VIDIOC_TRY_FMT:
-               err = put_v4l2_format32(&karg.v2f, up);
+               err = put_v4l2_format32(up_native, up);
                break;
 
        case VIDIOC_CREATE_BUFS:
-               err = put_v4l2_create32(&karg.v2crt, up);
+               err = put_v4l2_create32(up_native, up);
                break;
 
+       case VIDIOC_PREPARE_BUF:
        case VIDIOC_QUERYBUF:
        case VIDIOC_QBUF:
        case VIDIOC_DQBUF:
-               err = put_v4l2_buffer32(&karg.v2b, up);
+               err = put_v4l2_buffer32(up_native, up);
                break;
 
        case VIDIOC_ENUMSTD:
-               err = put_v4l2_standard32(&karg.v2s, up);
+               err = put_v4l2_standard32(up_native, up);
                break;
 
        case VIDIOC_ENUMINPUT:
-               err = put_v4l2_input32(&karg.v2i, up);
+               err = put_v4l2_input32(up_native, up);
                break;
        }
        return err;
@@ -1026,8 +1239,8 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
 
        default:
                printk(KERN_WARNING "compat_ioctl32: "
-                       "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n",
-                       _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd), cmd);
+                      "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n",
+                      _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd), cmd);
                break;
        }
        return ret;