Merge git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb
[pandora-kernel.git] / drivers / media / video / videodev.c
index 5bf2256..6616e65 100644 (file)
@@ -392,6 +392,12 @@ static ssize_t show_name(struct device *cd,
        return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name);
 }
 
+static struct device_attribute video_device_attrs[] = {
+       __ATTR(name, S_IRUGO, show_name, NULL),
+       __ATTR(index, S_IRUGO, show_index, NULL),
+       __ATTR_NULL
+};
+
 struct video_device *video_device_alloc(void)
 {
        struct video_device *vfd;
@@ -420,12 +426,6 @@ static void video_release(struct device *cd)
        vfd->release(vfd);
 }
 
-static struct device_attribute video_device_attrs[] = {
-       __ATTR(name, S_IRUGO, show_name, NULL),
-       __ATTR(index, S_IRUGO, show_index, NULL),
-       __ATTR_NULL
-};
-
 static struct class video_class = {
        .name    = VIDEO_NAME,
        .dev_attrs = video_device_attrs,
@@ -710,7 +710,7 @@ static inline void v4l_print_ext_ctrls(unsigned int cmd,
        printk(KERN_CONT "\n");
 };
 
-static inline int check_ext_ctrls(struct v4l2_ext_controls *c)
+static inline int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
 {
        __u32 i;
 
@@ -721,8 +721,11 @@ static inline int check_ext_ctrls(struct v4l2_ext_controls *c)
                c->controls[i].reserved2[1] = 0;
        }
        /* V4L2_CID_PRIVATE_BASE cannot be used as control class
-        * when using extended controls. */
-       if (c->ctrl_class == V4L2_CID_PRIVATE_BASE)
+          when using extended controls.
+          Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL
+          is it allowed for backwards compatibility.
+        */
+       if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE)
                return 0;
        /* Check that all controls are from the same control class. */
        for (i = 0; i < c->count; i++) {
@@ -1426,7 +1429,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
                        ctrls.controls = &ctrl;
                        ctrl.id = p->id;
                        ctrl.value = p->value;
-                       if (check_ext_ctrls(&ctrls)) {
+                       if (check_ext_ctrls(&ctrls, 1)) {
                                ret = vfd->vidioc_g_ext_ctrls(file, fh, &ctrls);
                                if (ret == 0)
                                        p->value = ctrl.value;
@@ -1462,7 +1465,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
                ctrls.controls = &ctrl;
                ctrl.id = p->id;
                ctrl.value = p->value;
-               if (check_ext_ctrls(&ctrls))
+               if (check_ext_ctrls(&ctrls, 1))
                        ret = vfd->vidioc_s_ext_ctrls(file, fh, &ctrls);
                break;
        }
@@ -1473,7 +1476,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
                p->error_idx = p->count;
                if (!vfd->vidioc_g_ext_ctrls)
                        break;
-               if (check_ext_ctrls(p))
+               if (check_ext_ctrls(p, 0))
                        ret = vfd->vidioc_g_ext_ctrls(file, fh, p);
                v4l_print_ext_ctrls(cmd, vfd, p, !ret);
                break;
@@ -1486,7 +1489,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
                if (!vfd->vidioc_s_ext_ctrls)
                        break;
                v4l_print_ext_ctrls(cmd, vfd, p, 1);
-               if (check_ext_ctrls(p))
+               if (check_ext_ctrls(p, 0))
                        ret = vfd->vidioc_s_ext_ctrls(file, fh, p);
                break;
        }
@@ -1498,7 +1501,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
                if (!vfd->vidioc_try_ext_ctrls)
                        break;
                v4l_print_ext_ctrls(cmd, vfd, p, 1);
-               if (check_ext_ctrls(p))
+               if (check_ext_ctrls(p, 0))
                        ret = vfd->vidioc_try_ext_ctrls(file, fh, p);
                break;
        }
@@ -2008,25 +2011,8 @@ out:
 }
 EXPORT_SYMBOL(video_ioctl2);
 
-struct index_info {
-       struct device *dev;
-       unsigned int used[VIDEO_NUM_DEVICES];
-};
-
-static int __fill_index_info(struct device *cd, void *data)
-{
-       struct index_info *info = data;
-       struct video_device *vfd = container_of(cd, struct video_device,
-                                               class_dev);
-
-       if (info->dev == vfd->dev)
-               info->used[vfd->index] = 1;
-
-       return 0;
-}
-
 /**
- * assign_index - assign stream number based on parent device
+ * get_index - assign stream number based on parent device
  * @vdev: video_device to assign index number to, vdev->dev should be assigned
  * @num: -1 if auto assign, requested number otherwise
  *
@@ -2036,44 +2022,34 @@ static int __fill_index_info(struct device *cd, void *data)
  */
 static int get_index(struct video_device *vdev, int num)
 {
-       struct index_info *info;
+       u32 used = 0;
+       const int max_index = sizeof(used) * 8 - 1;
        int i;
-       int ret = 0;
 
-       if (num >= VIDEO_NUM_DEVICES)
+       /* Currently a single v4l driver instance cannot create more than
+          32 devices.
+          Increase to u64 or an array of u32 if more are needed. */
+       if (num > max_index) {
+               printk(KERN_ERR "videodev: %s num is too large\n", __func__);
                return -EINVAL;
-
-       info = kzalloc(sizeof(*info), GFP_KERNEL);
-       if (!info)
-               return -ENOMEM;
-
-       info->dev = vdev->dev;
-
-       ret = class_for_each_device(&video_class, info,
-                                       __fill_index_info);
-
-       if (ret < 0)
-               goto out;
-
-       if (num >= 0) {
-               if (!info->used[num])
-                       ret = num;
-               else
-                       ret = -ENFILE;
-
-               goto out;
        }
 
        for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
-               if (info->used[i])
-                       continue;
-               ret = i;
-               goto out;
+               if (video_device[i] != NULL &&
+                   video_device[i] != vdev &&
+                   video_device[i]->dev == vdev->dev) {
+                       used |= 1 << video_device[i]->index;
+               }
        }
 
-out:
-       kfree(info);
-       return ret;
+       if (num >= 0) {
+               if (used & (1 << num))
+                       return -ENFILE;
+               return num;
+       }
+
+       i = ffz(used);
+       return i > max_index ? -ENFILE : i;
 }
 
 static const struct file_operations video_fops;
@@ -2169,28 +2145,27 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
        vfd->minor=i;
 
        ret = get_index(vfd, index);
+       vfd->index = ret;
+
+       mutex_unlock(&videodev_lock);
+
        if (ret < 0) {
-               printk(KERN_ERR "%s: get_index failed\n",
-                      __func__);
+               printk(KERN_ERR "%s: get_index failed\n", __func__);
                goto fail_minor;
        }
 
-       vfd->index = ret;
-
-       mutex_unlock(&videodev_lock);
        mutex_init(&vfd->lock);
 
        /* sysfs class */
        memset(&vfd->class_dev, 0x00, sizeof(vfd->class_dev));
-       if (vfd->dev)
-               vfd->class_dev.parent = vfd->dev;
        vfd->class_dev.class       = &video_class;
        vfd->class_dev.devt        = MKDEV(VIDEO_MAJOR, vfd->minor);
+       if (vfd->dev)
+               vfd->class_dev.parent = vfd->dev;
        sprintf(vfd->class_dev.bus_id, "%s%d", name_base, i - base);
        ret = device_register(&vfd->class_dev);
        if (ret < 0) {
-               printk(KERN_ERR "%s: device_register failed\n",
-                      __func__);
+               printk(KERN_ERR "%s: device_register failed\n", __func__);
                goto fail_minor;
        }