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