Commit 2096a5dcf9704f5a86ecba37169eb813aaf0431c
Committed by
Mauro Carvalho Chehab
1 parent
0070d91e5b
Exists in
master
and in
4 other branches
[media] v4l: subdev: Add device node support
Create a device node named subdevX for every registered subdev. As the device node is registered before the subdev core::s_config function is called, return -EGAIN on open until initialization completes. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Vimarsh Zutshi <vimarsh.zutshi@gmail.com> Acked-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Showing 8 changed files with 158 additions and 22 deletions Side-by-side Diff
Documentation/video4linux/v4l2-framework.txt
... | ... | @@ -319,6 +319,22 @@ |
319 | 319 | up the device, but once the subdev is registered it is completely transparent. |
320 | 320 | |
321 | 321 | |
322 | +V4L2 sub-device userspace API | |
323 | +----------------------------- | |
324 | + | |
325 | +Beside exposing a kernel API through the v4l2_subdev_ops structure, V4L2 | |
326 | +sub-devices can also be controlled directly by userspace applications. | |
327 | + | |
328 | +Device nodes named v4l-subdevX can be created in /dev to access sub-devices | |
329 | +directly. If a sub-device supports direct userspace configuration it must set | |
330 | +the V4L2_SUBDEV_FL_HAS_DEVNODE flag before being registered. | |
331 | + | |
332 | +After registering sub-devices, the v4l2_device driver can create device nodes | |
333 | +for all registered sub-devices marked with V4L2_SUBDEV_FL_HAS_DEVNODE by calling | |
334 | +v4l2_device_register_subdev_nodes(). Those device nodes will be automatically | |
335 | +removed when sub-devices are unregistered. | |
336 | + | |
337 | + | |
322 | 338 | I2C sub-device drivers |
323 | 339 | ---------------------- |
324 | 340 |
drivers/media/video/Makefile
drivers/media/video/v4l2-dev.c
... | ... | @@ -408,13 +408,14 @@ |
408 | 408 | } |
409 | 409 | |
410 | 410 | /** |
411 | - * video_register_device - register video4linux devices | |
411 | + * __video_register_device - register video4linux devices | |
412 | 412 | * @vdev: video device structure we want to register |
413 | 413 | * @type: type of device to register |
414 | 414 | * @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ... |
415 | 415 | * -1 == first free) |
416 | 416 | * @warn_if_nr_in_use: warn if the desired device node number |
417 | 417 | * was already in use and another number was chosen instead. |
418 | + * @owner: module that owns the video device node | |
418 | 419 | * |
419 | 420 | * The registration code assigns minor numbers and device node numbers |
420 | 421 | * based on the requested type and registers the new device node with |
421 | 422 | |
... | ... | @@ -435,9 +436,11 @@ |
435 | 436 | * %VFL_TYPE_VBI - Vertical blank data (undecoded) |
436 | 437 | * |
437 | 438 | * %VFL_TYPE_RADIO - A radio card |
439 | + * | |
440 | + * %VFL_TYPE_SUBDEV - A subdevice | |
438 | 441 | */ |
439 | -static int __video_register_device(struct video_device *vdev, int type, int nr, | |
440 | - int warn_if_nr_in_use) | |
442 | +int __video_register_device(struct video_device *vdev, int type, int nr, | |
443 | + int warn_if_nr_in_use, struct module *owner) | |
441 | 444 | { |
442 | 445 | int i = 0; |
443 | 446 | int ret; |
... | ... | @@ -469,6 +472,9 @@ |
469 | 472 | case VFL_TYPE_RADIO: |
470 | 473 | name_base = "radio"; |
471 | 474 | break; |
475 | + case VFL_TYPE_SUBDEV: | |
476 | + name_base = "v4l-subdev"; | |
477 | + break; | |
472 | 478 | default: |
473 | 479 | printk(KERN_ERR "%s called with unknown type: %d\n", |
474 | 480 | __func__, type); |
... | ... | @@ -552,7 +558,7 @@ |
552 | 558 | goto cleanup; |
553 | 559 | } |
554 | 560 | vdev->cdev->ops = &v4l2_fops; |
555 | - vdev->cdev->owner = vdev->fops->owner; | |
561 | + vdev->cdev->owner = owner; | |
556 | 562 | ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); |
557 | 563 | if (ret < 0) { |
558 | 564 | printk(KERN_ERR "%s: cdev_add failed\n", __func__); |
... | ... | @@ -597,18 +603,7 @@ |
597 | 603 | vdev->minor = -1; |
598 | 604 | return ret; |
599 | 605 | } |
600 | - | |
601 | -int video_register_device(struct video_device *vdev, int type, int nr) | |
602 | -{ | |
603 | - return __video_register_device(vdev, type, nr, 1); | |
604 | -} | |
605 | -EXPORT_SYMBOL(video_register_device); | |
606 | - | |
607 | -int video_register_device_no_warn(struct video_device *vdev, int type, int nr) | |
608 | -{ | |
609 | - return __video_register_device(vdev, type, nr, 0); | |
610 | -} | |
611 | -EXPORT_SYMBOL(video_register_device_no_warn); | |
606 | +EXPORT_SYMBOL(__video_register_device); | |
612 | 607 | |
613 | 608 | /** |
614 | 609 | * video_unregister_device - unregister a video4linux device |
drivers/media/video/v4l2-device.c
... | ... | @@ -124,16 +124,20 @@ |
124 | 124 | /* Check for valid input */ |
125 | 125 | if (v4l2_dev == NULL || sd == NULL || !sd->name[0]) |
126 | 126 | return -EINVAL; |
127 | + | |
127 | 128 | /* Warn if we apparently re-register a subdev */ |
128 | 129 | WARN_ON(sd->v4l2_dev != NULL); |
130 | + | |
129 | 131 | if (!try_module_get(sd->owner)) |
130 | 132 | return -ENODEV; |
133 | + | |
131 | 134 | sd->v4l2_dev = v4l2_dev; |
132 | 135 | if (sd->internal_ops && sd->internal_ops->registered) { |
133 | 136 | err = sd->internal_ops->registered(sd); |
134 | 137 | if (err) |
135 | 138 | return err; |
136 | 139 | } |
140 | + | |
137 | 141 | /* This just returns 0 if either of the two args is NULL */ |
138 | 142 | err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler); |
139 | 143 | if (err) { |
140 | 144 | |
141 | 145 | |
142 | 146 | |
143 | 147 | |
... | ... | @@ -141,24 +145,57 @@ |
141 | 145 | sd->internal_ops->unregistered(sd); |
142 | 146 | return err; |
143 | 147 | } |
148 | + | |
144 | 149 | spin_lock(&v4l2_dev->lock); |
145 | 150 | list_add_tail(&sd->list, &v4l2_dev->subdevs); |
146 | 151 | spin_unlock(&v4l2_dev->lock); |
152 | + | |
147 | 153 | return 0; |
148 | 154 | } |
149 | 155 | EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); |
150 | 156 | |
157 | +int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) | |
158 | +{ | |
159 | + struct video_device *vdev; | |
160 | + struct v4l2_subdev *sd; | |
161 | + int err; | |
162 | + | |
163 | + /* Register a device node for every subdev marked with the | |
164 | + * V4L2_SUBDEV_FL_HAS_DEVNODE flag. | |
165 | + */ | |
166 | + list_for_each_entry(sd, &v4l2_dev->subdevs, list) { | |
167 | + if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)) | |
168 | + continue; | |
169 | + | |
170 | + vdev = &sd->devnode; | |
171 | + strlcpy(vdev->name, sd->name, sizeof(vdev->name)); | |
172 | + vdev->v4l2_dev = v4l2_dev; | |
173 | + vdev->fops = &v4l2_subdev_fops; | |
174 | + vdev->release = video_device_release_empty; | |
175 | + err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, | |
176 | + sd->owner); | |
177 | + if (err < 0) | |
178 | + return err; | |
179 | + } | |
180 | + | |
181 | + return 0; | |
182 | +} | |
183 | +EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes); | |
184 | + | |
151 | 185 | void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) |
152 | 186 | { |
153 | 187 | /* return if it isn't registered */ |
154 | 188 | if (sd == NULL || sd->v4l2_dev == NULL) |
155 | 189 | return; |
190 | + | |
156 | 191 | spin_lock(&sd->v4l2_dev->lock); |
157 | 192 | list_del(&sd->list); |
158 | 193 | spin_unlock(&sd->v4l2_dev->lock); |
159 | 194 | if (sd->internal_ops && sd->internal_ops->unregistered) |
160 | 195 | sd->internal_ops->unregistered(sd); |
161 | 196 | sd->v4l2_dev = NULL; |
197 | + | |
198 | + video_unregister_device(&sd->devnode); | |
162 | 199 | module_put(sd->owner); |
163 | 200 | } |
164 | 201 | EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); |
drivers/media/video/v4l2-subdev.c
1 | +/* | |
2 | + * V4L2 subdevice support. | |
3 | + * | |
4 | + * Copyright (C) 2010 Nokia Corporation | |
5 | + * | |
6 | + * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License as published by | |
10 | + * the Free Software Foundation. | |
11 | + * | |
12 | + * This program is distributed in the hope that it will be useful, | |
13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | + * GNU General Public License for more details. | |
16 | + * | |
17 | + * You should have received a copy of the GNU General Public License | |
18 | + * along with this program; if not, write to the Free Software | |
19 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
20 | + */ | |
21 | + | |
22 | +#include <linux/types.h> | |
23 | +#include <linux/ioctl.h> | |
24 | +#include <linux/videodev2.h> | |
25 | + | |
26 | +#include <media/v4l2-device.h> | |
27 | +#include <media/v4l2-ioctl.h> | |
28 | + | |
29 | +static int subdev_open(struct file *file) | |
30 | +{ | |
31 | + return 0; | |
32 | +} | |
33 | + | |
34 | +static int subdev_close(struct file *file) | |
35 | +{ | |
36 | + return 0; | |
37 | +} | |
38 | + | |
39 | +static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |
40 | +{ | |
41 | + switch (cmd) { | |
42 | + default: | |
43 | + return -ENOIOCTLCMD; | |
44 | + } | |
45 | + | |
46 | + return 0; | |
47 | +} | |
48 | + | |
49 | +static long subdev_ioctl(struct file *file, unsigned int cmd, | |
50 | + unsigned long arg) | |
51 | +{ | |
52 | + return video_usercopy(file, cmd, arg, subdev_do_ioctl); | |
53 | +} | |
54 | + | |
55 | +const struct v4l2_file_operations v4l2_subdev_fops = { | |
56 | + .owner = THIS_MODULE, | |
57 | + .open = subdev_open, | |
58 | + .unlocked_ioctl = subdev_ioctl, | |
59 | + .release = subdev_close, | |
60 | +}; |
include/media/v4l2-dev.h
... | ... | @@ -21,7 +21,8 @@ |
21 | 21 | #define VFL_TYPE_GRABBER 0 |
22 | 22 | #define VFL_TYPE_VBI 1 |
23 | 23 | #define VFL_TYPE_RADIO 2 |
24 | -#define VFL_TYPE_MAX 3 | |
24 | +#define VFL_TYPE_SUBDEV 3 | |
25 | +#define VFL_TYPE_MAX 4 | |
25 | 26 | |
26 | 27 | struct v4l2_ioctl_callbacks; |
27 | 28 | struct video_device; |
28 | 29 | |
29 | 30 | |
... | ... | @@ -102,15 +103,26 @@ |
102 | 103 | /* dev to video-device */ |
103 | 104 | #define to_video_device(cd) container_of(cd, struct video_device, dev) |
104 | 105 | |
106 | +int __must_check __video_register_device(struct video_device *vdev, int type, | |
107 | + int nr, int warn_if_nr_in_use, struct module *owner); | |
108 | + | |
105 | 109 | /* Register video devices. Note that if video_register_device fails, |
106 | 110 | the release() callback of the video_device structure is *not* called, so |
107 | 111 | the caller is responsible for freeing any data. Usually that means that |
108 | 112 | you call video_device_release() on failure. */ |
109 | -int __must_check video_register_device(struct video_device *vdev, int type, int nr); | |
113 | +static inline int __must_check video_register_device(struct video_device *vdev, | |
114 | + int type, int nr) | |
115 | +{ | |
116 | + return __video_register_device(vdev, type, nr, 1, vdev->fops->owner); | |
117 | +} | |
110 | 118 | |
111 | 119 | /* Same as video_register_device, but no warning is issued if the desired |
112 | 120 | device node number was already in use. */ |
113 | -int __must_check video_register_device_no_warn(struct video_device *vdev, int type, int nr); | |
121 | +static inline int __must_check video_register_device_no_warn( | |
122 | + struct video_device *vdev, int type, int nr) | |
123 | +{ | |
124 | + return __video_register_device(vdev, type, nr, 0, vdev->fops->owner); | |
125 | +} | |
114 | 126 | |
115 | 127 | /* Unregister video devices. Will do nothing if vdev == NULL or |
116 | 128 | video_is_registered() returns false. */ |
include/media/v4l2-device.h
... | ... | @@ -96,6 +96,12 @@ |
96 | 96 | wasn't registered. In that case it will do nothing. */ |
97 | 97 | void v4l2_device_unregister_subdev(struct v4l2_subdev *sd); |
98 | 98 | |
99 | +/* Register device nodes for all subdev of the v4l2 device that are marked with | |
100 | + * the V4L2_SUBDEV_FL_HAS_DEVNODE flag. | |
101 | + */ | |
102 | +int __must_check | |
103 | +v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev); | |
104 | + | |
99 | 105 | /* Iterate over all subdevs. */ |
100 | 106 | #define v4l2_device_for_each_subdev(sd, v4l2_dev) \ |
101 | 107 | list_for_each_entry(sd, &(v4l2_dev)->subdevs, list) |
include/media/v4l2-subdev.h
... | ... | @@ -22,6 +22,7 @@ |
22 | 22 | #define _V4L2_SUBDEV_H |
23 | 23 | |
24 | 24 | #include <media/v4l2-common.h> |
25 | +#include <media/v4l2-dev.h> | |
25 | 26 | #include <media/v4l2-mediabus.h> |
26 | 27 | |
27 | 28 | /* generic v4l2_device notify callback notification values */ |
28 | 29 | |
... | ... | @@ -431,9 +432,11 @@ |
431 | 432 | #define V4L2_SUBDEV_NAME_SIZE 32 |
432 | 433 | |
433 | 434 | /* Set this flag if this subdev is a i2c device. */ |
434 | -#define V4L2_SUBDEV_FL_IS_I2C (1U << 0) | |
435 | +#define V4L2_SUBDEV_FL_IS_I2C (1U << 0) | |
435 | 436 | /* Set this flag if this subdev is a spi device. */ |
436 | -#define V4L2_SUBDEV_FL_IS_SPI (1U << 1) | |
437 | +#define V4L2_SUBDEV_FL_IS_SPI (1U << 1) | |
438 | +/* Set this flag if this subdev needs a device node. */ | |
439 | +#define V4L2_SUBDEV_FL_HAS_DEVNODE (1U << 2) | |
437 | 440 | |
438 | 441 | /* Each instance of a subdev driver should create this struct, either |
439 | 442 | stand-alone or embedded in a larger struct. |
440 | 443 | |
... | ... | @@ -455,7 +458,14 @@ |
455 | 458 | /* pointer to private data */ |
456 | 459 | void *dev_priv; |
457 | 460 | void *host_priv; |
461 | + /* subdev device node */ | |
462 | + struct video_device devnode; | |
458 | 463 | }; |
464 | + | |
465 | +#define vdev_to_v4l2_subdev(vdev) \ | |
466 | + container_of(vdev, struct v4l2_subdev, devnode) | |
467 | + | |
468 | +extern const struct v4l2_file_operations v4l2_subdev_fops; | |
459 | 469 | |
460 | 470 | static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p) |
461 | 471 | { |