diff options
author | Hans Verkuil <hverkuil@xs4all.nl> | 2008-08-29 16:31:35 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-10-12 07:36:56 -0400 |
commit | 7f8ecfab7a1d256707502207c56dd4bf3e3a026e (patch) | |
tree | 2ba6079ba59017d03f53e0ba068e099008ef21d5 | |
parent | 187565c43a95bcf7798c58d518ddd765933d24da (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.c | 131 | ||||
-rw-r--r-- | include/media/v4l2-dev.h | 3 |
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 | */ | ||
66 | static struct video_device *video_device[VIDEO_NUM_DEVICES]; | ||
67 | static DEFINE_MUTEX(videodev_lock); | ||
68 | |||
63 | struct video_device *video_device_alloc(void) | 69 | struct 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 | } |
80 | EXPORT_SYMBOL(video_device_release_empty); | 86 | EXPORT_SYMBOL(video_device_release_empty); |
81 | 87 | ||
88 | /* Called when the last user of the character device is gone. */ | ||
89 | static 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 */ | ||
110 | static struct kobj_type v4l2_ktype_cdev_default = { | ||
111 | .release = v4l2_chardev_release, | ||
112 | }; | ||
113 | |||
82 | static void video_release(struct device *cd) | 114 | static 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 | ||
89 | static struct class video_class = { | 125 | static 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 | |||
99 | static struct video_device *video_device[VIDEO_NUM_DEVICES]; | ||
100 | static DEFINE_MUTEX(videodev_lock); | ||
101 | |||
102 | struct video_device *video_devdata(struct file *file) | 131 | struct 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 | } |
106 | EXPORT_SYMBOL(video_devdata); | 135 | EXPORT_SYMBOL(video_devdata); |
107 | 136 | ||
108 | /* | ||
109 | * Open a video device - FIXME: Obsoleted | ||
110 | */ | ||
111 | static 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 | ||
318 | del_cdev: | ||
319 | cdev_del(&vfd->cdev); | ||
320 | |||
315 | fail_minor: | 321 | fail_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 | ||
332 | void video_unregister_device(struct video_device *vfd) | 338 | void 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 | } |
342 | EXPORT_SYMBOL(video_unregister_device); | 342 | EXPORT_SYMBOL(video_unregister_device); |
343 | 343 | ||
344 | /* | 344 | /* |
345 | * Video fs operations | ||
346 | */ | ||
347 | static 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 | |||
357 | static int __init videodev_init(void) | 347 | static 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 | ||
377 | static void __exit videodev_exit(void) | 370 | static 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 | ||
383 | module_init(videodev_init) | 378 | module_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 */ |