V4L/DVB (8078): Introduce "index" attribute for persistent video4linux device nodes
brandon@ifup.org [Sat, 21 Jun 2008 01:58:53 +0000 (22:58 -0300)]
A number of V4L drivers have a mod param to specify their preferred minors.
This is because it is often desirable for applications to have a static /dev
name for a particular device.  However, using minors has several disadvantages:

  1) the requested minor may already be taken
  2) using a mod param is driver specific
  3) it requires every driver to add a param
  4) requires configuration by hand

This patch introduces an "index" attribute that when combined with udev rules
can create static device paths like this:

/dev/v4l/by-path/pci-0000\:00\:1d.2-usb-0\:1\:1.0-video0
/dev/v4l/by-path/pci-0000\:00\:1d.2-usb-0\:1\:1.0-video1
/dev/v4l/by-path/pci-0000\:00\:1d.2-usb-0\:1\:1.0-video2

$ ls -la /dev/v4l/by-path/pci-0000\:00\:1d.2-usb-0\:1\:1.0-video0
lrwxrwxrwx 1 root root 12 2008-04-28 00:02 /dev/v4l/by-path/pci-0000:00:1d.2-usb-0:1:1.0-video0 -> ../../video1

These paths are steady across reboots and should be resistant to rearranging
across Kernel versions.

video_register_device_index is available to drivers to request a
specific index number.

Signed-off-by: Brandon Philips <bphilips@suse.de>
Signed-off-by: Kees Cook <kees@outflux.net>
Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>

drivers/media/video/videodev.c
include/media/v4l2-dev.h

index 6cf6ad7..9c539eb 100644 (file)
@@ -372,6 +372,14 @@ EXPORT_SYMBOL(v4l_printk_ioctl);
  *     sysfs stuff
  */
 
+static ssize_t show_index(struct device *cd,
+                        struct device_attribute *attr, char *buf)
+{
+       struct video_device *vfd = container_of(cd, struct video_device,
+                                               class_dev);
+       return sprintf(buf, "%i\n", vfd->index);
+}
+
 static ssize_t show_name(struct device *cd,
                         struct device_attribute *attr, char *buf)
 {
@@ -410,6 +418,7 @@ static void video_release(struct device *cd)
 
 static struct device_attribute video_device_attrs[] = {
        __ATTR(name, S_IRUGO, show_name, NULL),
+       __ATTR(index, S_IRUGO, show_index, NULL),
        __ATTR_NULL
 };
 
@@ -1900,8 +1909,82 @@ 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
+ * @vdev: video_device to assign index number to, vdev->dev should be assigned
+ * @num: -1 if auto assign, requested number otherwise
+ *
+ *
+ * returns -ENFILE if num is already in use, a free index number if
+ * successful.
+ */
+static int get_index(struct video_device *vdev, int num)
+{
+       struct index_info *info;
+       int i;
+       int ret = 0;
+
+       if (num >= VIDEO_NUM_DEVICES)
+               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;
+       }
+
+out:
+       kfree(info);
+       return ret;
+}
+
 static const struct file_operations video_fops;
 
+int video_register_device(struct video_device *vfd, int type, int nr)
+{
+       return video_register_device_index(vfd, type, nr, -1);
+}
+EXPORT_SYMBOL(video_register_device);
+
 /**
  *     video_register_device - register video4linux devices
  *     @vfd:  video device structure we want to register
@@ -1927,7 +2010,8 @@ static const struct file_operations video_fops;
  *     %VFL_TYPE_RADIO - A radio card
  */
 
-int video_register_device(struct video_device *vfd, int type, int nr)
+int video_register_device_index(struct video_device *vfd, int type, int nr,
+                                       int index)
 {
        int i=0;
        int base;
@@ -1984,6 +2068,16 @@ int video_register_device(struct video_device *vfd, int type, int nr)
        }
        video_device[i]=vfd;
        vfd->minor=i;
+
+       ret = get_index(vfd, index);
+       if (ret < 0) {
+               printk(KERN_ERR "%s: get_index failed\n",
+                      __func__);
+               goto fail_minor;
+       }
+
+       vfd->index = ret;
+
        mutex_unlock(&videodev_lock);
        mutex_init(&vfd->lock);
 
@@ -2017,7 +2111,7 @@ fail_minor:
        mutex_unlock(&videodev_lock);
        return ret;
 }
-EXPORT_SYMBOL(video_register_device);
+EXPORT_SYMBOL(video_register_device_index);
 
 /**
  *     video_unregister_device - unregister a video4linux device
index 40b0810..9e6e4f1 100644 (file)
@@ -96,6 +96,8 @@ struct video_device
        int type;       /* v4l1 */
        int type2;      /* v4l2 */
        int minor;
+       /* attribute to diferentiate multiple indexs on one physical device */
+       int index;
 
        int debug;      /* Activates debug level*/
 
@@ -347,6 +349,8 @@ void *priv;
 
 /* Version 2 functions */
 extern int video_register_device(struct video_device *vfd, int type, int nr);
+int video_register_device_index(struct video_device *vfd, int type, int nr,
+                                       int index);
 void video_unregister_device(struct video_device *);
 extern int video_ioctl2(struct inode *inode, struct file *file,
                          unsigned int cmd, unsigned long arg);