diff options
Diffstat (limited to 'linux')
-rw-r--r-- | linux/drivers/media/video/v4l2-dev.c | 152 | ||||
-rw-r--r-- | linux/include/media/v4l2-dev.h | 3 |
2 files changed, 72 insertions, 83 deletions
diff --git a/linux/drivers/media/video/v4l2-dev.c b/linux/drivers/media/video/v4l2-dev.c index b5a65beca..0d081cb91 100644 --- a/linux/drivers/media/video/v4l2-dev.c +++ b/linux/drivers/media/video/v4l2-dev.c @@ -80,6 +80,12 @@ static CLASS_DEVICE_ATTR(index, S_IRUGO, show_index, NULL); static CLASS_DEVICE_ATTR(name, S_IRUGO, show_name, NULL); #endif +/* + * Active devices + */ +static struct video_device *video_device[VIDEO_NUM_DEVICES]; +static DEFINE_MUTEX(videodev_lock); + struct video_device *video_device_alloc(void) { return kzalloc(sizeof(struct video_device), GFP_KERNEL); @@ -99,6 +105,32 @@ void video_device_release_empty(struct video_device *vfd) } 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) + panic("videodev: bad release"); + + /* Free up this device for reuse */ + video_device[vfd->minor] = NULL; + 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, +}; + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) static void video_release(struct class_device *cd) #else @@ -107,7 +139,11 @@ static void video_release(struct device *cd) { struct video_device *vfd = container_of(cd, struct video_device, dev); - 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 = { @@ -120,13 +156,6 @@ static struct class video_class = { #endif }; -/* - * Active devices - */ - -static struct video_device *video_device[VIDEO_NUM_DEVICES]; -static DEFINE_MUTEX(videodev_lock); - struct video_device *video_devdata(struct file *file) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) @@ -137,51 +166,6 @@ struct video_device *video_devdata(struct file *file) } 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; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) - const struct file_operations *old_fops; -#else - struct file_operations *old_fops; -#endif - - if (minor >= VIDEO_NUM_DEVICES) - return -ENODEV; - 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); - return -ENODEV; - } - } - old_fops = file->f_op; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) - file->f_op = fops_get(vfl->fops); -#else - file->f_op = (struct file_operations *)fops_get(vfl->fops); -#endif - 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); - return err; -} - /** * get_index - assign stream number based on parent device * @vdev: video_device to assign index number to, vdev->dev should be assigned @@ -301,6 +285,13 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, return -EINVAL; } + /* Initialize the character device */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) + cdev_init(&vfd->cdev, vfd->fops); +#else + cdev_init(&vfd->cdev, (struct file_operations *)vfd->fops); +#endif + vfd->cdev.owner = vfd->fops->owner; /* pick a minor number */ mutex_lock(&videodev_lock); if (nr >= 0 && nr < end-base) { @@ -334,6 +325,11 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, goto fail_minor; } + 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, 0, sizeof(vfd->dev)); /* The memset above cleared the device's drvdata, so @@ -354,7 +350,7 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, #endif if (ret < 0) { printk(KERN_ERR "%s: device_register failed\n", __func__); - goto fail_minor; + goto del_cdev; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) ret = class_device_create_file(&vfd->dev, &class_device_attr_name); @@ -362,19 +358,25 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, printk(KERN_ERR "%s: class_device_create_file 'name' failed\n", __func__); class_device_unregister(&vfd->dev); - goto fail_minor; + goto del_cdev; } ret = class_device_create_file(&vfd->dev, &class_device_attr_index); if (ret < 0) { printk(KERN_ERR "%s: class_device_create_file 'index' failed\n", __func__); class_device_unregister(&vfd->dev); - goto fail_minor; + goto del_cdev; } #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; @@ -394,51 +396,33 @@ 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; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) class_device_unregister(&vfd->dev); #else device_unregister(&vfd->dev); #endif - 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 LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) - if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, &video_fops)) { -#else - if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, - (struct file_operations *)&video_fops)) { -#endif - 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; } @@ -448,8 +432,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) diff --git a/linux/include/media/v4l2-dev.h b/linux/include/media/v4l2-dev.h index ba05b8164..9158ad8b5 100644 --- a/linux/include/media/v4l2-dev.h +++ b/linux/include/media/v4l2-dev.h @@ -12,6 +12,7 @@ #include <linux/poll.h> #include <linux/fs.h> #include <linux/device.h> +#include <linux/cdev.h> #include <linux/mutex.h> #include <linux/videodev2.h> @@ -51,6 +52,8 @@ struct video_device #else struct class_device dev; #endif + struct cdev cdev; /* character device */ + void (*cdev_release)(struct kobject *kobj); struct device *parent; /* device parent */ /* device info */ |