502 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*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: " fmt9 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 */19void 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 */28void 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 main34 * handler is not used in any request.35 *36 * The main handler can be identified by having a NULL ops pointer in37 * 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 in44 * outstanding requests, then unbind and put those objects before45 * 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 53static 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 83static 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 95static 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 110static 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 119static 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 125struct 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}139EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_find);140 141struct v4l2_ctrl *142v4l2_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 146 return (ref && ref->p_req_valid) ? ref->ctrl : NULL;147}148EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_ctrl_find);149 150static 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 170static struct media_request_object *171v4l2_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, and192 * 2) v4l2_ctrl_request_complete() could not allocate a193 * control handler object to store the completed state in.194 *195 * So return ENOMEM to indicate that there was an out-of-memory196 * 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 219int 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 261int 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 324void 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 means336 * that this request doesn't have any controls and so just337 * 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 }376 if (ref->p_req_valid)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}393EXPORT_SYMBOL(v4l2_ctrl_request_complete);394 395int 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 means411 * that this request doesn't have any controls and so just412 * 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 445 if (r->p_req_valid) {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 461 ret = req_to_new(r);462 if (ret) {463 v4l2_ctrl_unlock(master);464 goto error;465 }466 master->cluster[i]->is_new = 1;467 r->req_done = true;468 }469 }470 /*471 * For volatile autoclusters that are currently in auto mode472 * we need to discover if it will be set to manual mode.473 * If so, then we have to copy the current volatile values474 * first since those will become the new manual values (which475 * may be overwritten by explicit new values from this set476 * 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 copy484 * 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 497error:498 media_request_object_put(obj);499 return ret;500}501EXPORT_SYMBOL(v4l2_ctrl_request_setup);502