V4L/DVB (12543): v4l: introduce string control support.
authorHans Verkuil <hverkuil@xs4all.nl>
Tue, 11 Aug 2009 21:47:18 +0000 (18:47 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Sat, 12 Sep 2009 15:19:17 +0000 (12:19 -0300)
The upcoming RDS encoder needs support for string controls. This patch
implements the core implementation.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/v4l2-common.c
drivers/media/video/v4l2-compat-ioctl32.c
drivers/media/video/v4l2-ioctl.c
include/linux/videodev2.h

index b91d66a..536150c 100644 (file)
@@ -156,6 +156,8 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
                return -EINVAL;
        if (qctrl->flags & V4L2_CTRL_FLAG_GRABBED)
                return -EBUSY;
+       if (qctrl->type == V4L2_CTRL_TYPE_STRING)
+               return 0;
        if (qctrl->type == V4L2_CTRL_TYPE_BUTTON ||
            qctrl->type == V4L2_CTRL_TYPE_INTEGER64 ||
            qctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
index 0056b11..f788c41 100644 (file)
@@ -600,9 +600,35 @@ 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.
+ *
+ * Note that there are currently no controls of this type, but at least the
+ * compat32 code is in place to properly handle such controls. Please
+ * remove this note once the first pointer controls are added. */
+static inline int ctrl_is_pointer(u32 id)
+{
+       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 +652,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 +671,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 +692,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++;
index c32e676..30cc334 100644 (file)
@@ -513,11 +513,12 @@ static inline void v4l_print_ext_ctrls(unsigned int cmd,
        dbgarg(cmd, "");
        printk(KERN_CONT "class=0x%x", c->ctrl_class);
        for (i = 0; i < c->count; i++) {
-               if (show_vals)
+               if (show_vals && !c->controls[i].size)
                        printk(KERN_CONT " id/val=0x%x/0x%x",
                                c->controls[i].id, c->controls[i].value);
                else
-                       printk(KERN_CONT " id=0x%x", c->controls[i].id);
+                       printk(KERN_CONT " id=0x%x,size=%u",
+                               c->controls[i].id, c->controls[i].size);
        }
        printk(KERN_CONT "\n");
 };
@@ -528,10 +529,9 @@ static inline int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
 
        /* zero the reserved fields */
        c->reserved[0] = c->reserved[1] = 0;
-       for (i = 0; i < c->count; i++) {
+       for (i = 0; i < c->count; i++)
                c->controls[i].reserved2[0] = 0;
-               c->controls[i].reserved2[1] = 0;
-       }
+
        /* V4L2_CID_PRIVATE_BASE cannot be used as control class
           when using extended controls.
           Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL
index 293ba18..9ab4cbe 100644 (file)
@@ -167,6 +167,7 @@ enum v4l2_ctrl_type {
        V4L2_CTRL_TYPE_BUTTON        = 4,
        V4L2_CTRL_TYPE_INTEGER64     = 5,
        V4L2_CTRL_TYPE_CTRL_CLASS    = 6,
+       V4L2_CTRL_TYPE_STRING        = 7,
 };
 
 enum v4l2_tuner_type {
@@ -795,11 +796,12 @@ struct v4l2_control {
 
 struct v4l2_ext_control {
        __u32 id;
-       __u32 reserved2[2];
+       __u32 size;
+       __u32 reserved2[1];
        union {
                __s32 value;
                __s64 value64;
-               void *reserved;
+               char *string;
        };
 } __attribute__ ((packed));