V4L/DVB (13331): mxl5005s: provide ability to override QAM gain for HVR-1600
[safe/jmp/linux-2.6] / drivers / media / video / v4l2-compat-ioctl32.c
index 0056b11..997975d 100644 (file)
@@ -600,9 +600,37 @@ struct v4l2_ext_controls32 {
        compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */
 };
 
+struct v4l2_ext_control32 {
+       __u32 id;
+       __u32 size;
+       __u32 reserved2[1];
+       union {
+               __s32 value;
+               __s64 value64;
+               compat_caddr_t string; /* actually char * */
+       };
+} __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)
+{
+       switch (id) {
+       case V4L2_CID_RDS_TX_PS_NAME:
+       case V4L2_CID_RDS_TX_RADIO_TEXT:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
 static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
 {
-       struct v4l2_ext_control __user *ucontrols;
+       struct v4l2_ext_control32 __user *ucontrols;
        struct v4l2_ext_control __user *kcontrols;
        int n;
        compat_caddr_t p;
@@ -626,15 +654,17 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
        kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control));
        kp->controls = kcontrols;
        while (--n >= 0) {
-               if (copy_in_user(&kcontrols->id, &ucontrols->id, sizeof(__u32)))
-                       return -EFAULT;
-               if (copy_in_user(&kcontrols->reserved2, &ucontrols->reserved2, sizeof(ucontrols->reserved2)))
-                       return -EFAULT;
-               /* Note: if the void * part of the union ever becomes relevant
-                  then we need to know the type of the control in order to do
-                  the right thing here. Luckily, that is not yet an issue. */
-               if (copy_in_user(&kcontrols->value, &ucontrols->value, sizeof(ucontrols->value)))
+               if (copy_in_user(kcontrols, ucontrols, sizeof(*kcontrols)))
                        return -EFAULT;
+               if (ctrl_is_pointer(kcontrols->id)) {
+                       void __user *s;
+
+                       if (get_user(p, &ucontrols->string))
+                               return -EFAULT;
+                       s = compat_ptr(p);
+                       if (put_user(s, &kcontrols->string))
+                               return -EFAULT;
+               }
                ucontrols++;
                kcontrols++;
        }
@@ -643,7 +673,7 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
 
 static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
 {
-       struct v4l2_ext_control __user *ucontrols;
+       struct v4l2_ext_control32 __user *ucontrols;
        struct v4l2_ext_control __user *kcontrols = kp->controls;
        int n = kp->count;
        compat_caddr_t p;
@@ -664,15 +694,14 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
                return -EFAULT;
 
        while (--n >= 0) {
-               if (copy_in_user(&ucontrols->id, &kcontrols->id, sizeof(__u32)))
-                       return -EFAULT;
-               if (copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2,
-                                       sizeof(ucontrols->reserved2)))
-                       return -EFAULT;
-               /* Note: if the void * part of the union ever becomes relevant
-                  then we need to know the type of the control in order to do
-                  the right thing here. Luckily, that is not yet an issue. */
-               if (copy_in_user(&ucontrols->value, &kcontrols->value, sizeof(ucontrols->value)))
+               unsigned size = sizeof(*ucontrols);
+
+               /* 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))
+                       size -= sizeof(ucontrols->value64);
+               if (copy_in_user(ucontrols, kcontrols, size))
                        return -EFAULT;
                ucontrols++;
                kcontrols++;