mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 14:34:27 -05:00 
			
		
		
		
	Build system: x86_64 Build-tested: bcm2708, bcm2709, bcm2710, bcm2711 Run-tested: bcm2708/RPiB+, bcm2709/RPi3B, bcm2710/RPi3B, bcm2711/RPi4B Signed-off-by: Marty Jones <mj8263788@gmail.com> Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
		
			
				
	
	
		
			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;
 | 
						|
 };
 | 
						|
 
 |