mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-29 18:19:02 +00:00
689 lines
22 KiB
Diff
689 lines
22 KiB
Diff
|
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;
|
||
|
};
|
||
|
|