diff options
Diffstat (limited to 'target/linux/bcm27xx/patches-5.15/950-0465-v4l2-ctrls-add-support-for-dynamically-allocated-arr.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.15/950-0465-v4l2-ctrls-add-support-for-dynamically-allocated-arr.patch | 688 |
1 files changed, 688 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.15/950-0465-v4l2-ctrls-add-support-for-dynamically-allocated-arr.patch b/target/linux/bcm27xx/patches-5.15/950-0465-v4l2-ctrls-add-support-for-dynamically-allocated-arr.patch new file mode 100644 index 0000000000..03074222fe --- /dev/null +++ b/target/linux/bcm27xx/patches-5.15/950-0465-v4l2-ctrls-add-support-for-dynamically-allocated-arr.patch @@ -0,0 +1,688 @@ +From 613b860fe9c37b96d9706b02fe77933f086f9896 Mon Sep 17 00:00:00 2001 +From: Hans Verkuil <hverkuil-cisco@xs4all.nl> +Date: Thu, 15 Apr 2021 13:56:53 +0200 +Subject: [PATCH] v4l2-ctrls: add support for dynamically allocated + arrays. + +Implement support for dynamically allocated arrays. + +Most of the changes concern keeping track of the number of elements +of the array and the number of elements allocated for the array and +reallocating memory if needed. + +Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> +(cherry picked from commit fd5d45e6561f6f8c406b81aeddecaa11f0bd15af) +--- + drivers/media/v4l2-core/v4l2-ctrls-api.c | 103 ++++++++--- + drivers/media/v4l2-core/v4l2-ctrls-core.c | 182 +++++++++++++++---- + drivers/media/v4l2-core/v4l2-ctrls-priv.h | 3 +- + drivers/media/v4l2-core/v4l2-ctrls-request.c | 13 +- + include/media/v4l2-ctrls.h | 42 ++++- + 5 files changed, 272 insertions(+), 71 deletions(-) + +--- a/drivers/media/v4l2-core/v4l2-ctrls-api.c ++++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c +@@ -97,29 +97,47 @@ static int def_to_user(struct v4l2_ext_c + return ptr_to_user(c, ctrl, ctrl->p_new); + } + +-/* Helper function: copy the caller-provider value to the given control value */ +-static int user_to_ptr(struct v4l2_ext_control *c, +- struct v4l2_ctrl *ctrl, +- union v4l2_ctrl_ptr ptr) ++/* Helper function: copy the caller-provider value as the new control value */ ++static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) + { + int ret; + u32 size; + +- ctrl->is_new = 1; ++ ctrl->is_new = 0; ++ if (ctrl->is_dyn_array && ++ c->size > ctrl->p_dyn_alloc_elems * ctrl->elem_size) { ++ void *old = ctrl->p_dyn; ++ void *tmp = kvzalloc(2 * c->size, GFP_KERNEL); ++ ++ if (!tmp) ++ return -ENOMEM; ++ memcpy(tmp, ctrl->p_new.p, ctrl->elems * ctrl->elem_size); ++ memcpy(tmp + c->size, ctrl->p_cur.p, ctrl->elems * ctrl->elem_size); ++ ctrl->p_new.p = tmp; ++ ctrl->p_cur.p = tmp + c->size; ++ ctrl->p_dyn = tmp; ++ ctrl->p_dyn_alloc_elems = c->size / ctrl->elem_size; ++ kvfree(old); ++ } ++ + if (ctrl->is_ptr && !ctrl->is_string) { ++ unsigned int elems = c->size / ctrl->elem_size; + unsigned int idx; + +- ret = copy_from_user(ptr.p, c->ptr, c->size) ? -EFAULT : 0; +- if (ret || !ctrl->is_array) +- return ret; +- for (idx = c->size / ctrl->elem_size; idx < ctrl->elems; idx++) +- ctrl->type_ops->init(ctrl, idx, ptr); ++ if (copy_from_user(ctrl->p_new.p, c->ptr, c->size)) ++ return -EFAULT; ++ ctrl->is_new = 1; ++ if (ctrl->is_dyn_array) ++ ctrl->new_elems = elems; ++ else if (ctrl->is_array) ++ for (idx = elems; idx < ctrl->elems; idx++) ++ ctrl->type_ops->init(ctrl, idx, ctrl->p_new); + return 0; + } + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER64: +- *ptr.p_s64 = c->value64; ++ *ctrl->p_new.p_s64 = c->value64; + break; + case V4L2_CTRL_TYPE_STRING: + size = c->size; +@@ -127,32 +145,27 @@ static int user_to_ptr(struct v4l2_ext_c + return -ERANGE; + if (size > ctrl->maximum + 1) + size = ctrl->maximum + 1; +- ret = copy_from_user(ptr.p_char, c->string, size) ? -EFAULT : 0; ++ ret = copy_from_user(ctrl->p_new.p_char, c->string, size) ? -EFAULT : 0; + if (!ret) { +- char last = ptr.p_char[size - 1]; ++ char last = ctrl->p_new.p_char[size - 1]; + +- ptr.p_char[size - 1] = 0; ++ ctrl->p_new.p_char[size - 1] = 0; + /* + * If the string was longer than ctrl->maximum, + * then return an error. + */ +- if (strlen(ptr.p_char) == ctrl->maximum && last) ++ if (strlen(ctrl->p_new.p_char) == ctrl->maximum && last) + return -ERANGE; + } + return ret; + default: +- *ptr.p_s32 = c->value; ++ *ctrl->p_new.p_s32 = c->value; + break; + } ++ ctrl->is_new = 1; + return 0; + } + +-/* Helper function: copy the caller-provider value as the new control value */ +-static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) +-{ +- return user_to_ptr(c, ctrl, ctrl->p_new); +-} +- + /* + * VIDIOC_G/TRY/S_EXT_CTRLS implementation + */ +@@ -254,7 +267,31 @@ static int prepare_ext_ctrls(struct v4l2 + have_clusters = true; + if (ctrl->cluster[0] != ctrl) + ref = find_ref_lock(hdl, ctrl->cluster[0]->id); +- if (ctrl->is_ptr && !ctrl->is_string) { ++ if (ctrl->is_dyn_array) { ++ unsigned int max_size = ctrl->dims[0] * ctrl->elem_size; ++ unsigned int tot_size = ctrl->elem_size; ++ ++ if (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL) ++ tot_size *= ref->p_req_elems; ++ else ++ tot_size *= ctrl->elems; ++ ++ c->size = ctrl->elem_size * (c->size / ctrl->elem_size); ++ if (get) { ++ if (c->size < tot_size) { ++ c->size = tot_size; ++ return -ENOSPC; ++ } ++ c->size = tot_size; ++ } else { ++ if (c->size > max_size) { ++ c->size = max_size; ++ return -ENOSPC; ++ } ++ if (!c->size) ++ return -EFAULT; ++ } ++ } else if (ctrl->is_ptr && !ctrl->is_string) { + unsigned int tot_size = ctrl->elems * ctrl->elem_size; + + if (c->size < tot_size) { +@@ -346,7 +383,7 @@ static int class_check(struct v4l2_ctrl_ + * + * Note that v4l2_g_ext_ctrls_common() with 'which' set to + * V4L2_CTRL_WHICH_REQUEST_VAL is only called if the request was +- * completed, and in that case valid_p_req is true for all controls. ++ * completed, and in that case p_req_valid is true for all controls. + */ + int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl, + struct v4l2_ext_controls *cs, +@@ -430,7 +467,9 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ + + if (is_default) + ret = def_to_user(cs->controls + idx, ref->ctrl); +- else if (is_request && ref->valid_p_req) ++ else if (is_request && ref->p_req_dyn_enomem) ++ ret = -ENOMEM; ++ else if (is_request && ref->p_req_valid) + ret = req_to_user(cs->controls + idx, ref); + else if (is_volatile) + ret = new_to_user(cs->controls + idx, ref->ctrl); +@@ -457,6 +496,17 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_ha + } + EXPORT_SYMBOL(v4l2_g_ext_ctrls); + ++/* Validate a new control */ ++static int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new) ++{ ++ unsigned int idx; ++ int err = 0; ++ ++ for (idx = 0; !err && idx < ctrl->new_elems; idx++) ++ err = ctrl->type_ops->validate(ctrl, idx, p_new); ++ return err; ++} ++ + /* Validate controls. */ + static int validate_ctrls(struct v4l2_ext_controls *cs, + struct v4l2_ctrl_helper *helpers, +@@ -872,6 +922,9 @@ int __v4l2_ctrl_s_ctrl_compound(struct v + /* It's a driver bug if this happens. */ + if (WARN_ON(ctrl->type != type)) + return -EINVAL; ++ /* Setting dynamic arrays is not (yet?) supported. */ ++ if (WARN_ON(ctrl->is_dyn_array)) ++ return -EINVAL; + memcpy(ctrl->p_new.p, p, ctrl->elems * ctrl->elem_size); + return set_ctrl(NULL, ctrl, 0); + } +--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c ++++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c +@@ -809,11 +809,12 @@ EXPORT_SYMBOL(v4l2_ctrl_notify); + + /* Copy the one value to another. */ + static void ptr_to_ptr(struct v4l2_ctrl *ctrl, +- union v4l2_ctrl_ptr from, union v4l2_ctrl_ptr to) ++ union v4l2_ctrl_ptr from, union v4l2_ctrl_ptr to, ++ unsigned int elems) + { + if (ctrl == NULL) + return; +- memcpy(to.p, from.p_const, ctrl->elems * ctrl->elem_size); ++ memcpy(to.p, from.p_const, elems * ctrl->elem_size); + } + + /* Copy the new value to the current value. */ +@@ -826,8 +827,11 @@ void new_to_cur(struct v4l2_fh *fh, stru + + /* has_changed is set by cluster_changed */ + changed = ctrl->has_changed; +- if (changed) +- ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur); ++ if (changed) { ++ if (ctrl->is_dyn_array) ++ ctrl->elems = ctrl->new_elems; ++ ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur, ctrl->elems); ++ } + + if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) { + /* Note: CH_FLAGS is only set for auto clusters. */ +@@ -857,36 +861,122 @@ void cur_to_new(struct v4l2_ctrl *ctrl) + { + if (ctrl == NULL) + return; +- ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new); ++ if (ctrl->is_dyn_array) ++ ctrl->new_elems = ctrl->elems; ++ ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new, ctrl->new_elems); ++} ++ ++static bool req_alloc_dyn_array(struct v4l2_ctrl_ref *ref, u32 elems) ++{ ++ void *tmp; ++ ++ if (elems < ref->p_req_dyn_alloc_elems) ++ return true; ++ ++ tmp = kvmalloc(elems * ref->ctrl->elem_size, GFP_KERNEL); ++ ++ if (!tmp) { ++ ref->p_req_dyn_enomem = true; ++ return false; ++ } ++ ref->p_req_dyn_enomem = false; ++ kvfree(ref->p_req.p); ++ ref->p_req.p = tmp; ++ ref->p_req_dyn_alloc_elems = elems; ++ return true; + } + + /* Copy the new value to the request value */ + void new_to_req(struct v4l2_ctrl_ref *ref) + { ++ struct v4l2_ctrl *ctrl; ++ + if (!ref) + return; +- ptr_to_ptr(ref->ctrl, ref->ctrl->p_new, ref->p_req); +- ref->valid_p_req = true; ++ ++ ctrl = ref->ctrl; ++ if (ctrl->is_dyn_array && !req_alloc_dyn_array(ref, ctrl->new_elems)) ++ return; ++ ++ ref->p_req_elems = ctrl->new_elems; ++ ptr_to_ptr(ctrl, ctrl->p_new, ref->p_req, ref->p_req_elems); ++ ref->p_req_valid = true; + } + + /* Copy the current value to the request value */ + void cur_to_req(struct v4l2_ctrl_ref *ref) + { ++ struct v4l2_ctrl *ctrl; ++ + if (!ref) + return; +- ptr_to_ptr(ref->ctrl, ref->ctrl->p_cur, ref->p_req); +- ref->valid_p_req = true; ++ ++ ctrl = ref->ctrl; ++ if (ctrl->is_dyn_array && !req_alloc_dyn_array(ref, ctrl->elems)) ++ return; ++ ++ ref->p_req_elems = ctrl->elems; ++ ptr_to_ptr(ctrl, ctrl->p_cur, ref->p_req, ctrl->elems); ++ ref->p_req_valid = true; + } + + /* Copy the request value to the new value */ +-void req_to_new(struct v4l2_ctrl_ref *ref) ++int req_to_new(struct v4l2_ctrl_ref *ref) + { ++ struct v4l2_ctrl *ctrl; ++ + if (!ref) +- return; +- if (ref->valid_p_req) +- ptr_to_ptr(ref->ctrl, ref->p_req, ref->ctrl->p_new); +- else +- ptr_to_ptr(ref->ctrl, ref->ctrl->p_cur, ref->ctrl->p_new); ++ return 0; ++ ++ ctrl = ref->ctrl; ++ ++ /* ++ * This control was never set in the request, so just use the current ++ * value. ++ */ ++ if (!ref->p_req_valid) { ++ if (ctrl->is_dyn_array) ++ ctrl->new_elems = ctrl->elems; ++ ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new, ctrl->new_elems); ++ return 0; ++ } ++ ++ /* Not a dynamic array, so just copy the request value */ ++ if (!ctrl->is_dyn_array) { ++ ptr_to_ptr(ctrl, ref->p_req, ctrl->p_new, ctrl->new_elems); ++ return 0; ++ } ++ ++ /* Sanity check, should never happen */ ++ if (WARN_ON(!ref->p_req_dyn_alloc_elems)) ++ return -ENOMEM; ++ ++ /* ++ * Check if the number of elements in the request is more than the ++ * elements in ctrl->p_dyn. If so, attempt to realloc ctrl->p_dyn. ++ * Note that p_dyn is allocated with twice the number of elements ++ * in the dynamic array since it has to store both the current and ++ * new value of such a control. ++ */ ++ if (ref->p_req_elems > ctrl->p_dyn_alloc_elems) { ++ unsigned int sz = ref->p_req_elems * ctrl->elem_size; ++ void *old = ctrl->p_dyn; ++ void *tmp = kvzalloc(2 * sz, GFP_KERNEL); ++ ++ if (!tmp) ++ return -ENOMEM; ++ memcpy(tmp, ctrl->p_new.p, ctrl->elems * ctrl->elem_size); ++ memcpy(tmp + sz, ctrl->p_cur.p, ctrl->elems * ctrl->elem_size); ++ ctrl->p_new.p = tmp; ++ ctrl->p_cur.p = tmp + sz; ++ ctrl->p_dyn = tmp; ++ ctrl->p_dyn_alloc_elems = ref->p_req_elems; ++ kvfree(old); ++ } ++ ++ ctrl->new_elems = ref->p_req_elems; ++ ptr_to_ptr(ctrl, ref->p_req, ctrl->p_new, ctrl->new_elems); ++ return 0; + } + + /* Control range checking */ +@@ -928,17 +1018,6 @@ int check_range(enum v4l2_ctrl_type type + } + } + +-/* Validate a new control */ +-int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new) +-{ +- unsigned idx; +- int err = 0; +- +- for (idx = 0; !err && idx < ctrl->elems; idx++) +- err = ctrl->type_ops->validate(ctrl, idx, p_new); +- return err; +-} +- + /* Set the handler's error code if it wasn't set earlier already */ + static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err) + { +@@ -983,6 +1062,8 @@ void v4l2_ctrl_handler_free(struct v4l2_ + /* Free all nodes */ + list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) { + list_del(&ref->node); ++ if (ref->p_req_dyn_alloc_elems) ++ kvfree(ref->p_req.p); + kfree(ref); + } + /* Free all controls owned by the handler */ +@@ -990,6 +1071,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ + list_del(&ctrl->node); + list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node) + list_del(&sev->node); ++ kvfree(ctrl->p_dyn); + kvfree(ctrl); + } + kvfree(hdl->buckets); +@@ -1105,7 +1187,7 @@ int handler_new_ref(struct v4l2_ctrl_han + if (hdl->error) + return hdl->error; + +- if (allocate_req) ++ if (allocate_req && !ctrl->is_dyn_array) + size_extra_req = ctrl->elems * ctrl->elem_size; + new_ref = kzalloc(sizeof(*new_ref) + size_extra_req, GFP_KERNEL); + if (!new_ref) +@@ -1273,7 +1355,6 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s + elem_size = sizeof(s32); + break; + } +- tot_ctrl_size = elem_size * elems; + + /* Sanity checks */ + if (id == 0 || name == NULL || !elem_size || +@@ -1294,17 +1375,33 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s + handler_set_err(hdl, -EINVAL); + return NULL; + } ++ if (flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY) { ++ /* ++ * For now only support this for one-dimensional arrays only. ++ * ++ * This can be relaxed in the future, but this will ++ * require more effort. ++ */ ++ if (nr_of_dims != 1) { ++ handler_set_err(hdl, -EINVAL); ++ return NULL; ++ } ++ /* Start with just 1 element */ ++ elems = 1; ++ } + ++ tot_ctrl_size = elem_size * elems; + sz_extra = 0; + if (type == V4L2_CTRL_TYPE_BUTTON) + flags |= V4L2_CTRL_FLAG_WRITE_ONLY | + V4L2_CTRL_FLAG_EXECUTE_ON_WRITE; + else if (type == V4L2_CTRL_TYPE_CTRL_CLASS) + flags |= V4L2_CTRL_FLAG_READ_ONLY; +- else if (type == V4L2_CTRL_TYPE_INTEGER64 || +- type == V4L2_CTRL_TYPE_STRING || +- type >= V4L2_CTRL_COMPOUND_TYPES || +- is_array) ++ else if (!(flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY) && ++ (type == V4L2_CTRL_TYPE_INTEGER64 || ++ type == V4L2_CTRL_TYPE_STRING || ++ type >= V4L2_CTRL_COMPOUND_TYPES || ++ is_array)) + sz_extra += 2 * tot_ctrl_size; + + if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const) +@@ -1333,7 +1430,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s + ctrl->is_ptr = is_array || type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string; + ctrl->is_int = !ctrl->is_ptr && type != V4L2_CTRL_TYPE_INTEGER64; + ctrl->is_array = is_array; ++ ctrl->is_dyn_array = !!(flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY); + ctrl->elems = elems; ++ ctrl->new_elems = elems; + ctrl->nr_of_dims = nr_of_dims; + if (nr_of_dims) + memcpy(ctrl->dims, dims, nr_of_dims * sizeof(dims[0])); +@@ -1346,6 +1445,16 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s + ctrl->cur.val = ctrl->val = def; + data = &ctrl[1]; + ++ if (ctrl->is_dyn_array) { ++ ctrl->p_dyn_alloc_elems = elems; ++ ctrl->p_dyn = kvzalloc(2 * elems * elem_size, GFP_KERNEL); ++ if (!ctrl->p_dyn) { ++ kvfree(ctrl); ++ return NULL; ++ } ++ data = ctrl->p_dyn; ++ } ++ + if (!ctrl->is_int) { + ctrl->p_new.p = data; + ctrl->p_cur.p = data + tot_ctrl_size; +@@ -1355,7 +1464,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s + } + + if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const) { +- ctrl->p_def.p = ctrl->p_cur.p + tot_ctrl_size; ++ if (ctrl->is_dyn_array) ++ ctrl->p_def.p = &ctrl[1]; ++ else ++ ctrl->p_def.p = ctrl->p_cur.p + tot_ctrl_size; + memcpy(ctrl->p_def.p, p_def.p_const, elem_size); + } + +@@ -1365,6 +1477,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(s + } + + if (handler_new_ref(hdl, ctrl, NULL, false, false)) { ++ kvfree(ctrl->p_dyn); + kvfree(ctrl); + return NULL; + } +@@ -1702,6 +1815,9 @@ static int cluster_changed(struct v4l2_c + continue; + } + ++ if (ctrl->elems != ctrl->new_elems) ++ ctrl_changed = true; ++ + for (idx = 0; !ctrl_changed && idx < ctrl->elems; idx++) + ctrl_changed = !ctrl->type_ops->equal(ctrl, idx, + ctrl->p_cur, ctrl->p_new); +--- a/drivers/media/v4l2-core/v4l2-ctrls-priv.h ++++ b/drivers/media/v4l2-core/v4l2-ctrls-priv.h +@@ -57,10 +57,9 @@ void cur_to_new(struct v4l2_ctrl *ctrl); + void cur_to_req(struct v4l2_ctrl_ref *ref); + void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags); + void new_to_req(struct v4l2_ctrl_ref *ref); +-void req_to_new(struct v4l2_ctrl_ref *ref); ++int req_to_new(struct v4l2_ctrl_ref *ref); + void send_initial_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl); + void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes); +-int validate_new(const struct v4l2_ctrl *ctrl, union v4l2_ctrl_ptr p_new); + int handler_new_ref(struct v4l2_ctrl_handler *hdl, + struct v4l2_ctrl *ctrl, + struct v4l2_ctrl_ref **ctrl_ref, +--- a/drivers/media/v4l2-core/v4l2-ctrls-request.c ++++ b/drivers/media/v4l2-core/v4l2-ctrls-request.c +@@ -143,7 +143,7 @@ v4l2_ctrl_request_hdl_ctrl_find(struct v + { + struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id); + +- return (ref && ref->valid_p_req) ? ref->ctrl : NULL; ++ return (ref && ref->p_req_valid) ? ref->ctrl : NULL; + } + EXPORT_SYMBOL_GPL(v4l2_ctrl_request_hdl_ctrl_find); + +@@ -373,7 +373,7 @@ void v4l2_ctrl_request_complete(struct m + v4l2_ctrl_unlock(master); + continue; + } +- if (ref->valid_p_req) ++ if (ref->p_req_valid) + continue; + + /* Copy the current control value into the request */ +@@ -442,7 +442,7 @@ int v4l2_ctrl_request_setup(struct media + struct v4l2_ctrl_ref *r = + find_ref(hdl, master->cluster[i]->id); + +- if (r->valid_p_req) { ++ if (r->p_req_valid) { + have_new_data = true; + break; + } +@@ -458,7 +458,11 @@ int v4l2_ctrl_request_setup(struct media + struct v4l2_ctrl_ref *r = + find_ref(hdl, master->cluster[i]->id); + +- req_to_new(r); ++ ret = req_to_new(r); ++ if (ret) { ++ v4l2_ctrl_unlock(master); ++ goto error; ++ } + master->cluster[i]->is_new = 1; + r->req_done = true; + } +@@ -490,6 +494,7 @@ int v4l2_ctrl_request_setup(struct media + break; + } + ++error: + media_request_object_put(obj); + return ret; + } +--- a/include/media/v4l2-ctrls.h ++++ b/include/media/v4l2-ctrls.h +@@ -181,6 +181,8 @@ typedef void (*v4l2_ctrl_notify_fnc)(str + * and/or has type %V4L2_CTRL_TYPE_STRING. In other words, &struct + * v4l2_ext_control uses field p to point to the data. + * @is_array: If set, then this control contains an N-dimensional array. ++ * @is_dyn_array: If set, then this control contains a dynamically sized 1-dimensional array. ++ * If this is set, then @is_array is also set. + * @has_volatiles: If set, then one or more members of the cluster are volatile. + * Drivers should never touch this flag. + * @call_notify: If set, then call the handler's notify function whenever the +@@ -201,6 +203,9 @@ typedef void (*v4l2_ctrl_notify_fnc)(str + * @step: The control's step value for non-menu controls. + * @elems: The number of elements in the N-dimensional array. + * @elem_size: The size in bytes of the control. ++ * @new_elems: The number of elements in p_new. This is the same as @elems, ++ * except for dynamic arrays. In that case it is in the range of ++ * 1 to @p_dyn_alloc_elems. + * @dims: The size of each dimension. + * @nr_of_dims:The number of dimensions in @dims. + * @menu_skip_mask: The control's skip mask for menu controls. This makes it +@@ -219,15 +224,21 @@ typedef void (*v4l2_ctrl_notify_fnc)(str + * :math:`ceil(\frac{maximum - minimum}{step}) + 1`. + * Used only if the @type is %V4L2_CTRL_TYPE_INTEGER_MENU. + * @flags: The control's flags. +- * @cur: Structure to store the current value. +- * @cur.val: The control's current value, if the @type is represented via +- * a u32 integer (see &enum v4l2_ctrl_type). +- * @val: The control's new s32 value. + * @priv: The control's private pointer. For use by the driver. It is + * untouched by the control framework. Note that this pointer is + * not freed when the control is deleted. Should this be needed + * then a new internal bitfield can be added to tell the framework + * to free this pointer. ++ * @p_dyn: Pointer to the dynamically allocated array. Only valid if ++ * @is_dyn_array is true. ++ * @p_dyn_alloc_elems: The number of elements in the dynamically allocated ++ * array for both the cur and new values. So @p_dyn is actually ++ * sized for 2 * @p_dyn_alloc_elems * @elem_size. Only valid if ++ * @is_dyn_array is true. ++ * @cur: Structure to store the current value. ++ * @cur.val: The control's current value, if the @type is represented via ++ * a u32 integer (see &enum v4l2_ctrl_type). ++ * @val: The control's new s32 value. + * @p_def: The control's default value represented via a union which + * provides a standard way of accessing control types + * through a pointer (for compound controls only). +@@ -256,6 +267,7 @@ struct v4l2_ctrl { + unsigned int is_string:1; + unsigned int is_ptr:1; + unsigned int is_array:1; ++ unsigned int is_dyn_array:1; + unsigned int has_volatiles:1; + unsigned int call_notify:1; + unsigned int manual_mode_value:8; +@@ -268,6 +280,7 @@ struct v4l2_ctrl { + s64 minimum, maximum, default_value; + u32 elems; + u32 elem_size; ++ u32 new_elems; + u32 dims[V4L2_CTRL_MAX_DIMS]; + u32 nr_of_dims; + union { +@@ -280,6 +293,8 @@ struct v4l2_ctrl { + }; + unsigned long flags; + void *priv; ++ void *p_dyn; ++ u32 p_dyn_alloc_elems; + s32 val; + struct { + s32 val; +@@ -305,12 +320,22 @@ struct v4l2_ctrl { + * the control has been applied. This prevents applying controls + * from a cluster with multiple controls twice (when the first + * control of a cluster is applied, they all are). +- * @valid_p_req: If set, then p_req contains the control value for the request. ++ * @p_req_valid: If set, then p_req contains the control value for the request. ++ * @p_req_dyn_enomem: If set, then p_req is invalid since allocating space for ++ * a dynamic array failed. Attempting to read this value shall ++ * result in ENOMEM. Only valid if ctrl->is_dyn_array is true. ++ * @p_req_dyn_alloc_elems: The number of elements allocated for the dynamic ++ * array. Only valid if @p_req_valid and ctrl->is_dyn_array are ++ * true. ++ * @p_req_elems: The number of elements in @p_req. This is the same as ++ * ctrl->elems, except for dynamic arrays. In that case it is in ++ * the range of 1 to @p_req_dyn_alloc_elems. Only valid if ++ * @p_req_valid is true. + * @p_req: If the control handler containing this control reference + * is bound to a media request, then this points to the + * value of the control that must be applied when the request + * is executed, or to the value of the control at the time +- * that the request was completed. If @valid_p_req is false, ++ * that the request was completed. If @p_req_valid is false, + * then this control was never set for this request and the + * control will not be updated when this request is applied. + * +@@ -325,7 +350,10 @@ struct v4l2_ctrl_ref { + struct v4l2_ctrl_helper *helper; + bool from_other_dev; + bool req_done; +- bool valid_p_req; ++ bool p_req_valid; ++ bool p_req_dyn_enomem; ++ u32 p_req_dyn_alloc_elems; ++ u32 p_req_elems; + union v4l2_ctrl_ptr p_req; + }; + |