]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/media/video/v4l2-dev.c
Merge commit 'v2.6.28-rc1' into sched/urgent
[linux-2.6-omap-h63xx.git] / drivers / media / video / v4l2-dev.c
index 155fdec9ac7d7c4fc0aa773581021fbe59ab3355..ccd6566a515e1c293ec85f8f067f48fb4fea4643 100644 (file)
@@ -42,6 +42,7 @@ static ssize_t show_index(struct device *cd,
                         struct device_attribute *attr, char *buf)
 {
        struct video_device *vfd = container_of(cd, struct video_device, dev);
+
        return sprintf(buf, "%i\n", vfd->index);
 }
 
@@ -49,6 +50,7 @@ static ssize_t show_name(struct device *cd,
                         struct device_attribute *attr, char *buf)
 {
        struct video_device *vfd = container_of(cd, struct video_device, dev);
+
        return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name);
 }
 
@@ -58,12 +60,16 @@ static struct device_attribute video_device_attrs[] = {
        __ATTR_NULL
 };
 
+/*
+ *     Active devices
+ */
+static struct video_device *video_device[VIDEO_NUM_DEVICES];
+static DEFINE_MUTEX(videodev_lock);
+static DECLARE_BITMAP(video_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES);
+
 struct video_device *video_device_alloc(void)
 {
-       struct video_device *vfd;
-
-       vfd = kzalloc(sizeof(*vfd), GFP_KERNEL);
-       return vfd;
+       return kzalloc(sizeof(struct video_device), GFP_KERNEL);
 }
 EXPORT_SYMBOL(video_device_alloc);
 
@@ -73,16 +79,52 @@ void video_device_release(struct video_device *vfd)
 }
 EXPORT_SYMBOL(video_device_release);
 
+void video_device_release_empty(struct video_device *vfd)
+{
+       /* Do nothing */
+       /* Only valid when the video_device struct is a static. */
+}
+EXPORT_SYMBOL(video_device_release_empty);
+
+/* Called when the last user of the character device is gone. */
+static void v4l2_chardev_release(struct kobject *kobj)
+{
+       struct video_device *vfd = container_of(kobj, struct video_device, cdev.kobj);
+
+       mutex_lock(&videodev_lock);
+       if (video_device[vfd->minor] != vfd) {
+               mutex_unlock(&videodev_lock);
+               BUG();
+               return;
+       }
+
+       /* Free up this device for reuse */
+       video_device[vfd->minor] = NULL;
+       clear_bit(vfd->num, video_nums[vfd->vfl_type]);
+       mutex_unlock(&videodev_lock);
+
+       /* Release the character device */
+       vfd->cdev_release(kobj);
+       /* Release video_device and perform other
+          cleanups as needed. */
+       if (vfd->release)
+               vfd->release(vfd);
+}
+
+/* The new kobj_type for the character device */
+static struct kobj_type v4l2_ktype_cdev_default = {
+       .release = v4l2_chardev_release,
+};
+
 static void video_release(struct device *cd)
 {
        struct video_device *vfd = container_of(cd, struct video_device, dev);
 
-#if 1
-       /* needed until all drivers are fixed */
-       if (!vfd->release)
-               return;
-#endif
-       vfd->release(vfd);
+       /* It's now safe to delete the char device.
+          This will either trigger the v4l2_chardev_release immediately (if
+          the refcount goes to 0) or later when the last user of the
+          character device closes it. */
+       cdev_del(&vfd->cdev);
 }
 
 static struct class video_class = {
@@ -91,87 +133,12 @@ static struct class video_class = {
        .dev_release = video_release,
 };
 
-/*
- *     Active devices
- */
-
-static struct video_device *video_device[VIDEO_NUM_DEVICES];
-static DEFINE_MUTEX(videodev_lock);
-
 struct video_device *video_devdata(struct file *file)
 {
        return video_device[iminor(file->f_path.dentry->d_inode)];
 }
 EXPORT_SYMBOL(video_devdata);
 
-/*
- *     Open a video device - FIXME: Obsoleted
- */
-static int video_open(struct inode *inode, struct file *file)
-{
-       unsigned int minor = iminor(inode);
-       int err = 0;
-       struct video_device *vfl;
-       const struct file_operations *old_fops;
-
-       if (minor >= VIDEO_NUM_DEVICES)
-               return -ENODEV;
-       lock_kernel();
-       mutex_lock(&videodev_lock);
-       vfl = video_device[minor];
-       if (vfl == NULL) {
-               mutex_unlock(&videodev_lock);
-               request_module("char-major-%d-%d", VIDEO_MAJOR, minor);
-               mutex_lock(&videodev_lock);
-               vfl = video_device[minor];
-               if (vfl == NULL) {
-                       mutex_unlock(&videodev_lock);
-                       unlock_kernel();
-                       return -ENODEV;
-               }
-       }
-       old_fops = file->f_op;
-       file->f_op = fops_get(vfl->fops);
-       if (file->f_op->open)
-               err = file->f_op->open(inode, file);
-       if (err) {
-               fops_put(file->f_op);
-               file->f_op = fops_get(old_fops);
-       }
-       fops_put(old_fops);
-       mutex_unlock(&videodev_lock);
-       unlock_kernel();
-       return err;
-}
-
-/*
- * open/release helper functions -- handle exclusive opens
- * Should be removed soon
- */
-int video_exclusive_open(struct inode *inode, struct file *file)
-{
-       struct video_device *vfl = video_devdata(file);
-       int retval = 0;
-
-       mutex_lock(&vfl->lock);
-       if (vfl->users)
-               retval = -EBUSY;
-       else
-               vfl->users++;
-       mutex_unlock(&vfl->lock);
-       return retval;
-}
-EXPORT_SYMBOL(video_exclusive_open);
-
-int video_exclusive_release(struct inode *inode, struct file *file)
-{
-       struct video_device *vfl = video_devdata(file);
-
-       vfl->users--;
-       return 0;
-}
-EXPORT_SYMBOL(video_exclusive_release);
-
 /**
  * get_index - assign stream number based on parent device
  * @vdev: video_device to assign index number to, vdev->dev should be assigned
@@ -252,33 +219,29 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
                                        int index)
 {
        int i = 0;
-       int base;
-       int end;
        int ret;
-       char *name_base;
+       int minor_offset = 0;
+       int minor_cnt = VIDEO_NUM_DEVICES;
+       const char *name_base;
+       void *priv = video_get_drvdata(vfd);
+
+       /* the release callback MUST be present */
+       BUG_ON(!vfd->release);
 
        if (vfd == NULL)
                return -EINVAL;
 
        switch (type) {
        case VFL_TYPE_GRABBER:
-               base = MINOR_VFL_TYPE_GRABBER_MIN;
-               end = MINOR_VFL_TYPE_GRABBER_MAX+1;
                name_base = "video";
                break;
        case VFL_TYPE_VTX:
-               base = MINOR_VFL_TYPE_VTX_MIN;
-               end = MINOR_VFL_TYPE_VTX_MAX+1;
                name_base = "vtx";
                break;
        case VFL_TYPE_VBI:
-               base = MINOR_VFL_TYPE_VBI_MIN;
-               end = MINOR_VFL_TYPE_VBI_MAX+1;
                name_base = "vbi";
                break;
        case VFL_TYPE_RADIO:
-               base = MINOR_VFL_TYPE_RADIO_MIN;
-               end = MINOR_VFL_TYPE_RADIO_MAX+1;
                name_base = "radio";
                break;
        default:
@@ -287,28 +250,70 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
                return -EINVAL;
        }
 
+       vfd->vfl_type = type;
+
+#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
+       /* Keep the ranges for the first four types for historical
+        * reasons.
+        * Newer devices (not yet in place) should use the range
+        * of 128-191 and just pick the first free minor there
+        * (new style). */
+       switch (type) {
+       case VFL_TYPE_GRABBER:
+               minor_offset = 0;
+               minor_cnt = 64;
+               break;
+       case VFL_TYPE_RADIO:
+               minor_offset = 64;
+               minor_cnt = 64;
+               break;
+       case VFL_TYPE_VTX:
+               minor_offset = 192;
+               minor_cnt = 32;
+               break;
+       case VFL_TYPE_VBI:
+               minor_offset = 224;
+               minor_cnt = 32;
+               break;
+       default:
+               minor_offset = 128;
+               minor_cnt = 64;
+               break;
+       }
+#endif
+
+       /* Initialize the character device */
+       cdev_init(&vfd->cdev, vfd->fops);
+       vfd->cdev.owner = vfd->fops->owner;
        /* pick a minor number */
        mutex_lock(&videodev_lock);
-       if (nr >= 0  &&  nr < end-base) {
-               /* use the one the driver asked for */
-               i = base + nr;
-               if (NULL != video_device[i]) {
-                       mutex_unlock(&videodev_lock);
-                       return -ENFILE;
-               }
-       } else {
-               /* use first free */
-               for (i = base; i < end; i++)
-                       if (NULL == video_device[i])
-                               break;
-               if (i == end) {
-                       mutex_unlock(&videodev_lock);
-                       return -ENFILE;
-               }
+       nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr);
+       if (nr == minor_cnt)
+               nr = find_first_zero_bit(video_nums[type], minor_cnt);
+       if (nr == minor_cnt) {
+               printk(KERN_ERR "could not get a free kernel number\n");
+               mutex_unlock(&videodev_lock);
+               return -ENFILE;
        }
-       video_device[i] = vfd;
-       vfd->vfl_type = type;
-       vfd->minor = i;
+#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
+       /* 1-on-1 mapping of kernel number to minor number */
+       i = nr;
+#else
+       /* The kernel number and minor numbers are independent */
+       for (i = 0; i < VIDEO_NUM_DEVICES; i++)
+               if (video_device[i] == NULL)
+                       break;
+       if (i == VIDEO_NUM_DEVICES) {
+               mutex_unlock(&videodev_lock);
+               printk(KERN_ERR "could not get a free minor\n");
+               return -ENFILE;
+       }
+#endif
+       vfd->minor = i + minor_offset;
+       vfd->num = nr;
+       set_bit(nr, video_nums[type]);
+       BUG_ON(video_device[vfd->minor]);
+       video_device[vfd->minor] = vfd;
 
        ret = get_index(vfd, index);
        vfd->index = ret;
@@ -320,35 +325,41 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
                goto fail_minor;
        }
 
-       mutex_init(&vfd->lock);
-
+       ret = cdev_add(&vfd->cdev, MKDEV(VIDEO_MAJOR, vfd->minor), 1);
+       if (ret < 0) {
+               printk(KERN_ERR "%s: cdev_add failed\n", __func__);
+               goto fail_minor;
+       }
        /* sysfs class */
-       memset(&vfd->dev, 0x00, sizeof(vfd->dev));
+       memset(&vfd->dev, 0, sizeof(vfd->dev));
+       /* The memset above cleared the device's drvdata, so
+          put back the copy we made earlier. */
+       video_set_drvdata(vfd, priv);
        vfd->dev.class = &video_class;
        vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor);
        if (vfd->parent)
                vfd->dev.parent = vfd->parent;
-       sprintf(vfd->dev.bus_id, "%s%d", name_base, i - base);
+       sprintf(vfd->dev.bus_id, "%s%d", name_base, nr);
        ret = device_register(&vfd->dev);
        if (ret < 0) {
                printk(KERN_ERR "%s: device_register failed\n", __func__);
-               goto fail_minor;
+               goto del_cdev;
        }
-
-#if 1
-       /* needed until all drivers are fixed */
-       if (!vfd->release)
-               printk(KERN_WARNING "videodev: \"%s\" has no release callback. "
-                      "Please fix your driver for proper sysfs support, see "
-                      "http://lwn.net/Articles/36850/\n", vfd->name);
-#endif
+       /* Remember the cdev's release function */
+       vfd->cdev_release = vfd->cdev.kobj.ktype->release;
+       /* Install our own */
+       vfd->cdev.kobj.ktype = &v4l2_ktype_cdev_default;
        return 0;
 
+del_cdev:
+       cdev_del(&vfd->cdev);
+
 fail_minor:
        mutex_lock(&videodev_lock);
        video_device[vfd->minor] = NULL;
-       vfd->minor = -1;
+       clear_bit(vfd->num, video_nums[type]);
        mutex_unlock(&videodev_lock);
+       vfd->minor = -1;
        return ret;
 }
 EXPORT_SYMBOL(video_register_device_index);
@@ -363,42 +374,29 @@ EXPORT_SYMBOL(video_register_device_index);
 
 void video_unregister_device(struct video_device *vfd)
 {
-       mutex_lock(&videodev_lock);
-       if (video_device[vfd->minor] != vfd)
-               panic("videodev: bad unregister");
-
-       video_device[vfd->minor] = NULL;
        device_unregister(&vfd->dev);
-       mutex_unlock(&videodev_lock);
 }
 EXPORT_SYMBOL(video_unregister_device);
 
-/*
- * Video fs operations
- */
-static const struct file_operations video_fops = {
-       .owner          = THIS_MODULE,
-       .llseek         = no_llseek,
-       .open           = video_open,
-};
-
 /*
  *     Initialise video for linux
  */
-
 static int __init videodev_init(void)
 {
+       dev_t dev = MKDEV(VIDEO_MAJOR, 0);
        int ret;
 
        printk(KERN_INFO "Linux video capture interface: v2.00\n");
-       if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, &video_fops)) {
-               printk(KERN_WARNING "video_dev: unable to get major %d\n", VIDEO_MAJOR);
-               return -EIO;
+       ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
+       if (ret < 0) {
+               printk(KERN_WARNING "videodev: unable to get major %d\n",
+                               VIDEO_MAJOR);
+               return ret;
        }
 
        ret = class_register(&video_class);
        if (ret < 0) {
-               unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME);
+               unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
                printk(KERN_WARNING "video_dev: class_register failed\n");
                return -EIO;
        }
@@ -408,8 +406,10 @@ static int __init videodev_init(void)
 
 static void __exit videodev_exit(void)
 {
+       dev_t dev = MKDEV(VIDEO_MAJOR, 0);
+
        class_unregister(&video_class);
-       unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME);
+       unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
 }
 
 module_init(videodev_init)