aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil@xs4all.nl>2008-08-29 16:31:35 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2008-10-12 07:36:56 -0400
commit7f8ecfab7a1d256707502207c56dd4bf3e3a026e (patch)
tree2ba6079ba59017d03f53e0ba068e099008ef21d5
parent187565c43a95bcf7798c58d518ddd765933d24da (diff)
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: Mike Isely <isely@isely.net> Tested-by: Laurent Pinchart <laurent.pinchart@skynet.be> Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Acked-by: David Ellingsworth <david@identd.dyndns.org> Reviewed-by: Hans de Goede <j.w.r.degoede@hhs.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/media/video/v4l2-dev.c131
-rw-r--r--include/media/v4l2-dev.h3
2 files changed, 66 insertions, 68 deletions
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index 99f7ee4bc503..02b9cc76d36b 100644
--- a/drivers/media/video/v4l2-dev.c
+++ b/drivers/media/video/v4l2-dev.c
@@ -60,6 +60,12 @@ static struct device_attribute video_device_attrs[] = {
60 __ATTR_NULL 60 __ATTR_NULL
61}; 61};
62 62
63/*
64 * Active devices
65 */
66static struct video_device *video_device[VIDEO_NUM_DEVICES];
67static DEFINE_MUTEX(videodev_lock);
68
63struct video_device *video_device_alloc(void) 69struct video_device *video_device_alloc(void)
64{ 70{
65 return kzalloc(sizeof(struct video_device), GFP_KERNEL); 71 return kzalloc(sizeof(struct video_device), GFP_KERNEL);
@@ -79,11 +85,41 @@ void video_device_release_empty(struct video_device *vfd)
79} 85}
80EXPORT_SYMBOL(video_device_release_empty); 86EXPORT_SYMBOL(video_device_release_empty);
81 87
88/* Called when the last user of the character device is gone. */
89static void v4l2_chardev_release(struct kobject *kobj)
90{
91 struct video_device *vfd = container_of(kobj, struct video_device, cdev.kobj);
92
93 mutex_lock(&videodev_lock);
94 if (video_device[vfd->minor] != vfd)
95 panic("videodev: bad release");
96
97 /* Free up this device for reuse */
98 video_device[vfd->minor] = NULL;
99 mutex_unlock(&videodev_lock);
100
101 /* Release the character device */
102 vfd->cdev_release(kobj);
103 /* Release video_device and perform other
104 cleanups as needed. */
105 if (vfd->release)
106 vfd->release(vfd);
107}
108
109/* The new kobj_type for the character device */
110static struct kobj_type v4l2_ktype_cdev_default = {
111 .release = v4l2_chardev_release,
112};
113
82static void video_release(struct device *cd) 114static void video_release(struct device *cd)
83{ 115{
84 struct video_device *vfd = container_of(cd, struct video_device, dev); 116 struct video_device *vfd = container_of(cd, struct video_device, dev);
85 117
86 vfd->release(vfd); 118 /* It's now safe to delete the char device.
119 This will either trigger the v4l2_chardev_release immediately (if
120 the refcount goes to 0) or later when the last user of the
121 character device closes it. */
122 cdev_del(&vfd->cdev);
87} 123}
88 124
89static struct class video_class = { 125static struct class video_class = {
@@ -92,56 +128,12 @@ static struct class video_class = {
92 .dev_release = video_release, 128 .dev_release = video_release,
93}; 129};
94 130
95/*
96 * Active devices
97 */
98
99static struct video_device *video_device[VIDEO_NUM_DEVICES];
100static DEFINE_MUTEX(videodev_lock);
101
102struct video_device *video_devdata(struct file *file) 131struct video_device *video_devdata(struct file *file)
103{ 132{
104 return video_device[iminor(file->f_path.dentry->d_inode)]; 133 return video_device[iminor(file->f_path.dentry->d_inode)];
105} 134}
106EXPORT_SYMBOL(video_devdata); 135EXPORT_SYMBOL(video_devdata);
107 136
108/*
109 * Open a video device - FIXME: Obsoleted
110 */
111static int video_open(struct inode *inode, struct file *file)
112{
113 unsigned int minor = iminor(inode);
114 int err = 0;
115 struct video_device *vfl;
116 const struct file_operations *old_fops;
117
118 if (minor >= VIDEO_NUM_DEVICES)
119 return -ENODEV;
120 mutex_lock(&videodev_lock);
121 vfl = video_device[minor];
122 if (vfl == NULL) {
123 mutex_unlock(&videodev_lock);
124 request_module("char-major-%d-%d", VIDEO_MAJOR, minor);
125 mutex_lock(&videodev_lock);
126 vfl = video_device[minor];
127 if (vfl == NULL) {
128 mutex_unlock(&videodev_lock);
129 return -ENODEV;
130 }
131 }
132 old_fops = file->f_op;
133 file->f_op = fops_get(vfl->fops);
134 if (file->f_op->open)
135 err = file->f_op->open(inode, file);
136 if (err) {
137 fops_put(file->f_op);
138 file->f_op = fops_get(old_fops);
139 }
140 fops_put(old_fops);
141 mutex_unlock(&videodev_lock);
142 return err;
143}
144
145/** 137/**
146 * get_index - assign stream number based on parent device 138 * get_index - assign stream number based on parent device
147 * @vdev: video_device to assign index number to, vdev->dev should be assigned 139 * @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 return -EINVAL; 253 return -EINVAL;
262 } 254 }
263 255
256 /* Initialize the character device */
257 cdev_init(&vfd->cdev, vfd->fops);
258 vfd->cdev.owner = vfd->fops->owner;
264 /* pick a minor number */ 259 /* pick a minor number */
265 mutex_lock(&videodev_lock); 260 mutex_lock(&videodev_lock);
266 if (nr >= 0 && nr < end-base) { 261 if (nr >= 0 && nr < end-base) {
@@ -294,6 +289,11 @@ int video_register_device_index(struct video_device *vfd, int type, int nr,
294 goto fail_minor; 289 goto fail_minor;
295 } 290 }
296 291
292 ret = cdev_add(&vfd->cdev, MKDEV(VIDEO_MAJOR, vfd->minor), 1);
293 if (ret < 0) {
294 printk(KERN_ERR "%s: cdev_add failed\n", __func__);
295 goto fail_minor;
296 }
297 /* sysfs class */ 297 /* sysfs class */
298 memset(&vfd->dev, 0, sizeof(vfd->dev)); 298 memset(&vfd->dev, 0, sizeof(vfd->dev));
299 /* The memset above cleared the device's drvdata, so 299 /* 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 ret = device_register(&vfd->dev); 307 ret = device_register(&vfd->dev);
308 if (ret < 0) { 308 if (ret < 0) {
309 printk(KERN_ERR "%s: device_register failed\n", __func__); 309 printk(KERN_ERR "%s: device_register failed\n", __func__);
310 goto fail_minor; 310 goto del_cdev;
311 } 311 }
312 312 /* Remember the cdev's release function */
313 vfd->cdev_release = vfd->cdev.kobj.ktype->release;
314 /* Install our own */
315 vfd->cdev.kobj.ktype = &v4l2_ktype_cdev_default;
313 return 0; 316 return 0;
314 317
318del_cdev:
319 cdev_del(&vfd->cdev);
320
315fail_minor: 321fail_minor:
316 mutex_lock(&videodev_lock); 322 mutex_lock(&videodev_lock);
317 video_device[vfd->minor] = NULL; 323 video_device[vfd->minor] = NULL;
@@ -331,42 +337,29 @@ EXPORT_SYMBOL(video_register_device_index);
331 337
332void video_unregister_device(struct video_device *vfd) 338void video_unregister_device(struct video_device *vfd)
333{ 339{
334 mutex_lock(&videodev_lock);
335 if (video_device[vfd->minor] != vfd)
336 panic("videodev: bad unregister");
337
338 video_device[vfd->minor] = NULL;
339 device_unregister(&vfd->dev); 340 device_unregister(&vfd->dev);
340 mutex_unlock(&videodev_lock);
341} 341}
342EXPORT_SYMBOL(video_unregister_device); 342EXPORT_SYMBOL(video_unregister_device);
343 343
344/* 344/*
345 * Video fs operations
346 */
347static const struct file_operations video_fops = {
348 .owner = THIS_MODULE,
349 .llseek = no_llseek,
350 .open = video_open,
351};
352
353/*
354 * Initialise video for linux 345 * Initialise video for linux
355 */ 346 */
356
357static int __init videodev_init(void) 347static int __init videodev_init(void)
358{ 348{
349 dev_t dev = MKDEV(VIDEO_MAJOR, 0);
359 int ret; 350 int ret;
360 351
361 printk(KERN_INFO "Linux video capture interface: v2.00\n"); 352 printk(KERN_INFO "Linux video capture interface: v2.00\n");
362 if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, &video_fops)) { 353 ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
363 printk(KERN_WARNING "video_dev: unable to get major %d\n", VIDEO_MAJOR); 354 if (ret < 0) {
364 return -EIO; 355 printk(KERN_WARNING "videodev: unable to get major %d\n",
356 VIDEO_MAJOR);
357 return ret;
365 } 358 }
366 359
367 ret = class_register(&video_class); 360 ret = class_register(&video_class);
368 if (ret < 0) { 361 if (ret < 0) {
369 unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME); 362 unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
370 printk(KERN_WARNING "video_dev: class_register failed\n"); 363 printk(KERN_WARNING "video_dev: class_register failed\n");
371 return -EIO; 364 return -EIO;
372 } 365 }
@@ -376,8 +369,10 @@ static int __init videodev_init(void)
376 369
377static void __exit videodev_exit(void) 370static void __exit videodev_exit(void)
378{ 371{
372 dev_t dev = MKDEV(VIDEO_MAJOR, 0);
373
379 class_unregister(&video_class); 374 class_unregister(&video_class);
380 unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME); 375 unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
381} 376}
382 377
383module_init(videodev_init) 378module_init(videodev_init)
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index fb92e3d22d7e..d129b56e1a9f 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -12,6 +12,7 @@
12#include <linux/poll.h> 12#include <linux/poll.h>
13#include <linux/fs.h> 13#include <linux/fs.h>
14#include <linux/device.h> 14#include <linux/device.h>
15#include <linux/cdev.h>
15#include <linux/mutex.h> 16#include <linux/mutex.h>
16#include <linux/videodev2.h> 17#include <linux/videodev2.h>
17 18
@@ -47,6 +48,8 @@ struct video_device
47 48
48 /* sysfs */ 49 /* sysfs */
49 struct device dev; /* v4l device */ 50 struct device dev; /* v4l device */
51 struct cdev cdev; /* character device */
52 void (*cdev_release)(struct kobject *kobj);
50 struct device *parent; /* device parent */ 53 struct device *parent; /* device parent */
51 54
52 /* device info */ 55 /* device info */