Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later
2 : : /*
3 : : V4L2 device support.
4 : :
5 : : Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
6 : :
7 : : */
8 : :
9 : : #include <linux/types.h>
10 : : #include <linux/ioctl.h>
11 : : #include <linux/module.h>
12 : : #include <linux/slab.h>
13 : : #include <linux/videodev2.h>
14 : : #include <media/v4l2-device.h>
15 : : #include <media/v4l2-ctrls.h>
16 : :
17 : 828 : int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
18 : : {
19 [ + - ]: 828 : if (v4l2_dev == NULL)
20 : : return -EINVAL;
21 : :
22 : 828 : INIT_LIST_HEAD(&v4l2_dev->subdevs);
23 : 828 : spin_lock_init(&v4l2_dev->lock);
24 : 828 : v4l2_prio_init(&v4l2_dev->prio);
25 : : kref_init(&v4l2_dev->ref);
26 : 828 : get_device(dev);
27 : 828 : v4l2_dev->dev = dev;
28 [ - + ]: 828 : if (dev == NULL) {
29 : : /* If dev == NULL, then name must be filled in by the caller */
30 [ # # # # ]: 0 : if (WARN_ON(!v4l2_dev->name[0]))
31 : : return -EINVAL;
32 : 0 : return 0;
33 : : }
34 : :
35 : : /* Set name to driver name + device name if it is empty. */
36 [ + - ]: 828 : if (!v4l2_dev->name[0])
37 : 2484 : snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
38 : 828 : dev->driver->name, dev_name(dev));
39 [ + + ]: 828 : if (!dev_get_drvdata(dev))
40 : : dev_set_drvdata(dev, v4l2_dev);
41 : : return 0;
42 : : }
43 : : EXPORT_SYMBOL_GPL(v4l2_device_register);
44 : :
45 : 0 : static void v4l2_device_release(struct kref *ref)
46 : : {
47 : : struct v4l2_device *v4l2_dev =
48 : 0 : container_of(ref, struct v4l2_device, ref);
49 : :
50 [ # # ]: 0 : if (v4l2_dev->release)
51 : 0 : v4l2_dev->release(v4l2_dev);
52 : 0 : }
53 : :
54 : 0 : int v4l2_device_put(struct v4l2_device *v4l2_dev)
55 : : {
56 : 0 : return kref_put(&v4l2_dev->ref, v4l2_device_release);
57 : : }
58 : : EXPORT_SYMBOL_GPL(v4l2_device_put);
59 : :
60 : 0 : int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename,
61 : : atomic_t *instance)
62 : : {
63 : 0 : int num = atomic_inc_return(instance) - 1;
64 : 0 : int len = strlen(basename);
65 : :
66 [ # # ]: 0 : if (basename[len - 1] >= '0' && basename[len - 1] <= '9')
67 : 0 : snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
68 : : "%s-%d", basename, num);
69 : : else
70 : 0 : snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
71 : : "%s%d", basename, num);
72 : 0 : return num;
73 : : }
74 : : EXPORT_SYMBOL_GPL(v4l2_device_set_name);
75 : :
76 : 0 : void v4l2_device_disconnect(struct v4l2_device *v4l2_dev)
77 : : {
78 [ # # ]: 0 : if (v4l2_dev->dev == NULL)
79 : 0 : return;
80 : :
81 [ # # ]: 0 : if (dev_get_drvdata(v4l2_dev->dev) == v4l2_dev)
82 : : dev_set_drvdata(v4l2_dev->dev, NULL);
83 : 0 : put_device(v4l2_dev->dev);
84 : 0 : v4l2_dev->dev = NULL;
85 : : }
86 : : EXPORT_SYMBOL_GPL(v4l2_device_disconnect);
87 : :
88 : 0 : void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
89 : : {
90 : : struct v4l2_subdev *sd, *next;
91 : :
92 : : /* Just return if v4l2_dev is NULL or if it was already
93 : : * unregistered before. */
94 [ # # # # ]: 0 : if (v4l2_dev == NULL || !v4l2_dev->name[0])
95 : 0 : return;
96 : 0 : v4l2_device_disconnect(v4l2_dev);
97 : :
98 : : /* Unregister subdevs */
99 [ # # ]: 0 : list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) {
100 : 0 : v4l2_device_unregister_subdev(sd);
101 [ # # ]: 0 : if (sd->flags & V4L2_SUBDEV_FL_IS_I2C)
102 : 0 : v4l2_i2c_subdev_unregister(sd);
103 [ # # ]: 0 : else if (sd->flags & V4L2_SUBDEV_FL_IS_SPI)
104 : 0 : v4l2_spi_subdev_unregister(sd);
105 : : }
106 : : /* Mark as unregistered, thus preventing duplicate unregistrations */
107 : 0 : v4l2_dev->name[0] = '\0';
108 : : }
109 : : EXPORT_SYMBOL_GPL(v4l2_device_unregister);
110 : :
111 : 0 : int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
112 : : struct v4l2_subdev *sd)
113 : : {
114 : : #if defined(CONFIG_MEDIA_CONTROLLER)
115 : 0 : struct media_entity *entity = &sd->entity;
116 : : #endif
117 : : int err;
118 : :
119 : : /* Check for valid input */
120 [ # # # # : 0 : if (!v4l2_dev || !sd || sd->v4l2_dev || !sd->name[0])
# # ]
121 : : return -EINVAL;
122 : :
123 : : /*
124 : : * The reason to acquire the module here is to avoid unloading
125 : : * a module of sub-device which is registered to a media
126 : : * device. To make it possible to unload modules for media
127 : : * devices that also register sub-devices, do not
128 : : * try_module_get() such sub-device owners.
129 : : */
130 [ # # # # : 0 : sd->owner_v4l2_dev = v4l2_dev->dev && v4l2_dev->dev->driver &&
# # ]
131 : 0 : sd->owner == v4l2_dev->dev->driver->owner;
132 : :
133 [ # # # # ]: 0 : if (!sd->owner_v4l2_dev && !try_module_get(sd->owner))
134 : : return -ENODEV;
135 : :
136 : 0 : sd->v4l2_dev = v4l2_dev;
137 : : /* This just returns 0 if either of the two args is NULL */
138 : 0 : err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler,
139 : : NULL, true);
140 [ # # ]: 0 : if (err)
141 : : goto error_module;
142 : :
143 : : #if defined(CONFIG_MEDIA_CONTROLLER)
144 : : /* Register the entity. */
145 [ # # ]: 0 : if (v4l2_dev->mdev) {
146 : 0 : err = media_device_register_entity(v4l2_dev->mdev, entity);
147 [ # # ]: 0 : if (err < 0)
148 : : goto error_module;
149 : : }
150 : : #endif
151 : :
152 [ # # # # ]: 0 : if (sd->internal_ops && sd->internal_ops->registered) {
153 : 0 : err = sd->internal_ops->registered(sd);
154 [ # # ]: 0 : if (err)
155 : : goto error_unregister;
156 : : }
157 : :
158 : : spin_lock(&v4l2_dev->lock);
159 : 0 : list_add_tail(&sd->list, &v4l2_dev->subdevs);
160 : : spin_unlock(&v4l2_dev->lock);
161 : :
162 : 0 : return 0;
163 : :
164 : : error_unregister:
165 : : #if defined(CONFIG_MEDIA_CONTROLLER)
166 : 0 : media_device_unregister_entity(entity);
167 : : #endif
168 : : error_module:
169 [ # # ]: 0 : if (!sd->owner_v4l2_dev)
170 : 0 : module_put(sd->owner);
171 : 0 : sd->v4l2_dev = NULL;
172 : 0 : return err;
173 : : }
174 : : EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
175 : :
176 : 0 : static void v4l2_subdev_release(struct v4l2_subdev *sd)
177 : : {
178 [ # # ]: 0 : struct module *owner = !sd->owner_v4l2_dev ? sd->owner : NULL;
179 : :
180 [ # # # # ]: 0 : if (sd->internal_ops && sd->internal_ops->release)
181 : 0 : sd->internal_ops->release(sd);
182 : 0 : sd->devnode = NULL;
183 : 0 : module_put(owner);
184 : 0 : }
185 : :
186 : 0 : static void v4l2_device_release_subdev_node(struct video_device *vdev)
187 : : {
188 : 0 : v4l2_subdev_release(video_get_drvdata(vdev));
189 : 0 : kfree(vdev);
190 : 0 : }
191 : :
192 : 0 : int __v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev,
193 : : bool read_only)
194 : : {
195 : : struct video_device *vdev;
196 : : struct v4l2_subdev *sd;
197 : : int err;
198 : :
199 : : /* Register a device node for every subdev marked with the
200 : : * V4L2_SUBDEV_FL_HAS_DEVNODE flag.
201 : : */
202 [ # # ]: 0 : list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
203 [ # # ]: 0 : if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
204 : 0 : continue;
205 : :
206 [ # # ]: 0 : if (sd->devnode)
207 : 0 : continue;
208 : :
209 : 0 : vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
210 [ # # ]: 0 : if (!vdev) {
211 : : err = -ENOMEM;
212 : : goto clean_up;
213 : : }
214 : :
215 : : video_set_drvdata(vdev, sd);
216 : 0 : strscpy(vdev->name, sd->name, sizeof(vdev->name));
217 : 0 : vdev->dev_parent = sd->dev;
218 : 0 : vdev->v4l2_dev = v4l2_dev;
219 : 0 : vdev->fops = &v4l2_subdev_fops;
220 : 0 : vdev->release = v4l2_device_release_subdev_node;
221 : 0 : vdev->ctrl_handler = sd->ctrl_handler;
222 [ # # ]: 0 : if (read_only)
223 : 0 : set_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags);
224 : 0 : err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
225 : : sd->owner);
226 [ # # ]: 0 : if (err < 0) {
227 : 0 : kfree(vdev);
228 : 0 : goto clean_up;
229 : : }
230 : 0 : sd->devnode = vdev;
231 : : #if defined(CONFIG_MEDIA_CONTROLLER)
232 : 0 : sd->entity.info.dev.major = VIDEO_MAJOR;
233 : 0 : sd->entity.info.dev.minor = vdev->minor;
234 : :
235 : : /* Interface is created by __video_register_device() */
236 [ # # ]: 0 : if (vdev->v4l2_dev->mdev) {
237 : : struct media_link *link;
238 : :
239 : 0 : link = media_create_intf_link(&sd->entity,
240 : 0 : &vdev->intf_devnode->intf,
241 : : MEDIA_LNK_FL_ENABLED |
242 : : MEDIA_LNK_FL_IMMUTABLE);
243 [ # # ]: 0 : if (!link) {
244 : : err = -ENOMEM;
245 : : goto clean_up;
246 : : }
247 : : }
248 : : #endif
249 : : }
250 : : return 0;
251 : :
252 : : clean_up:
253 [ # # ]: 0 : list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
254 [ # # ]: 0 : if (!sd->devnode)
255 : : break;
256 : 0 : video_unregister_device(sd->devnode);
257 : : }
258 : :
259 : : return err;
260 : : }
261 : : EXPORT_SYMBOL_GPL(__v4l2_device_register_subdev_nodes);
262 : :
263 : 0 : void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
264 : : {
265 : : struct v4l2_device *v4l2_dev;
266 : :
267 : : /* return if it isn't registered */
268 [ # # # # ]: 0 : if (sd == NULL || sd->v4l2_dev == NULL)
269 : 0 : return;
270 : :
271 : : v4l2_dev = sd->v4l2_dev;
272 : :
273 : : spin_lock(&v4l2_dev->lock);
274 : : list_del(&sd->list);
275 : : spin_unlock(&v4l2_dev->lock);
276 : :
277 [ # # # # ]: 0 : if (sd->internal_ops && sd->internal_ops->unregistered)
278 : 0 : sd->internal_ops->unregistered(sd);
279 : 0 : sd->v4l2_dev = NULL;
280 : :
281 : : #if defined(CONFIG_MEDIA_CONTROLLER)
282 [ # # ]: 0 : if (v4l2_dev->mdev) {
283 : : /*
284 : : * No need to explicitly remove links, as both pads and
285 : : * links are removed by the function below, in the right order
286 : : */
287 : 0 : media_device_unregister_entity(&sd->entity);
288 : : }
289 : : #endif
290 [ # # ]: 0 : if (sd->devnode)
291 : 0 : video_unregister_device(sd->devnode);
292 : : else
293 : 0 : v4l2_subdev_release(sd);
294 : : }
295 : : EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
|