diff options
Diffstat (limited to 'drivers/media/video/v4l2-dev.c')
-rw-r--r-- | drivers/media/video/v4l2-dev.c | 365 |
1 files changed, 271 insertions, 94 deletions
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index ccd6566a515e..7ad6711ee327 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c | |||
@@ -9,7 +9,7 @@ | |||
9 | * as published by the Free Software Foundation; either version | 9 | * as published by the Free Software Foundation; either version |
10 | * 2 of the License, or (at your option) any later version. | 10 | * 2 of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * Authors: Alan Cox, <alan@redhat.com> (version 1) | 12 | * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1) |
13 | * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2) | 13 | * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2) |
14 | * | 14 | * |
15 | * Fixes: 20000516 Claudio Matsuoka <claudio@conectiva.com> | 15 | * Fixes: 20000516 Claudio Matsuoka <claudio@conectiva.com> |
@@ -30,6 +30,7 @@ | |||
30 | #include <asm/system.h> | 30 | #include <asm/system.h> |
31 | 31 | ||
32 | #include <media/v4l2-common.h> | 32 | #include <media/v4l2-common.h> |
33 | #include <media/v4l2-device.h> | ||
33 | 34 | ||
34 | #define VIDEO_NUM_DEVICES 256 | 35 | #define VIDEO_NUM_DEVICES 256 |
35 | #define VIDEO_NAME "video4linux" | 36 | #define VIDEO_NAME "video4linux" |
@@ -41,17 +42,17 @@ | |||
41 | static ssize_t show_index(struct device *cd, | 42 | static ssize_t show_index(struct device *cd, |
42 | struct device_attribute *attr, char *buf) | 43 | struct device_attribute *attr, char *buf) |
43 | { | 44 | { |
44 | struct video_device *vfd = container_of(cd, struct video_device, dev); | 45 | struct video_device *vdev = to_video_device(cd); |
45 | 46 | ||
46 | return sprintf(buf, "%i\n", vfd->index); | 47 | return sprintf(buf, "%i\n", vdev->index); |
47 | } | 48 | } |
48 | 49 | ||
49 | static ssize_t show_name(struct device *cd, | 50 | static ssize_t show_name(struct device *cd, |
50 | struct device_attribute *attr, char *buf) | 51 | struct device_attribute *attr, char *buf) |
51 | { | 52 | { |
52 | struct video_device *vfd = container_of(cd, struct video_device, dev); | 53 | struct video_device *vdev = to_video_device(cd); |
53 | 54 | ||
54 | return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name); | 55 | return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name); |
55 | } | 56 | } |
56 | 57 | ||
57 | static struct device_attribute video_device_attrs[] = { | 58 | static struct device_attribute video_device_attrs[] = { |
@@ -73,64 +74,64 @@ struct video_device *video_device_alloc(void) | |||
73 | } | 74 | } |
74 | EXPORT_SYMBOL(video_device_alloc); | 75 | EXPORT_SYMBOL(video_device_alloc); |
75 | 76 | ||
76 | void video_device_release(struct video_device *vfd) | 77 | void video_device_release(struct video_device *vdev) |
77 | { | 78 | { |
78 | kfree(vfd); | 79 | kfree(vdev); |
79 | } | 80 | } |
80 | EXPORT_SYMBOL(video_device_release); | 81 | EXPORT_SYMBOL(video_device_release); |
81 | 82 | ||
82 | void video_device_release_empty(struct video_device *vfd) | 83 | void video_device_release_empty(struct video_device *vdev) |
83 | { | 84 | { |
84 | /* Do nothing */ | 85 | /* Do nothing */ |
85 | /* Only valid when the video_device struct is a static. */ | 86 | /* Only valid when the video_device struct is a static. */ |
86 | } | 87 | } |
87 | EXPORT_SYMBOL(video_device_release_empty); | 88 | EXPORT_SYMBOL(video_device_release_empty); |
88 | 89 | ||
89 | /* Called when the last user of the character device is gone. */ | 90 | static inline void video_get(struct video_device *vdev) |
90 | static void v4l2_chardev_release(struct kobject *kobj) | ||
91 | { | 91 | { |
92 | struct video_device *vfd = container_of(kobj, struct video_device, cdev.kobj); | 92 | get_device(&vdev->dev); |
93 | } | ||
94 | |||
95 | static inline void video_put(struct video_device *vdev) | ||
96 | { | ||
97 | put_device(&vdev->dev); | ||
98 | } | ||
99 | |||
100 | /* Called when the last user of the video device exits. */ | ||
101 | static void v4l2_device_release(struct device *cd) | ||
102 | { | ||
103 | struct video_device *vdev = to_video_device(cd); | ||
93 | 104 | ||
94 | mutex_lock(&videodev_lock); | 105 | mutex_lock(&videodev_lock); |
95 | if (video_device[vfd->minor] != vfd) { | 106 | if (video_device[vdev->minor] != vdev) { |
96 | mutex_unlock(&videodev_lock); | 107 | mutex_unlock(&videodev_lock); |
97 | BUG(); | 108 | /* should not happen */ |
109 | WARN_ON(1); | ||
98 | return; | 110 | return; |
99 | } | 111 | } |
100 | 112 | ||
101 | /* Free up this device for reuse */ | 113 | /* Free up this device for reuse */ |
102 | video_device[vfd->minor] = NULL; | 114 | video_device[vdev->minor] = NULL; |
103 | clear_bit(vfd->num, video_nums[vfd->vfl_type]); | ||
104 | mutex_unlock(&videodev_lock); | ||
105 | 115 | ||
106 | /* Release the character device */ | 116 | /* Delete the cdev on this minor as well */ |
107 | vfd->cdev_release(kobj); | 117 | cdev_del(vdev->cdev); |
108 | /* Release video_device and perform other | 118 | /* Just in case some driver tries to access this from |
109 | cleanups as needed. */ | 119 | the release() callback. */ |
110 | if (vfd->release) | 120 | vdev->cdev = NULL; |
111 | vfd->release(vfd); | ||
112 | } | ||
113 | 121 | ||
114 | /* The new kobj_type for the character device */ | 122 | /* Mark minor as free */ |
115 | static struct kobj_type v4l2_ktype_cdev_default = { | 123 | clear_bit(vdev->num, video_nums[vdev->vfl_type]); |
116 | .release = v4l2_chardev_release, | ||
117 | }; | ||
118 | 124 | ||
119 | static void video_release(struct device *cd) | 125 | mutex_unlock(&videodev_lock); |
120 | { | ||
121 | struct video_device *vfd = container_of(cd, struct video_device, dev); | ||
122 | 126 | ||
123 | /* It's now safe to delete the char device. | 127 | /* Release video_device and perform other |
124 | This will either trigger the v4l2_chardev_release immediately (if | 128 | cleanups as needed. */ |
125 | the refcount goes to 0) or later when the last user of the | 129 | vdev->release(vdev); |
126 | character device closes it. */ | ||
127 | cdev_del(&vfd->cdev); | ||
128 | } | 130 | } |
129 | 131 | ||
130 | static struct class video_class = { | 132 | static struct class video_class = { |
131 | .name = VIDEO_NAME, | 133 | .name = VIDEO_NAME, |
132 | .dev_attrs = video_device_attrs, | 134 | .dev_attrs = video_device_attrs, |
133 | .dev_release = video_release, | ||
134 | }; | 135 | }; |
135 | 136 | ||
136 | struct video_device *video_devdata(struct file *file) | 137 | struct video_device *video_devdata(struct file *file) |
@@ -139,13 +140,163 @@ struct video_device *video_devdata(struct file *file) | |||
139 | } | 140 | } |
140 | EXPORT_SYMBOL(video_devdata); | 141 | EXPORT_SYMBOL(video_devdata); |
141 | 142 | ||
143 | static ssize_t v4l2_read(struct file *filp, char __user *buf, | ||
144 | size_t sz, loff_t *off) | ||
145 | { | ||
146 | struct video_device *vdev = video_devdata(filp); | ||
147 | |||
148 | if (!vdev->fops->read) | ||
149 | return -EINVAL; | ||
150 | if (video_is_unregistered(vdev)) | ||
151 | return -EIO; | ||
152 | return vdev->fops->read(filp, buf, sz, off); | ||
153 | } | ||
154 | |||
155 | static ssize_t v4l2_write(struct file *filp, const char __user *buf, | ||
156 | size_t sz, loff_t *off) | ||
157 | { | ||
158 | struct video_device *vdev = video_devdata(filp); | ||
159 | |||
160 | if (!vdev->fops->write) | ||
161 | return -EINVAL; | ||
162 | if (video_is_unregistered(vdev)) | ||
163 | return -EIO; | ||
164 | return vdev->fops->write(filp, buf, sz, off); | ||
165 | } | ||
166 | |||
167 | static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) | ||
168 | { | ||
169 | struct video_device *vdev = video_devdata(filp); | ||
170 | |||
171 | if (!vdev->fops->poll || video_is_unregistered(vdev)) | ||
172 | return DEFAULT_POLLMASK; | ||
173 | return vdev->fops->poll(filp, poll); | ||
174 | } | ||
175 | |||
176 | static int v4l2_ioctl(struct inode *inode, struct file *filp, | ||
177 | unsigned int cmd, unsigned long arg) | ||
178 | { | ||
179 | struct video_device *vdev = video_devdata(filp); | ||
180 | |||
181 | if (!vdev->fops->ioctl) | ||
182 | return -ENOTTY; | ||
183 | /* Allow ioctl to continue even if the device was unregistered. | ||
184 | Things like dequeueing buffers might still be useful. */ | ||
185 | return vdev->fops->ioctl(inode, filp, cmd, arg); | ||
186 | } | ||
187 | |||
188 | static long v4l2_unlocked_ioctl(struct file *filp, | ||
189 | unsigned int cmd, unsigned long arg) | ||
190 | { | ||
191 | struct video_device *vdev = video_devdata(filp); | ||
192 | |||
193 | if (!vdev->fops->unlocked_ioctl) | ||
194 | return -ENOTTY; | ||
195 | /* Allow ioctl to continue even if the device was unregistered. | ||
196 | Things like dequeueing buffers might still be useful. */ | ||
197 | return vdev->fops->unlocked_ioctl(filp, cmd, arg); | ||
198 | } | ||
199 | |||
200 | #ifdef CONFIG_COMPAT | ||
201 | static long v4l2_compat_ioctl(struct file *filp, | ||
202 | unsigned int cmd, unsigned long arg) | ||
203 | { | ||
204 | struct video_device *vdev = video_devdata(filp); | ||
205 | |||
206 | if (!vdev->fops->compat_ioctl) | ||
207 | return -ENOIOCTLCMD; | ||
208 | /* Allow ioctl to continue even if the device was unregistered. | ||
209 | Things like dequeueing buffers might still be useful. */ | ||
210 | return vdev->fops->compat_ioctl(filp, cmd, arg); | ||
211 | } | ||
212 | #endif | ||
213 | |||
214 | static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) | ||
215 | { | ||
216 | struct video_device *vdev = video_devdata(filp); | ||
217 | |||
218 | if (!vdev->fops->mmap || | ||
219 | video_is_unregistered(vdev)) | ||
220 | return -ENODEV; | ||
221 | return vdev->fops->mmap(filp, vm); | ||
222 | } | ||
223 | |||
224 | /* Override for the open function */ | ||
225 | static int v4l2_open(struct inode *inode, struct file *filp) | ||
226 | { | ||
227 | struct video_device *vdev; | ||
228 | int ret; | ||
229 | |||
230 | /* Check if the video device is available */ | ||
231 | mutex_lock(&videodev_lock); | ||
232 | vdev = video_devdata(filp); | ||
233 | /* return ENODEV if the video device has been removed | ||
234 | already or if it is not registered anymore. */ | ||
235 | if (vdev == NULL || video_is_unregistered(vdev)) { | ||
236 | mutex_unlock(&videodev_lock); | ||
237 | return -ENODEV; | ||
238 | } | ||
239 | /* and increase the device refcount */ | ||
240 | video_get(vdev); | ||
241 | mutex_unlock(&videodev_lock); | ||
242 | ret = vdev->fops->open(inode, filp); | ||
243 | /* decrease the refcount in case of an error */ | ||
244 | if (ret) | ||
245 | video_put(vdev); | ||
246 | return ret; | ||
247 | } | ||
248 | |||
249 | /* Override for the release function */ | ||
250 | static int v4l2_release(struct inode *inode, struct file *filp) | ||
251 | { | ||
252 | struct video_device *vdev = video_devdata(filp); | ||
253 | int ret = vdev->fops->release(inode, filp); | ||
254 | |||
255 | /* decrease the refcount unconditionally since the release() | ||
256 | return value is ignored. */ | ||
257 | video_put(vdev); | ||
258 | return ret; | ||
259 | } | ||
260 | |||
261 | static const struct file_operations v4l2_unlocked_fops = { | ||
262 | .owner = THIS_MODULE, | ||
263 | .read = v4l2_read, | ||
264 | .write = v4l2_write, | ||
265 | .open = v4l2_open, | ||
266 | .mmap = v4l2_mmap, | ||
267 | .unlocked_ioctl = v4l2_unlocked_ioctl, | ||
268 | #ifdef CONFIG_COMPAT | ||
269 | .compat_ioctl = v4l2_compat_ioctl, | ||
270 | #endif | ||
271 | .release = v4l2_release, | ||
272 | .poll = v4l2_poll, | ||
273 | .llseek = no_llseek, | ||
274 | }; | ||
275 | |||
276 | static const struct file_operations v4l2_fops = { | ||
277 | .owner = THIS_MODULE, | ||
278 | .read = v4l2_read, | ||
279 | .write = v4l2_write, | ||
280 | .open = v4l2_open, | ||
281 | .mmap = v4l2_mmap, | ||
282 | .ioctl = v4l2_ioctl, | ||
283 | #ifdef CONFIG_COMPAT | ||
284 | .compat_ioctl = v4l2_compat_ioctl, | ||
285 | #endif | ||
286 | .release = v4l2_release, | ||
287 | .poll = v4l2_poll, | ||
288 | .llseek = no_llseek, | ||
289 | }; | ||
290 | |||
142 | /** | 291 | /** |
143 | * get_index - assign stream number based on parent device | 292 | * get_index - assign stream number based on parent device |
144 | * @vdev: video_device to assign index number to, vdev->dev should be assigned | 293 | * @vdev: video_device to assign index number to, vdev->parent should be assigned |
145 | * @num: -1 if auto assign, requested number otherwise | 294 | * @num: -1 if auto assign, requested number otherwise |
146 | * | 295 | * |
296 | * Note that when this is called the new device has not yet been registered | ||
297 | * in the video_device array. | ||
147 | * | 298 | * |
148 | * returns -ENFILE if num is already in use, a free index number if | 299 | * Returns -ENFILE if num is already in use, a free index number if |
149 | * successful. | 300 | * successful. |
150 | */ | 301 | */ |
151 | static int get_index(struct video_device *vdev, int num) | 302 | static int get_index(struct video_device *vdev, int num) |
@@ -162,9 +313,12 @@ static int get_index(struct video_device *vdev, int num) | |||
162 | return -EINVAL; | 313 | return -EINVAL; |
163 | } | 314 | } |
164 | 315 | ||
316 | /* Some drivers do not set the parent. In that case always return 0. */ | ||
317 | if (vdev->parent == NULL) | ||
318 | return 0; | ||
319 | |||
165 | for (i = 0; i < VIDEO_NUM_DEVICES; i++) { | 320 | for (i = 0; i < VIDEO_NUM_DEVICES; i++) { |
166 | if (video_device[i] != NULL && | 321 | if (video_device[i] != NULL && |
167 | video_device[i] != vdev && | ||
168 | video_device[i]->parent == vdev->parent) { | 322 | video_device[i]->parent == vdev->parent) { |
169 | used |= 1 << video_device[i]->index; | 323 | used |= 1 << video_device[i]->index; |
170 | } | 324 | } |
@@ -180,17 +334,15 @@ static int get_index(struct video_device *vdev, int num) | |||
180 | return i > max_index ? -ENFILE : i; | 334 | return i > max_index ? -ENFILE : i; |
181 | } | 335 | } |
182 | 336 | ||
183 | static const struct file_operations video_fops; | 337 | int video_register_device(struct video_device *vdev, int type, int nr) |
184 | |||
185 | int video_register_device(struct video_device *vfd, int type, int nr) | ||
186 | { | 338 | { |
187 | return video_register_device_index(vfd, type, nr, -1); | 339 | return video_register_device_index(vdev, type, nr, -1); |
188 | } | 340 | } |
189 | EXPORT_SYMBOL(video_register_device); | 341 | EXPORT_SYMBOL(video_register_device); |
190 | 342 | ||
191 | /** | 343 | /** |
192 | * video_register_device_index - register video4linux devices | 344 | * video_register_device_index - register video4linux devices |
193 | * @vfd: video device structure we want to register | 345 | * @vdev: video device structure we want to register |
194 | * @type: type of device to register | 346 | * @type: type of device to register |
195 | * @nr: which device number (0 == /dev/video0, 1 == /dev/video1, ... | 347 | * @nr: which device number (0 == /dev/video0, 1 == /dev/video1, ... |
196 | * -1 == first free) | 348 | * -1 == first free) |
@@ -214,8 +366,7 @@ EXPORT_SYMBOL(video_register_device); | |||
214 | * | 366 | * |
215 | * %VFL_TYPE_RADIO - A radio card | 367 | * %VFL_TYPE_RADIO - A radio card |
216 | */ | 368 | */ |
217 | 369 | int video_register_device_index(struct video_device *vdev, int type, int nr, | |
218 | int video_register_device_index(struct video_device *vfd, int type, int nr, | ||
219 | int index) | 370 | int index) |
220 | { | 371 | { |
221 | int i = 0; | 372 | int i = 0; |
@@ -223,14 +374,19 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, | |||
223 | int minor_offset = 0; | 374 | int minor_offset = 0; |
224 | int minor_cnt = VIDEO_NUM_DEVICES; | 375 | int minor_cnt = VIDEO_NUM_DEVICES; |
225 | const char *name_base; | 376 | const char *name_base; |
226 | void *priv = video_get_drvdata(vfd); | 377 | void *priv = video_get_drvdata(vdev); |
227 | 378 | ||
228 | /* the release callback MUST be present */ | 379 | /* A minor value of -1 marks this video device as never |
229 | BUG_ON(!vfd->release); | 380 | having been registered */ |
381 | if (vdev) | ||
382 | vdev->minor = -1; | ||
230 | 383 | ||
231 | if (vfd == NULL) | 384 | /* the release callback MUST be present */ |
385 | WARN_ON(!vdev || !vdev->release); | ||
386 | if (!vdev || !vdev->release) | ||
232 | return -EINVAL; | 387 | return -EINVAL; |
233 | 388 | ||
389 | /* Part 1: check device type */ | ||
234 | switch (type) { | 390 | switch (type) { |
235 | case VFL_TYPE_GRABBER: | 391 | case VFL_TYPE_GRABBER: |
236 | name_base = "video"; | 392 | name_base = "video"; |
@@ -250,8 +406,12 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, | |||
250 | return -EINVAL; | 406 | return -EINVAL; |
251 | } | 407 | } |
252 | 408 | ||
253 | vfd->vfl_type = type; | 409 | vdev->vfl_type = type; |
410 | vdev->cdev = NULL; | ||
411 | if (vdev->v4l2_dev) | ||
412 | vdev->parent = vdev->v4l2_dev->dev; | ||
254 | 413 | ||
414 | /* Part 2: find a free minor, kernel number and device index. */ | ||
255 | #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES | 415 | #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES |
256 | /* Keep the ranges for the first four types for historical | 416 | /* Keep the ranges for the first four types for historical |
257 | * reasons. | 417 | * reasons. |
@@ -282,10 +442,7 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, | |||
282 | } | 442 | } |
283 | #endif | 443 | #endif |
284 | 444 | ||
285 | /* Initialize the character device */ | 445 | /* Pick a minor number */ |
286 | cdev_init(&vfd->cdev, vfd->fops); | ||
287 | vfd->cdev.owner = vfd->fops->owner; | ||
288 | /* pick a minor number */ | ||
289 | mutex_lock(&videodev_lock); | 446 | mutex_lock(&videodev_lock); |
290 | nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr); | 447 | nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr); |
291 | if (nr == minor_cnt) | 448 | if (nr == minor_cnt) |
@@ -309,72 +466,92 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, | |||
309 | return -ENFILE; | 466 | return -ENFILE; |
310 | } | 467 | } |
311 | #endif | 468 | #endif |
312 | vfd->minor = i + minor_offset; | 469 | vdev->minor = i + minor_offset; |
313 | vfd->num = nr; | 470 | vdev->num = nr; |
314 | set_bit(nr, video_nums[type]); | 471 | set_bit(nr, video_nums[type]); |
315 | BUG_ON(video_device[vfd->minor]); | 472 | /* Should not happen since we thought this minor was free */ |
316 | video_device[vfd->minor] = vfd; | 473 | WARN_ON(video_device[vdev->minor] != NULL); |
317 | 474 | ret = vdev->index = get_index(vdev, index); | |
318 | ret = get_index(vfd, index); | ||
319 | vfd->index = ret; | ||
320 | |||
321 | mutex_unlock(&videodev_lock); | 475 | mutex_unlock(&videodev_lock); |
322 | 476 | ||
323 | if (ret < 0) { | 477 | if (ret < 0) { |
324 | printk(KERN_ERR "%s: get_index failed\n", __func__); | 478 | printk(KERN_ERR "%s: get_index failed\n", __func__); |
325 | goto fail_minor; | 479 | goto cleanup; |
326 | } | 480 | } |
327 | 481 | ||
328 | ret = cdev_add(&vfd->cdev, MKDEV(VIDEO_MAJOR, vfd->minor), 1); | 482 | /* Part 3: Initialize the character device */ |
483 | vdev->cdev = cdev_alloc(); | ||
484 | if (vdev->cdev == NULL) { | ||
485 | ret = -ENOMEM; | ||
486 | goto cleanup; | ||
487 | } | ||
488 | if (vdev->fops->unlocked_ioctl) | ||
489 | vdev->cdev->ops = &v4l2_unlocked_fops; | ||
490 | else | ||
491 | vdev->cdev->ops = &v4l2_fops; | ||
492 | vdev->cdev->owner = vdev->fops->owner; | ||
493 | ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); | ||
329 | if (ret < 0) { | 494 | if (ret < 0) { |
330 | printk(KERN_ERR "%s: cdev_add failed\n", __func__); | 495 | printk(KERN_ERR "%s: cdev_add failed\n", __func__); |
331 | goto fail_minor; | 496 | kfree(vdev->cdev); |
497 | vdev->cdev = NULL; | ||
498 | goto cleanup; | ||
332 | } | 499 | } |
333 | /* sysfs class */ | 500 | |
334 | memset(&vfd->dev, 0, sizeof(vfd->dev)); | 501 | /* Part 4: register the device with sysfs */ |
502 | memset(&vdev->dev, 0, sizeof(vdev->dev)); | ||
335 | /* The memset above cleared the device's drvdata, so | 503 | /* The memset above cleared the device's drvdata, so |
336 | put back the copy we made earlier. */ | 504 | put back the copy we made earlier. */ |
337 | video_set_drvdata(vfd, priv); | 505 | video_set_drvdata(vdev, priv); |
338 | vfd->dev.class = &video_class; | 506 | vdev->dev.class = &video_class; |
339 | vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor); | 507 | vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); |
340 | if (vfd->parent) | 508 | if (vdev->parent) |
341 | vfd->dev.parent = vfd->parent; | 509 | vdev->dev.parent = vdev->parent; |
342 | sprintf(vfd->dev.bus_id, "%s%d", name_base, nr); | 510 | dev_set_name(&vdev->dev, "%s%d", name_base, nr); |
343 | ret = device_register(&vfd->dev); | 511 | ret = device_register(&vdev->dev); |
344 | if (ret < 0) { | 512 | if (ret < 0) { |
345 | printk(KERN_ERR "%s: device_register failed\n", __func__); | 513 | printk(KERN_ERR "%s: device_register failed\n", __func__); |
346 | goto del_cdev; | 514 | goto cleanup; |
347 | } | 515 | } |
348 | /* Remember the cdev's release function */ | 516 | /* Register the release callback that will be called when the last |
349 | vfd->cdev_release = vfd->cdev.kobj.ktype->release; | 517 | reference to the device goes away. */ |
350 | /* Install our own */ | 518 | vdev->dev.release = v4l2_device_release; |
351 | vfd->cdev.kobj.ktype = &v4l2_ktype_cdev_default; | ||
352 | return 0; | ||
353 | 519 | ||
354 | del_cdev: | 520 | /* Part 5: Activate this minor. The char device can now be used. */ |
355 | cdev_del(&vfd->cdev); | 521 | mutex_lock(&videodev_lock); |
522 | video_device[vdev->minor] = vdev; | ||
523 | mutex_unlock(&videodev_lock); | ||
524 | return 0; | ||
356 | 525 | ||
357 | fail_minor: | 526 | cleanup: |
358 | mutex_lock(&videodev_lock); | 527 | mutex_lock(&videodev_lock); |
359 | video_device[vfd->minor] = NULL; | 528 | if (vdev->cdev) |
360 | clear_bit(vfd->num, video_nums[type]); | 529 | cdev_del(vdev->cdev); |
530 | clear_bit(vdev->num, video_nums[type]); | ||
361 | mutex_unlock(&videodev_lock); | 531 | mutex_unlock(&videodev_lock); |
362 | vfd->minor = -1; | 532 | /* Mark this video device as never having been registered. */ |
533 | vdev->minor = -1; | ||
363 | return ret; | 534 | return ret; |
364 | } | 535 | } |
365 | EXPORT_SYMBOL(video_register_device_index); | 536 | EXPORT_SYMBOL(video_register_device_index); |
366 | 537 | ||
367 | /** | 538 | /** |
368 | * video_unregister_device - unregister a video4linux device | 539 | * video_unregister_device - unregister a video4linux device |
369 | * @vfd: the device to unregister | 540 | * @vdev: the device to unregister |
370 | * | 541 | * |
371 | * This unregisters the passed device and deassigns the minor | 542 | * This unregisters the passed device. Future open calls will |
372 | * number. Future open calls will be met with errors. | 543 | * be met with errors. |
373 | */ | 544 | */ |
374 | 545 | void video_unregister_device(struct video_device *vdev) | |
375 | void video_unregister_device(struct video_device *vfd) | ||
376 | { | 546 | { |
377 | device_unregister(&vfd->dev); | 547 | /* Check if vdev was ever registered at all */ |
548 | if (!vdev || vdev->minor < 0) | ||
549 | return; | ||
550 | |||
551 | mutex_lock(&videodev_lock); | ||
552 | set_bit(V4L2_FL_UNREGISTERED, &vdev->flags); | ||
553 | mutex_unlock(&videodev_lock); | ||
554 | device_unregister(&vdev->dev); | ||
378 | } | 555 | } |
379 | EXPORT_SYMBOL(video_unregister_device); | 556 | EXPORT_SYMBOL(video_unregister_device); |
380 | 557 | ||