Commit 7f8ecfab authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

V4L/DVB (8852): v4l2: use register_chrdev_region instead of register_chrdev

Replace the old register_chrdev with the more flexible register_chrdev_region.

Ensure that the release() is called when the very last chardev usage was
released, and not when the sysfs devices were removed. This should simplify
hotpluggable drivers considerably.
Tested-by: default avatarMike Isely <isely@isely.net>
Tested-by: default avatarLaurent Pinchart <laurent.pinchart@skynet.be>
Signed-off-by: default avatarHans Verkuil <hverkuil@xs4all.nl>
Acked-by: default avatarDavid Ellingsworth <david@identd.dyndns.org>
Reviewed-by: default avatarHans de Goede <j.w.r.degoede@hhs.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 187565c4
...@@ -60,6 +60,12 @@ static struct device_attribute video_device_attrs[] = { ...@@ -60,6 +60,12 @@ static struct device_attribute video_device_attrs[] = {
__ATTR_NULL __ATTR_NULL
}; };
/*
* Active devices
*/
static struct video_device *video_device[VIDEO_NUM_DEVICES];
static DEFINE_MUTEX(videodev_lock);
struct video_device *video_device_alloc(void) struct video_device *video_device_alloc(void)
{ {
return kzalloc(sizeof(struct video_device), GFP_KERNEL); return kzalloc(sizeof(struct video_device), GFP_KERNEL);
...@@ -79,11 +85,41 @@ void video_device_release_empty(struct video_device *vfd) ...@@ -79,11 +85,41 @@ void video_device_release_empty(struct video_device *vfd)
} }
EXPORT_SYMBOL(video_device_release_empty); 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,
};
static void video_release(struct device *cd) static void video_release(struct device *cd)
{ {
struct video_device *vfd = container_of(cd, struct video_device, dev); 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 = { static struct class video_class = {
...@@ -92,56 +128,12 @@ static struct class video_class = { ...@@ -92,56 +128,12 @@ static struct class video_class = {
.dev_release = video_release, .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) struct video_device *video_devdata(struct file *file)
{ {
return video_device[iminor(file->f_path.dentry->d_inode)]; return video_device[iminor(file->f_path.dentry->d_inode)];
} }
EXPORT_SYMBOL(video_devdata); 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;
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;
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);
return err;
}
/** /**
* get_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 * @vdev: video_device to assign index number to, vdev->dev should be assigned
...@@ -261,6 +253,9 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, ...@@ -261,6 +253,9 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
return -EINVAL; return -EINVAL;
} }
/* Initialize the character device */
cdev_init(&vfd->cdev, vfd->fops);
vfd->cdev.owner = vfd->fops->owner;
/* pick a minor number */ /* pick a minor number */
mutex_lock(&videodev_lock); mutex_lock(&videodev_lock);
if (nr >= 0 && nr < end-base) { if (nr >= 0 && nr < end-base) {
...@@ -294,6 +289,11 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, ...@@ -294,6 +289,11 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
goto fail_minor; 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 */ /* sysfs class */
memset(&vfd->dev, 0, sizeof(vfd->dev)); memset(&vfd->dev, 0, sizeof(vfd->dev));
/* The memset above cleared the device's drvdata, so /* The memset above cleared the device's drvdata, so
...@@ -307,11 +307,17 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, ...@@ -307,11 +307,17 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
ret = device_register(&vfd->dev); ret = device_register(&vfd->dev);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__); printk(KERN_ERR "%s: device_register failed\n", __func__);
goto fail_minor; goto del_cdev;
} }
/* 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; return 0;
del_cdev:
cdev_del(&vfd->cdev);
fail_minor: fail_minor:
mutex_lock(&videodev_lock); mutex_lock(&videodev_lock);
video_device[vfd->minor] = NULL; video_device[vfd->minor] = NULL;
...@@ -331,42 +337,29 @@ EXPORT_SYMBOL(video_register_device_index); ...@@ -331,42 +337,29 @@ EXPORT_SYMBOL(video_register_device_index);
void video_unregister_device(struct video_device *vfd) 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); device_unregister(&vfd->dev);
mutex_unlock(&videodev_lock);
} }
EXPORT_SYMBOL(video_unregister_device); 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 * Initialise video for linux
*/ */
static int __init videodev_init(void) static int __init videodev_init(void)
{ {
dev_t dev = MKDEV(VIDEO_MAJOR, 0);
int ret; int ret;
printk(KERN_INFO "Linux video capture interface: v2.00\n"); printk(KERN_INFO "Linux video capture interface: v2.00\n");
if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, &video_fops)) { ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
printk(KERN_WARNING "video_dev: unable to get major %d\n", VIDEO_MAJOR); if (ret < 0) {
return -EIO; printk(KERN_WARNING "videodev: unable to get major %d\n",
VIDEO_MAJOR);
return ret;
} }
ret = class_register(&video_class); ret = class_register(&video_class);
if (ret < 0) { 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"); printk(KERN_WARNING "video_dev: class_register failed\n");
return -EIO; return -EIO;
} }
...@@ -376,8 +369,10 @@ static int __init videodev_init(void) ...@@ -376,8 +369,10 @@ static int __init videodev_init(void)
static void __exit videodev_exit(void) static void __exit videodev_exit(void)
{ {
dev_t dev = MKDEV(VIDEO_MAJOR, 0);
class_unregister(&video_class); class_unregister(&video_class);
unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME); unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
} }
module_init(videodev_init) module_init(videodev_init)
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/cdev.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
...@@ -47,6 +48,8 @@ struct video_device ...@@ -47,6 +48,8 @@ struct video_device
/* sysfs */ /* sysfs */
struct device dev; /* v4l device */ struct device dev; /* v4l device */
struct cdev cdev; /* character device */
void (*cdev_release)(struct kobject *kobj);
struct device *parent; /* device parent */ struct device *parent; /* device parent */
/* device info */ /* device info */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment