Commit | Line | Data |
---|---|---|
71c689dc HV |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * V4L2 controls framework Request API implementation. | |
4 | * | |
5 | * Copyright (C) 2018-2021 Hans Verkuil <hverkuil-cisco@xs4all.nl> | |
6 | */ | |
7 | ||
8 | #define pr_fmt(fmt) "v4l2-ctrls: " fmt | |
9 | ||
10 | #include <linux/export.h> | |
11 | #include <linux/slab.h> | |
12 | #include <media/v4l2-ctrls.h> | |
13 | #include <media/v4l2-dev.h> | |
14 | #include <media/v4l2-ioctl.h> | |
15 | ||
16 | #include "v4l2-ctrls-priv.h" | |
17 | ||
18 | /* Initialize the request-related fields in a control handler */ | |
19 | void v4l2_ctrl_handler_init_request(struct v4l2_ctrl_handler *hdl) | |
20 | { | |
21 | INIT_LIST_HEAD(&hdl->requests); | |
22 | INIT_LIST_HEAD(&hdl->requests_queued); | |
23 | hdl->request_is_queued = false; | |
24 | media_request_object_init(&hdl->req_obj); | |
25 | } | |
26 | ||
27 | /* Free the request-related fields in a control handler */ | |
28 | void v4l2_ctrl_handler_free_request(struct v4l2_ctrl_handler *hdl) | |
29 | { | |
30 | struct v4l2_ctrl_handler *req, *next_req; | |
31 | ||
32 | /* | |
33 | * Do nothing if this isn't the main handler or the main | |
34 | * handler is not used in any request. | |
35 | * | |
36 | * The main handler can be identified by having a NULL ops pointer in | |
37 | * the request object. | |
38 | */ | |
39 | if (hdl->req_obj.ops || list_empty(&hdl->requests)) | |
40 | return; | |
41 | ||
42 | /* | |
43 | * If the main handler is freed and it is used by handler objects in | |
44 | * outstanding requests, then unbind and put those objects before | |
45 | * freeing the main handler. | |
46 | */ | |
47 | list_for_each_entry_safe(req, next_req, &hdl->requests, requests) { | |
48 | media_request_object_unbind(&req->req_obj); | |
49 | media_request_object_put(&req->req_obj); | |
50 | } | |
51 | } | |
52 | ||
53 | static int v4l2_ctrl_request_clone(struct v4l2_ctrl_handler *hdl, | |
54 | const struct v4l2_ctrl_handler *from) | |
55 | { | |
56 | struct v4l2_ctrl_ref *ref; | |
57 | int err = 0; | |
58 | ||
59 | if (WARN_ON(!hdl || hdl == from)) | |
60 | return -EINVAL; | |
61 | ||
62 | if (hdl->error) | |
63 | return hdl->error; | |
64 | ||
65 | WARN_ON(hdl->lock != &hdl->_lock); | |
66 | ||
67 | mutex_lock(from->lock); | |
68 | list_for_each_entry(ref, &from->ctrl_refs, node) { | |
69 | struct v4l2_ctrl *ctrl = ref->ctrl; | |
70 | struct v4l2_ctrl_ref *new_ref; | |
71 | ||
72 | /* Skip refs inherited from other devices */ | |
73 | if (ref->from_other_dev) | |
74 | continue; | |
75 | err = handler_new_ref(hdl, ctrl, &new_ref, false, true); | |
76 | if (err) | |
77 | break; | |
78 | } | |
79 | mutex_unlock(from->lock); | |
80 | return err; | |
81 | } | |
82 | ||
83 | static void v4l2_ctrl_request_queue(struct media_request_object *obj) | |
84 | { | |
85 | struct v4l2_ctrl_handler *hdl = | |
86 | container_of(obj, struct v4l2_ctrl_handler, req_obj); | |
87 | struct v4l2_ctrl_handler *main_hdl = obj->priv; | |
88 | ||
89 | mutex_lock(main_hdl->lock); | |
90 | list_add_tail(&hdl->requests_queued, &main_hdl->requests_queued); | |
91 | hdl->request_is_queued = true; | |
92 | mutex_unlock(main_hdl->lock); | |
93 | } | |
94 | ||
95 | static void v4l2_ctrl_request_unbind(struct media_request_object *obj) | |
96 | { | |
97 | struct v4l2_ctrl_handler *hdl = | |
98 | container_of(obj, struct v4l2_ctrl_handler, req_obj); | |
99 | struct v4l2_ctrl_handler *main_hdl = obj->priv; | |
100 | ||
101 | mutex_lock(main_hdl->lock); | |
102 | list_del_init(&hdl->requests); | |
103 | if (hdl->request_is_queued) { | |
104 | list_del_init(&hdl->requests_queued); | |
105 | hdl->request_is_queued = false; | |
106 | } | |
107 | mutex_unlock(main_hdl->lock); | |
108 | } | |
109 | ||
110 | static void v4l2_ctrl_request_release(struct media_request_object *obj) | |
111 | { | |
112 | struct v4l2_ctrl_handler *hdl = | |
113 | container_of(obj, struct v4l2_ctrl_handler, req_obj); | |
114 | ||
115 | v4l2_ctrl_handler_free(hdl); | |
116 | kfree(hdl); | |
117 | } | |
118 | ||
119 | static const struct media_request_object_ops req_ops = { | |
120 | .queue = v4l2_ctrl_request_queue, | |
121 | .unbind = v4l2_ctrl_request_unbind, | |
122 | .release = v4l2_ctrl_request_release, | |
123 | }; | |
124 | ||
125 | struct v4l2_ctrl_handler *v4l2_ctrl_request_hdl_find(struct media_request *req, | |
126 | struct v4l2_ctrl_handler *parent) | |
127 | { | |
128 | struct media_request_object *obj; | |
129 | ||
130 | if (WARN_ON(req->state != MEDIA_REQUEST_STATE_VALIDATING && | |
131 | req->state != MEDIA_REQUEST_STATE_QUEUED)) | |
132 | return NULL; | |
133 | ||
134 | obj = media_request_object_find(req, &req_ops, parent); | |
135 | if (obj) | |
136 | return container_of(obj, struct v4l2_ctrl_handler, req_obj); | |
137 | return NULL; | |
138 | } | |
139 | EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_find); | |
140 | ||
141 | struct v4l2_ctrl * | |
142 | v4l2_ctrl_request_hdl_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id) | |
143 | { | |
144 | struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id); | |
145 | ||
fb582cba | 146 | return (ref && ref->p_req_valid) ? ref->ctrl : NULL; |
71c689dc HV |
147 | } |
148 | EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_ctrl_find); | |
149 | ||
150 | static int v4l2_ctrl_request_bind(struct media_request *req, | |
151 | struct v4l2_ctrl_handler *hdl, | |
152 | struct v4l2_ctrl_handler *from) | |
153 | { | |
154 | int ret; | |
155 | ||
156 | ret = v4l2_ctrl_request_clone(hdl, from); | |
157 | ||
158 | if (!ret) { | |
159 | ret = media_request_object_bind(req, &req_ops, | |
160 | from, false, &hdl->req_obj); | |
161 | if (!ret) { | |
162 | mutex_lock(from->lock); | |
163 | list_add_tail(&hdl->requests, &from->requests); | |
164 | mutex_unlock(from->lock); | |
165 | } | |
166 | } | |
167 | return ret; | |
168 | } | |
169 | ||
170 | static struct media_request_object * | |
171 | v4l2_ctrls_find_req_obj(struct v4l2_ctrl_handler *hdl, | |
172 | struct media_request *req, bool set) | |
173 | { | |
174 | struct media_request_object *obj; | |
175 | struct v4l2_ctrl_handler *new_hdl; | |
176 | int ret; | |
177 | ||
178 | if (IS_ERR(req)) | |
179 | return ERR_CAST(req); | |
180 | ||
181 | if (set && WARN_ON(req->state != MEDIA_REQUEST_STATE_UPDATING)) | |
182 | return ERR_PTR(-EBUSY); | |
183 | ||
184 | obj = media_request_object_find(req, &req_ops, hdl); | |
185 | if (obj) | |
186 | return obj; | |
187 | /* | |
188 | * If there are no controls in this completed request, | |
189 | * then that can only happen if: | |
190 | * | |
191 | * 1) no controls were present in the queued request, and | |
192 | * 2) v4l2_ctrl_request_complete() could not allocate a | |
193 | * control handler object to store the completed state in. | |
194 | * | |
195 | * So return ENOMEM to indicate that there was an out-of-memory | |
196 | * error. | |
197 | */ | |
198 | if (!set) | |
199 | return ERR_PTR(-ENOMEM); | |
200 | ||
201 | new_hdl = kzalloc(sizeof(*new_hdl), GFP_KERNEL); | |
202 | if (!new_hdl) | |
203 | return ERR_PTR(-ENOMEM); | |
204 | ||
205 | obj = &new_hdl->req_obj; | |
206 | ret = v4l2_ctrl_handler_init(new_hdl, (hdl->nr_of_buckets - 1) * 8); | |
207 | if (!ret) | |
208 | ret = v4l2_ctrl_request_bind(req, new_hdl, hdl); | |
209 | if (ret) { | |
210 | v4l2_ctrl_handler_free(new_hdl); | |
211 | kfree(new_hdl); | |
212 | return ERR_PTR(ret); | |
213 | } | |
214 | ||
215 | media_request_object_get(obj); | |
216 | return obj; | |
217 | } | |
218 | ||
219 | int v4l2_g_ext_ctrls_request(struct v4l2_ctrl_handler *hdl, struct video_device *vdev, | |
220 | struct media_device *mdev, struct v4l2_ext_controls *cs) | |
221 | { | |
222 | struct media_request_object *obj = NULL; | |
223 | struct media_request *req = NULL; | |
224 | int ret; | |
225 | ||
226 | if (!mdev || cs->request_fd < 0) | |
227 | return -EINVAL; | |
228 | ||
229 | req = media_request_get_by_fd(mdev, cs->request_fd); | |
230 | if (IS_ERR(req)) | |
231 | return PTR_ERR(req); | |
232 | ||
233 | if (req->state != MEDIA_REQUEST_STATE_COMPLETE) { | |
234 | media_request_put(req); | |
235 | return -EACCES; | |
236 | } | |
237 | ||
238 | ret = media_request_lock_for_access(req); | |
239 | if (ret) { | |
240 | media_request_put(req); | |
241 | return ret; | |
242 | } | |
243 | ||
244 | obj = v4l2_ctrls_find_req_obj(hdl, req, false); | |
245 | if (IS_ERR(obj)) { | |
246 | media_request_unlock_for_access(req); | |
247 | media_request_put(req); | |
248 | return PTR_ERR(obj); | |
249 | } | |
250 | ||
251 | hdl = container_of(obj, struct v4l2_ctrl_handler, | |
252 | req_obj); | |
253 | ret = v4l2_g_ext_ctrls_common(hdl, cs, vdev); | |
254 | ||
255 | media_request_unlock_for_access(req); | |
256 | media_request_object_put(obj); | |
257 | media_request_put(req); | |
258 | return ret; | |
259 | } | |
260 | ||
261 | int try_set_ext_ctrls_request(struct v4l2_fh *fh, | |
262 | struct v4l2_ctrl_handler *hdl, | |
263 | struct video_device *vdev, | |
264 | struct media_device *mdev, | |
265 | struct v4l2_ext_controls *cs, bool set) | |
266 | { | |
267 | struct media_request_object *obj = NULL; | |
268 | struct media_request *req = NULL; | |
269 | int ret; | |
270 | ||
271 | if (!mdev) { | |
272 | dprintk(vdev, "%s: missing media device\n", | |
273 | video_device_node_name(vdev)); | |
274 | return -EINVAL; | |
275 | } | |
276 | ||
277 | if (cs->request_fd < 0) { | |
278 | dprintk(vdev, "%s: invalid request fd %d\n", | |
279 | video_device_node_name(vdev), cs->request_fd); | |
280 | return -EINVAL; | |
281 | } | |
282 | ||
283 | req = media_request_get_by_fd(mdev, cs->request_fd); | |
284 | if (IS_ERR(req)) { | |
285 | dprintk(vdev, "%s: cannot find request fd %d\n", | |
286 | video_device_node_name(vdev), cs->request_fd); | |
287 | return PTR_ERR(req); | |
288 | } | |
289 | ||
290 | ret = media_request_lock_for_update(req); | |
291 | if (ret) { | |
292 | dprintk(vdev, "%s: cannot lock request fd %d\n", | |
293 | video_device_node_name(vdev), cs->request_fd); | |
294 | media_request_put(req); | |
295 | return ret; | |
296 | } | |
297 | ||
298 | obj = v4l2_ctrls_find_req_obj(hdl, req, set); | |
299 | if (IS_ERR(obj)) { | |
300 | dprintk(vdev, | |
301 | "%s: cannot find request object for request fd %d\n", | |
302 | video_device_node_name(vdev), | |
303 | cs->request_fd); | |
304 | media_request_unlock_for_update(req); | |
305 | media_request_put(req); | |
306 | return PTR_ERR(obj); | |
307 | } | |
308 | ||
309 | hdl = container_of(obj, struct v4l2_ctrl_handler, | |
310 | req_obj); | |
311 | ret = try_set_ext_ctrls_common(fh, hdl, cs, vdev, set); | |
312 | if (ret) | |
313 | dprintk(vdev, | |
314 | "%s: try_set_ext_ctrls_common failed (%d)\n", | |
315 | video_device_node_name(vdev), ret); | |
316 | ||
317 | media_request_unlock_for_update(req); | |
318 | media_request_object_put(obj); | |
319 | media_request_put(req); | |
320 | ||
321 | return ret; | |
322 | } | |
323 | ||
324 | void v4l2_ctrl_request_complete(struct media_request *req, | |
325 | struct v4l2_ctrl_handler *main_hdl) | |
326 | { | |
327 | struct media_request_object *obj; | |
328 | struct v4l2_ctrl_handler *hdl; | |
329 | struct v4l2_ctrl_ref *ref; | |
330 | ||
331 | if (!req || !main_hdl) | |
332 | return; | |
333 | ||
334 | /* | |
335 | * Note that it is valid if nothing was found. It means | |
336 | * that this request doesn't have any controls and so just | |
337 | * wants to leave the controls unchanged. | |
338 | */ | |
339 | obj = media_request_object_find(req, &req_ops, main_hdl); | |
340 | if (!obj) { | |
341 | int ret; | |
342 | ||
343 | /* Create a new request so the driver can return controls */ | |
344 | hdl = kzalloc(sizeof(*hdl), GFP_KERNEL); | |
345 | if (!hdl) | |
346 | return; | |
347 | ||
348 | ret = v4l2_ctrl_handler_init(hdl, (main_hdl->nr_of_buckets - 1) * 8); | |
349 | if (!ret) | |
350 | ret = v4l2_ctrl_request_bind(req, hdl, main_hdl); | |
351 | if (ret) { | |
352 | v4l2_ctrl_handler_free(hdl); | |
353 | kfree(hdl); | |
354 | return; | |
355 | } | |
356 | hdl->request_is_queued = true; | |
357 | obj = media_request_object_find(req, &req_ops, main_hdl); | |
358 | } | |
359 | hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj); | |
360 | ||
361 | list_for_each_entry(ref, &hdl->ctrl_refs, node) { | |
362 | struct v4l2_ctrl *ctrl = ref->ctrl; | |
363 | struct v4l2_ctrl *master = ctrl->cluster[0]; | |
364 | unsigned int i; | |
365 | ||
366 | if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) { | |
367 | v4l2_ctrl_lock(master); | |
368 | /* g_volatile_ctrl will update the current control values */ | |
369 | for (i = 0; i < master->ncontrols; i++) | |
370 | cur_to_new(master->cluster[i]); | |
371 | call_op(master, g_volatile_ctrl); | |
372 | new_to_req(ref); | |
373 | v4l2_ctrl_unlock(master); | |
374 | continue; | |
375 | } | |
fb582cba | 376 | if (ref->p_req_valid) |
71c689dc HV |
377 | continue; |
378 | ||
379 | /* Copy the current control value into the request */ | |
380 | v4l2_ctrl_lock(ctrl); | |
381 | cur_to_req(ref); | |
382 | v4l2_ctrl_unlock(ctrl); | |
383 | } | |
384 | ||
385 | mutex_lock(main_hdl->lock); | |
386 | WARN_ON(!hdl->request_is_queued); | |
387 | list_del_init(&hdl->requests_queued); | |
388 | hdl->request_is_queued = false; | |
389 | mutex_unlock(main_hdl->lock); | |
390 | media_request_object_complete(obj); | |
391 | media_request_object_put(obj); | |
392 | } | |
393 | EXPORT_SYMBOL(v4l2_ctrl_request_complete); | |
394 | ||
395 | int v4l2_ctrl_request_setup(struct media_request *req, | |
396 | struct v4l2_ctrl_handler *main_hdl) | |
397 | { | |
398 | struct media_request_object *obj; | |
399 | struct v4l2_ctrl_handler *hdl; | |
400 | struct v4l2_ctrl_ref *ref; | |
401 | int ret = 0; | |
402 | ||
403 | if (!req || !main_hdl) | |
404 | return 0; | |
405 | ||
406 | if (WARN_ON(req->state != MEDIA_REQUEST_STATE_QUEUED)) | |
407 | return -EBUSY; | |
408 | ||
409 | /* | |
410 | * Note that it is valid if nothing was found. It means | |
411 | * that this request doesn't have any controls and so just | |
412 | * wants to leave the controls unchanged. | |
413 | */ | |
414 | obj = media_request_object_find(req, &req_ops, main_hdl); | |
415 | if (!obj) | |
416 | return 0; | |
417 | if (obj->completed) { | |
418 | media_request_object_put(obj); | |
419 | return -EBUSY; | |
420 | } | |
421 | hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj); | |
422 | ||
423 | list_for_each_entry(ref, &hdl->ctrl_refs, node) | |
424 | ref->req_done = false; | |
425 | ||
426 | list_for_each_entry(ref, &hdl->ctrl_refs, node) { | |
427 | struct v4l2_ctrl *ctrl = ref->ctrl; | |
428 | struct v4l2_ctrl *master = ctrl->cluster[0]; | |
429 | bool have_new_data = false; | |
430 | int i; | |
431 | ||
432 | /* | |
433 | * Skip if this control was already handled by a cluster. | |
434 | * Skip button controls and read-only controls. | |
435 | */ | |
436 | if (ref->req_done || (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)) | |
437 | continue; | |
438 | ||
439 | v4l2_ctrl_lock(master); | |
440 | for (i = 0; i < master->ncontrols; i++) { | |
441 | if (master->cluster[i]) { | |
442 | struct v4l2_ctrl_ref *r = | |
443 | find_ref(hdl, master->cluster[i]->id); | |
444 | ||
fb582cba | 445 | if (r->p_req_valid) { |
71c689dc HV |
446 | have_new_data = true; |
447 | break; | |
448 | } | |
449 | } | |
450 | } | |
451 | if (!have_new_data) { | |
452 | v4l2_ctrl_unlock(master); | |
453 | continue; | |
454 | } | |
455 | ||
456 | for (i = 0; i < master->ncontrols; i++) { | |
457 | if (master->cluster[i]) { | |
458 | struct v4l2_ctrl_ref *r = | |
459 | find_ref(hdl, master->cluster[i]->id); | |
460 | ||
fb582cba HV |
461 | ret = req_to_new(r); |
462 | if (ret) { | |
463 | v4l2_ctrl_unlock(master); | |
464 | goto error; | |
465 | } | |
71c689dc HV |
466 | master->cluster[i]->is_new = 1; |
467 | r->req_done = true; | |
468 | } | |
469 | } | |
470 | /* | |
471 | * For volatile autoclusters that are currently in auto mode | |
472 | * we need to discover if it will be set to manual mode. | |
473 | * If so, then we have to copy the current volatile values | |
474 | * first since those will become the new manual values (which | |
475 | * may be overwritten by explicit new values from this set | |
476 | * of controls). | |
477 | */ | |
478 | if (master->is_auto && master->has_volatiles && | |
479 | !is_cur_manual(master)) { | |
480 | s32 new_auto_val = *master->p_new.p_s32; | |
481 | ||
482 | /* | |
483 | * If the new value == the manual value, then copy | |
484 | * the current volatile values. | |
485 | */ | |
486 | if (new_auto_val == master->manual_mode_value) | |
487 | update_from_auto_cluster(master); | |
488 | } | |
489 | ||
490 | ret = try_or_set_cluster(NULL, master, true, 0); | |
491 | v4l2_ctrl_unlock(master); | |
492 | ||
493 | if (ret) | |
494 | break; | |
495 | } | |
496 | ||
fb582cba | 497 | error: |
71c689dc HV |
498 | media_request_object_put(obj); |
499 | return ret; | |
500 | } | |
501 | EXPORT_SYMBOL(v4l2_ctrl_request_setup); |