All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v7 0/7] media: Implement UVC v1.5 ROI
@ 2022-06-28  7:56 Yunke Cao
  2022-06-28  7:56 ` [PATCH v7 1/7] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
                   ` (7 more replies)
  0 siblings, 8 replies; 32+ messages in thread
From: Yunke Cao @ 2022-06-28  7:56 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Nicolas Dufresne, Mauro Carvalho Chehab
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

This patch set implements UVC v1.5 region of interest using V4L2
control API.

ROI control is consisted two uvc specific controls.
1. A rectangle control with a newly added type V4L2_CTRL_TYPE_RECT.
2. An auto control with type bitmask.

V4L2_CTRL_WHICH_MIN/MAX_VAL is added to support the rectangle control.

Tested on two different usb cameras using v4l2-compliance, v4l2-ctl
and calling ioctls.

1/7 add V4L2_CTRL_TYPE_RECT.
2/7 and 3/7 support compound types in UVC.
4/7 implement ROI in UVC.
5/7 is a cherry-pick for Hans' implementation of
V4L2_CTRL_WHICH_MIN/MAX_VAL in v4l2-core.
6/7 support MIN/MAX in UVC.
7/7 document the changes.

Changelog since v6:
-Add patch 2 and 3 to support compound types properly in UVC and
implement ROI on top of them.
-Reorder the patches.

Changelog since v5:
-Add a __uvc_ctrl_get_p_rect_to_user instead of modifying
 __uvc_ctrl_get.
-Support V4L2_CTRL_FLAG_NEXT_COMPOUND correctly.
-Fix formats.

Changelog since v4:
-Cherry-pick the original patch
 "v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL".
-Split patch "media: uvcvideo: implement UVC v1.5 ROI" into two patches.
 The codes for supporting min/max in uvc are in patch 4/5 now.
-Minor fixes. Detailed changelog in patches

Changelog since v3:
- Reordered/sliced the patches.
  1. Add rect type.
  2. Add min/max.
  3. Add the roi controls (including init to default).
  4. Document the roi controls.
- Define the roi controls as uvc-specific in uvcvideo.h.
- Modified documentation.
- Removed the vivid change. Given the controls are now uvc-specific.
  I'm not sure how valuable it is to add it in vivid. Let me know
  otherwise.

Hans Verkuil (1):
  v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL

Yunke Cao (6):
  media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
  media: uvcvideo: add uvc_ctrl_get_fixed for getting default value
  media: uvcvideo: Add support for compound controls
  media: uvcvideo: implement UVC v1.5 ROI
  media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL
  media: uvcvideo: document UVC v1.5 ROI

 .../userspace-api/media/drivers/uvcvideo.rst  |  61 +++
 .../media/v4l/vidioc-g-ext-ctrls.rst          |  11 +-
 .../media/videodev2.h.rst.exceptions          |   3 +
 drivers/media/i2c/imx214.c                    |   5 +-
 .../media/platform/qcom/venus/venc_ctrls.c    |   4 +
 drivers/media/usb/uvc/uvc_ctrl.c              | 479 ++++++++++++++++--
 drivers/media/usb/uvc/uvc_v4l2.c              |  20 +-
 drivers/media/usb/uvc/uvcvideo.h              |  14 +
 drivers/media/v4l2-core/v4l2-ctrls-api.c      |  51 +-
 drivers/media/v4l2-core/v4l2-ctrls-core.c     | 155 +++++-
 drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
 include/media/v4l2-ctrls.h                    |  34 +-
 include/uapi/linux/usb/video.h                |   1 +
 include/uapi/linux/uvcvideo.h                 |  13 +
 include/uapi/linux/v4l2-controls.h            |   8 +
 include/uapi/linux/videodev2.h                |   4 +
 16 files changed, 788 insertions(+), 79 deletions(-)

-- 
2.37.0.rc0.161.g10f37bed90-goog


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH v7 1/7] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
  2022-06-28  7:56 [PATCH v7 0/7] media: Implement UVC v1.5 ROI Yunke Cao
@ 2022-06-28  7:56 ` Yunke Cao
  2022-08-24  8:40   ` Laurent Pinchart
  2022-08-24  8:50   ` Hans Verkuil
  2022-06-28  7:57 ` [PATCH v7 2/7] media: uvcvideo: add uvc_ctrl_get_fixed for getting default value Yunke Cao
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 32+ messages in thread
From: Yunke Cao @ 2022-06-28  7:56 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Nicolas Dufresne, Mauro Carvalho Chehab
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Add p_rect to struct v4l2_ext_control with basic support in
v4l2-ctrls.

Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
Signed-off-by: Yunke Cao <yunkec@google.com>
---
 .../media/v4l/vidioc-g-ext-ctrls.rst          |  4 ++++
 .../media/videodev2.h.rst.exceptions          |  1 +
 drivers/media/v4l2-core/v4l2-ctrls-core.c     | 20 +++++++++++++++++++
 include/media/v4l2-ctrls.h                    |  2 ++
 include/uapi/linux/videodev2.h                |  2 ++
 5 files changed, 29 insertions(+)

diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
index 29971a45a2d4..7473baa4e977 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
@@ -189,6 +189,10 @@ still cause this situation.
       - ``p_area``
       - A pointer to a struct :c:type:`v4l2_area`. Valid if this control is
         of type ``V4L2_CTRL_TYPE_AREA``.
+    * - struct :c:type:`v4l2_rect` *
+      - ``p_rect``
+      - A pointer to a struct :c:type:`v4l2_rect`. Valid if this control is
+        of type ``V4L2_CTRL_TYPE_RECT``.
     * - struct :c:type:`v4l2_ctrl_h264_sps` *
       - ``p_h264_sps``
       - A pointer to a struct :c:type:`v4l2_ctrl_h264_sps`. Valid if this control is
diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
index 9cbb7a0c354a..7b423475281d 100644
--- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
+++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
@@ -147,6 +147,7 @@ replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type`
 replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type`
 replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
 replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_RECT :c:type:`v4l2_ctrl_type`
 replace symbol V4L2_CTRL_TYPE_FWHT_PARAMS :c:type:`v4l2_ctrl_type`
 replace symbol V4L2_CTRL_TYPE_VP8_FRAME :c:type:`v4l2_ctrl_type`
 replace symbol V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR :c:type:`v4l2_ctrl_type`
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index 949c1884d9c1..35d43ba650db 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -84,6 +84,11 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx,
 		return ptr1.p_u16[idx] == ptr2.p_u16[idx];
 	case V4L2_CTRL_TYPE_U32:
 		return ptr1.p_u32[idx] == ptr2.p_u32[idx];
+	case V4L2_CTRL_TYPE_RECT:
+		return ptr1.p_rect->top == ptr2.p_rect->top &&
+		       ptr1.p_rect->left == ptr2.p_rect->left &&
+		       ptr1.p_rect->height == ptr2.p_rect->height &&
+		       ptr1.p_rect->width == ptr2.p_rect->width;
 	default:
 		if (ctrl->is_int)
 			return ptr1.p_s32[idx] == ptr2.p_s32[idx];
@@ -307,6 +312,11 @@ static void std_log(const struct v4l2_ctrl *ctrl)
 	case V4L2_CTRL_TYPE_VP9_FRAME:
 		pr_cont("VP9_FRAME");
 		break;
+	case V4L2_CTRL_TYPE_RECT:
+		pr_cont("%ux%u@%dx%d",
+			ptr.p_rect->width, ptr.p_rect->height,
+			ptr.p_rect->left, ptr.p_rect->top);
+		break;
 	default:
 		pr_cont("unknown type %d", ctrl->type);
 		break;
@@ -525,6 +535,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
 	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
 	struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params;
 	struct v4l2_area *area;
+	struct v4l2_rect *rect;
 	void *p = ptr.p + idx * ctrl->elem_size;
 	unsigned int i;
 
@@ -888,6 +899,12 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
 			return -EINVAL;
 		break;
 
+	case V4L2_CTRL_TYPE_RECT:
+		rect = p;
+		if (!rect->width || !rect->height)
+			return -EINVAL;
+		break;
+
 	default:
 		return -EINVAL;
 	}
@@ -1455,6 +1472,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 	case V4L2_CTRL_TYPE_AREA:
 		elem_size = sizeof(struct v4l2_area);
 		break;
+	case V4L2_CTRL_TYPE_RECT:
+		elem_size = sizeof(struct v4l2_rect);
+		break;
 	default:
 		if (type < V4L2_CTRL_COMPOUND_TYPES)
 			elem_size = sizeof(s32);
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index b3ce438f1329..919e104de50b 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -58,6 +58,7 @@ struct video_device;
  * @p_hdr10_cll:		Pointer to an HDR10 Content Light Level structure.
  * @p_hdr10_mastering:		Pointer to an HDR10 Mastering Display structure.
  * @p_area:			Pointer to an area.
+ * @p_rect:			Pointer to a rectangle.
  * @p:				Pointer to a compound value.
  * @p_const:			Pointer to a constant compound value.
  */
@@ -87,6 +88,7 @@ union v4l2_ctrl_ptr {
 	struct v4l2_ctrl_hdr10_cll_info *p_hdr10_cll;
 	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
 	struct v4l2_area *p_area;
+	struct v4l2_rect *p_rect;
 	void *p;
 	const void *p_const;
 };
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 343b95107fce..2e36bb610ea6 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -1754,6 +1754,7 @@ struct v4l2_ext_control {
 		__u16 __user *p_u16;
 		__u32 __user *p_u32;
 		struct v4l2_area __user *p_area;
+		struct v4l2_rect __user *p_rect;
 		struct v4l2_ctrl_h264_sps __user *p_h264_sps;
 		struct v4l2_ctrl_h264_pps *p_h264_pps;
 		struct v4l2_ctrl_h264_scaling_matrix __user *p_h264_scaling_matrix;
@@ -1813,6 +1814,7 @@ enum v4l2_ctrl_type {
 	V4L2_CTRL_TYPE_U16	     = 0x0101,
 	V4L2_CTRL_TYPE_U32	     = 0x0102,
 	V4L2_CTRL_TYPE_AREA          = 0x0106,
+	V4L2_CTRL_TYPE_RECT	     = 0x0107,
 
 	V4L2_CTRL_TYPE_HDR10_CLL_INFO		= 0x0110,
 	V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY	= 0x0111,
-- 
2.37.0.rc0.161.g10f37bed90-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v7 2/7] media: uvcvideo: add uvc_ctrl_get_fixed for getting default value
  2022-06-28  7:56 [PATCH v7 0/7] media: Implement UVC v1.5 ROI Yunke Cao
  2022-06-28  7:56 ` [PATCH v7 1/7] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
@ 2022-06-28  7:57 ` Yunke Cao
  2022-08-24 16:24   ` Laurent Pinchart
  2022-06-28  7:57 ` [PATCH v7 3/7] media: uvcvideo: Add support for compound controls Yunke Cao
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Yunke Cao @ 2022-06-28  7:57 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Nicolas Dufresne, Mauro Carvalho Chehab
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Introduce a new uvc_ctrl_get_fixed. It simply calls query_v4l2_ctrl()
for now, but is easier to extend to support compound controls and
V4L2_CTRL_WHICH_MIN/MAX_VAL in the following patches.

Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
Signed-off-by: Yunke Cao <yunkec@google.com>
---
 drivers/media/usb/uvc/uvc_ctrl.c | 13 +++++++++++++
 drivers/media/usb/uvc/uvc_v4l2.c |  6 +-----
 drivers/media/usb/uvc/uvcvideo.h |  2 ++
 3 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 0e78233fc8a0..772d9d28a520 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1697,6 +1697,19 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
 	return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
 }
 
+int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
+		       struct v4l2_ext_control *xctrl)
+{
+	struct v4l2_queryctrl qc = { .id = xctrl->id };
+	int ret = uvc_query_v4l2_ctrl(chain, &qc);
+
+	if (ret < 0)
+		return ret;
+
+	xctrl->value = qc.default_value;
+	return 0;
+}
+
 int uvc_ctrl_set(struct uvc_fh *handle,
 	struct v4l2_ext_control *xctrl)
 {
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 648dcd579e81..0366d05895a9 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1042,15 +1042,11 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
 
 	if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
 		for (i = 0; i < ctrls->count; ++ctrl, ++i) {
-			struct v4l2_queryctrl qc = { .id = ctrl->id };
-
-			ret = uvc_query_v4l2_ctrl(chain, &qc);
+			ret = uvc_ctrl_get_fixed(chain, ctrl);
 			if (ret < 0) {
 				ctrls->error_idx = i;
 				return ret;
 			}
-
-			ctrl->value = qc.default_value;
 		}
 
 		return 0;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index c5b4febd2d94..ba028ba7c34e 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -901,6 +901,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
 }
 
 int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
+int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
+		       struct v4l2_ext_control *xctrl);
 int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
 int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
 			   bool read);
-- 
2.37.0.rc0.161.g10f37bed90-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v7 3/7] media: uvcvideo: Add support for compound controls
  2022-06-28  7:56 [PATCH v7 0/7] media: Implement UVC v1.5 ROI Yunke Cao
  2022-06-28  7:56 ` [PATCH v7 1/7] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
  2022-06-28  7:57 ` [PATCH v7 2/7] media: uvcvideo: add uvc_ctrl_get_fixed for getting default value Yunke Cao
@ 2022-06-28  7:57 ` Yunke Cao
  2022-06-30  6:42   ` Ricardo Ribalda
  2022-08-24 19:56   ` Laurent Pinchart
  2022-06-28  7:57 ` [PATCH v7 4/7] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
                   ` (4 subsequent siblings)
  7 siblings, 2 replies; 32+ messages in thread
From: Yunke Cao @ 2022-06-28  7:57 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Nicolas Dufresne, Mauro Carvalho Chehab
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Supports getting/setting current value.
Supports getting default value.
Handles V4L2_CTRL_FLAG_NEXT_COMPOUND.

Signed-off-by: Yunke Cao <yunkec@google.com>
---
 drivers/media/usb/uvc/uvc_ctrl.c | 279 ++++++++++++++++++++++++++-----
 drivers/media/usb/uvc/uvcvideo.h |   4 +
 2 files changed, 238 insertions(+), 45 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 772d9d28a520..508ee04afbcd 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -815,6 +815,34 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
 	}
 }
 
+/* Extract the byte array specified by mapping->offset and mapping->size
+ * stored at 'data' to the output array 'data_out'.
+ */
+static int uvc_get_array(struct uvc_control_mapping *mapping, const u8 *data,
+			 u8 *data_out)
+{
+	// Only supports byte-aligned data.
+	if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
+		return -EINVAL;
+
+	memcpy(data_out, data + mapping->offset / 8, mapping->size / 8);
+	return 0;
+}
+
+/* Copy the byte array 'data_in' to the destination specified by mapping->offset
+ * and mapping->size stored at 'data'.
+ */
+static int uvc_set_array(struct uvc_control_mapping *mapping, const u8 *data_in,
+			 u8 *data)
+{
+	// Only supports byte-aligned data.
+	if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
+		return -EINVAL;
+
+	memcpy(data + mapping->offset / 8, data_in, mapping->size / 8);
+	return 0;
+}
+
 /* ------------------------------------------------------------------------
  * Terminal and unit management
  */
@@ -831,7 +859,7 @@ static int uvc_entity_match_guid(const struct uvc_entity *entity,
 
 static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
 	struct uvc_control_mapping **mapping, struct uvc_control **control,
-	int next)
+	int next, int next_compound)
 {
 	struct uvc_control *ctrl;
 	struct uvc_control_mapping *map;
@@ -846,14 +874,18 @@ static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
 			continue;
 
 		list_for_each_entry(map, &ctrl->info.mappings, list) {
-			if ((map->id == v4l2_id) && !next) {
+			if (map->id == v4l2_id && !next && !next_compound) {
 				*control = ctrl;
 				*mapping = map;
 				return;
 			}
 
 			if ((*mapping == NULL || (*mapping)->id > map->id) &&
-			    (map->id > v4l2_id) && next) {
+			    (map->id > v4l2_id) &&
+			    ((map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES &&
+			      next) ||
+			     (map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES &&
+			      next_compound))) {
 				*control = ctrl;
 				*mapping = map;
 			}
@@ -867,6 +899,7 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
 	struct uvc_control *ctrl = NULL;
 	struct uvc_entity *entity;
 	int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;
+	int next_compound = v4l2_id & V4L2_CTRL_FLAG_NEXT_COMPOUND;
 
 	*mapping = NULL;
 
@@ -875,12 +908,13 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
 
 	/* Find the control. */
 	list_for_each_entry(entity, &chain->entities, chain) {
-		__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
-		if (ctrl && !next)
+		__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next,
+				   next_compound);
+		if (ctrl && !next && !next_compound)
 			return ctrl;
 	}
 
-	if (ctrl == NULL && !next)
+	if (!ctrl && !next && !next_compound)
 		uvc_dbg(chain->dev, CONTROL, "Control 0x%08x not found\n",
 			v4l2_id);
 
@@ -943,6 +977,39 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
 	return 0;
 }
 
+static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain,
+			       struct uvc_control *ctrl)
+{
+	int ret = 0;
+
+	if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
+		return -EACCES;
+
+	if (ctrl->loaded)
+		return 0;
+
+	if (ctrl->entity->get_cur) {
+		ret = ctrl->entity->get_cur(chain->dev,
+			ctrl->entity,
+			ctrl->info.selector,
+			uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+			ctrl->info.size);
+	} else {
+		ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
+				     ctrl->entity->id, chain->dev->intfnum,
+				     ctrl->info.selector,
+				     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+				     ctrl->info.size);
+	}
+
+	if (ret < 0)
+		return ret;
+
+	ctrl->loaded = 1;
+
+	return ret;
+}
+
 static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping,
 				const u8 *data)
 {
@@ -963,35 +1030,19 @@ static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping,
 	return value;
 }
 
-static int __uvc_ctrl_get(struct uvc_video_chain *chain,
-	struct uvc_control *ctrl, struct uvc_control_mapping *mapping,
-	s32 *value)
+static int __uvc_ctrl_get_std(struct uvc_video_chain *chain,
+			      struct uvc_control *ctrl,
+			      struct uvc_control_mapping *mapping,
+			      s32 *value)
 {
 	int ret;
 
-	if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
-		return -EACCES;
-
-	if (!ctrl->loaded) {
-		if (ctrl->entity->get_cur) {
-			ret = ctrl->entity->get_cur(chain->dev,
-				ctrl->entity,
-				ctrl->info.selector,
-				uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
-				ctrl->info.size);
-		} else {
-			ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
-				ctrl->entity->id,
-				chain->dev->intfnum,
-				ctrl->info.selector,
-				uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
-				ctrl->info.size);
-		}
-		if (ret < 0)
-			return ret;
+	if (mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
+		return -EINVAL;
 
-		ctrl->loaded = 1;
-	}
+	ret = __uvc_ctrl_load_cur(chain, ctrl);
+	if (ret < 0)
+		return ret;
 
 	*value = __uvc_ctrl_get_value(mapping,
 				uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
@@ -999,6 +1050,57 @@ static int __uvc_ctrl_get(struct uvc_video_chain *chain,
 	return 0;
 }
 
+static int __uvc_ctrl_get_compound_to_user(struct uvc_control_mapping *mapping,
+					   struct uvc_control *ctrl,
+					   int id,
+					   struct v4l2_ext_control *xctrl)
+{
+	int ret, size;
+	u8 *data;
+
+	if (WARN_ON(!mapping->size % 8))
+		return -EINVAL;
+
+	size = mapping->size / 8;
+	if (xctrl->size < size) {
+		xctrl->size = size;
+		return -ENOSPC;
+	}
+
+	data = kmalloc(size, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	ret = mapping->get_array(mapping, uvc_ctrl_data(ctrl, id), data);
+	if (ret < 0)
+		goto out;
+
+	ret = copy_to_user(xctrl->ptr, data, size) ? -EFAULT : 0;
+
+out:
+	kfree(data);
+	return ret;
+}
+
+static int __uvc_ctrl_get_compound(struct uvc_video_chain *chain,
+				   struct uvc_control *ctrl,
+				   struct uvc_control_mapping *mapping,
+				   struct v4l2_ext_control *xctrl)
+{
+	int ret;
+
+	if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
+		return -EINVAL;
+
+	ret = __uvc_ctrl_load_cur(chain, ctrl);
+	if (ret < 0)
+		return ret;
+
+	return __uvc_ctrl_get_compound_to_user(mapping, ctrl,
+					       UVC_CTRL_DATA_CURRENT,
+					       xctrl);
+}
+
 static int __uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
 				  u32 found_id)
 {
@@ -1102,10 +1204,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
 
 	if (mapping->master_id)
 		__uvc_find_control(ctrl->entity, mapping->master_id,
-				   &master_map, &master_ctrl, 0);
+				   &master_map, &master_ctrl, 0, 0);
 	if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
-		s32 val;
-		int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
+		int ret;
+		s32 val = 0;
+
+		if (master_map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
+			return -EINVAL;
+
+		ret = __uvc_ctrl_get_std(chain, master_ctrl, master_map, &val);
 		if (ret < 0)
 			return ret;
 
@@ -1113,6 +1220,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
 				v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
 	}
 
+	if (v4l2_ctrl->type >= V4L2_CTRL_COMPOUND_TYPES) {
+		v4l2_ctrl->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD;
+		v4l2_ctrl->default_value = 0;
+		v4l2_ctrl->minimum = 0;
+		v4l2_ctrl->maximum = 0;
+		v4l2_ctrl->step = 0;
+		return 0;
+	}
+
 	if (!ctrl->cached) {
 		int ret = uvc_ctrl_populate_cache(chain, ctrl);
 		if (ret < 0)
@@ -1346,11 +1462,12 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
 	u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
 	s32 val = 0;
 
-	__uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0);
+	__uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0, 0);
 	if (ctrl == NULL)
 		return;
 
-	if (__uvc_ctrl_get(chain, ctrl, mapping, &val) == 0)
+	if (mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES ||
+	    __uvc_ctrl_get_std(chain, ctrl, mapping, &val) == 0)
 		changes |= V4L2_EVENT_CTRL_CH_VALUE;
 
 	uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
@@ -1517,7 +1634,8 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
 		u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
 		s32 val = 0;
 
-		if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0)
+		if (mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES ||
+		    __uvc_ctrl_get_std(handle->chain, ctrl, mapping, &val) == 0)
 			changes |= V4L2_EVENT_CTRL_CH_VALUE;
 
 		uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val,
@@ -1647,7 +1765,7 @@ static int uvc_ctrl_find_ctrl_idx(struct uvc_entity *entity,
 
 	for (i = 0; i < ctrls->count; i++) {
 		__uvc_find_control(entity, ctrls->controls[i].id, &mapping,
-				   &ctrl_found, 0);
+				   &ctrl_found, 0, 0);
 		if (uvc_control == ctrl_found)
 			return i;
 	}
@@ -1694,11 +1812,14 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
 	if (ctrl == NULL)
 		return -EINVAL;
 
-	return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
+	if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
+		return __uvc_ctrl_get_std(chain, ctrl, mapping, &xctrl->value);
+	else
+		return __uvc_ctrl_get_compound(chain, ctrl, mapping, xctrl);
 }
 
-int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
-		       struct v4l2_ext_control *xctrl)
+int __uvc_ctrl_get_fixed_std(struct uvc_video_chain *chain,
+			     struct v4l2_ext_control *xctrl)
 {
 	struct v4l2_queryctrl qc = { .id = xctrl->id };
 	int ret = uvc_query_v4l2_ctrl(chain, &qc);
@@ -1710,6 +1831,56 @@ int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
 	return 0;
 }
 
+int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
+		       struct v4l2_ext_control *xctrl)
+{
+	struct uvc_control *ctrl;
+	struct uvc_control_mapping *mapping;
+	int ret;
+
+	if (__uvc_query_v4l2_class(chain, xctrl->id, 0) >= 0)
+		return -EACCES;
+
+	ctrl = uvc_find_control(chain, xctrl->id, &mapping);
+	if (!ctrl)
+		return -EINVAL;
+
+	if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
+		return __uvc_ctrl_get_fixed_std(chain, xctrl);
+
+	if (!ctrl->cached) {
+		ret = uvc_ctrl_populate_cache(chain, ctrl);
+		if (ret < 0)
+			return ret;
+	}
+
+	return __uvc_ctrl_get_compound_to_user(mapping, ctrl, UVC_CTRL_DATA_DEF,
+					       xctrl);
+}
+
+int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
+			    struct v4l2_ext_control *xctrl,
+			    struct uvc_control *ctrl)
+{
+	int ret;
+	u8 *data;
+
+	data = kmalloc(xctrl->size, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	ret = copy_from_user(data, xctrl->ptr, xctrl->size);
+	if (ret < 0)
+		goto out;
+
+	ret = mapping->set_array(mapping, data,
+			uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
+
+out:
+	kfree(data);
+	return ret;
+}
+
 int uvc_ctrl_set(struct uvc_fh *handle,
 	struct v4l2_ext_control *xctrl)
 {
@@ -1820,8 +1991,14 @@ int uvc_ctrl_set(struct uvc_fh *handle,
 		       ctrl->info.size);
 	}
 
-	mapping->set(mapping, value,
-		uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
+	if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES) {
+		mapping->set(mapping, value,
+			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
+	} else {
+		ret = __uvc_ctrl_set_compound(mapping, xctrl, ctrl);
+		if (ret < 0)
+			return ret;
+	}
 
 	if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
 		ctrl->handle = handle;
@@ -2220,10 +2397,14 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
 		return -ENOMEM;
 	}
 
-	if (map->get == NULL)
+	if (!map->get && map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
 		map->get = uvc_get_le_value;
-	if (map->set == NULL)
+	if (!map->set && map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
 		map->set = uvc_set_le_value;
+	if (!map->get_array && map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
+		map->get_array = uvc_get_array;
+	if (!map->set_array && map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
+		map->set_array = uvc_set_array;
 
 	for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
 		if (V4L2_CTRL_ID2WHICH(uvc_control_classes[i]) ==
@@ -2233,6 +2414,14 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
 		}
 	}
 
+	if (map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES &&
+	    WARN_ON(!map->get || !map->set))
+		return -EINVAL;
+
+	if (map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES &&
+	    WARN_ON(!map->get_array || !map->set_array))
+		return -EINVAL;
+
 	list_add_tail(&map->list, &ctrl->info.mappings);
 	uvc_dbg(chain->dev, CONTROL, "Adding mapping '%s' to control %pUl/%u\n",
 		uvc_map_get_name(map), ctrl->info.entity,
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index ba028ba7c34e..2f9b75faae83 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -262,8 +262,12 @@ struct uvc_control_mapping {
 
 	s32 (*get)(struct uvc_control_mapping *mapping, u8 query,
 		   const u8 *data);
+	int (*get_array)(struct uvc_control_mapping *mapping, const u8 *data,
+			 u8 *data_out);
 	void (*set)(struct uvc_control_mapping *mapping, s32 value,
 		    u8 *data);
+	int (*set_array)(struct uvc_control_mapping *mapping, const u8 *data_in,
+			 u8 *data);
 };
 
 struct uvc_control {
-- 
2.37.0.rc0.161.g10f37bed90-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v7 4/7] media: uvcvideo: implement UVC v1.5 ROI
  2022-06-28  7:56 [PATCH v7 0/7] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (2 preceding siblings ...)
  2022-06-28  7:57 ` [PATCH v7 3/7] media: uvcvideo: Add support for compound controls Yunke Cao
@ 2022-06-28  7:57 ` Yunke Cao
  2022-06-30  6:42   ` Ricardo Ribalda
  2022-08-24 20:36   ` Laurent Pinchart
  2022-06-28  7:57 ` [PATCH v7 5/7] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
                   ` (3 subsequent siblings)
  7 siblings, 2 replies; 32+ messages in thread
From: Yunke Cao @ 2022-06-28  7:57 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Nicolas Dufresne, Mauro Carvalho Chehab
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Implement support for ROI as described in UVC 1.5:
4.2.2.1.20 Digital Region of Interest (ROI) Control

ROI control is implemented using V4L2 control API as
two uvc-specific controls:
V4L2_CID_UVC_REGION_OF_INTEREST_RECT and
V4L2_CID_UVC_REGION_OF_INTEREST_AUTO.

Signed-off-by: Yunke Cao <yunkec@google.com>
---
 drivers/media/usb/uvc/uvc_ctrl.c   | 146 ++++++++++++++++++++++++++++-
 drivers/media/usb/uvc/uvc_v4l2.c   |   5 +-
 drivers/media/usb/uvc/uvcvideo.h   |   7 ++
 include/uapi/linux/usb/video.h     |   1 +
 include/uapi/linux/uvcvideo.h      |  13 +++
 include/uapi/linux/v4l2-controls.h |   8 ++
 6 files changed, 178 insertions(+), 2 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 508ee04afbcd..00482269233a 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -355,6 +355,23 @@ static const struct uvc_control_info uvc_ctrls[] = {
 		.flags		= UVC_CTRL_FLAG_GET_CUR
 				| UVC_CTRL_FLAG_AUTO_UPDATE,
 	},
+	/* UVC_CTRL_FLAG_AUTO_UPDATE is needed because the RoI may get updated
+	 * by sensors.
+	 * "This RoI should be the same as specified in most recent SET_CUR
+	 * except in the case where the ‘Auto Detect and Track’ and/or
+	 * ‘Image Stabilization’ bit have been set."
+	 * 4.2.2.1.20 Digital Region of Interest (ROI) Control
+	 */
+	{
+		.entity		= UVC_GUID_UVC_CAMERA,
+		.selector	= UVC_CT_REGION_OF_INTEREST_CONTROL,
+		.index		= 21,
+		.size		= 10,
+		.flags		= UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
+				| UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
+				| UVC_CTRL_FLAG_GET_DEF
+				| UVC_CTRL_FLAG_AUTO_UPDATE,
+	},
 };
 
 static const u32 uvc_control_classes[] = {
@@ -429,6 +446,64 @@ static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping,
 	data[first+1] = min_t(int, abs(value), 0xff);
 }
 
+static void uvc_to_v4l2_rect(struct v4l2_rect *v4l2_rect,
+			     const struct uvc_rect *uvc_rect)
+{
+	v4l2_rect->top = uvc_rect->top;
+	v4l2_rect->left = uvc_rect->left;
+	v4l2_rect->height = uvc_rect->bottom - uvc_rect->top + 1;
+	v4l2_rect->width = uvc_rect->right - uvc_rect->left + 1;
+}
+
+static int v4l2_to_uvc_rect(struct uvc_rect *uvc_rect,
+			    const struct v4l2_rect *v4l2_rect)
+{
+	// Safely converts s32 and u32 to u16.
+	if (v4l2_rect->top > U16_MAX || v4l2_rect->top < 0 ||
+	    v4l2_rect->left > U16_MAX || v4l2_rect->left < 0 ||
+	    v4l2_rect->height > U16_MAX || v4l2_rect->height == 0 ||
+	    v4l2_rect->width > U16_MAX || v4l2_rect->width == 0 ||
+	    v4l2_rect->height + v4l2_rect->top - 1 > U16_MAX ||
+	    v4l2_rect->width + v4l2_rect->left - 1 > U16_MAX)
+		return -ERANGE;
+
+	uvc_rect->top = v4l2_rect->top;
+	uvc_rect->left = v4l2_rect->left;
+	uvc_rect->bottom = v4l2_rect->height + v4l2_rect->top - 1;
+	uvc_rect->right = v4l2_rect->width + v4l2_rect->left - 1;
+	return 0;
+}
+
+static int uvc_get_array_rect(struct uvc_control_mapping *mapping,
+			      const u8 *data,  u8 *data_out)
+{
+	struct uvc_rect *uvc_rect;
+
+	if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
+		return -EINVAL;
+	if (WARN_ON(mapping->size / 8 != sizeof(struct uvc_rect)))
+		return -EINVAL;
+
+	uvc_rect = (struct uvc_rect *)(data + mapping->offset / 8);
+	uvc_to_v4l2_rect((struct v4l2_rect *)data_out, uvc_rect);
+
+	return 0;
+}
+
+static int uvc_set_array_rect(struct uvc_control_mapping *mapping,
+			      const u8 *data_in, u8 *data)
+{
+	struct uvc_rect *uvc_rect;
+
+	if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
+		return -EINVAL;
+	if (WARN_ON(mapping->size / 8 != sizeof(struct uvc_rect)))
+		return -EINVAL;
+
+	uvc_rect = (struct uvc_rect *)(data + mapping->offset / 8);
+	return v4l2_to_uvc_rect(uvc_rect, (struct v4l2_rect *)data_in);
+}
+
 static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 	{
 		.id		= V4L2_CID_BRIGHTNESS,
@@ -728,6 +803,26 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
 		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
 		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
 	},
+	{
+		.id		= V4L2_CID_UVC_REGION_OF_INTEREST_RECT,
+		.entity		= UVC_GUID_UVC_CAMERA,
+		.selector	= UVC_CT_REGION_OF_INTEREST_CONTROL,
+		.size		= 64,
+		.offset		= 0,
+		.v4l2_type	= V4L2_CTRL_TYPE_RECT,
+		.data_type	= UVC_CTRL_DATA_TYPE_RECT,
+		.get_array	= uvc_get_array_rect,
+		.set_array	= uvc_set_array_rect,
+	},
+	{
+		.id		= V4L2_CID_UVC_REGION_OF_INTEREST_AUTO,
+		.entity		= UVC_GUID_UVC_CAMERA,
+		.selector	= UVC_CT_REGION_OF_INTEREST_CONTROL,
+		.size		= 16,
+		.offset		= 64,
+		.v4l2_type	= V4L2_CTRL_TYPE_BITMASK,
+		.data_type	= UVC_CTRL_DATA_TYPE_BITMASK,
+	},
 };
 
 /* ------------------------------------------------------------------------
@@ -1061,7 +1156,16 @@ static int __uvc_ctrl_get_compound_to_user(struct uvc_control_mapping *mapping,
 	if (WARN_ON(!mapping->size % 8))
 		return -EINVAL;
 
-	size = mapping->size / 8;
+	switch (mapping->v4l2_type) {
+	case V4L2_CTRL_TYPE_RECT:
+		size = sizeof(struct v4l2_rect);
+		break;
+
+	default:
+		size = mapping->size / 8;
+		break;
+	}
+
 	if (xctrl->size < size) {
 		xctrl->size = size;
 		return -ENOSPC;
@@ -2578,6 +2682,40 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev,
 	}
 }
 
+static int uvc_ctrl_init_roi(struct uvc_device *dev, struct uvc_control *ctrl)
+{
+	int ret;
+
+	ret = uvc_query_ctrl(dev, UVC_GET_DEF, ctrl->entity->id, dev->intfnum,
+			     UVC_CT_REGION_OF_INTEREST_CONTROL,
+			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
+			     ctrl->info.size);
+	if (ret)
+		goto out;
+
+	/*
+	 * Most firmwares have wrong GET_CURRENT configuration. E.g. it's
+	 * below GET_MIN, or have rectangle coordinates mixed up. This
+	 * causes problems sometimes, because we are unable to set
+	 * auto-controls value without first setting ROI rectangle to
+	 * valid configuration.
+	 *
+	 * We expect that default configuration is always correct and
+	 * is within the GET_MIN / GET_MAX boundaries.
+	 *
+	 * Set current ROI configuration to GET_DEF, so that we will
+	 * always have properly configured ROI.
+	 */
+	ret = uvc_query_ctrl(dev, UVC_SET_CUR, 1, dev->intfnum,
+			     UVC_CT_REGION_OF_INTEREST_CONTROL,
+			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
+			     ctrl->info.size);
+out:
+	if (ret)
+		dev_err(&dev->udev->dev, "Failed to fixup ROI (%d).\n", ret);
+	return ret;
+}
+
 /*
  * Add control information and hardcoded stock control mappings to the given
  * device.
@@ -2590,6 +2728,7 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
 	const struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
 	const struct uvc_control_mapping *mend =
 		mapping + ARRAY_SIZE(uvc_ctrl_mappings);
+	const u8 camera_entity[16] = UVC_GUID_UVC_CAMERA;
 
 	/* XU controls initialization requires querying the device for control
 	 * information. As some buggy UVC devices will crash when queried
@@ -2610,6 +2749,11 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
 			 * GET_INFO on standard controls.
 			 */
 			uvc_ctrl_get_flags(chain->dev, ctrl, &ctrl->info);
+
+			if (ctrl->info.selector ==
+				UVC_CT_REGION_OF_INTEREST_CONTROL &&
+			    uvc_entity_match_guid(ctrl->entity, camera_entity))
+				uvc_ctrl_init_roi(chain->dev, ctrl);
 			break;
 		 }
 	}
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 0366d05895a9..aad61af36271 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -998,7 +998,10 @@ static int uvc_ioctl_query_ext_ctrl(struct file *file, void *fh,
 	qec->step = qc.step;
 	qec->default_value = qc.default_value;
 	qec->flags = qc.flags;
-	qec->elem_size = 4;
+	if (qc.type == V4L2_CTRL_TYPE_RECT)
+		qec->elem_size = sizeof(struct v4l2_rect);
+	else
+		qec->elem_size = 4;
 	qec->elems = 1;
 	qec->nr_of_dims = 0;
 	memset(qec->dims, 0, sizeof(qec->dims));
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 2f9b75faae83..9ff95bbad251 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -431,6 +431,13 @@ struct uvc_streaming_header {
 	u8 bTriggerUsage;
 };
 
+struct uvc_rect {
+	u16 top;
+	u16 left;
+	u16 bottom;
+	u16 right;
+} __packed;
+
 enum uvc_buffer_state {
 	UVC_BUF_STATE_IDLE	= 0,
 	UVC_BUF_STATE_QUEUED	= 1,
diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h
index bfdae12cdacf..9076a444758a 100644
--- a/include/uapi/linux/usb/video.h
+++ b/include/uapi/linux/usb/video.h
@@ -104,6 +104,7 @@
 #define UVC_CT_ROLL_ABSOLUTE_CONTROL			0x0f
 #define UVC_CT_ROLL_RELATIVE_CONTROL			0x10
 #define UVC_CT_PRIVACY_CONTROL				0x11
+#define UVC_CT_REGION_OF_INTEREST_CONTROL		0x14
 
 /* A.9.5. Processing Unit Control Selectors */
 #define UVC_PU_CONTROL_UNDEFINED			0x00
diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h
index 8288137387c0..e2feee15cb1b 100644
--- a/include/uapi/linux/uvcvideo.h
+++ b/include/uapi/linux/uvcvideo.h
@@ -16,6 +16,7 @@
 #define UVC_CTRL_DATA_TYPE_BOOLEAN	3
 #define UVC_CTRL_DATA_TYPE_ENUM		4
 #define UVC_CTRL_DATA_TYPE_BITMASK	5
+#define UVC_CTRL_DATA_TYPE_RECT		6
 
 /* Control flags */
 #define UVC_CTRL_FLAG_SET_CUR		(1 << 0)
@@ -36,6 +37,18 @@
 	 UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES | \
 	 UVC_CTRL_FLAG_GET_DEF)
 
+/* V4L2 private controls */
+#define V4L2_CID_UVC_REGION_OF_INTEREST_RECT	(V4L2_CID_CAMERA_UVC_BASE + 1)
+#define V4L2_CID_UVC_REGION_OF_INTEREST_AUTO	(V4L2_CID_CAMERA_UVC_BASE + 2)
+#define V4L2_UVC_REGION_OF_INTEREST_AUTO_EXPOSURE		(1 << 0)
+#define V4L2_UVC_REGION_OF_INTEREST_AUTO_IRIS			(1 << 1)
+#define V4L2_UVC_REGION_OF_INTEREST_AUTO_WHITE_BALANCE		(1 << 2)
+#define V4L2_UVC_REGION_OF_INTEREST_AUTO_FOCUS			(1 << 3)
+#define V4L2_UVC_REGION_OF_INTEREST_AUTO_FACE_DETECT		(1 << 4)
+#define V4L2_UVC_REGION_OF_INTEREST_AUTO_DETECT_AND_TRACK	(1 << 5)
+#define V4L2_UVC_REGION_OF_INTEREST_AUTO_IMAGE_STABILIZATION	(1 << 6)
+#define V4L2_UVC_REGION_OF_INTEREST_AUTO_HIGHER_QUALITY		(1 << 7)
+
 struct uvc_menu_info {
 	__u32 value;
 	__u8 name[32];
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index dfff69ed88f7..d85be46e46f2 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -1013,6 +1013,14 @@ enum v4l2_auto_focus_range {
 
 #define V4L2_CID_CAMERA_SENSOR_ROTATION		(V4L2_CID_CAMERA_CLASS_BASE+35)
 
+/* CAMERA-class private control IDs */
+
+/*
+ * The base for the uvc driver controls. See linux/uvcvideo.h for the list
+ * of controls. We reserve 16 controls for this driver.
+ */
+#define V4L2_CID_CAMERA_UVC_BASE		(V4L2_CID_CAMERA_CLASS_BASE + 0x1000)
+
 /* FM Modulator class control IDs */
 
 #define V4L2_CID_FM_TX_CLASS_BASE		(V4L2_CTRL_CLASS_FM_TX | 0x900)
-- 
2.37.0.rc0.161.g10f37bed90-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v7 5/7] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2022-06-28  7:56 [PATCH v7 0/7] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (3 preceding siblings ...)
  2022-06-28  7:57 ` [PATCH v7 4/7] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
@ 2022-06-28  7:57 ` Yunke Cao
  2022-08-24 16:20   ` Laurent Pinchart
  2022-06-28  7:57 ` [PATCH v7 6/7] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 32+ messages in thread
From: Yunke Cao @ 2022-06-28  7:57 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Nicolas Dufresne, Mauro Carvalho Chehab
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

From: Hans Verkuil <hverkuil-cisco@xs4all.nl>

Add the capability of retrieving the min and max values of a
compound control.

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Yunke Cao <yunkec@google.com>
---
git am from https://lore.kernel.org/all/20191119113457.57833-3-hverkuil-cisco@xs4all.nl/
-Fixed some merge conflits.
-Fixed the build error in drivers/media/platform/qcom/venus.

 .../media/v4l/vidioc-g-ext-ctrls.rst          |   7 +-
 .../media/videodev2.h.rst.exceptions          |   2 +
 drivers/media/i2c/imx214.c                    |   5 +-
 .../media/platform/qcom/venus/venc_ctrls.c    |   4 +
 drivers/media/v4l2-core/v4l2-ctrls-api.c      |  51 +++++--
 drivers/media/v4l2-core/v4l2-ctrls-core.c     | 135 ++++++++++++++++--
 drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
 include/media/v4l2-ctrls.h                    |  32 ++++-
 include/uapi/linux/videodev2.h                |   2 +
 9 files changed, 214 insertions(+), 28 deletions(-)

diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
index 7473baa4e977..65a5f878cc28 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
@@ -284,14 +284,17 @@ still cause this situation.
       - Which value of the control to get/set/try.
     * - :cspan:`2` ``V4L2_CTRL_WHICH_CUR_VAL`` will return the current value of
 	the control, ``V4L2_CTRL_WHICH_DEF_VAL`` will return the default
+	value of the control, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
+	value of the control, ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
 	value of the control and ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
 	these controls have to be retrieved from a request or tried/set for
 	a request. In the latter case the ``request_fd`` field contains the
 	file descriptor of the request that should be used. If the device
 	does not support requests, then ``EACCES`` will be returned.
 
-	When using ``V4L2_CTRL_WHICH_DEF_VAL`` be aware that you can only
-	get the default value of the control, you cannot set or try it.
+	When using ``V4L2_CTRL_WHICH_DEF_VAL``, ``V4L2_CTRL_WHICH_MIN_VAL``
+	or ``V4L2_CTRL_WHICH_MAX_VAL`` be aware that you can only get the
+	default/minimum/maximum value of the control, you cannot set or try it.
 
 	For backwards compatibility you can also use a control class here
 	(see :ref:`ctrl-class`). In that case all controls have to
diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
index 7b423475281d..e2dde31d76df 100644
--- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
+++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
@@ -553,6 +553,8 @@ ignore define V4L2_CTRL_DRIVER_PRIV
 ignore define V4L2_CTRL_MAX_DIMS
 ignore define V4L2_CTRL_WHICH_CUR_VAL
 ignore define V4L2_CTRL_WHICH_DEF_VAL
+ignore define V4L2_CTRL_WHICH_MIN_VAL
+ignore define V4L2_CTRL_WHICH_MAX_VAL
 ignore define V4L2_CTRL_WHICH_REQUEST_VAL
 ignore define V4L2_OUT_CAP_CUSTOM_TIMINGS
 ignore define V4L2_CID_MAX_CTRLS
diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c
index 83c1737abeec..aa0bfd18f7b7 100644
--- a/drivers/media/i2c/imx214.c
+++ b/drivers/media/i2c/imx214.c
@@ -1037,7 +1037,10 @@ static int imx214_probe(struct i2c_client *client)
 	imx214->unit_size = v4l2_ctrl_new_std_compound(&imx214->ctrls,
 				NULL,
 				V4L2_CID_UNIT_CELL_SIZE,
-				v4l2_ctrl_ptr_create((void *)&unit_size));
+				v4l2_ctrl_ptr_create((void *)&unit_size),
+				v4l2_ctrl_ptr_create(NULL),
+				v4l2_ctrl_ptr_create(NULL));
+
 	ret = imx214->ctrls.error;
 	if (ret) {
 		dev_err(&client->dev, "%s control init failed (%d)\n",
diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
index ed44e5800759..4af8f9ca12a6 100644
--- a/drivers/media/platform/qcom/venus/venc_ctrls.c
+++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
@@ -579,10 +579,14 @@ int venc_ctrl_init(struct venus_inst *inst)
 
 	v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
 				   V4L2_CID_COLORIMETRY_HDR10_CLL_INFO,
+				   v4l2_ctrl_ptr_create(NULL),
+				   v4l2_ctrl_ptr_create(NULL),
 				   v4l2_ctrl_ptr_create(NULL));
 
 	v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
 				   V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY,
+				   v4l2_ctrl_ptr_create(NULL),
+				   v4l2_ctrl_ptr_create(NULL),
 				   v4l2_ctrl_ptr_create(NULL));
 
 	v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
index db9baa0bd05f..8a9c816b0dab 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
@@ -97,6 +97,28 @@ static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
 	return ptr_to_user(c, ctrl, ctrl->p_new);
 }
 
+/* Helper function: copy the minimum control value back to the caller */
+static int min_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
+{
+	int idx;
+
+	for (idx = 0; idx < ctrl->elems; idx++)
+		ctrl->type_ops->minimum(ctrl, idx, ctrl->p_new);
+
+	return ptr_to_user(c, ctrl, ctrl->p_new);
+}
+
+/* Helper function: copy the maximum control value back to the caller */
+static int max_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
+{
+	int idx;
+
+	for (idx = 0; idx < ctrl->elems; idx++)
+		ctrl->type_ops->maximum(ctrl, idx, ctrl->p_new);
+
+	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,
@@ -220,8 +242,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
 		cs->error_idx = i;
 
 		if (cs->which &&
-		    cs->which != V4L2_CTRL_WHICH_DEF_VAL &&
-		    cs->which != V4L2_CTRL_WHICH_REQUEST_VAL &&
+		    (cs->which < V4L2_CTRL_WHICH_DEF_VAL ||
+		     cs->which > V4L2_CTRL_WHICH_MAX_VAL) &&
 		    V4L2_CTRL_ID2WHICH(id) != cs->which) {
 			dprintk(vdev,
 				"invalid which 0x%x or control id 0x%x\n",
@@ -335,8 +357,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
  */
 static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
 {
-	if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL ||
-	    which == V4L2_CTRL_WHICH_REQUEST_VAL)
+	if (which == 0 || (which >= V4L2_CTRL_WHICH_DEF_VAL &&
+			   which <= V4L2_CTRL_WHICH_MAX_VAL))
 		return 0;
 	return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
 }
@@ -356,10 +378,12 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
 	struct v4l2_ctrl_helper *helpers = helper;
 	int ret;
 	int i, j;
-	bool is_default, is_request;
+	bool is_default, is_request, is_min, is_max;
 
 	is_default = (cs->which == V4L2_CTRL_WHICH_DEF_VAL);
 	is_request = (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL);
+	is_min = (cs->which == V4L2_CTRL_WHICH_MIN_VAL);
+	is_max = (cs->which == V4L2_CTRL_WHICH_MAX_VAL);
 
 	cs->error_idx = cs->count;
 	cs->which = V4L2_CTRL_ID2WHICH(cs->which);
@@ -399,13 +423,14 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
 
 		/*
 		 * g_volatile_ctrl will update the new control values.
-		 * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL and
+		 * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL,
+		 * V4L2_CTRL_WHICH_MIN_VAL, V4L2_CTRL_WHICH_MAX_VAL and
 		 * V4L2_CTRL_WHICH_REQUEST_VAL. In the case of requests
 		 * it is v4l2_ctrl_request_complete() that copies the
 		 * volatile controls at the time of request completion
 		 * to the request, so you don't want to do that again.
 		 */
-		if (!is_default && !is_request &&
+		if (!is_default && !is_request && !is_min && !is_max &&
 		    ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
 		    (master->has_volatiles && !is_cur_manual(master)))) {
 			for (j = 0; j < master->ncontrols; j++)
@@ -432,6 +457,10 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
 				ret = def_to_user(cs->controls + idx, ref->ctrl);
 			else if (is_request && ref->valid_p_req)
 				ret = req_to_user(cs->controls + idx, ref);
+			else if (is_min)
+				ret = min_to_user(cs->controls + idx, ref->ctrl);
+			else if (is_max)
+				ret = max_to_user(cs->controls + idx, ref->ctrl);
 			else if (is_volatile)
 				ret = new_to_user(cs->controls + idx, ref->ctrl);
 			else
@@ -523,9 +552,11 @@ int try_set_ext_ctrls_common(struct v4l2_fh *fh,
 
 	cs->error_idx = cs->count;
 
-	/* Default value cannot be changed */
-	if (cs->which == V4L2_CTRL_WHICH_DEF_VAL) {
-		dprintk(vdev, "%s: cannot change default value\n",
+	/* Default/minimum/maximum values cannot be changed */
+	if (cs->which == V4L2_CTRL_WHICH_DEF_VAL ||
+	    cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
+	    cs->which == V4L2_CTRL_WHICH_MAX_VAL) {
+		dprintk(vdev, "%s: cannot change default/min/max value\n",
 			video_device_node_name(vdev));
 		return -EINVAL;
 	}
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index 35d43ba650db..8c5828c7c6d3 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -186,6 +186,28 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
 	}
 }
 
+static void std_min_compound(const struct v4l2_ctrl *ctrl, u32 idx,
+			     union v4l2_ctrl_ptr ptr)
+{
+	void *p = ptr.p + idx * ctrl->elem_size;
+
+	if (ctrl->p_min.p_const)
+		memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
+	else
+		memset(p, 0, ctrl->elem_size);
+}
+
+static void std_max_compound(const struct v4l2_ctrl *ctrl, u32 idx,
+			     union v4l2_ctrl_ptr ptr)
+{
+	void *p = ptr.p + idx * ctrl->elem_size;
+
+	if (ctrl->p_max.p_const)
+		memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
+	else
+		memset(p, 0, ctrl->elem_size);
+}
+
 static void std_init(const struct v4l2_ctrl *ctrl, u32 idx,
 		     union v4l2_ctrl_ptr ptr)
 {
@@ -224,6 +246,82 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx,
 	}
 }
 
+static void std_minimum(const struct v4l2_ctrl *ctrl, u32 idx,
+			union v4l2_ctrl_ptr ptr)
+{
+	switch (ctrl->type) {
+	case V4L2_CTRL_TYPE_STRING:
+		idx *= ctrl->elem_size;
+		memset(ptr.p_char + idx, ' ', ctrl->minimum);
+		ptr.p_char[idx + ctrl->minimum] = '\0';
+		break;
+	case V4L2_CTRL_TYPE_INTEGER64:
+		ptr.p_s64[idx] = ctrl->minimum;
+		break;
+	case V4L2_CTRL_TYPE_INTEGER:
+	case V4L2_CTRL_TYPE_INTEGER_MENU:
+	case V4L2_CTRL_TYPE_MENU:
+	case V4L2_CTRL_TYPE_BITMASK:
+	case V4L2_CTRL_TYPE_BOOLEAN:
+		ptr.p_s32[idx] = ctrl->minimum;
+		break;
+	case V4L2_CTRL_TYPE_BUTTON:
+	case V4L2_CTRL_TYPE_CTRL_CLASS:
+		ptr.p_s32[idx] = 0;
+		break;
+	case V4L2_CTRL_TYPE_U8:
+		ptr.p_u8[idx] = ctrl->minimum;
+		break;
+	case V4L2_CTRL_TYPE_U16:
+		ptr.p_u16[idx] = ctrl->minimum;
+		break;
+	case V4L2_CTRL_TYPE_U32:
+		ptr.p_u32[idx] = ctrl->minimum;
+		break;
+	default:
+		std_min_compound(ctrl, idx, ptr);
+		break;
+	}
+}
+
+static void std_maximum(const struct v4l2_ctrl *ctrl, u32 idx,
+			union v4l2_ctrl_ptr ptr)
+{
+	switch (ctrl->type) {
+	case V4L2_CTRL_TYPE_STRING:
+		idx *= ctrl->elem_size;
+		memset(ptr.p_char + idx, ' ', ctrl->maximum);
+		ptr.p_char[idx + ctrl->maximum] = '\0';
+		break;
+	case V4L2_CTRL_TYPE_INTEGER64:
+		ptr.p_s64[idx] = ctrl->maximum;
+		break;
+	case V4L2_CTRL_TYPE_INTEGER:
+	case V4L2_CTRL_TYPE_INTEGER_MENU:
+	case V4L2_CTRL_TYPE_MENU:
+	case V4L2_CTRL_TYPE_BITMASK:
+	case V4L2_CTRL_TYPE_BOOLEAN:
+		ptr.p_s32[idx] = ctrl->maximum;
+		break;
+	case V4L2_CTRL_TYPE_BUTTON:
+	case V4L2_CTRL_TYPE_CTRL_CLASS:
+		ptr.p_s32[idx] = 0;
+		break;
+	case V4L2_CTRL_TYPE_U8:
+		ptr.p_u8[idx] = ctrl->maximum;
+		break;
+	case V4L2_CTRL_TYPE_U16:
+		ptr.p_u16[idx] = ctrl->maximum;
+		break;
+	case V4L2_CTRL_TYPE_U32:
+		ptr.p_u32[idx] = ctrl->maximum;
+		break;
+	default:
+		std_max_compound(ctrl, idx, ptr);
+		break;
+	}
+}
+
 static void std_log(const struct v4l2_ctrl *ctrl)
 {
 	union v4l2_ctrl_ptr ptr = ctrl->p_cur;
@@ -986,6 +1084,8 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
 static const struct v4l2_ctrl_type_ops std_type_ops = {
 	.equal = std_equal,
 	.init = std_init,
+	.minimum = std_minimum,
+	.maximum = std_maximum,
 	.log = std_log,
 	.validate = std_validate,
 };
@@ -1368,7 +1468,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 			s64 min, s64 max, u64 step, s64 def,
 			const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size,
 			u32 flags, const char * const *qmenu,
-			const s64 *qmenu_int, const union v4l2_ctrl_ptr p_def,
+			const s64 *qmenu_int,
+			const union v4l2_ctrl_ptr p_def,
+			const union v4l2_ctrl_ptr p_min,
+			const union v4l2_ctrl_ptr p_max,
 			void *priv)
 {
 	struct v4l2_ctrl *ctrl;
@@ -1515,7 +1618,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 		sz_extra += 2 * tot_ctrl_size;
 
 	if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const)
-		sz_extra += elem_size;
+		sz_extra += elem_size * 3;
 
 	ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
 	if (ctrl == NULL) {
@@ -1565,6 +1668,13 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 		ctrl->p_def.p = ctrl->p_cur.p + tot_ctrl_size;
 		memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
 	}
+	if (type >= V4L2_CTRL_COMPOUND_TYPES &&
+	    p_min.p_const && p_max.p_const) {
+		ctrl->p_min.p = ctrl->p_cur.p + 2 * tot_ctrl_size;
+		memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
+		ctrl->p_max.p = ctrl->p_cur.p + 3 * tot_ctrl_size;
+		memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
+	}
 
 	for (idx = 0; idx < elems; idx++) {
 		ctrl->type_ops->init(ctrl, idx, ctrl->p_cur);
@@ -1617,7 +1727,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
 			type, min, max,
 			is_menu ? cfg->menu_skip_mask : step, def,
 			cfg->dims, cfg->elem_size,
-			flags, qmenu, qmenu_int, cfg->p_def, priv);
+			flags, qmenu, qmenu_int, cfg->p_def, cfg->p_min,
+			cfg->p_max, priv);
 	if (ctrl)
 		ctrl->is_private = cfg->is_private;
 	return ctrl;
@@ -1642,7 +1753,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
 	}
 	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
 			     min, max, step, def, NULL, 0,
-			     flags, NULL, NULL, ptr_null, NULL);
+			     flags, NULL, NULL, ptr_null, ptr_null,
+			     ptr_null, NULL);
 }
 EXPORT_SYMBOL(v4l2_ctrl_new_std);
 
@@ -1675,7 +1787,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
 	}
 	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
 			     0, max, mask, def, NULL, 0,
-			     flags, qmenu, qmenu_int, ptr_null, NULL);
+			     flags, qmenu, qmenu_int, ptr_null, ptr_null,
+			     ptr_null, NULL);
 }
 EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
 
@@ -1707,7 +1820,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
 	}
 	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
 			     0, max, mask, def, NULL, 0,
-			     flags, qmenu, NULL, ptr_null, NULL);
+			     flags, qmenu, NULL, ptr_null, ptr_null,
+			     ptr_null, NULL);
 
 }
 EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
@@ -1715,7 +1829,9 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
 /* Helper function for standard compound controls */
 struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
 				const struct v4l2_ctrl_ops *ops, u32 id,
-				const union v4l2_ctrl_ptr p_def)
+				const union v4l2_ctrl_ptr p_def,
+				const union v4l2_ctrl_ptr p_min,
+				const union v4l2_ctrl_ptr p_max)
 {
 	const char *name;
 	enum v4l2_ctrl_type type;
@@ -1729,7 +1845,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
 	}
 	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
 			     min, max, step, def, NULL, 0,
-			     flags, NULL, NULL, p_def, NULL);
+			     flags, NULL, NULL, p_def, p_min, p_max, NULL);
 }
 EXPORT_SYMBOL(v4l2_ctrl_new_std_compound);
 
@@ -1753,7 +1869,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
 	}
 	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
 			     0, max, 0, def, NULL, 0,
-			     flags, NULL, qmenu_int, ptr_null, NULL);
+			     flags, NULL, qmenu_int, ptr_null, ptr_null,
+			     ptr_null, NULL);
 }
 EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
 
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 21470de62d72..0f713b9a95f9 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -893,7 +893,9 @@ static bool check_ext_ctrls(struct v4l2_ext_controls *c, unsigned long ioctl)
 			return false;
 		break;
 	case V4L2_CTRL_WHICH_DEF_VAL:
-		/* Default value cannot be changed */
+	case V4L2_CTRL_WHICH_MIN_VAL:
+	case V4L2_CTRL_WHICH_MAX_VAL:
+		/* Default, minimum or maximum value cannot be changed */
 		if (ioctl == VIDIOC_S_EXT_CTRLS ||
 		    ioctl == VIDIOC_TRY_EXT_CTRLS) {
 			c->error_idx = c->count;
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 919e104de50b..c8ab8c44d7c6 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -131,6 +131,8 @@ struct v4l2_ctrl_ops {
  *
  * @equal: return true if both values are equal.
  * @init: initialize the value.
+ * @minimum: set the value to the minimum value of the control.
+ * @maximum: set the value to the maximum value of the control.
  * @log: log the value.
  * @validate: validate the value. Return 0 on success and a negative value
  *	otherwise.
@@ -141,6 +143,10 @@ struct v4l2_ctrl_type_ops {
 		      union v4l2_ctrl_ptr ptr2);
 	void (*init)(const struct v4l2_ctrl *ctrl, u32 idx,
 		     union v4l2_ctrl_ptr ptr);
+	void (*minimum)(const struct v4l2_ctrl *ctrl, u32 idx,
+			union v4l2_ctrl_ptr ptr);
+	void (*maximum)(const struct v4l2_ctrl *ctrl, u32 idx,
+			union v4l2_ctrl_ptr ptr);
 	void (*log)(const struct v4l2_ctrl *ctrl);
 	int (*validate)(const struct v4l2_ctrl *ctrl, u32 idx,
 			union v4l2_ctrl_ptr ptr);
@@ -237,6 +243,12 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv);
  * @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).
+ * @p_min:	The control's minimum value represented via a union which
+ *		provides a standard way of accessing control types
+ *		through a pointer (for compound controls only).
+ * @p_max:	The control's maximum value represented via a union which
+ *		provides a standard way of accessing control types
+ *		through a pointer (for compound controls only).
  * @p_cur:	The control's current value represented via a union which
  *		provides a standard way of accessing control types
  *		through a pointer.
@@ -292,6 +304,8 @@ struct v4l2_ctrl {
 	} cur;
 
 	union v4l2_ctrl_ptr p_def;
+	union v4l2_ctrl_ptr p_min;
+	union v4l2_ctrl_ptr p_max;
 	union v4l2_ctrl_ptr p_new;
 	union v4l2_ctrl_ptr p_cur;
 };
@@ -398,6 +412,8 @@ struct v4l2_ctrl_handler {
  * @step:	The control's step value for non-menu controls.
  * @def:	The control's default value.
  * @p_def:	The control's default value for compound controls.
+ * @p_min:	The control's minimum value for compound controls.
+ * @p_max:	The control's maximum value for compound controls.
  * @dims:	The size of each dimension.
  * @elem_size:	The size in bytes of the control.
  * @flags:	The control's flags.
@@ -427,6 +443,8 @@ struct v4l2_ctrl_config {
 	u64 step;
 	s64 def;
 	union v4l2_ctrl_ptr p_def;
+	union v4l2_ctrl_ptr p_min;
+	union v4l2_ctrl_ptr p_max;
 	u32 dims[V4L2_CTRL_MAX_DIMS];
 	u32 elem_size;
 	u32 flags;
@@ -696,17 +714,21 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
  * @ops:       The control ops.
  * @id:        The control ID.
  * @p_def:     The control's default value.
+ * @p_min:     The control's minimum value.
+ * @p_max:     The control's maximum value.
  *
- * Sames as v4l2_ctrl_new_std(), but with support to compound controls, thanks
- * to the @p_def field. Use v4l2_ctrl_ptr_create() to create @p_def from a
- * pointer. Use v4l2_ctrl_ptr_create(NULL) if the default value of the
- * compound control should be all zeroes.
+ * Same as v4l2_ctrl_new_std(), but with support to compound controls, thanks
+ * to the @p_def/min/max field. Use v4l2_ctrl_ptr_create() to create
+ * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
+ * default/min/max value of the compound control should be all zeroes.
  *
  */
 struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
 					     const struct v4l2_ctrl_ops *ops,
 					     u32 id,
-					     const union v4l2_ctrl_ptr p_def);
+					     const union v4l2_ctrl_ptr p_def,
+					     const union v4l2_ctrl_ptr p_min,
+					     const union v4l2_ctrl_ptr p_max);
 
 /**
  * v4l2_ctrl_new_int_menu() - Create a new standard V4L2 integer menu control.
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 2e36bb610ea6..134f6a65eacc 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -1796,6 +1796,8 @@ struct v4l2_ext_controls {
 #define V4L2_CTRL_WHICH_CUR_VAL   0
 #define V4L2_CTRL_WHICH_DEF_VAL   0x0f000000
 #define V4L2_CTRL_WHICH_REQUEST_VAL 0x0f010000
+#define V4L2_CTRL_WHICH_MIN_VAL   0x0f020000
+#define V4L2_CTRL_WHICH_MAX_VAL   0x0f030000
 
 enum v4l2_ctrl_type {
 	V4L2_CTRL_TYPE_INTEGER	     = 1,
-- 
2.37.0.rc0.161.g10f37bed90-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v7 6/7] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL
  2022-06-28  7:56 [PATCH v7 0/7] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (4 preceding siblings ...)
  2022-06-28  7:57 ` [PATCH v7 5/7] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
@ 2022-06-28  7:57 ` Yunke Cao
  2022-06-30  6:41   ` Ricardo Ribalda
  2022-08-24 20:03   ` Laurent Pinchart
  2022-06-28  7:57 ` [PATCH v7 7/7] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
  2022-07-14 23:25 ` [PATCH v7 0/7] media: Implement " Yunke Cao
  7 siblings, 2 replies; 32+ messages in thread
From: Yunke Cao @ 2022-06-28  7:57 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Nicolas Dufresne, Mauro Carvalho Chehab
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Add support for V4L2_CTRL_WHICH_MIN/MAX_VAL in uvc driver.
It is useful for the V4L2_CID_UVC_REGION_OF_INTEREST_RECT control.

Signed-off-by: Yunke Cao <yunkec@google.com>
---
 drivers/media/usb/uvc/uvc_ctrl.c | 73 ++++++++++++++++++++++++++------
 drivers/media/usb/uvc/uvc_v4l2.c | 11 +++--
 drivers/media/usb/uvc/uvcvideo.h |  3 +-
 3 files changed, 70 insertions(+), 17 deletions(-)

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 00482269233a..b569d6824ac1 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1923,7 +1923,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
 }
 
 int __uvc_ctrl_get_fixed_std(struct uvc_video_chain *chain,
-			     struct v4l2_ext_control *xctrl)
+			     struct v4l2_ext_control *xctrl, u32 v4l2_which)
 {
 	struct v4l2_queryctrl qc = { .id = xctrl->id };
 	int ret = uvc_query_v4l2_ctrl(chain, &qc);
@@ -1931,16 +1931,69 @@ int __uvc_ctrl_get_fixed_std(struct uvc_video_chain *chain,
 	if (ret < 0)
 		return ret;
 
-	xctrl->value = qc.default_value;
+	switch (v4l2_which) {
+	case V4L2_CTRL_WHICH_DEF_VAL:
+		xctrl->value = qc.default_value;
+		break;
+	case V4L2_CTRL_WHICH_MIN_VAL:
+		xctrl->value = qc.minimum;
+		break;
+	case V4L2_CTRL_WHICH_MAX_VAL:
+		xctrl->value = qc.maximum;
+		break;
+	default:
+		return -EINVAL;
+	}
+
 	return 0;
 }
 
+static int __uvc_ctrl_get_fixed_compound(struct uvc_video_chain *chain,
+					 struct uvc_control_mapping *mapping,
+					 struct uvc_control *ctrl,
+					 u32 v4l2_which,
+					 struct v4l2_ext_control *xctrl)
+{
+	int ret;
+	u32 flag, id;
+
+	if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
+		return -EINVAL;
+
+	switch (v4l2_which) {
+	case V4L2_CTRL_WHICH_DEF_VAL:
+		flag = UVC_CTRL_FLAG_GET_DEF;
+		id = UVC_CTRL_DATA_DEF;
+		break;
+	case V4L2_CTRL_WHICH_MIN_VAL:
+		flag = UVC_CTRL_FLAG_GET_MIN;
+		id = UVC_CTRL_DATA_MIN;
+		break;
+	case V4L2_CTRL_WHICH_MAX_VAL:
+		flag = UVC_CTRL_FLAG_GET_MAX;
+		id = UVC_CTRL_DATA_MAX;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (!(ctrl->info.flags & flag) && flag != UVC_CTRL_FLAG_GET_DEF)
+		return -EACCES;
+
+	if (!ctrl->cached) {
+		ret = uvc_ctrl_populate_cache(chain, ctrl);
+		if (ret < 0)
+			return ret;
+	}
+
+	return __uvc_ctrl_get_compound_to_user(mapping, ctrl, id, xctrl);
+}
+
 int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
-		       struct v4l2_ext_control *xctrl)
+		       struct v4l2_ext_control *xctrl, u32 v4l2_which)
 {
 	struct uvc_control *ctrl;
 	struct uvc_control_mapping *mapping;
-	int ret;
 
 	if (__uvc_query_v4l2_class(chain, xctrl->id, 0) >= 0)
 		return -EACCES;
@@ -1950,16 +2003,10 @@ int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
 		return -EINVAL;
 
 	if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
-		return __uvc_ctrl_get_fixed_std(chain, xctrl);
+		return __uvc_ctrl_get_fixed_std(chain, xctrl, v4l2_which);
 
-	if (!ctrl->cached) {
-		ret = uvc_ctrl_populate_cache(chain, ctrl);
-		if (ret < 0)
-			return ret;
-	}
-
-	return __uvc_ctrl_get_compound_to_user(mapping, ctrl, UVC_CTRL_DATA_DEF,
-					       xctrl);
+	return __uvc_ctrl_get_fixed_compound(chain, mapping, ctrl, v4l2_which,
+					     xctrl);
 }
 
 int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index aad61af36271..004e3b764737 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1043,16 +1043,21 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
 	if (ret < 0)
 		return ret;
 
-	if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
+	switch (ctrls->which) {
+	case V4L2_CTRL_WHICH_DEF_VAL:
+	case V4L2_CTRL_WHICH_MIN_VAL:
+	case V4L2_CTRL_WHICH_MAX_VAL:
 		for (i = 0; i < ctrls->count; ++ctrl, ++i) {
-			ret = uvc_ctrl_get_fixed(chain, ctrl);
+			ret = uvc_ctrl_get_fixed(chain, ctrl, ctrls->which);
 			if (ret < 0) {
 				ctrls->error_idx = i;
 				return ret;
 			}
 		}
-
 		return 0;
+
+	default:
+		break;
 	}
 
 	ret = uvc_ctrl_begin(chain);
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 9ff95bbad251..54cc47bc2d33 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -913,7 +913,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
 
 int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
 int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
-		       struct v4l2_ext_control *xctrl);
+		       struct v4l2_ext_control *xctrl,
+		       u32 v4l2_which);
 int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
 int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
 			   bool read);
-- 
2.37.0.rc0.161.g10f37bed90-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v7 7/7] media: uvcvideo: document UVC v1.5 ROI
  2022-06-28  7:56 [PATCH v7 0/7] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (5 preceding siblings ...)
  2022-06-28  7:57 ` [PATCH v7 6/7] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
@ 2022-06-28  7:57 ` Yunke Cao
  2022-06-30  6:41   ` Ricardo Ribalda
  2022-08-24 20:17   ` Laurent Pinchart
  2022-07-14 23:25 ` [PATCH v7 0/7] media: Implement " Yunke Cao
  7 siblings, 2 replies; 32+ messages in thread
From: Yunke Cao @ 2022-06-28  7:57 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Nicolas Dufresne, Mauro Carvalho Chehab
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao

Added documentation of V4L2_CID_UVC_REGION_OF_INTEREST_RECT and
V4L2_CID_UVC_REGION_OF_INTEREST_AUTO.

Signed-off-by: Yunke Cao <yunkec@google.com>
---
 .../userspace-api/media/drivers/uvcvideo.rst  | 61 +++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
index a290f9fadae9..ee4c182aa274 100644
--- a/Documentation/userspace-api/media/drivers/uvcvideo.rst
+++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst
@@ -181,6 +181,7 @@ Argument: struct uvc_xu_control_mapping
 	UVC_CTRL_DATA_TYPE_BOOLEAN	Boolean
 	UVC_CTRL_DATA_TYPE_ENUM		Enumeration
 	UVC_CTRL_DATA_TYPE_BITMASK	Bitmask
+	UVC_CTRL_DATA_TYPE_RECT		Rectangular area
 
 
 UVCIOC_CTRL_QUERY - Query a UVC XU control
@@ -255,3 +256,63 @@ Argument: struct uvc_xu_control_query
 	__u8	query		Request code to send to the device
 	__u16	size		Control data size (in bytes)
 	__u8	*data		Control value
+
+Private V4L2 controls
+---------------------
+
+A few UVC specific V4L2 control IDs are listed below.
+
+``V4L2_CID_UVC_REGION_OF_INTEREST_RECT (struct)``
+	This control determines the region of interest (ROI). ROI is an
+	rectangular area represented by a struct :c:type:`v4l2_rect`. The
+	rectangle is in global sensor coordinates and pixel units. It is
+	independent of the field of view, not impacted by any cropping or
+	scaling.
+
+	Use ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` to query
+	the range of rectangle sizes. For example, a device can have a minimum
+	ROI rectangle of 1x1@0x0 and a maximum of 640x480@0x0.
+
+	Setting a ROI allows the camera to optimize the capture for the region.
+	The value of ``V4L2_CID_REGION_OF_INTEREST_AUTO`` control determines
+	the detailed behavior.
+
+
+``V4L2_CID_UVC_REGION_OF_INTEREST_AUTO (bitmask)``
+	This determines which, if any, on board features should track to the
+	Region of Interest specified by the current value of
+	``V4L2_CID_UVD__REGION_OF_INTEREST_RECT``.
+
+	Max value is a mask indicating all supported Auto
+	Controls.
+
+.. flat-table::
+    :header-rows:  0
+    :stub-columns: 0
+
+    * - ``V4L2_REGION_OF_INTEREST_AUTO_EXPOSURE``
+      - Setting this to true enables automatic exposure time for the specified
+	region.
+    * - ``V4L2_REGION_OF_INTEREST_AUTO_IRIS``
+      - Setting this to true enables automatic iris aperture for the specified
+	region.
+    * - ``V4L2_REGION_OF_INTEREST_AUTO_WHITE_BALANCE``
+      - Setting this to true enables automatic white balance adjustment for the
+	specified region.
+    * - ``V4L2_REGION_OF_INTEREST_AUTO_FOCUS``
+      - Setting this to true enables automatic focus adjustment for the
+	specified region.
+    * - ``V4L2_REGION_OF_INTEREST_AUTO_FACE_DETECT``
+      - Setting this to true enables automatic face detection for the
+	specified region.
+    * - ``V4L2_REGION_OF_INTEREST_AUTO_DETECT_AND_TRACK``
+      - Setting this to true enables automatic face detection and tracking. The
+	current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
+	the driver.
+    * - ``V4L2_REGION_OF_INTEREST_AUTO_IMAGE_STABILIZATION``
+      - Setting this to true enables automatic image stabilization. The
+	current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
+	the driver.
+    * - ``V4L2_REGION_OF_INTEREST_AUTO_HIGHER_QUALITY``
+      - Setting this to true enables automatically capture the specified region
+	with higher quality if possible.
-- 
2.37.0.rc0.161.g10f37bed90-goog


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 7/7] media: uvcvideo: document UVC v1.5 ROI
  2022-06-28  7:57 ` [PATCH v7 7/7] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
@ 2022-06-30  6:41   ` Ricardo Ribalda
  2022-08-24 20:17   ` Laurent Pinchart
  1 sibling, 0 replies; 32+ messages in thread
From: Ricardo Ribalda @ 2022-06-30  6:41 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Laurent Pinchart, Nicolas Dufresne,
	Mauro Carvalho Chehab, Tomasz Figa, Sergey Senozhatsky,
	linux-media

On Tue, 28 Jun 2022 at 09:58, Yunke Cao <yunkec@google.com> wrote:
>
> Added documentation of V4L2_CID_UVC_REGION_OF_INTEREST_RECT and
> V4L2_CID_UVC_REGION_OF_INTEREST_AUTO.
>
> Signed-off-by: Yunke Cao <yunkec@google.com>
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> ---
>  .../userspace-api/media/drivers/uvcvideo.rst  | 61 +++++++++++++++++++
>  1 file changed, 61 insertions(+)
>
> diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> index a290f9fadae9..ee4c182aa274 100644
> --- a/Documentation/userspace-api/media/drivers/uvcvideo.rst
> +++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> @@ -181,6 +181,7 @@ Argument: struct uvc_xu_control_mapping
>         UVC_CTRL_DATA_TYPE_BOOLEAN      Boolean
>         UVC_CTRL_DATA_TYPE_ENUM         Enumeration
>         UVC_CTRL_DATA_TYPE_BITMASK      Bitmask
> +       UVC_CTRL_DATA_TYPE_RECT         Rectangular area
>
>
>  UVCIOC_CTRL_QUERY - Query a UVC XU control
> @@ -255,3 +256,63 @@ Argument: struct uvc_xu_control_query
>         __u8    query           Request code to send to the device
>         __u16   size            Control data size (in bytes)
>         __u8    *data           Control value
> +
> +Private V4L2 controls
> +---------------------
> +
> +A few UVC specific V4L2 control IDs are listed below.
> +
> +``V4L2_CID_UVC_REGION_OF_INTEREST_RECT (struct)``
> +       This control determines the region of interest (ROI). ROI is an
> +       rectangular area represented by a struct :c:type:`v4l2_rect`. The
> +       rectangle is in global sensor coordinates and pixel units. It is
> +       independent of the field of view, not impacted by any cropping or
> +       scaling.
> +
> +       Use ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` to query
> +       the range of rectangle sizes. For example, a device can have a minimum
> +       ROI rectangle of 1x1@0x0 and a maximum of 640x480@0x0.
> +
> +       Setting a ROI allows the camera to optimize the capture for the region.
> +       The value of ``V4L2_CID_REGION_OF_INTEREST_AUTO`` control determines
> +       the detailed behavior.
> +
> +
> +``V4L2_CID_UVC_REGION_OF_INTEREST_AUTO (bitmask)``
> +       This determines which, if any, on board features should track to the
> +       Region of Interest specified by the current value of
> +       ``V4L2_CID_UVD__REGION_OF_INTEREST_RECT``.
> +
> +       Max value is a mask indicating all supported Auto
> +       Controls.
> +
> +.. flat-table::
> +    :header-rows:  0
> +    :stub-columns: 0
> +
> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_EXPOSURE``
> +      - Setting this to true enables automatic exposure time for the specified
> +       region.
> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_IRIS``
> +      - Setting this to true enables automatic iris aperture for the specified
> +       region.
> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_WHITE_BALANCE``
> +      - Setting this to true enables automatic white balance adjustment for the
> +       specified region.
> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_FOCUS``
> +      - Setting this to true enables automatic focus adjustment for the
> +       specified region.
> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_FACE_DETECT``
> +      - Setting this to true enables automatic face detection for the
> +       specified region.
> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_DETECT_AND_TRACK``
> +      - Setting this to true enables automatic face detection and tracking. The
> +       current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
> +       the driver.
> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_IMAGE_STABILIZATION``
> +      - Setting this to true enables automatic image stabilization. The
> +       current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
> +       the driver.
> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_HIGHER_QUALITY``
> +      - Setting this to true enables automatically capture the specified region
> +       with higher quality if possible.
> --
> 2.37.0.rc0.161.g10f37bed90-goog
>


-- 
Ricardo Ribalda

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 6/7] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL
  2022-06-28  7:57 ` [PATCH v7 6/7] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
@ 2022-06-30  6:41   ` Ricardo Ribalda
  2022-08-24 20:03   ` Laurent Pinchart
  1 sibling, 0 replies; 32+ messages in thread
From: Ricardo Ribalda @ 2022-06-30  6:41 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Laurent Pinchart, Nicolas Dufresne,
	Mauro Carvalho Chehab, Tomasz Figa, Sergey Senozhatsky,
	linux-media

On Tue, 28 Jun 2022 at 09:57, Yunke Cao <yunkec@google.com> wrote:
>
> Add support for V4L2_CTRL_WHICH_MIN/MAX_VAL in uvc driver.
> It is useful for the V4L2_CID_UVC_REGION_OF_INTEREST_RECT control.
>
> Signed-off-by: Yunke Cao <yunkec@google.com>
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> ---
>  drivers/media/usb/uvc/uvc_ctrl.c | 73 ++++++++++++++++++++++++++------
>  drivers/media/usb/uvc/uvc_v4l2.c | 11 +++--
>  drivers/media/usb/uvc/uvcvideo.h |  3 +-
>  3 files changed, 70 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 00482269233a..b569d6824ac1 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -1923,7 +1923,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
>  }
>
>  int __uvc_ctrl_get_fixed_std(struct uvc_video_chain *chain,
> -                            struct v4l2_ext_control *xctrl)
> +                            struct v4l2_ext_control *xctrl, u32 v4l2_which)
>  {
>         struct v4l2_queryctrl qc = { .id = xctrl->id };
>         int ret = uvc_query_v4l2_ctrl(chain, &qc);
> @@ -1931,16 +1931,69 @@ int __uvc_ctrl_get_fixed_std(struct uvc_video_chain *chain,
>         if (ret < 0)
>                 return ret;
>
> -       xctrl->value = qc.default_value;
> +       switch (v4l2_which) {
> +       case V4L2_CTRL_WHICH_DEF_VAL:
> +               xctrl->value = qc.default_value;
> +               break;
> +       case V4L2_CTRL_WHICH_MIN_VAL:
> +               xctrl->value = qc.minimum;
> +               break;
> +       case V4L2_CTRL_WHICH_MAX_VAL:
> +               xctrl->value = qc.maximum;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
>         return 0;
>  }
>
> +static int __uvc_ctrl_get_fixed_compound(struct uvc_video_chain *chain,
> +                                        struct uvc_control_mapping *mapping,
> +                                        struct uvc_control *ctrl,
> +                                        u32 v4l2_which,
> +                                        struct v4l2_ext_control *xctrl)
> +{
> +       int ret;
> +       u32 flag, id;
> +
> +       if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> +               return -EINVAL;
> +
> +       switch (v4l2_which) {
> +       case V4L2_CTRL_WHICH_DEF_VAL:
> +               flag = UVC_CTRL_FLAG_GET_DEF;
> +               id = UVC_CTRL_DATA_DEF;
> +               break;
> +       case V4L2_CTRL_WHICH_MIN_VAL:
> +               flag = UVC_CTRL_FLAG_GET_MIN;
> +               id = UVC_CTRL_DATA_MIN;
> +               break;
> +       case V4L2_CTRL_WHICH_MAX_VAL:
> +               flag = UVC_CTRL_FLAG_GET_MAX;
> +               id = UVC_CTRL_DATA_MAX;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       if (!(ctrl->info.flags & flag) && flag != UVC_CTRL_FLAG_GET_DEF)
> +               return -EACCES;
> +
> +       if (!ctrl->cached) {
> +               ret = uvc_ctrl_populate_cache(chain, ctrl);
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       return __uvc_ctrl_get_compound_to_user(mapping, ctrl, id, xctrl);
> +}
> +
>  int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> -                      struct v4l2_ext_control *xctrl)
> +                      struct v4l2_ext_control *xctrl, u32 v4l2_which)
>  {
>         struct uvc_control *ctrl;
>         struct uvc_control_mapping *mapping;
> -       int ret;
>
>         if (__uvc_query_v4l2_class(chain, xctrl->id, 0) >= 0)
>                 return -EACCES;
> @@ -1950,16 +2003,10 @@ int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
>                 return -EINVAL;
>
>         if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> -               return __uvc_ctrl_get_fixed_std(chain, xctrl);
> +               return __uvc_ctrl_get_fixed_std(chain, xctrl, v4l2_which);
>
> -       if (!ctrl->cached) {
> -               ret = uvc_ctrl_populate_cache(chain, ctrl);
> -               if (ret < 0)
> -                       return ret;
> -       }
> -
> -       return __uvc_ctrl_get_compound_to_user(mapping, ctrl, UVC_CTRL_DATA_DEF,
> -                                              xctrl);
> +       return __uvc_ctrl_get_fixed_compound(chain, mapping, ctrl, v4l2_which,
> +                                            xctrl);
>  }
>
>  int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index aad61af36271..004e3b764737 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -1043,16 +1043,21 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
>         if (ret < 0)
>                 return ret;
>
> -       if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
> +       switch (ctrls->which) {
> +       case V4L2_CTRL_WHICH_DEF_VAL:
> +       case V4L2_CTRL_WHICH_MIN_VAL:
> +       case V4L2_CTRL_WHICH_MAX_VAL:
>                 for (i = 0; i < ctrls->count; ++ctrl, ++i) {
> -                       ret = uvc_ctrl_get_fixed(chain, ctrl);
> +                       ret = uvc_ctrl_get_fixed(chain, ctrl, ctrls->which);
>                         if (ret < 0) {
>                                 ctrls->error_idx = i;
>                                 return ret;
>                         }
>                 }
> -
>                 return 0;
> +
> +       default:
> +               break;
>         }
>
>         ret = uvc_ctrl_begin(chain);
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 9ff95bbad251..54cc47bc2d33 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -913,7 +913,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
>
>  int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
>  int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> -                      struct v4l2_ext_control *xctrl);
> +                      struct v4l2_ext_control *xctrl,
> +                      u32 v4l2_which);
>  int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
>  int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
>                            bool read);
> --
> 2.37.0.rc0.161.g10f37bed90-goog
>


-- 
Ricardo Ribalda

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 4/7] media: uvcvideo: implement UVC v1.5 ROI
  2022-06-28  7:57 ` [PATCH v7 4/7] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
@ 2022-06-30  6:42   ` Ricardo Ribalda
  2022-08-24 20:36   ` Laurent Pinchart
  1 sibling, 0 replies; 32+ messages in thread
From: Ricardo Ribalda @ 2022-06-30  6:42 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Laurent Pinchart, Nicolas Dufresne,
	Mauro Carvalho Chehab, Tomasz Figa, Sergey Senozhatsky,
	linux-media

On Tue, 28 Jun 2022 at 09:57, Yunke Cao <yunkec@google.com> wrote:
>
> Implement support for ROI as described in UVC 1.5:
> 4.2.2.1.20 Digital Region of Interest (ROI) Control
>
> ROI control is implemented using V4L2 control API as
> two uvc-specific controls:
> V4L2_CID_UVC_REGION_OF_INTEREST_RECT and
> V4L2_CID_UVC_REGION_OF_INTEREST_AUTO.
>
> Signed-off-by: Yunke Cao <yunkec@google.com>
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> ---
>  drivers/media/usb/uvc/uvc_ctrl.c   | 146 ++++++++++++++++++++++++++++-
>  drivers/media/usb/uvc/uvc_v4l2.c   |   5 +-
>  drivers/media/usb/uvc/uvcvideo.h   |   7 ++
>  include/uapi/linux/usb/video.h     |   1 +
>  include/uapi/linux/uvcvideo.h      |  13 +++
>  include/uapi/linux/v4l2-controls.h |   8 ++
>  6 files changed, 178 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 508ee04afbcd..00482269233a 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -355,6 +355,23 @@ static const struct uvc_control_info uvc_ctrls[] = {
>                 .flags          = UVC_CTRL_FLAG_GET_CUR
>                                 | UVC_CTRL_FLAG_AUTO_UPDATE,
>         },
> +       /* UVC_CTRL_FLAG_AUTO_UPDATE is needed because the RoI may get updated
> +        * by sensors.
> +        * "This RoI should be the same as specified in most recent SET_CUR
> +        * except in the case where the ‘Auto Detect and Track’ and/or
> +        * ‘Image Stabilization’ bit have been set."
> +        * 4.2.2.1.20 Digital Region of Interest (ROI) Control
> +        */
> +       {
> +               .entity         = UVC_GUID_UVC_CAMERA,
> +               .selector       = UVC_CT_REGION_OF_INTEREST_CONTROL,
> +               .index          = 21,
> +               .size           = 10,
> +               .flags          = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
> +                               | UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
> +                               | UVC_CTRL_FLAG_GET_DEF
> +                               | UVC_CTRL_FLAG_AUTO_UPDATE,
> +       },
>  };
>
>  static const u32 uvc_control_classes[] = {
> @@ -429,6 +446,64 @@ static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping,
>         data[first+1] = min_t(int, abs(value), 0xff);
>  }
>
> +static void uvc_to_v4l2_rect(struct v4l2_rect *v4l2_rect,
> +                            const struct uvc_rect *uvc_rect)
> +{
> +       v4l2_rect->top = uvc_rect->top;
> +       v4l2_rect->left = uvc_rect->left;
> +       v4l2_rect->height = uvc_rect->bottom - uvc_rect->top + 1;
> +       v4l2_rect->width = uvc_rect->right - uvc_rect->left + 1;
> +}
> +
> +static int v4l2_to_uvc_rect(struct uvc_rect *uvc_rect,
> +                           const struct v4l2_rect *v4l2_rect)
> +{
> +       // Safely converts s32 and u32 to u16.
> +       if (v4l2_rect->top > U16_MAX || v4l2_rect->top < 0 ||
> +           v4l2_rect->left > U16_MAX || v4l2_rect->left < 0 ||
> +           v4l2_rect->height > U16_MAX || v4l2_rect->height == 0 ||
> +           v4l2_rect->width > U16_MAX || v4l2_rect->width == 0 ||
> +           v4l2_rect->height + v4l2_rect->top - 1 > U16_MAX ||
> +           v4l2_rect->width + v4l2_rect->left - 1 > U16_MAX)
> +               return -ERANGE;
> +
> +       uvc_rect->top = v4l2_rect->top;
> +       uvc_rect->left = v4l2_rect->left;
> +       uvc_rect->bottom = v4l2_rect->height + v4l2_rect->top - 1;
> +       uvc_rect->right = v4l2_rect->width + v4l2_rect->left - 1;
> +       return 0;
> +}
> +
> +static int uvc_get_array_rect(struct uvc_control_mapping *mapping,
> +                             const u8 *data,  u8 *data_out)
> +{
> +       struct uvc_rect *uvc_rect;
> +
> +       if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> +               return -EINVAL;
> +       if (WARN_ON(mapping->size / 8 != sizeof(struct uvc_rect)))
> +               return -EINVAL;
> +
> +       uvc_rect = (struct uvc_rect *)(data + mapping->offset / 8);
> +       uvc_to_v4l2_rect((struct v4l2_rect *)data_out, uvc_rect);
> +
> +       return 0;
> +}
> +
> +static int uvc_set_array_rect(struct uvc_control_mapping *mapping,
> +                             const u8 *data_in, u8 *data)
> +{
> +       struct uvc_rect *uvc_rect;
> +
> +       if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> +               return -EINVAL;
> +       if (WARN_ON(mapping->size / 8 != sizeof(struct uvc_rect)))
> +               return -EINVAL;
> +
> +       uvc_rect = (struct uvc_rect *)(data + mapping->offset / 8);
> +       return v4l2_to_uvc_rect(uvc_rect, (struct v4l2_rect *)data_in);
> +}
> +
>  static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>         {
>                 .id             = V4L2_CID_BRIGHTNESS,
> @@ -728,6 +803,26 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>                 .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
>                 .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
>         },
> +       {
> +               .id             = V4L2_CID_UVC_REGION_OF_INTEREST_RECT,
> +               .entity         = UVC_GUID_UVC_CAMERA,
> +               .selector       = UVC_CT_REGION_OF_INTEREST_CONTROL,
> +               .size           = 64,
> +               .offset         = 0,
> +               .v4l2_type      = V4L2_CTRL_TYPE_RECT,
> +               .data_type      = UVC_CTRL_DATA_TYPE_RECT,
> +               .get_array      = uvc_get_array_rect,
> +               .set_array      = uvc_set_array_rect,
> +       },
> +       {
> +               .id             = V4L2_CID_UVC_REGION_OF_INTEREST_AUTO,
> +               .entity         = UVC_GUID_UVC_CAMERA,
> +               .selector       = UVC_CT_REGION_OF_INTEREST_CONTROL,
> +               .size           = 16,
> +               .offset         = 64,
> +               .v4l2_type      = V4L2_CTRL_TYPE_BITMASK,
> +               .data_type      = UVC_CTRL_DATA_TYPE_BITMASK,
> +       },
>  };
>
>  /* ------------------------------------------------------------------------
> @@ -1061,7 +1156,16 @@ static int __uvc_ctrl_get_compound_to_user(struct uvc_control_mapping *mapping,
>         if (WARN_ON(!mapping->size % 8))
>                 return -EINVAL;
>
> -       size = mapping->size / 8;
> +       switch (mapping->v4l2_type) {
> +       case V4L2_CTRL_TYPE_RECT:
> +               size = sizeof(struct v4l2_rect);
> +               break;
> +
> +       default:
> +               size = mapping->size / 8;
> +               break;
> +       }
> +
>         if (xctrl->size < size) {
>                 xctrl->size = size;
>                 return -ENOSPC;
> @@ -2578,6 +2682,40 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev,
>         }
>  }
>
> +static int uvc_ctrl_init_roi(struct uvc_device *dev, struct uvc_control *ctrl)
> +{
> +       int ret;
> +
> +       ret = uvc_query_ctrl(dev, UVC_GET_DEF, ctrl->entity->id, dev->intfnum,
> +                            UVC_CT_REGION_OF_INTEREST_CONTROL,
> +                            uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
> +                            ctrl->info.size);
> +       if (ret)
> +               goto out;
> +
> +       /*
> +        * Most firmwares have wrong GET_CURRENT configuration. E.g. it's
> +        * below GET_MIN, or have rectangle coordinates mixed up. This
> +        * causes problems sometimes, because we are unable to set
> +        * auto-controls value without first setting ROI rectangle to
> +        * valid configuration.
> +        *
> +        * We expect that default configuration is always correct and
> +        * is within the GET_MIN / GET_MAX boundaries.
> +        *
> +        * Set current ROI configuration to GET_DEF, so that we will
> +        * always have properly configured ROI.
> +        */
> +       ret = uvc_query_ctrl(dev, UVC_SET_CUR, 1, dev->intfnum,
> +                            UVC_CT_REGION_OF_INTEREST_CONTROL,
> +                            uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
> +                            ctrl->info.size);
> +out:
> +       if (ret)
> +               dev_err(&dev->udev->dev, "Failed to fixup ROI (%d).\n", ret);
> +       return ret;
> +}
> +
>  /*
>   * Add control information and hardcoded stock control mappings to the given
>   * device.
> @@ -2590,6 +2728,7 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
>         const struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
>         const struct uvc_control_mapping *mend =
>                 mapping + ARRAY_SIZE(uvc_ctrl_mappings);
> +       const u8 camera_entity[16] = UVC_GUID_UVC_CAMERA;
>
>         /* XU controls initialization requires querying the device for control
>          * information. As some buggy UVC devices will crash when queried
> @@ -2610,6 +2749,11 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
>                          * GET_INFO on standard controls.
>                          */
>                         uvc_ctrl_get_flags(chain->dev, ctrl, &ctrl->info);
> +
> +                       if (ctrl->info.selector ==
> +                               UVC_CT_REGION_OF_INTEREST_CONTROL &&
> +                           uvc_entity_match_guid(ctrl->entity, camera_entity))
> +                               uvc_ctrl_init_roi(chain->dev, ctrl);
>                         break;
>                  }
>         }
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index 0366d05895a9..aad61af36271 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -998,7 +998,10 @@ static int uvc_ioctl_query_ext_ctrl(struct file *file, void *fh,
>         qec->step = qc.step;
>         qec->default_value = qc.default_value;
>         qec->flags = qc.flags;
> -       qec->elem_size = 4;
> +       if (qc.type == V4L2_CTRL_TYPE_RECT)
> +               qec->elem_size = sizeof(struct v4l2_rect);
> +       else
> +               qec->elem_size = 4;
>         qec->elems = 1;
>         qec->nr_of_dims = 0;
>         memset(qec->dims, 0, sizeof(qec->dims));
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 2f9b75faae83..9ff95bbad251 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -431,6 +431,13 @@ struct uvc_streaming_header {
>         u8 bTriggerUsage;
>  };
>
> +struct uvc_rect {
> +       u16 top;
> +       u16 left;
> +       u16 bottom;
> +       u16 right;
> +} __packed;
> +
>  enum uvc_buffer_state {
>         UVC_BUF_STATE_IDLE      = 0,
>         UVC_BUF_STATE_QUEUED    = 1,
> diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h
> index bfdae12cdacf..9076a444758a 100644
> --- a/include/uapi/linux/usb/video.h
> +++ b/include/uapi/linux/usb/video.h
> @@ -104,6 +104,7 @@
>  #define UVC_CT_ROLL_ABSOLUTE_CONTROL                   0x0f
>  #define UVC_CT_ROLL_RELATIVE_CONTROL                   0x10
>  #define UVC_CT_PRIVACY_CONTROL                         0x11
> +#define UVC_CT_REGION_OF_INTEREST_CONTROL              0x14
>
>  /* A.9.5. Processing Unit Control Selectors */
>  #define UVC_PU_CONTROL_UNDEFINED                       0x00
> diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h
> index 8288137387c0..e2feee15cb1b 100644
> --- a/include/uapi/linux/uvcvideo.h
> +++ b/include/uapi/linux/uvcvideo.h
> @@ -16,6 +16,7 @@
>  #define UVC_CTRL_DATA_TYPE_BOOLEAN     3
>  #define UVC_CTRL_DATA_TYPE_ENUM                4
>  #define UVC_CTRL_DATA_TYPE_BITMASK     5
> +#define UVC_CTRL_DATA_TYPE_RECT                6
>
>  /* Control flags */
>  #define UVC_CTRL_FLAG_SET_CUR          (1 << 0)
> @@ -36,6 +37,18 @@
>          UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES | \
>          UVC_CTRL_FLAG_GET_DEF)
>
> +/* V4L2 private controls */
> +#define V4L2_CID_UVC_REGION_OF_INTEREST_RECT   (V4L2_CID_CAMERA_UVC_BASE + 1)
> +#define V4L2_CID_UVC_REGION_OF_INTEREST_AUTO   (V4L2_CID_CAMERA_UVC_BASE + 2)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_EXPOSURE              (1 << 0)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_IRIS                  (1 << 1)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_WHITE_BALANCE         (1 << 2)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_FOCUS                 (1 << 3)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_FACE_DETECT           (1 << 4)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_DETECT_AND_TRACK      (1 << 5)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_IMAGE_STABILIZATION   (1 << 6)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_HIGHER_QUALITY                (1 << 7)
> +
>  struct uvc_menu_info {
>         __u32 value;
>         __u8 name[32];
> diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
> index dfff69ed88f7..d85be46e46f2 100644
> --- a/include/uapi/linux/v4l2-controls.h
> +++ b/include/uapi/linux/v4l2-controls.h
> @@ -1013,6 +1013,14 @@ enum v4l2_auto_focus_range {
>
>  #define V4L2_CID_CAMERA_SENSOR_ROTATION                (V4L2_CID_CAMERA_CLASS_BASE+35)
>
> +/* CAMERA-class private control IDs */
> +
> +/*
> + * The base for the uvc driver controls. See linux/uvcvideo.h for the list
> + * of controls. We reserve 16 controls for this driver.
> + */
> +#define V4L2_CID_CAMERA_UVC_BASE               (V4L2_CID_CAMERA_CLASS_BASE + 0x1000)
> +
>  /* FM Modulator class control IDs */
>
>  #define V4L2_CID_FM_TX_CLASS_BASE              (V4L2_CTRL_CLASS_FM_TX | 0x900)
> --
> 2.37.0.rc0.161.g10f37bed90-goog
>


-- 
Ricardo Ribalda

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 3/7] media: uvcvideo: Add support for compound controls
  2022-06-28  7:57 ` [PATCH v7 3/7] media: uvcvideo: Add support for compound controls Yunke Cao
@ 2022-06-30  6:42   ` Ricardo Ribalda
  2022-08-24 19:56   ` Laurent Pinchart
  1 sibling, 0 replies; 32+ messages in thread
From: Ricardo Ribalda @ 2022-06-30  6:42 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Laurent Pinchart, Nicolas Dufresne,
	Mauro Carvalho Chehab, Tomasz Figa, Sergey Senozhatsky,
	linux-media

On Tue, 28 Jun 2022 at 09:57, Yunke Cao <yunkec@google.com> wrote:
>
> Supports getting/setting current value.
> Supports getting default value.
> Handles V4L2_CTRL_FLAG_NEXT_COMPOUND.
>
> Signed-off-by: Yunke Cao <yunkec@google.com>
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> ---
>  drivers/media/usb/uvc/uvc_ctrl.c | 279 ++++++++++++++++++++++++++-----
>  drivers/media/usb/uvc/uvcvideo.h |   4 +
>  2 files changed, 238 insertions(+), 45 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 772d9d28a520..508ee04afbcd 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -815,6 +815,34 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
>         }
>  }
>
> +/* Extract the byte array specified by mapping->offset and mapping->size
> + * stored at 'data' to the output array 'data_out'.
> + */
> +static int uvc_get_array(struct uvc_control_mapping *mapping, const u8 *data,
> +                        u8 *data_out)
> +{
> +       // Only supports byte-aligned data.
> +       if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> +               return -EINVAL;
> +
> +       memcpy(data_out, data + mapping->offset / 8, mapping->size / 8);
> +       return 0;
> +}
> +
> +/* Copy the byte array 'data_in' to the destination specified by mapping->offset
> + * and mapping->size stored at 'data'.
> + */
> +static int uvc_set_array(struct uvc_control_mapping *mapping, const u8 *data_in,
> +                        u8 *data)
> +{
> +       // Only supports byte-aligned data.
> +       if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> +               return -EINVAL;
> +
> +       memcpy(data + mapping->offset / 8, data_in, mapping->size / 8);
> +       return 0;
> +}
> +
>  /* ------------------------------------------------------------------------
>   * Terminal and unit management
>   */
> @@ -831,7 +859,7 @@ static int uvc_entity_match_guid(const struct uvc_entity *entity,
>
>  static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
>         struct uvc_control_mapping **mapping, struct uvc_control **control,
> -       int next)
> +       int next, int next_compound)
>  {
>         struct uvc_control *ctrl;
>         struct uvc_control_mapping *map;
> @@ -846,14 +874,18 @@ static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
>                         continue;
>
>                 list_for_each_entry(map, &ctrl->info.mappings, list) {
> -                       if ((map->id == v4l2_id) && !next) {
> +                       if (map->id == v4l2_id && !next && !next_compound) {
>                                 *control = ctrl;
>                                 *mapping = map;
>                                 return;
>                         }
>
>                         if ((*mapping == NULL || (*mapping)->id > map->id) &&
> -                           (map->id > v4l2_id) && next) {
> +                           (map->id > v4l2_id) &&
> +                           ((map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES &&
> +                             next) ||
> +                            (map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES &&
> +                             next_compound))) {
>                                 *control = ctrl;
>                                 *mapping = map;
>                         }
> @@ -867,6 +899,7 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
>         struct uvc_control *ctrl = NULL;
>         struct uvc_entity *entity;
>         int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;
> +       int next_compound = v4l2_id & V4L2_CTRL_FLAG_NEXT_COMPOUND;
>
>         *mapping = NULL;
>
> @@ -875,12 +908,13 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
>
>         /* Find the control. */
>         list_for_each_entry(entity, &chain->entities, chain) {
> -               __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
> -               if (ctrl && !next)
> +               __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next,
> +                                  next_compound);
> +               if (ctrl && !next && !next_compound)
>                         return ctrl;
>         }
>
> -       if (ctrl == NULL && !next)
> +       if (!ctrl && !next && !next_compound)
>                 uvc_dbg(chain->dev, CONTROL, "Control 0x%08x not found\n",
>                         v4l2_id);
>
> @@ -943,6 +977,39 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
>         return 0;
>  }
>
> +static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain,
> +                              struct uvc_control *ctrl)
> +{
> +       int ret = 0;
> +
> +       if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
> +               return -EACCES;
> +
> +       if (ctrl->loaded)
> +               return 0;
> +
> +       if (ctrl->entity->get_cur) {
> +               ret = ctrl->entity->get_cur(chain->dev,
> +                       ctrl->entity,
> +                       ctrl->info.selector,
> +                       uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> +                       ctrl->info.size);
> +       } else {
> +               ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
> +                                    ctrl->entity->id, chain->dev->intfnum,
> +                                    ctrl->info.selector,
> +                                    uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> +                                    ctrl->info.size);
> +       }
> +
> +       if (ret < 0)
> +               return ret;
> +
> +       ctrl->loaded = 1;
> +
> +       return ret;
> +}
> +
>  static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping,
>                                 const u8 *data)
>  {
> @@ -963,35 +1030,19 @@ static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping,
>         return value;
>  }
>
> -static int __uvc_ctrl_get(struct uvc_video_chain *chain,
> -       struct uvc_control *ctrl, struct uvc_control_mapping *mapping,
> -       s32 *value)
> +static int __uvc_ctrl_get_std(struct uvc_video_chain *chain,
> +                             struct uvc_control *ctrl,
> +                             struct uvc_control_mapping *mapping,
> +                             s32 *value)
>  {
>         int ret;
>
> -       if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
> -               return -EACCES;
> -
> -       if (!ctrl->loaded) {
> -               if (ctrl->entity->get_cur) {
> -                       ret = ctrl->entity->get_cur(chain->dev,
> -                               ctrl->entity,
> -                               ctrl->info.selector,
> -                               uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> -                               ctrl->info.size);
> -               } else {
> -                       ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
> -                               ctrl->entity->id,
> -                               chain->dev->intfnum,
> -                               ctrl->info.selector,
> -                               uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> -                               ctrl->info.size);
> -               }
> -               if (ret < 0)
> -                       return ret;
> +       if (mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> +               return -EINVAL;
>
> -               ctrl->loaded = 1;
> -       }
> +       ret = __uvc_ctrl_load_cur(chain, ctrl);
> +       if (ret < 0)
> +               return ret;
>
>         *value = __uvc_ctrl_get_value(mapping,
>                                 uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> @@ -999,6 +1050,57 @@ static int __uvc_ctrl_get(struct uvc_video_chain *chain,
>         return 0;
>  }
>
> +static int __uvc_ctrl_get_compound_to_user(struct uvc_control_mapping *mapping,
> +                                          struct uvc_control *ctrl,
> +                                          int id,
> +                                          struct v4l2_ext_control *xctrl)
> +{
> +       int ret, size;
> +       u8 *data;
> +
> +       if (WARN_ON(!mapping->size % 8))
> +               return -EINVAL;
> +
> +       size = mapping->size / 8;
> +       if (xctrl->size < size) {
> +               xctrl->size = size;
> +               return -ENOSPC;
> +       }
> +
> +       data = kmalloc(size, GFP_KERNEL);
> +       if (!data)
> +               return -ENOMEM;
> +
> +       ret = mapping->get_array(mapping, uvc_ctrl_data(ctrl, id), data);
> +       if (ret < 0)
> +               goto out;
> +
> +       ret = copy_to_user(xctrl->ptr, data, size) ? -EFAULT : 0;
> +
> +out:
> +       kfree(data);
> +       return ret;
> +}
> +
> +static int __uvc_ctrl_get_compound(struct uvc_video_chain *chain,
> +                                  struct uvc_control *ctrl,
> +                                  struct uvc_control_mapping *mapping,
> +                                  struct v4l2_ext_control *xctrl)
> +{
> +       int ret;
> +
> +       if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> +               return -EINVAL;
> +
> +       ret = __uvc_ctrl_load_cur(chain, ctrl);
> +       if (ret < 0)
> +               return ret;
> +
> +       return __uvc_ctrl_get_compound_to_user(mapping, ctrl,
> +                                              UVC_CTRL_DATA_CURRENT,
> +                                              xctrl);
> +}
> +
>  static int __uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
>                                   u32 found_id)
>  {
> @@ -1102,10 +1204,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
>
>         if (mapping->master_id)
>                 __uvc_find_control(ctrl->entity, mapping->master_id,
> -                                  &master_map, &master_ctrl, 0);
> +                                  &master_map, &master_ctrl, 0, 0);
>         if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
> -               s32 val;
> -               int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
> +               int ret;
> +               s32 val = 0;
> +
> +               if (master_map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> +                       return -EINVAL;
> +
> +               ret = __uvc_ctrl_get_std(chain, master_ctrl, master_map, &val);
>                 if (ret < 0)
>                         return ret;
>
> @@ -1113,6 +1220,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
>                                 v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
>         }
>
> +       if (v4l2_ctrl->type >= V4L2_CTRL_COMPOUND_TYPES) {
> +               v4l2_ctrl->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD;
> +               v4l2_ctrl->default_value = 0;
> +               v4l2_ctrl->minimum = 0;
> +               v4l2_ctrl->maximum = 0;
> +               v4l2_ctrl->step = 0;
> +               return 0;
> +       }
> +
>         if (!ctrl->cached) {
>                 int ret = uvc_ctrl_populate_cache(chain, ctrl);
>                 if (ret < 0)
> @@ -1346,11 +1462,12 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
>         u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
>         s32 val = 0;
>
> -       __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0);
> +       __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0, 0);
>         if (ctrl == NULL)
>                 return;
>
> -       if (__uvc_ctrl_get(chain, ctrl, mapping, &val) == 0)
> +       if (mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES ||
> +           __uvc_ctrl_get_std(chain, ctrl, mapping, &val) == 0)
>                 changes |= V4L2_EVENT_CTRL_CH_VALUE;
>
>         uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
> @@ -1517,7 +1634,8 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
>                 u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
>                 s32 val = 0;
>
> -               if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0)
> +               if (mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES ||
> +                   __uvc_ctrl_get_std(handle->chain, ctrl, mapping, &val) == 0)
>                         changes |= V4L2_EVENT_CTRL_CH_VALUE;
>
>                 uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val,
> @@ -1647,7 +1765,7 @@ static int uvc_ctrl_find_ctrl_idx(struct uvc_entity *entity,
>
>         for (i = 0; i < ctrls->count; i++) {
>                 __uvc_find_control(entity, ctrls->controls[i].id, &mapping,
> -                                  &ctrl_found, 0);
> +                                  &ctrl_found, 0, 0);
>                 if (uvc_control == ctrl_found)
>                         return i;
>         }
> @@ -1694,11 +1812,14 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
>         if (ctrl == NULL)
>                 return -EINVAL;
>
> -       return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
> +       if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> +               return __uvc_ctrl_get_std(chain, ctrl, mapping, &xctrl->value);
> +       else
> +               return __uvc_ctrl_get_compound(chain, ctrl, mapping, xctrl);
>  }
>
> -int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> -                      struct v4l2_ext_control *xctrl)
> +int __uvc_ctrl_get_fixed_std(struct uvc_video_chain *chain,
> +                            struct v4l2_ext_control *xctrl)
>  {
>         struct v4l2_queryctrl qc = { .id = xctrl->id };
>         int ret = uvc_query_v4l2_ctrl(chain, &qc);
> @@ -1710,6 +1831,56 @@ int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
>         return 0;
>  }
>
> +int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> +                      struct v4l2_ext_control *xctrl)
> +{
> +       struct uvc_control *ctrl;
> +       struct uvc_control_mapping *mapping;
> +       int ret;
> +
> +       if (__uvc_query_v4l2_class(chain, xctrl->id, 0) >= 0)
> +               return -EACCES;
> +
> +       ctrl = uvc_find_control(chain, xctrl->id, &mapping);
> +       if (!ctrl)
> +               return -EINVAL;
> +
> +       if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> +               return __uvc_ctrl_get_fixed_std(chain, xctrl);
> +
> +       if (!ctrl->cached) {
> +               ret = uvc_ctrl_populate_cache(chain, ctrl);
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       return __uvc_ctrl_get_compound_to_user(mapping, ctrl, UVC_CTRL_DATA_DEF,
> +                                              xctrl);
> +}
> +
> +int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
> +                           struct v4l2_ext_control *xctrl,
> +                           struct uvc_control *ctrl)
> +{
> +       int ret;
> +       u8 *data;
> +
> +       data = kmalloc(xctrl->size, GFP_KERNEL);
> +       if (!data)
> +               return -ENOMEM;
> +
> +       ret = copy_from_user(data, xctrl->ptr, xctrl->size);
> +       if (ret < 0)
> +               goto out;
> +
> +       ret = mapping->set_array(mapping, data,
> +                       uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> +
> +out:
> +       kfree(data);
> +       return ret;
> +}
> +
>  int uvc_ctrl_set(struct uvc_fh *handle,
>         struct v4l2_ext_control *xctrl)
>  {
> @@ -1820,8 +1991,14 @@ int uvc_ctrl_set(struct uvc_fh *handle,
>                        ctrl->info.size);
>         }
>
> -       mapping->set(mapping, value,
> -               uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> +       if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES) {
> +               mapping->set(mapping, value,
> +                            uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> +       } else {
> +               ret = __uvc_ctrl_set_compound(mapping, xctrl, ctrl);
> +               if (ret < 0)
> +                       return ret;
> +       }
>
>         if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
>                 ctrl->handle = handle;
> @@ -2220,10 +2397,14 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
>                 return -ENOMEM;
>         }
>
> -       if (map->get == NULL)
> +       if (!map->get && map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
>                 map->get = uvc_get_le_value;
> -       if (map->set == NULL)
> +       if (!map->set && map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
>                 map->set = uvc_set_le_value;
> +       if (!map->get_array && map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> +               map->get_array = uvc_get_array;
> +       if (!map->set_array && map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> +               map->set_array = uvc_set_array;
>
>         for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
>                 if (V4L2_CTRL_ID2WHICH(uvc_control_classes[i]) ==
> @@ -2233,6 +2414,14 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
>                 }
>         }
>
> +       if (map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES &&
> +           WARN_ON(!map->get || !map->set))
> +               return -EINVAL;
> +
> +       if (map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES &&
> +           WARN_ON(!map->get_array || !map->set_array))
> +               return -EINVAL;
> +
>         list_add_tail(&map->list, &ctrl->info.mappings);
>         uvc_dbg(chain->dev, CONTROL, "Adding mapping '%s' to control %pUl/%u\n",
>                 uvc_map_get_name(map), ctrl->info.entity,
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index ba028ba7c34e..2f9b75faae83 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -262,8 +262,12 @@ struct uvc_control_mapping {
>
>         s32 (*get)(struct uvc_control_mapping *mapping, u8 query,
>                    const u8 *data);
> +       int (*get_array)(struct uvc_control_mapping *mapping, const u8 *data,
> +                        u8 *data_out);
>         void (*set)(struct uvc_control_mapping *mapping, s32 value,
>                     u8 *data);
> +       int (*set_array)(struct uvc_control_mapping *mapping, const u8 *data_in,
> +                        u8 *data);
>  };
>
>  struct uvc_control {
> --
> 2.37.0.rc0.161.g10f37bed90-goog
>


-- 
Ricardo Ribalda

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 0/7] media: Implement UVC v1.5 ROI
  2022-06-28  7:56 [PATCH v7 0/7] media: Implement UVC v1.5 ROI Yunke Cao
                   ` (6 preceding siblings ...)
  2022-06-28  7:57 ` [PATCH v7 7/7] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
@ 2022-07-14 23:25 ` Yunke Cao
  2022-07-15  0:52   ` Laurent Pinchart
  7 siblings, 1 reply; 32+ messages in thread
From: Yunke Cao @ 2022-07-14 23:25 UTC (permalink / raw)
  To: Hans Verkuil, Laurent Pinchart, Nicolas Dufresne, Mauro Carvalho Chehab
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Laurent,

Do you have time to review this version? Ricardo has already reviewed
it, I hope it is easier to review now.

Thanks in advance!
Yunke



On Tue, Jun 28, 2022 at 4:57 PM Yunke Cao <yunkec@google.com> wrote:
>
> This patch set implements UVC v1.5 region of interest using V4L2
> control API.
>
> ROI control is consisted two uvc specific controls.
> 1. A rectangle control with a newly added type V4L2_CTRL_TYPE_RECT.
> 2. An auto control with type bitmask.
>
> V4L2_CTRL_WHICH_MIN/MAX_VAL is added to support the rectangle control.
>
> Tested on two different usb cameras using v4l2-compliance, v4l2-ctl
> and calling ioctls.
>
> 1/7 add V4L2_CTRL_TYPE_RECT.
> 2/7 and 3/7 support compound types in UVC.
> 4/7 implement ROI in UVC.
> 5/7 is a cherry-pick for Hans' implementation of
> V4L2_CTRL_WHICH_MIN/MAX_VAL in v4l2-core.
> 6/7 support MIN/MAX in UVC.
> 7/7 document the changes.
>
> Changelog since v6:
> -Add patch 2 and 3 to support compound types properly in UVC and
> implement ROI on top of them.
> -Reorder the patches.
>
> Changelog since v5:
> -Add a __uvc_ctrl_get_p_rect_to_user instead of modifying
>  __uvc_ctrl_get.
> -Support V4L2_CTRL_FLAG_NEXT_COMPOUND correctly.
> -Fix formats.
>
> Changelog since v4:
> -Cherry-pick the original patch
>  "v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL".
> -Split patch "media: uvcvideo: implement UVC v1.5 ROI" into two patches.
>  The codes for supporting min/max in uvc are in patch 4/5 now.
> -Minor fixes. Detailed changelog in patches
>
> Changelog since v3:
> - Reordered/sliced the patches.
>   1. Add rect type.
>   2. Add min/max.
>   3. Add the roi controls (including init to default).
>   4. Document the roi controls.
> - Define the roi controls as uvc-specific in uvcvideo.h.
> - Modified documentation.
> - Removed the vivid change. Given the controls are now uvc-specific.
>   I'm not sure how valuable it is to add it in vivid. Let me know
>   otherwise.
>
> Hans Verkuil (1):
>   v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
>
> Yunke Cao (6):
>   media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
>   media: uvcvideo: add uvc_ctrl_get_fixed for getting default value
>   media: uvcvideo: Add support for compound controls
>   media: uvcvideo: implement UVC v1.5 ROI
>   media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL
>   media: uvcvideo: document UVC v1.5 ROI
>
>  .../userspace-api/media/drivers/uvcvideo.rst  |  61 +++
>  .../media/v4l/vidioc-g-ext-ctrls.rst          |  11 +-
>  .../media/videodev2.h.rst.exceptions          |   3 +
>  drivers/media/i2c/imx214.c                    |   5 +-
>  .../media/platform/qcom/venus/venc_ctrls.c    |   4 +
>  drivers/media/usb/uvc/uvc_ctrl.c              | 479 ++++++++++++++++--
>  drivers/media/usb/uvc/uvc_v4l2.c              |  20 +-
>  drivers/media/usb/uvc/uvcvideo.h              |  14 +
>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  51 +-
>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 155 +++++-
>  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
>  include/media/v4l2-ctrls.h                    |  34 +-
>  include/uapi/linux/usb/video.h                |   1 +
>  include/uapi/linux/uvcvideo.h                 |  13 +
>  include/uapi/linux/v4l2-controls.h            |   8 +
>  include/uapi/linux/videodev2.h                |   4 +
>  16 files changed, 788 insertions(+), 79 deletions(-)
>
> --
> 2.37.0.rc0.161.g10f37bed90-goog
>

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 0/7] media: Implement UVC v1.5 ROI
  2022-07-14 23:25 ` [PATCH v7 0/7] media: Implement " Yunke Cao
@ 2022-07-15  0:52   ` Laurent Pinchart
  2022-09-27  4:32     ` Yunke Cao
  0 siblings, 1 reply; 32+ messages in thread
From: Laurent Pinchart @ 2022-07-15  0:52 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Nicolas Dufresne, Mauro Carvalho Chehab,
	Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Yunke,

On Fri, Jul 15, 2022 at 08:25:20AM +0900, Yunke Cao wrote:
> Hi Laurent,
> 
> Do you have time to review this version? Ricardo has already reviewed
> it, I hope it is easier to review now.

I'll try find time, but I doubt it will be before a couple of weeks.

> On Tue, Jun 28, 2022 at 4:57 PM Yunke Cao <yunkec@google.com> wrote:
> >
> > This patch set implements UVC v1.5 region of interest using V4L2
> > control API.
> >
> > ROI control is consisted two uvc specific controls.
> > 1. A rectangle control with a newly added type V4L2_CTRL_TYPE_RECT.
> > 2. An auto control with type bitmask.
> >
> > V4L2_CTRL_WHICH_MIN/MAX_VAL is added to support the rectangle control.
> >
> > Tested on two different usb cameras using v4l2-compliance, v4l2-ctl
> > and calling ioctls.
> >
> > 1/7 add V4L2_CTRL_TYPE_RECT.
> > 2/7 and 3/7 support compound types in UVC.
> > 4/7 implement ROI in UVC.
> > 5/7 is a cherry-pick for Hans' implementation of
> > V4L2_CTRL_WHICH_MIN/MAX_VAL in v4l2-core.
> > 6/7 support MIN/MAX in UVC.
> > 7/7 document the changes.
> >
> > Changelog since v6:
> > -Add patch 2 and 3 to support compound types properly in UVC and
> > implement ROI on top of them.
> > -Reorder the patches.
> >
> > Changelog since v5:
> > -Add a __uvc_ctrl_get_p_rect_to_user instead of modifying
> >  __uvc_ctrl_get.
> > -Support V4L2_CTRL_FLAG_NEXT_COMPOUND correctly.
> > -Fix formats.
> >
> > Changelog since v4:
> > -Cherry-pick the original patch
> >  "v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL".
> > -Split patch "media: uvcvideo: implement UVC v1.5 ROI" into two patches.
> >  The codes for supporting min/max in uvc are in patch 4/5 now.
> > -Minor fixes. Detailed changelog in patches
> >
> > Changelog since v3:
> > - Reordered/sliced the patches.
> >   1. Add rect type.
> >   2. Add min/max.
> >   3. Add the roi controls (including init to default).
> >   4. Document the roi controls.
> > - Define the roi controls as uvc-specific in uvcvideo.h.
> > - Modified documentation.
> > - Removed the vivid change. Given the controls are now uvc-specific.
> >   I'm not sure how valuable it is to add it in vivid. Let me know
> >   otherwise.
> >
> > Hans Verkuil (1):
> >   v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
> >
> > Yunke Cao (6):
> >   media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
> >   media: uvcvideo: add uvc_ctrl_get_fixed for getting default value
> >   media: uvcvideo: Add support for compound controls
> >   media: uvcvideo: implement UVC v1.5 ROI
> >   media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL
> >   media: uvcvideo: document UVC v1.5 ROI
> >
> >  .../userspace-api/media/drivers/uvcvideo.rst  |  61 +++
> >  .../media/v4l/vidioc-g-ext-ctrls.rst          |  11 +-
> >  .../media/videodev2.h.rst.exceptions          |   3 +
> >  drivers/media/i2c/imx214.c                    |   5 +-
> >  .../media/platform/qcom/venus/venc_ctrls.c    |   4 +
> >  drivers/media/usb/uvc/uvc_ctrl.c              | 479 ++++++++++++++++--
> >  drivers/media/usb/uvc/uvc_v4l2.c              |  20 +-
> >  drivers/media/usb/uvc/uvcvideo.h              |  14 +
> >  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  51 +-
> >  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 155 +++++-
> >  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
> >  include/media/v4l2-ctrls.h                    |  34 +-
> >  include/uapi/linux/usb/video.h                |   1 +
> >  include/uapi/linux/uvcvideo.h                 |  13 +
> >  include/uapi/linux/v4l2-controls.h            |   8 +
> >  include/uapi/linux/videodev2.h                |   4 +
> >  16 files changed, 788 insertions(+), 79 deletions(-)

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 1/7] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
  2022-06-28  7:56 ` [PATCH v7 1/7] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
@ 2022-08-24  8:40   ` Laurent Pinchart
  2022-08-24  8:50   ` Hans Verkuil
  1 sibling, 0 replies; 32+ messages in thread
From: Laurent Pinchart @ 2022-08-24  8:40 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Nicolas Dufresne, Mauro Carvalho Chehab,
	Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Yunke,

Thank you for the patch.

On Tue, Jun 28, 2022 at 04:56:59PM +0900, Yunke Cao wrote:
> Add p_rect to struct v4l2_ext_control with basic support in
> v4l2-ctrls.

This looks good to me, so

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

I'm no expert on the V4L2 control framework though, a review from Hans
would be useful.

> Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
>  .../media/v4l/vidioc-g-ext-ctrls.rst          |  4 ++++
>  .../media/videodev2.h.rst.exceptions          |  1 +
>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 20 +++++++++++++++++++
>  include/media/v4l2-ctrls.h                    |  2 ++
>  include/uapi/linux/videodev2.h                |  2 ++
>  5 files changed, 29 insertions(+)
> 
> diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> index 29971a45a2d4..7473baa4e977 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> @@ -189,6 +189,10 @@ still cause this situation.
>        - ``p_area``
>        - A pointer to a struct :c:type:`v4l2_area`. Valid if this control is
>          of type ``V4L2_CTRL_TYPE_AREA``.
> +    * - struct :c:type:`v4l2_rect` *
> +      - ``p_rect``
> +      - A pointer to a struct :c:type:`v4l2_rect`. Valid if this control is
> +        of type ``V4L2_CTRL_TYPE_RECT``.
>      * - struct :c:type:`v4l2_ctrl_h264_sps` *
>        - ``p_h264_sps``
>        - A pointer to a struct :c:type:`v4l2_ctrl_h264_sps`. Valid if this control is
> diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> index 9cbb7a0c354a..7b423475281d 100644
> --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> @@ -147,6 +147,7 @@ replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type`
> +replace symbol V4L2_CTRL_TYPE_RECT :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_FWHT_PARAMS :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_VP8_FRAME :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR :c:type:`v4l2_ctrl_type`
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> index 949c1884d9c1..35d43ba650db 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> @@ -84,6 +84,11 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx,
>  		return ptr1.p_u16[idx] == ptr2.p_u16[idx];
>  	case V4L2_CTRL_TYPE_U32:
>  		return ptr1.p_u32[idx] == ptr2.p_u32[idx];
> +	case V4L2_CTRL_TYPE_RECT:
> +		return ptr1.p_rect->top == ptr2.p_rect->top &&
> +		       ptr1.p_rect->left == ptr2.p_rect->left &&
> +		       ptr1.p_rect->height == ptr2.p_rect->height &&
> +		       ptr1.p_rect->width == ptr2.p_rect->width;
>  	default:
>  		if (ctrl->is_int)
>  			return ptr1.p_s32[idx] == ptr2.p_s32[idx];
> @@ -307,6 +312,11 @@ static void std_log(const struct v4l2_ctrl *ctrl)
>  	case V4L2_CTRL_TYPE_VP9_FRAME:
>  		pr_cont("VP9_FRAME");
>  		break;
> +	case V4L2_CTRL_TYPE_RECT:
> +		pr_cont("%ux%u@%dx%d",
> +			ptr.p_rect->width, ptr.p_rect->height,
> +			ptr.p_rect->left, ptr.p_rect->top);
> +		break;
>  	default:
>  		pr_cont("unknown type %d", ctrl->type);
>  		break;
> @@ -525,6 +535,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
>  	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
>  	struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params;
>  	struct v4l2_area *area;
> +	struct v4l2_rect *rect;
>  	void *p = ptr.p + idx * ctrl->elem_size;
>  	unsigned int i;
>  
> @@ -888,6 +899,12 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
>  			return -EINVAL;
>  		break;
>  
> +	case V4L2_CTRL_TYPE_RECT:
> +		rect = p;
> +		if (!rect->width || !rect->height)
> +			return -EINVAL;
> +		break;
> +
>  	default:
>  		return -EINVAL;
>  	}
> @@ -1455,6 +1472,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  	case V4L2_CTRL_TYPE_AREA:
>  		elem_size = sizeof(struct v4l2_area);
>  		break;
> +	case V4L2_CTRL_TYPE_RECT:
> +		elem_size = sizeof(struct v4l2_rect);
> +		break;
>  	default:
>  		if (type < V4L2_CTRL_COMPOUND_TYPES)
>  			elem_size = sizeof(s32);
> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> index b3ce438f1329..919e104de50b 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -58,6 +58,7 @@ struct video_device;
>   * @p_hdr10_cll:		Pointer to an HDR10 Content Light Level structure.
>   * @p_hdr10_mastering:		Pointer to an HDR10 Mastering Display structure.
>   * @p_area:			Pointer to an area.
> + * @p_rect:			Pointer to a rectangle.
>   * @p:				Pointer to a compound value.
>   * @p_const:			Pointer to a constant compound value.
>   */
> @@ -87,6 +88,7 @@ union v4l2_ctrl_ptr {
>  	struct v4l2_ctrl_hdr10_cll_info *p_hdr10_cll;
>  	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
>  	struct v4l2_area *p_area;
> +	struct v4l2_rect *p_rect;
>  	void *p;
>  	const void *p_const;
>  };
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index 343b95107fce..2e36bb610ea6 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -1754,6 +1754,7 @@ struct v4l2_ext_control {
>  		__u16 __user *p_u16;
>  		__u32 __user *p_u32;
>  		struct v4l2_area __user *p_area;
> +		struct v4l2_rect __user *p_rect;
>  		struct v4l2_ctrl_h264_sps __user *p_h264_sps;
>  		struct v4l2_ctrl_h264_pps *p_h264_pps;
>  		struct v4l2_ctrl_h264_scaling_matrix __user *p_h264_scaling_matrix;
> @@ -1813,6 +1814,7 @@ enum v4l2_ctrl_type {
>  	V4L2_CTRL_TYPE_U16	     = 0x0101,
>  	V4L2_CTRL_TYPE_U32	     = 0x0102,
>  	V4L2_CTRL_TYPE_AREA          = 0x0106,
> +	V4L2_CTRL_TYPE_RECT	     = 0x0107,
>  
>  	V4L2_CTRL_TYPE_HDR10_CLL_INFO		= 0x0110,
>  	V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY	= 0x0111,

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 1/7] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
  2022-06-28  7:56 ` [PATCH v7 1/7] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
  2022-08-24  8:40   ` Laurent Pinchart
@ 2022-08-24  8:50   ` Hans Verkuil
  2022-08-24  8:51     ` Hans Verkuil
  2022-08-25  6:15     ` Yunke Cao
  1 sibling, 2 replies; 32+ messages in thread
From: Hans Verkuil @ 2022-08-24  8:50 UTC (permalink / raw)
  To: Yunke Cao, Laurent Pinchart, Nicolas Dufresne, Mauro Carvalho Chehab
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Yunke,

You will need to rebase this since some of the v4l2-ctrl internals
have changed.

On 28/06/2022 09:56, Yunke Cao wrote:
> Add p_rect to struct v4l2_ext_control with basic support in
> v4l2-ctrls.
> 
> Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
>  .../media/v4l/vidioc-g-ext-ctrls.rst          |  4 ++++
>  .../media/videodev2.h.rst.exceptions          |  1 +
>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 20 +++++++++++++++++++
>  include/media/v4l2-ctrls.h                    |  2 ++
>  include/uapi/linux/videodev2.h                |  2 ++
>  5 files changed, 29 insertions(+)
> 
> diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> index 29971a45a2d4..7473baa4e977 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> @@ -189,6 +189,10 @@ still cause this situation.
>        - ``p_area``
>        - A pointer to a struct :c:type:`v4l2_area`. Valid if this control is
>          of type ``V4L2_CTRL_TYPE_AREA``.
> +    * - struct :c:type:`v4l2_rect` *
> +      - ``p_rect``
> +      - A pointer to a struct :c:type:`v4l2_rect`. Valid if this control is
> +        of type ``V4L2_CTRL_TYPE_RECT``.
>      * - struct :c:type:`v4l2_ctrl_h264_sps` *
>        - ``p_h264_sps``
>        - A pointer to a struct :c:type:`v4l2_ctrl_h264_sps`. Valid if this control is

You also need to update Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst.

> diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> index 9cbb7a0c354a..7b423475281d 100644
> --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> @@ -147,6 +147,7 @@ replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type`
> +replace symbol V4L2_CTRL_TYPE_RECT :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_FWHT_PARAMS :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_VP8_FRAME :c:type:`v4l2_ctrl_type`
>  replace symbol V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR :c:type:`v4l2_ctrl_type`
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> index 949c1884d9c1..35d43ba650db 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> @@ -84,6 +84,11 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx,
>  		return ptr1.p_u16[idx] == ptr2.p_u16[idx];
>  	case V4L2_CTRL_TYPE_U32:
>  		return ptr1.p_u32[idx] == ptr2.p_u32[idx];
> +	case V4L2_CTRL_TYPE_RECT:
> +		return ptr1.p_rect->top == ptr2.p_rect->top &&
> +		       ptr1.p_rect->left == ptr2.p_rect->left &&
> +		       ptr1.p_rect->height == ptr2.p_rect->height &&
> +		       ptr1.p_rect->width == ptr2.p_rect->width;

You don't need to do anything here, it will fallback to a memcmp, and
that's fine for struct v4l2_rect.

>  	default:
>  		if (ctrl->is_int)
>  			return ptr1.p_s32[idx] == ptr2.p_s32[idx];
> @@ -307,6 +312,11 @@ static void std_log(const struct v4l2_ctrl *ctrl)
>  	case V4L2_CTRL_TYPE_VP9_FRAME:
>  		pr_cont("VP9_FRAME");
>  		break;
> +	case V4L2_CTRL_TYPE_RECT:
> +		pr_cont("%ux%u@%dx%d",
> +			ptr.p_rect->width, ptr.p_rect->height,
> +			ptr.p_rect->left, ptr.p_rect->top);
> +		break;
>  	default:
>  		pr_cont("unknown type %d", ctrl->type);
>  		break;
> @@ -525,6 +535,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
>  	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
>  	struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params;
>  	struct v4l2_area *area;
> +	struct v4l2_rect *rect;
>  	void *p = ptr.p + idx * ctrl->elem_size;
>  	unsigned int i;
>  
> @@ -888,6 +899,12 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
>  			return -EINVAL;
>  		break;
>  
> +	case V4L2_CTRL_TYPE_RECT:
> +		rect = p;
> +		if (!rect->width || !rect->height)
> +			return -EINVAL;
> +		break;
> +
>  	default:
>  		return -EINVAL;
>  	}
> @@ -1455,6 +1472,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  	case V4L2_CTRL_TYPE_AREA:
>  		elem_size = sizeof(struct v4l2_area);
>  		break;
> +	case V4L2_CTRL_TYPE_RECT:
> +		elem_size = sizeof(struct v4l2_rect);
> +		break;
>  	default:
>  		if (type < V4L2_CTRL_COMPOUND_TYPES)
>  			elem_size = sizeof(s32);
> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> index b3ce438f1329..919e104de50b 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -58,6 +58,7 @@ struct video_device;
>   * @p_hdr10_cll:		Pointer to an HDR10 Content Light Level structure.
>   * @p_hdr10_mastering:		Pointer to an HDR10 Mastering Display structure.
>   * @p_area:			Pointer to an area.
> + * @p_rect:			Pointer to a rectangle.
>   * @p:				Pointer to a compound value.
>   * @p_const:			Pointer to a constant compound value.
>   */
> @@ -87,6 +88,7 @@ union v4l2_ctrl_ptr {
>  	struct v4l2_ctrl_hdr10_cll_info *p_hdr10_cll;
>  	struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
>  	struct v4l2_area *p_area;
> +	struct v4l2_rect *p_rect;
>  	void *p;
>  	const void *p_const;
>  };
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index 343b95107fce..2e36bb610ea6 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -1754,6 +1754,7 @@ struct v4l2_ext_control {
>  		__u16 __user *p_u16;
>  		__u32 __user *p_u32;
>  		struct v4l2_area __user *p_area;
> +		struct v4l2_rect __user *p_rect;
>  		struct v4l2_ctrl_h264_sps __user *p_h264_sps;
>  		struct v4l2_ctrl_h264_pps *p_h264_pps;
>  		struct v4l2_ctrl_h264_scaling_matrix __user *p_h264_scaling_matrix;
> @@ -1813,6 +1814,7 @@ enum v4l2_ctrl_type {
>  	V4L2_CTRL_TYPE_U16	     = 0x0101,
>  	V4L2_CTRL_TYPE_U32	     = 0x0102,
>  	V4L2_CTRL_TYPE_AREA          = 0x0106,
> +	V4L2_CTRL_TYPE_RECT	     = 0x0107,
>  
>  	V4L2_CTRL_TYPE_HDR10_CLL_INFO		= 0x0110,
>  	V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY	= 0x0111,

Regards,

	Hans

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 1/7] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
  2022-08-24  8:50   ` Hans Verkuil
@ 2022-08-24  8:51     ` Hans Verkuil
  2022-08-25  6:15     ` Yunke Cao
  1 sibling, 0 replies; 32+ messages in thread
From: Hans Verkuil @ 2022-08-24  8:51 UTC (permalink / raw)
  To: Yunke Cao, Laurent Pinchart, Nicolas Dufresne, Mauro Carvalho Chehab
  Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media



On 24/08/2022 10:50, Hans Verkuil wrote:
> Hi Yunke,
> 
> You will need to rebase this since some of the v4l2-ctrl internals
> have changed.

Rebase to git://linuxtv.org/media_stage.git, master branch, to be precise.

	Hans

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 5/7] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2022-06-28  7:57 ` [PATCH v7 5/7] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
@ 2022-08-24 16:20   ` Laurent Pinchart
  2022-09-20 10:01     ` Yunke Cao
  2022-09-30  7:59     ` Hans Verkuil
  0 siblings, 2 replies; 32+ messages in thread
From: Laurent Pinchart @ 2022-08-24 16:20 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Nicolas Dufresne, Mauro Carvalho Chehab,
	Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Yunke and Hans,

Thank you for the patch.

On Tue, Jun 28, 2022 at 04:57:03PM +0900, Yunke Cao wrote:
> From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> 
> Add the capability of retrieving the min and max values of a
> compound control.
> 
> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
> git am from https://lore.kernel.org/all/20191119113457.57833-3-hverkuil-cisco@xs4all.nl/
> -Fixed some merge conflits.
> -Fixed the build error in drivers/media/platform/qcom/venus.
> 
>  .../media/v4l/vidioc-g-ext-ctrls.rst          |   7 +-
>  .../media/videodev2.h.rst.exceptions          |   2 +
>  drivers/media/i2c/imx214.c                    |   5 +-
>  .../media/platform/qcom/venus/venc_ctrls.c    |   4 +
>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  51 +++++--
>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 135 ++++++++++++++++--
>  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
>  include/media/v4l2-ctrls.h                    |  32 ++++-
>  include/uapi/linux/videodev2.h                |   2 +
>  9 files changed, 214 insertions(+), 28 deletions(-)
> 
> diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> index 7473baa4e977..65a5f878cc28 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> @@ -284,14 +284,17 @@ still cause this situation.
>        - Which value of the control to get/set/try.
>      * - :cspan:`2` ``V4L2_CTRL_WHICH_CUR_VAL`` will return the current value of
>  	the control, ``V4L2_CTRL_WHICH_DEF_VAL`` will return the default
> +	value of the control, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
> +	value of the control, ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum

The concept of minimum and maximum values for compound controls can be
quite ill-defined. Could we document here that the definition of the
minimum and maximum values are provided by the control documentation for
compound controls ?

>  	value of the control and ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
>  	these controls have to be retrieved from a request or tried/set for
>  	a request. In the latter case the ``request_fd`` field contains the
>  	file descriptor of the request that should be used. If the device
>  	does not support requests, then ``EACCES`` will be returned.
>  
> -	When using ``V4L2_CTRL_WHICH_DEF_VAL`` be aware that you can only
> -	get the default value of the control, you cannot set or try it.
> +	When using ``V4L2_CTRL_WHICH_DEF_VAL``, ``V4L2_CTRL_WHICH_MIN_VAL``
> +	or ``V4L2_CTRL_WHICH_MAX_VAL`` be aware that you can only get the
> +	default/minimum/maximum value of the control, you cannot set or try it.
>  
>  	For backwards compatibility you can also use a control class here
>  	(see :ref:`ctrl-class`). In that case all controls have to
> diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> index 7b423475281d..e2dde31d76df 100644
> --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> @@ -553,6 +553,8 @@ ignore define V4L2_CTRL_DRIVER_PRIV
>  ignore define V4L2_CTRL_MAX_DIMS
>  ignore define V4L2_CTRL_WHICH_CUR_VAL
>  ignore define V4L2_CTRL_WHICH_DEF_VAL
> +ignore define V4L2_CTRL_WHICH_MIN_VAL
> +ignore define V4L2_CTRL_WHICH_MAX_VAL
>  ignore define V4L2_CTRL_WHICH_REQUEST_VAL
>  ignore define V4L2_OUT_CAP_CUSTOM_TIMINGS
>  ignore define V4L2_CID_MAX_CTRLS
> diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c
> index 83c1737abeec..aa0bfd18f7b7 100644
> --- a/drivers/media/i2c/imx214.c
> +++ b/drivers/media/i2c/imx214.c
> @@ -1037,7 +1037,10 @@ static int imx214_probe(struct i2c_client *client)
>  	imx214->unit_size = v4l2_ctrl_new_std_compound(&imx214->ctrls,
>  				NULL,
>  				V4L2_CID_UNIT_CELL_SIZE,
> -				v4l2_ctrl_ptr_create((void *)&unit_size));
> +				v4l2_ctrl_ptr_create((void *)&unit_size),
> +				v4l2_ctrl_ptr_create(NULL),
> +				v4l2_ctrl_ptr_create(NULL));

Would it make sense to set min = max = default for read-only controls ?
You should also update the documentation of V4L2_CID_UNIT_CELL_SIZE (and
other controls below) to indicate how their minimum and maximum are
defined.

> +
>  	ret = imx214->ctrls.error;
>  	if (ret) {
>  		dev_err(&client->dev, "%s control init failed (%d)\n",
> diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
> index ed44e5800759..4af8f9ca12a6 100644
> --- a/drivers/media/platform/qcom/venus/venc_ctrls.c
> +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
> @@ -579,10 +579,14 @@ int venc_ctrl_init(struct venus_inst *inst)
>  
>  	v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
>  				   V4L2_CID_COLORIMETRY_HDR10_CLL_INFO,
> +				   v4l2_ctrl_ptr_create(NULL),
> +				   v4l2_ctrl_ptr_create(NULL),
>  				   v4l2_ctrl_ptr_create(NULL));
>  
>  	v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
>  				   V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY,
> +				   v4l2_ctrl_ptr_create(NULL),
> +				   v4l2_ctrl_ptr_create(NULL),
>  				   v4l2_ctrl_ptr_create(NULL));
>  
>  	v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> index db9baa0bd05f..8a9c816b0dab 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> @@ -97,6 +97,28 @@ static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
>  	return ptr_to_user(c, ctrl, ctrl->p_new);
>  }
>  
> +/* Helper function: copy the minimum control value back to the caller */
> +static int min_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> +{
> +	int idx;
> +
> +	for (idx = 0; idx < ctrl->elems; idx++)
> +		ctrl->type_ops->minimum(ctrl, idx, ctrl->p_new);
> +
> +	return ptr_to_user(c, ctrl, ctrl->p_new);
> +}
> +
> +/* Helper function: copy the maximum control value back to the caller */
> +static int max_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> +{
> +	int idx;
> +
> +	for (idx = 0; idx < ctrl->elems; idx++)
> +		ctrl->type_ops->maximum(ctrl, idx, ctrl->p_new);
> +
> +	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,
> @@ -220,8 +242,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>  		cs->error_idx = i;
>  
>  		if (cs->which &&
> -		    cs->which != V4L2_CTRL_WHICH_DEF_VAL &&
> -		    cs->which != V4L2_CTRL_WHICH_REQUEST_VAL &&
> +		    (cs->which < V4L2_CTRL_WHICH_DEF_VAL ||
> +		     cs->which > V4L2_CTRL_WHICH_MAX_VAL) &&
>  		    V4L2_CTRL_ID2WHICH(id) != cs->which) {
>  			dprintk(vdev,
>  				"invalid which 0x%x or control id 0x%x\n",
> @@ -335,8 +357,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>   */
>  static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
>  {
> -	if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL ||
> -	    which == V4L2_CTRL_WHICH_REQUEST_VAL)
> +	if (which == 0 || (which >= V4L2_CTRL_WHICH_DEF_VAL &&
> +			   which <= V4L2_CTRL_WHICH_MAX_VAL))
>  		return 0;
>  	return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
>  }
> @@ -356,10 +378,12 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
>  	struct v4l2_ctrl_helper *helpers = helper;
>  	int ret;
>  	int i, j;
> -	bool is_default, is_request;
> +	bool is_default, is_request, is_min, is_max;
>  
>  	is_default = (cs->which == V4L2_CTRL_WHICH_DEF_VAL);
>  	is_request = (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL);
> +	is_min = (cs->which == V4L2_CTRL_WHICH_MIN_VAL);
> +	is_max = (cs->which == V4L2_CTRL_WHICH_MAX_VAL);
>  
>  	cs->error_idx = cs->count;
>  	cs->which = V4L2_CTRL_ID2WHICH(cs->which);
> @@ -399,13 +423,14 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
>  
>  		/*
>  		 * g_volatile_ctrl will update the new control values.
> -		 * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL and
> +		 * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL,
> +		 * V4L2_CTRL_WHICH_MIN_VAL, V4L2_CTRL_WHICH_MAX_VAL and
>  		 * V4L2_CTRL_WHICH_REQUEST_VAL. In the case of requests
>  		 * it is v4l2_ctrl_request_complete() that copies the
>  		 * volatile controls at the time of request completion
>  		 * to the request, so you don't want to do that again.
>  		 */
> -		if (!is_default && !is_request &&
> +		if (!is_default && !is_request && !is_min && !is_max &&
>  		    ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
>  		    (master->has_volatiles && !is_cur_manual(master)))) {
>  			for (j = 0; j < master->ncontrols; j++)
> @@ -432,6 +457,10 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
>  				ret = def_to_user(cs->controls + idx, ref->ctrl);
>  			else if (is_request && ref->valid_p_req)
>  				ret = req_to_user(cs->controls + idx, ref);
> +			else if (is_min)
> +				ret = min_to_user(cs->controls + idx, ref->ctrl);
> +			else if (is_max)
> +				ret = max_to_user(cs->controls + idx, ref->ctrl);
>  			else if (is_volatile)
>  				ret = new_to_user(cs->controls + idx, ref->ctrl);
>  			else
> @@ -523,9 +552,11 @@ int try_set_ext_ctrls_common(struct v4l2_fh *fh,
>  
>  	cs->error_idx = cs->count;
>  
> -	/* Default value cannot be changed */
> -	if (cs->which == V4L2_CTRL_WHICH_DEF_VAL) {
> -		dprintk(vdev, "%s: cannot change default value\n",
> +	/* Default/minimum/maximum values cannot be changed */
> +	if (cs->which == V4L2_CTRL_WHICH_DEF_VAL ||
> +	    cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
> +	    cs->which == V4L2_CTRL_WHICH_MAX_VAL) {
> +		dprintk(vdev, "%s: cannot change default/min/max value\n",
>  			video_device_node_name(vdev));
>  		return -EINVAL;
>  	}
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> index 35d43ba650db..8c5828c7c6d3 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> @@ -186,6 +186,28 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
>  	}
>  }
>  
> +static void std_min_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> +			     union v4l2_ctrl_ptr ptr)
> +{
> +	void *p = ptr.p + idx * ctrl->elem_size;
> +
> +	if (ctrl->p_min.p_const)
> +		memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
> +	else
> +		memset(p, 0, ctrl->elem_size);
> +}
> +
> +static void std_max_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> +			     union v4l2_ctrl_ptr ptr)
> +{
> +	void *p = ptr.p + idx * ctrl->elem_size;
> +
> +	if (ctrl->p_max.p_const)
> +		memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
> +	else
> +		memset(p, 0, ctrl->elem_size);
> +}
> +
>  static void std_init(const struct v4l2_ctrl *ctrl, u32 idx,
>  		     union v4l2_ctrl_ptr ptr)
>  {
> @@ -224,6 +246,82 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx,
>  	}
>  }
>  
> +static void std_minimum(const struct v4l2_ctrl *ctrl, u32 idx,
> +			union v4l2_ctrl_ptr ptr)
> +{
> +	switch (ctrl->type) {
> +	case V4L2_CTRL_TYPE_STRING:
> +		idx *= ctrl->elem_size;
> +		memset(ptr.p_char + idx, ' ', ctrl->minimum);
> +		ptr.p_char[idx + ctrl->minimum] = '\0';
> +		break;
> +	case V4L2_CTRL_TYPE_INTEGER64:
> +		ptr.p_s64[idx] = ctrl->minimum;
> +		break;
> +	case V4L2_CTRL_TYPE_INTEGER:
> +	case V4L2_CTRL_TYPE_INTEGER_MENU:
> +	case V4L2_CTRL_TYPE_MENU:
> +	case V4L2_CTRL_TYPE_BITMASK:
> +	case V4L2_CTRL_TYPE_BOOLEAN:
> +		ptr.p_s32[idx] = ctrl->minimum;
> +		break;
> +	case V4L2_CTRL_TYPE_BUTTON:
> +	case V4L2_CTRL_TYPE_CTRL_CLASS:
> +		ptr.p_s32[idx] = 0;
> +		break;
> +	case V4L2_CTRL_TYPE_U8:
> +		ptr.p_u8[idx] = ctrl->minimum;
> +		break;
> +	case V4L2_CTRL_TYPE_U16:
> +		ptr.p_u16[idx] = ctrl->minimum;
> +		break;
> +	case V4L2_CTRL_TYPE_U32:
> +		ptr.p_u32[idx] = ctrl->minimum;
> +		break;
> +	default:
> +		std_min_compound(ctrl, idx, ptr);
> +		break;
> +	}
> +}
> +
> +static void std_maximum(const struct v4l2_ctrl *ctrl, u32 idx,
> +			union v4l2_ctrl_ptr ptr)
> +{
> +	switch (ctrl->type) {
> +	case V4L2_CTRL_TYPE_STRING:
> +		idx *= ctrl->elem_size;
> +		memset(ptr.p_char + idx, ' ', ctrl->maximum);
> +		ptr.p_char[idx + ctrl->maximum] = '\0';
> +		break;
> +	case V4L2_CTRL_TYPE_INTEGER64:
> +		ptr.p_s64[idx] = ctrl->maximum;
> +		break;
> +	case V4L2_CTRL_TYPE_INTEGER:
> +	case V4L2_CTRL_TYPE_INTEGER_MENU:
> +	case V4L2_CTRL_TYPE_MENU:
> +	case V4L2_CTRL_TYPE_BITMASK:
> +	case V4L2_CTRL_TYPE_BOOLEAN:
> +		ptr.p_s32[idx] = ctrl->maximum;
> +		break;
> +	case V4L2_CTRL_TYPE_BUTTON:
> +	case V4L2_CTRL_TYPE_CTRL_CLASS:
> +		ptr.p_s32[idx] = 0;
> +		break;
> +	case V4L2_CTRL_TYPE_U8:
> +		ptr.p_u8[idx] = ctrl->maximum;
> +		break;
> +	case V4L2_CTRL_TYPE_U16:
> +		ptr.p_u16[idx] = ctrl->maximum;
> +		break;
> +	case V4L2_CTRL_TYPE_U32:
> +		ptr.p_u32[idx] = ctrl->maximum;
> +		break;
> +	default:
> +		std_max_compound(ctrl, idx, ptr);
> +		break;
> +	}
> +}
> +
>  static void std_log(const struct v4l2_ctrl *ctrl)
>  {
>  	union v4l2_ctrl_ptr ptr = ctrl->p_cur;
> @@ -986,6 +1084,8 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
>  static const struct v4l2_ctrl_type_ops std_type_ops = {
>  	.equal = std_equal,
>  	.init = std_init,
> +	.minimum = std_minimum,
> +	.maximum = std_maximum,
>  	.log = std_log,
>  	.validate = std_validate,
>  };
> @@ -1368,7 +1468,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  			s64 min, s64 max, u64 step, s64 def,
>  			const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size,
>  			u32 flags, const char * const *qmenu,
> -			const s64 *qmenu_int, const union v4l2_ctrl_ptr p_def,
> +			const s64 *qmenu_int,
> +			const union v4l2_ctrl_ptr p_def,
> +			const union v4l2_ctrl_ptr p_min,
> +			const union v4l2_ctrl_ptr p_max,
>  			void *priv)
>  {
>  	struct v4l2_ctrl *ctrl;
> @@ -1515,7 +1618,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  		sz_extra += 2 * tot_ctrl_size;
>  
>  	if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const)
> -		sz_extra += elem_size;
> +		sz_extra += elem_size * 3;
>  
>  	ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
>  	if (ctrl == NULL) {
> @@ -1565,6 +1668,13 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  		ctrl->p_def.p = ctrl->p_cur.p + tot_ctrl_size;
>  		memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
>  	}
> +	if (type >= V4L2_CTRL_COMPOUND_TYPES &&
> +	    p_min.p_const && p_max.p_const) {
> +		ctrl->p_min.p = ctrl->p_cur.p + 2 * tot_ctrl_size;
> +		memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
> +		ctrl->p_max.p = ctrl->p_cur.p + 3 * tot_ctrl_size;
> +		memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
> +	}
>  
>  	for (idx = 0; idx < elems; idx++) {
>  		ctrl->type_ops->init(ctrl, idx, ctrl->p_cur);
> @@ -1617,7 +1727,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
>  			type, min, max,
>  			is_menu ? cfg->menu_skip_mask : step, def,
>  			cfg->dims, cfg->elem_size,
> -			flags, qmenu, qmenu_int, cfg->p_def, priv);
> +			flags, qmenu, qmenu_int, cfg->p_def, cfg->p_min,
> +			cfg->p_max, priv);
>  	if (ctrl)
>  		ctrl->is_private = cfg->is_private;
>  	return ctrl;
> @@ -1642,7 +1753,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
>  	}
>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>  			     min, max, step, def, NULL, 0,
> -			     flags, NULL, NULL, ptr_null, NULL);
> +			     flags, NULL, NULL, ptr_null, ptr_null,
> +			     ptr_null, NULL);
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_new_std);
>  
> @@ -1675,7 +1787,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
>  	}
>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>  			     0, max, mask, def, NULL, 0,
> -			     flags, qmenu, qmenu_int, ptr_null, NULL);
> +			     flags, qmenu, qmenu_int, ptr_null, ptr_null,
> +			     ptr_null, NULL);
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
>  
> @@ -1707,7 +1820,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
>  	}
>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>  			     0, max, mask, def, NULL, 0,
> -			     flags, qmenu, NULL, ptr_null, NULL);
> +			     flags, qmenu, NULL, ptr_null, ptr_null,
> +			     ptr_null, NULL);
>  
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
> @@ -1715,7 +1829,9 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
>  /* Helper function for standard compound controls */
>  struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
>  				const struct v4l2_ctrl_ops *ops, u32 id,
> -				const union v4l2_ctrl_ptr p_def)
> +				const union v4l2_ctrl_ptr p_def,
> +				const union v4l2_ctrl_ptr p_min,
> +				const union v4l2_ctrl_ptr p_max)
>  {
>  	const char *name;
>  	enum v4l2_ctrl_type type;
> @@ -1729,7 +1845,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
>  	}
>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>  			     min, max, step, def, NULL, 0,
> -			     flags, NULL, NULL, p_def, NULL);
> +			     flags, NULL, NULL, p_def, p_min, p_max, NULL);
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_new_std_compound);
>  
> @@ -1753,7 +1869,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
>  	}
>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>  			     0, max, 0, def, NULL, 0,
> -			     flags, NULL, qmenu_int, ptr_null, NULL);
> +			     flags, NULL, qmenu_int, ptr_null, ptr_null,
> +			     ptr_null, NULL);
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
>  
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index 21470de62d72..0f713b9a95f9 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -893,7 +893,9 @@ static bool check_ext_ctrls(struct v4l2_ext_controls *c, unsigned long ioctl)
>  			return false;
>  		break;
>  	case V4L2_CTRL_WHICH_DEF_VAL:
> -		/* Default value cannot be changed */
> +	case V4L2_CTRL_WHICH_MIN_VAL:
> +	case V4L2_CTRL_WHICH_MAX_VAL:
> +		/* Default, minimum or maximum value cannot be changed */
>  		if (ioctl == VIDIOC_S_EXT_CTRLS ||
>  		    ioctl == VIDIOC_TRY_EXT_CTRLS) {
>  			c->error_idx = c->count;
> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> index 919e104de50b..c8ab8c44d7c6 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -131,6 +131,8 @@ struct v4l2_ctrl_ops {
>   *
>   * @equal: return true if both values are equal.
>   * @init: initialize the value.
> + * @minimum: set the value to the minimum value of the control.
> + * @maximum: set the value to the maximum value of the control.
>   * @log: log the value.
>   * @validate: validate the value. Return 0 on success and a negative value
>   *	otherwise.
> @@ -141,6 +143,10 @@ struct v4l2_ctrl_type_ops {
>  		      union v4l2_ctrl_ptr ptr2);
>  	void (*init)(const struct v4l2_ctrl *ctrl, u32 idx,
>  		     union v4l2_ctrl_ptr ptr);
> +	void (*minimum)(const struct v4l2_ctrl *ctrl, u32 idx,
> +			union v4l2_ctrl_ptr ptr);
> +	void (*maximum)(const struct v4l2_ctrl *ctrl, u32 idx,
> +			union v4l2_ctrl_ptr ptr);
>  	void (*log)(const struct v4l2_ctrl *ctrl);
>  	int (*validate)(const struct v4l2_ctrl *ctrl, u32 idx,
>  			union v4l2_ctrl_ptr ptr);
> @@ -237,6 +243,12 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv);
>   * @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).
> + * @p_min:	The control's minimum value represented via a union which
> + *		provides a standard way of accessing control types
> + *		through a pointer (for compound controls only).
> + * @p_max:	The control's maximum value represented via a union which
> + *		provides a standard way of accessing control types
> + *		through a pointer (for compound controls only).
>   * @p_cur:	The control's current value represented via a union which
>   *		provides a standard way of accessing control types
>   *		through a pointer.
> @@ -292,6 +304,8 @@ struct v4l2_ctrl {
>  	} cur;
>  
>  	union v4l2_ctrl_ptr p_def;
> +	union v4l2_ctrl_ptr p_min;
> +	union v4l2_ctrl_ptr p_max;
>  	union v4l2_ctrl_ptr p_new;
>  	union v4l2_ctrl_ptr p_cur;
>  };
> @@ -398,6 +412,8 @@ struct v4l2_ctrl_handler {
>   * @step:	The control's step value for non-menu controls.
>   * @def:	The control's default value.
>   * @p_def:	The control's default value for compound controls.
> + * @p_min:	The control's minimum value for compound controls.
> + * @p_max:	The control's maximum value for compound controls.
>   * @dims:	The size of each dimension.
>   * @elem_size:	The size in bytes of the control.
>   * @flags:	The control's flags.
> @@ -427,6 +443,8 @@ struct v4l2_ctrl_config {
>  	u64 step;
>  	s64 def;
>  	union v4l2_ctrl_ptr p_def;
> +	union v4l2_ctrl_ptr p_min;
> +	union v4l2_ctrl_ptr p_max;
>  	u32 dims[V4L2_CTRL_MAX_DIMS];
>  	u32 elem_size;
>  	u32 flags;
> @@ -696,17 +714,21 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
>   * @ops:       The control ops.
>   * @id:        The control ID.
>   * @p_def:     The control's default value.
> + * @p_min:     The control's minimum value.
> + * @p_max:     The control's maximum value.
>   *
> - * Sames as v4l2_ctrl_new_std(), but with support to compound controls, thanks
> - * to the @p_def field. Use v4l2_ctrl_ptr_create() to create @p_def from a
> - * pointer. Use v4l2_ctrl_ptr_create(NULL) if the default value of the
> - * compound control should be all zeroes.
> + * Same as v4l2_ctrl_new_std(), but with support to compound controls, thanks
> + * to the @p_def/min/max field. Use v4l2_ctrl_ptr_create() to create

s/field/fields/

> + * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
> + * default/min/max value of the compound control should be all zeroes.

Hmmm... I expect that, for some compound control types, the concept of
minimum and maximum won't be defined. Wouldn't it better for a get
control operation on V4L2_CTRL_WHICH_{MIN,MAX}_VAL to return an error in
that case, instead of zeroed memory ?

>   *
>   */
>  struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
>  					     const struct v4l2_ctrl_ops *ops,
>  					     u32 id,
> -					     const union v4l2_ctrl_ptr p_def);
> +					     const union v4l2_ctrl_ptr p_def,
> +					     const union v4l2_ctrl_ptr p_min,
> +					     const union v4l2_ctrl_ptr p_max);
>  
>  /**
>   * v4l2_ctrl_new_int_menu() - Create a new standard V4L2 integer menu control.
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index 2e36bb610ea6..134f6a65eacc 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -1796,6 +1796,8 @@ struct v4l2_ext_controls {
>  #define V4L2_CTRL_WHICH_CUR_VAL   0
>  #define V4L2_CTRL_WHICH_DEF_VAL   0x0f000000
>  #define V4L2_CTRL_WHICH_REQUEST_VAL 0x0f010000
> +#define V4L2_CTRL_WHICH_MIN_VAL   0x0f020000
> +#define V4L2_CTRL_WHICH_MAX_VAL   0x0f030000
>  
>  enum v4l2_ctrl_type {
>  	V4L2_CTRL_TYPE_INTEGER	     = 1,

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 2/7] media: uvcvideo: add uvc_ctrl_get_fixed for getting default value
  2022-06-28  7:57 ` [PATCH v7 2/7] media: uvcvideo: add uvc_ctrl_get_fixed for getting default value Yunke Cao
@ 2022-08-24 16:24   ` Laurent Pinchart
  0 siblings, 0 replies; 32+ messages in thread
From: Laurent Pinchart @ 2022-08-24 16:24 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Nicolas Dufresne, Mauro Carvalho Chehab,
	Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Yunke,

Thank you for the patch.

On Tue, Jun 28, 2022 at 04:57:00PM +0900, Yunke Cao wrote:
> Introduce a new uvc_ctrl_get_fixed. It simply calls query_v4l2_ctrl()

"fixed" sounds a bit confusing to me. How about calling it
uvc_ctrl_get_boundary() or uvc_ctrl_get_limit() ? A better name could
possibly be found as the default value isn't a limit.
uvc_ctrl_get_info() comes to mind, but it wouldn't match the UVC
GET_INFO concept, which may be confusing.

> for now, but is easier to extend to support compound controls and
> V4L2_CTRL_WHICH_MIN/MAX_VAL in the following patches.
> 
> Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
>  drivers/media/usb/uvc/uvc_ctrl.c | 13 +++++++++++++
>  drivers/media/usb/uvc/uvc_v4l2.c |  6 +-----
>  drivers/media/usb/uvc/uvcvideo.h |  2 ++
>  3 files changed, 16 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 0e78233fc8a0..772d9d28a520 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -1697,6 +1697,19 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
>  	return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
>  }
>  
> +int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> +		       struct v4l2_ext_control *xctrl)
> +{
> +	struct v4l2_queryctrl qc = { .id = xctrl->id };
> +	int ret = uvc_query_v4l2_ctrl(chain, &qc);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	xctrl->value = qc.default_value;
> +	return 0;
> +}
> +
>  int uvc_ctrl_set(struct uvc_fh *handle,
>  	struct v4l2_ext_control *xctrl)
>  {
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index 648dcd579e81..0366d05895a9 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -1042,15 +1042,11 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
>  
>  	if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
>  		for (i = 0; i < ctrls->count; ++ctrl, ++i) {
> -			struct v4l2_queryctrl qc = { .id = ctrl->id };
> -
> -			ret = uvc_query_v4l2_ctrl(chain, &qc);
> +			ret = uvc_ctrl_get_fixed(chain, ctrl);
>  			if (ret < 0) {
>  				ctrls->error_idx = i;
>  				return ret;
>  			}
> -
> -			ctrl->value = qc.default_value;
>  		}
>  
>  		return 0;
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index c5b4febd2d94..ba028ba7c34e 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -901,6 +901,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
>  }
>  
>  int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
> +int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> +		       struct v4l2_ext_control *xctrl);
>  int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
>  int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
>  			   bool read);

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 3/7] media: uvcvideo: Add support for compound controls
  2022-06-28  7:57 ` [PATCH v7 3/7] media: uvcvideo: Add support for compound controls Yunke Cao
  2022-06-30  6:42   ` Ricardo Ribalda
@ 2022-08-24 19:56   ` Laurent Pinchart
  2022-08-29  5:39     ` Yunke Cao
  1 sibling, 1 reply; 32+ messages in thread
From: Laurent Pinchart @ 2022-08-24 19:56 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Nicolas Dufresne, Mauro Carvalho Chehab,
	Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Yunke,

Thank you for the patch.

On Tue, Jun 28, 2022 at 04:57:01PM +0900, Yunke Cao wrote:
> Supports getting/setting current value.
> Supports getting default value.
> Handles V4L2_CTRL_FLAG_NEXT_COMPOUND.
> 
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
>  drivers/media/usb/uvc/uvc_ctrl.c | 279 ++++++++++++++++++++++++++-----
>  drivers/media/usb/uvc/uvcvideo.h |   4 +
>  2 files changed, 238 insertions(+), 45 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 772d9d28a520..508ee04afbcd 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -815,6 +815,34 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
>  	}
>  }
>  
> +/* Extract the byte array specified by mapping->offset and mapping->size
> + * stored at 'data' to the output array 'data_out'.
> + */

Please use the

/*
 * multi
 * line
 */

style instead of

/* multi
 * line
 */

The uvcvideo driver has recently switched to the former in commit
699b9a86a3f0 ("media: uvcvideo: Fix comment blocks style").

> +static int uvc_get_array(struct uvc_control_mapping *mapping, const u8 *data,
> +			 u8 *data_out)
> +{
> +	// Only supports byte-aligned data.

And no C++-style comments either please.

> +	if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> +		return -EINVAL;
> +
> +	memcpy(data_out, data + mapping->offset / 8, mapping->size / 8);
> +	return 0;
> +}
> +
> +/* Copy the byte array 'data_in' to the destination specified by mapping->offset
> + * and mapping->size stored at 'data'.
> + */
> +static int uvc_set_array(struct uvc_control_mapping *mapping, const u8 *data_in,
> +			 u8 *data)
> +{
> +	// Only supports byte-aligned data.
> +	if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> +		return -EINVAL;
> +
> +	memcpy(data + mapping->offset / 8, data_in, mapping->size / 8);
> +	return 0;
> +}
> +

Could you add here a

static bool
uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
{
	return mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES;
}

and use it below ? That would be clearer I think.

>  /* ------------------------------------------------------------------------
>   * Terminal and unit management
>   */
> @@ -831,7 +859,7 @@ static int uvc_entity_match_guid(const struct uvc_entity *entity,
>  
>  static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
>  	struct uvc_control_mapping **mapping, struct uvc_control **control,
> -	int next)
> +	int next, int next_compound)
>  {
>  	struct uvc_control *ctrl;
>  	struct uvc_control_mapping *map;
> @@ -846,14 +874,18 @@ static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
>  			continue;
>  
>  		list_for_each_entry(map, &ctrl->info.mappings, list) {
> -			if ((map->id == v4l2_id) && !next) {
> +			if (map->id == v4l2_id && !next && !next_compound) {
>  				*control = ctrl;
>  				*mapping = map;
>  				return;
>  			}
>  
>  			if ((*mapping == NULL || (*mapping)->id > map->id) &&
> -			    (map->id > v4l2_id) && next) {
> +			    (map->id > v4l2_id) &&
> +			    ((map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES &&
> +			      next) ||
> +			     (map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES &&
> +			      next_compound))) {
>  				*control = ctrl;
>  				*mapping = map;
>  			}

This could become for instance

			if ((*mapping == NULL || (*mapping)->id > map->id) &&
			    (map->id > v4l2_id) &&
			    ((!uvc_ctrl_mapping_is_compound(map) && next) ||
			     (uvc_ctrl_mapping_is_compound(map) && next_compound))) {
				*control = ctrl;
				*mapping = map;
			}

> @@ -867,6 +899,7 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
>  	struct uvc_control *ctrl = NULL;
>  	struct uvc_entity *entity;
>  	int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;
> +	int next_compound = v4l2_id & V4L2_CTRL_FLAG_NEXT_COMPOUND;
>  
>  	*mapping = NULL;
>  
> @@ -875,12 +908,13 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
>  
>  	/* Find the control. */
>  	list_for_each_entry(entity, &chain->entities, chain) {
> -		__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
> -		if (ctrl && !next)
> +		__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next,
> +				   next_compound);
> +		if (ctrl && !next && !next_compound)
>  			return ctrl;
>  	}
>  
> -	if (ctrl == NULL && !next)
> +	if (!ctrl && !next && !next_compound)
>  		uvc_dbg(chain->dev, CONTROL, "Control 0x%08x not found\n",
>  			v4l2_id);
>  
> @@ -943,6 +977,39 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
>  	return 0;
>  }
>  
> +static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain,
> +			       struct uvc_control *ctrl)
> +{

I would declare

	u8 data;

here, and

> +	int ret = 0;
> +
> +	if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
> +		return -EACCES;
> +
> +	if (ctrl->loaded)
> +		return 0;
> +

set

	data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT);

here, and

> +	if (ctrl->entity->get_cur) {
> +		ret = ctrl->entity->get_cur(chain->dev,
> +			ctrl->entity,
> +			ctrl->info.selector,
> +			uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> +			ctrl->info.size);

improve indentation here

		ret = ctrl->entity->get_cur(chain->dev, ctrl->entity,
					    ctrl->info.selector, data,
					    ctrl->info.size);


> +	} else {

and also use data here

> +		ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
> +				     ctrl->entity->id, chain->dev->intfnum,
> +				     ctrl->info.selector,
> +				     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> +				     ctrl->info.size);

		ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
				     ctrl->entity->id, chain->dev->intfnum,
				     ctrl->info.selector, data,
				     ctrl->info.size);

> +	}
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	ctrl->loaded = 1;
> +
> +	return ret;
> +}
> +
>  static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping,
>  				const u8 *data)
>  {
> @@ -963,35 +1030,19 @@ static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping,
>  	return value;
>  }
>  
> -static int __uvc_ctrl_get(struct uvc_video_chain *chain,
> -	struct uvc_control *ctrl, struct uvc_control_mapping *mapping,
> -	s32 *value)
> +static int __uvc_ctrl_get_std(struct uvc_video_chain *chain,
> +			      struct uvc_control *ctrl,
> +			      struct uvc_control_mapping *mapping,
> +			      s32 *value)
>  {
>  	int ret;
>  
> -	if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
> -		return -EACCES;
> -
> -	if (!ctrl->loaded) {
> -		if (ctrl->entity->get_cur) {
> -			ret = ctrl->entity->get_cur(chain->dev,
> -				ctrl->entity,
> -				ctrl->info.selector,
> -				uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> -				ctrl->info.size);
> -		} else {
> -			ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
> -				ctrl->entity->id,
> -				chain->dev->intfnum,
> -				ctrl->info.selector,
> -				uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> -				ctrl->info.size);
> -		}
> -		if (ret < 0)
> -			return ret;
> +	if (mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> +		return -EINVAL;
>  
> -		ctrl->loaded = 1;
> -	}
> +	ret = __uvc_ctrl_load_cur(chain, ctrl);
> +	if (ret < 0)
> +		return ret;
>  
>  	*value = __uvc_ctrl_get_value(mapping,
>  				uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> @@ -999,6 +1050,57 @@ static int __uvc_ctrl_get(struct uvc_video_chain *chain,
>  	return 0;
>  }
>  
> +static int __uvc_ctrl_get_compound_to_user(struct uvc_control_mapping *mapping,

I don't like this name much. One option would be to rename the
__uvc_ctrl_get_compound() function below to
__uvc_ctrl_get_compound_cur() and call this function
__uvc_ctrl_get_compound(). Better options are likely possible.

> +					   struct uvc_control *ctrl,
> +					   int id,
> +					   struct v4l2_ext_control *xctrl)
> +{
> +	int ret, size;

size is never negative, make it an unsigned int.

> +	u8 *data;
> +
> +	if (WARN_ON(!mapping->size % 8))
> +		return -EINVAL;

This duplicates a check in the get_array function. Let's move all those
checks to the function that adds mappings.

> +
> +	size = mapping->size / 8;
> +	if (xctrl->size < size) {
> +		xctrl->size = size;
> +		return -ENOSPC;
> +	}
> +
> +	data = kmalloc(size, GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	ret = mapping->get_array(mapping, uvc_ctrl_data(ctrl, id), data);
> +	if (ret < 0)
> +		goto out;
> +
> +	ret = copy_to_user(xctrl->ptr, data, size) ? -EFAULT : 0;
> +
> +out:
> +	kfree(data);
> +	return ret;
> +}
> +
> +static int __uvc_ctrl_get_compound(struct uvc_video_chain *chain,
> +				   struct uvc_control *ctrl,
> +				   struct uvc_control_mapping *mapping,
> +				   struct v4l2_ext_control *xctrl)
> +{
> +	int ret;
> +
> +	if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> +		return -EINVAL;
> +
> +	ret = __uvc_ctrl_load_cur(chain, ctrl);
> +	if (ret < 0)
> +		return ret;
> +
> +	return __uvc_ctrl_get_compound_to_user(mapping, ctrl,
> +					       UVC_CTRL_DATA_CURRENT,
> +					       xctrl);
> +}
> +
>  static int __uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
>  				  u32 found_id)
>  {
> @@ -1102,10 +1204,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
>  
>  	if (mapping->master_id)
>  		__uvc_find_control(ctrl->entity, mapping->master_id,
> -				   &master_map, &master_ctrl, 0);
> +				   &master_map, &master_ctrl, 0, 0);
>  	if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
> -		s32 val;
> -		int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
> +		int ret;
> +		s32 val = 0;

Move ret after val.

> +
> +		if (master_map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> +			return -EINVAL;
> +
> +		ret = __uvc_ctrl_get_std(chain, master_ctrl, master_map, &val);
>  		if (ret < 0)
>  			return ret;
>  
> @@ -1113,6 +1220,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
>  				v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
>  	}
>  
> +	if (v4l2_ctrl->type >= V4L2_CTRL_COMPOUND_TYPES) {
> +		v4l2_ctrl->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD;
> +		v4l2_ctrl->default_value = 0;
> +		v4l2_ctrl->minimum = 0;
> +		v4l2_ctrl->maximum = 0;
> +		v4l2_ctrl->step = 0;
> +		return 0;
> +	}
> +
>  	if (!ctrl->cached) {
>  		int ret = uvc_ctrl_populate_cache(chain, ctrl);
>  		if (ret < 0)
> @@ -1346,11 +1462,12 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
>  	u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
>  	s32 val = 0;
>  
> -	__uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0);
> +	__uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0, 0);
>  	if (ctrl == NULL)
>  		return;
>  
> -	if (__uvc_ctrl_get(chain, ctrl, mapping, &val) == 0)
> +	if (mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES ||
> +	    __uvc_ctrl_get_std(chain, ctrl, mapping, &val) == 0)
>  		changes |= V4L2_EVENT_CTRL_CH_VALUE;
>  
>  	uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
> @@ -1517,7 +1634,8 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
>  		u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
>  		s32 val = 0;
>  
> -		if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0)
> +		if (mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES ||
> +		    __uvc_ctrl_get_std(handle->chain, ctrl, mapping, &val) == 0)
>  			changes |= V4L2_EVENT_CTRL_CH_VALUE;
>  
>  		uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val,
> @@ -1647,7 +1765,7 @@ static int uvc_ctrl_find_ctrl_idx(struct uvc_entity *entity,
>  
>  	for (i = 0; i < ctrls->count; i++) {
>  		__uvc_find_control(entity, ctrls->controls[i].id, &mapping,
> -				   &ctrl_found, 0);
> +				   &ctrl_found, 0, 0);
>  		if (uvc_control == ctrl_found)
>  			return i;
>  	}
> @@ -1694,11 +1812,14 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
>  	if (ctrl == NULL)
>  		return -EINVAL;
>  
> -	return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
> +	if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> +		return __uvc_ctrl_get_std(chain, ctrl, mapping, &xctrl->value);
> +	else
> +		return __uvc_ctrl_get_compound(chain, ctrl, mapping, xctrl);
>  }
>  
> -int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> -		       struct v4l2_ext_control *xctrl)
> +int __uvc_ctrl_get_fixed_std(struct uvc_video_chain *chain,
> +			     struct v4l2_ext_control *xctrl)
>  {
>  	struct v4l2_queryctrl qc = { .id = xctrl->id };
>  	int ret = uvc_query_v4l2_ctrl(chain, &qc);
> @@ -1710,6 +1831,56 @@ int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
>  	return 0;
>  }
>  
> +int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> +		       struct v4l2_ext_control *xctrl)
> +{
> +	struct uvc_control *ctrl;
> +	struct uvc_control_mapping *mapping;
> +	int ret;
> +
> +	if (__uvc_query_v4l2_class(chain, xctrl->id, 0) >= 0)
> +		return -EACCES;
> +

This check comes a bit out of the blue, I think it belongs to another
patch, with a commit message that clearly explain the rationale.

> +	ctrl = uvc_find_control(chain, xctrl->id, &mapping);
> +	if (!ctrl)
> +		return -EINVAL;

And here, you're duplicating the uvc_find_control() call from
uvc_query_v4l2_ctrl(), called in __uvc_ctrl_get_fixed_std().
Furthermore, you're not holding the ctrl_mutex lock here, which I think
is wrong.

I'd like to see a patch before this one that refactors what needs to be
refactored first, and then the introduction of compound types support
without any extra change like this. Splitting code out to
__uvc_ctrl_load_cur() could also be moved to a preparatory patch to
simplify this one, it's quite hard to review.

> +
> +	if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> +		return __uvc_ctrl_get_fixed_std(chain, xctrl);
> +
> +	if (!ctrl->cached) {
> +		ret = uvc_ctrl_populate_cache(chain, ctrl);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return __uvc_ctrl_get_compound_to_user(mapping, ctrl, UVC_CTRL_DATA_DEF,
> +					       xctrl);
> +}
> +
> +int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
> +			    struct v4l2_ext_control *xctrl,
> +			    struct uvc_control *ctrl)
> +{
> +	int ret;
> +	u8 *data;

Please move ret after data.

> +
> +	data = kmalloc(xctrl->size, GFP_KERNEL);

A limit on the size is needed to avoid denial of service attacks.

> +	if (!data)
> +		return -ENOMEM;
> +
> +	ret = copy_from_user(data, xctrl->ptr, xctrl->size);
> +	if (ret < 0)
> +		goto out;
> +
> +	ret = mapping->set_array(mapping, data,
> +			uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));

	ret = mapping->set_array(mapping, data,
				 uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));

The V4L2 control model is that the kernel can update the value of a
control if the requested value is out of range or otherwise not
acceptable. You should here copy the data back to the user.

> +
> +out:
> +	kfree(data);
> +	return ret;
> +}
> +
>  int uvc_ctrl_set(struct uvc_fh *handle,
>  	struct v4l2_ext_control *xctrl)
>  {
> @@ -1820,8 +1991,14 @@ int uvc_ctrl_set(struct uvc_fh *handle,
>  		       ctrl->info.size);
>  	}
>  
> -	mapping->set(mapping, value,
> -		uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> +	if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES) {
> +		mapping->set(mapping, value,
> +			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> +	} else {
> +		ret = __uvc_ctrl_set_compound(mapping, xctrl, ctrl);
> +		if (ret < 0)
> +			return ret;
> +	}
>  
>  	if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
>  		ctrl->handle = handle;
> @@ -2220,10 +2397,14 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
>  		return -ENOMEM;
>  	}
>  
> -	if (map->get == NULL)
> +	if (!map->get && map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
>  		map->get = uvc_get_le_value;
> -	if (map->set == NULL)
> +	if (!map->set && map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
>  		map->set = uvc_set_le_value;
> +	if (!map->get_array && map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> +		map->get_array = uvc_get_array;
> +	if (!map->set_array && map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> +		map->set_array = uvc_set_array;
>  
>  	for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
>  		if (V4L2_CTRL_ID2WHICH(uvc_control_classes[i]) ==
> @@ -2233,6 +2414,14 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
>  		}
>  	}
>  
> +	if (map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES &&
> +	    WARN_ON(!map->get || !map->set))
> +		return -EINVAL;
> +
> +	if (map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES &&
> +	    WARN_ON(!map->get_array || !map->set_array))
> +		return -EINVAL;
> +

Can this happen, given that you set them above if they're null ?

>  	list_add_tail(&map->list, &ctrl->info.mappings);
>  	uvc_dbg(chain->dev, CONTROL, "Adding mapping '%s' to control %pUl/%u\n",
>  		uvc_map_get_name(map), ctrl->info.entity,
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index ba028ba7c34e..2f9b75faae83 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -262,8 +262,12 @@ struct uvc_control_mapping {
>  
>  	s32 (*get)(struct uvc_control_mapping *mapping, u8 query,
>  		   const u8 *data);
> +	int (*get_array)(struct uvc_control_mapping *mapping, const u8 *data,
> +			 u8 *data_out);
>  	void (*set)(struct uvc_control_mapping *mapping, s32 value,
>  		    u8 *data);
> +	int (*set_array)(struct uvc_control_mapping *mapping, const u8 *data_in,
> +			 u8 *data);

Wouldn't those operations be better names get_compound and set_compound ?

>  };
>  
>  struct uvc_control {

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 6/7] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL
  2022-06-28  7:57 ` [PATCH v7 6/7] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
  2022-06-30  6:41   ` Ricardo Ribalda
@ 2022-08-24 20:03   ` Laurent Pinchart
  2022-09-20  7:52     ` Yunke Cao
  1 sibling, 1 reply; 32+ messages in thread
From: Laurent Pinchart @ 2022-08-24 20:03 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Nicolas Dufresne, Mauro Carvalho Chehab,
	Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Yunke,

Thank you for the patch.

On Tue, Jun 28, 2022 at 04:57:04PM +0900, Yunke Cao wrote:
> Add support for V4L2_CTRL_WHICH_MIN/MAX_VAL in uvc driver.
> It is useful for the V4L2_CID_UVC_REGION_OF_INTEREST_RECT control.
> 
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
>  drivers/media/usb/uvc/uvc_ctrl.c | 73 ++++++++++++++++++++++++++------
>  drivers/media/usb/uvc/uvc_v4l2.c | 11 +++--
>  drivers/media/usb/uvc/uvcvideo.h |  3 +-
>  3 files changed, 70 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 00482269233a..b569d6824ac1 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -1923,7 +1923,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
>  }
>  
>  int __uvc_ctrl_get_fixed_std(struct uvc_video_chain *chain,
> -			     struct v4l2_ext_control *xctrl)
> +			     struct v4l2_ext_control *xctrl, u32 v4l2_which)
>  {
>  	struct v4l2_queryctrl qc = { .id = xctrl->id };
>  	int ret = uvc_query_v4l2_ctrl(chain, &qc);
> @@ -1931,16 +1931,69 @@ int __uvc_ctrl_get_fixed_std(struct uvc_video_chain *chain,
>  	if (ret < 0)
>  		return ret;
>  
> -	xctrl->value = qc.default_value;
> +	switch (v4l2_which) {
> +	case V4L2_CTRL_WHICH_DEF_VAL:
> +		xctrl->value = qc.default_value;
> +		break;
> +	case V4L2_CTRL_WHICH_MIN_VAL:
> +		xctrl->value = qc.minimum;
> +		break;
> +	case V4L2_CTRL_WHICH_MAX_VAL:
> +		xctrl->value = qc.maximum;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
>  	return 0;
>  }
>  
> +static int __uvc_ctrl_get_fixed_compound(struct uvc_video_chain *chain,
> +					 struct uvc_control_mapping *mapping,
> +					 struct uvc_control *ctrl,
> +					 u32 v4l2_which,
> +					 struct v4l2_ext_control *xctrl)
> +{
> +	int ret;
> +	u32 flag, id;
> +
> +	if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> +		return -EINVAL;
> +

This can't happen.

> +	switch (v4l2_which) {
> +	case V4L2_CTRL_WHICH_DEF_VAL:
> +		flag = UVC_CTRL_FLAG_GET_DEF;
> +		id = UVC_CTRL_DATA_DEF;
> +		break;
> +	case V4L2_CTRL_WHICH_MIN_VAL:
> +		flag = UVC_CTRL_FLAG_GET_MIN;
> +		id = UVC_CTRL_DATA_MIN;
> +		break;
> +	case V4L2_CTRL_WHICH_MAX_VAL:
> +		flag = UVC_CTRL_FLAG_GET_MAX;
> +		id = UVC_CTRL_DATA_MAX;
> +		break;
> +	default:
> +		return -EINVAL;

Can this happen ?

> +	}
> +
> +	if (!(ctrl->info.flags & flag) && flag != UVC_CTRL_FLAG_GET_DEF)
> +		return -EACCES;
> +
> +	if (!ctrl->cached) {
> +		ret = uvc_ctrl_populate_cache(chain, ctrl);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return __uvc_ctrl_get_compound_to_user(mapping, ctrl, id, xctrl);
> +}
> +
>  int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> -		       struct v4l2_ext_control *xctrl)
> +		       struct v4l2_ext_control *xctrl, u32 v4l2_which)
>  {
>  	struct uvc_control *ctrl;
>  	struct uvc_control_mapping *mapping;
> -	int ret;
>  
>  	if (__uvc_query_v4l2_class(chain, xctrl->id, 0) >= 0)
>  		return -EACCES;
> @@ -1950,16 +2003,10 @@ int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
>  		return -EINVAL;
>  
>  	if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> -		return __uvc_ctrl_get_fixed_std(chain, xctrl);
> +		return __uvc_ctrl_get_fixed_std(chain, xctrl, v4l2_which);
>  
> -	if (!ctrl->cached) {
> -		ret = uvc_ctrl_populate_cache(chain, ctrl);
> -		if (ret < 0)
> -			return ret;
> -	}
> -
> -	return __uvc_ctrl_get_compound_to_user(mapping, ctrl, UVC_CTRL_DATA_DEF,
> -					       xctrl);
> +	return __uvc_ctrl_get_fixed_compound(chain, mapping, ctrl, v4l2_which,
> +					     xctrl);
>  }
>  
>  int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index aad61af36271..004e3b764737 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -1043,16 +1043,21 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
>  	if (ret < 0)
>  		return ret;
>  
> -	if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
> +	switch (ctrls->which) {
> +	case V4L2_CTRL_WHICH_DEF_VAL:
> +	case V4L2_CTRL_WHICH_MIN_VAL:
> +	case V4L2_CTRL_WHICH_MAX_VAL:
>  		for (i = 0; i < ctrls->count; ++ctrl, ++i) {
> -			ret = uvc_ctrl_get_fixed(chain, ctrl);
> +			ret = uvc_ctrl_get_fixed(chain, ctrl, ctrls->which);
>  			if (ret < 0) {
>  				ctrls->error_idx = i;
>  				return ret;
>  			}
>  		}
> -
>  		return 0;
> +
> +	default:

	case V4L2_CTRL_WHICH_CUR_VAL:

> +		break;

and

	default:
		return -EINVAL;

(please double-check that the error value is the right one). This should
be mentioned in the commit message.

>  	}
>  
>  	ret = uvc_ctrl_begin(chain);
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 9ff95bbad251..54cc47bc2d33 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -913,7 +913,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
>  
>  int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
>  int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> -		       struct v4l2_ext_control *xctrl);
> +		       struct v4l2_ext_control *xctrl,
> +		       u32 v4l2_which);
>  int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
>  int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
>  			   bool read);

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 7/7] media: uvcvideo: document UVC v1.5 ROI
  2022-06-28  7:57 ` [PATCH v7 7/7] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
  2022-06-30  6:41   ` Ricardo Ribalda
@ 2022-08-24 20:17   ` Laurent Pinchart
  2022-09-20  7:46     ` Yunke Cao
  1 sibling, 1 reply; 32+ messages in thread
From: Laurent Pinchart @ 2022-08-24 20:17 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Nicolas Dufresne, Mauro Carvalho Chehab,
	Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Yunke,

Thank you for the patch.

On Tue, Jun 28, 2022 at 04:57:05PM +0900, Yunke Cao wrote:
> Added documentation of V4L2_CID_UVC_REGION_OF_INTEREST_RECT and
> V4L2_CID_UVC_REGION_OF_INTEREST_AUTO.
> 
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
>  .../userspace-api/media/drivers/uvcvideo.rst  | 61 +++++++++++++++++++
>  1 file changed, 61 insertions(+)
> 
> diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> index a290f9fadae9..ee4c182aa274 100644
> --- a/Documentation/userspace-api/media/drivers/uvcvideo.rst
> +++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> @@ -181,6 +181,7 @@ Argument: struct uvc_xu_control_mapping
>  	UVC_CTRL_DATA_TYPE_BOOLEAN	Boolean
>  	UVC_CTRL_DATA_TYPE_ENUM		Enumeration
>  	UVC_CTRL_DATA_TYPE_BITMASK	Bitmask
> +	UVC_CTRL_DATA_TYPE_RECT		Rectangular area
>  
>  
>  UVCIOC_CTRL_QUERY - Query a UVC XU control
> @@ -255,3 +256,63 @@ Argument: struct uvc_xu_control_query
>  	__u8	query		Request code to send to the device
>  	__u16	size		Control data size (in bytes)
>  	__u8	*data		Control value
> +

You can add a second blank line here, as above the "Extension Unit (XU)
support" title.

> +Private V4L2 controls

"Driver-specific V4L2 controls" would be better, those controls are not
private.

> +---------------------
> +
> +A few UVC specific V4L2 control IDs are listed below.

s/UVC specific/UVC-specific/

But I'd write

The uvcvideo driver implements the following UVC-specific controls:

> +
> +``V4L2_CID_UVC_REGION_OF_INTEREST_RECT (struct)``
> +	This control determines the region of interest (ROI). ROI is an

s/is an/is a/

> +	rectangular area represented by a struct :c:type:`v4l2_rect`. The
> +	rectangle is in global sensor coordinates and pixel units. It is
> +	independent of the field of view, not impacted by any cropping or
> +	scaling.
> +
> +	Use ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` to query
> +	the range of rectangle sizes. For example, a device can have a minimum
> +	ROI rectangle of 1x1@0x0 and a maximum of 640x480@0x0.

Minimum and maximum values for rectangles are ill-defined. Are the top
and left coordinates always 0 ? If so that should be documented.

> +
> +	Setting a ROI allows the camera to optimize the capture for the region.
> +	The value of ``V4L2_CID_REGION_OF_INTEREST_AUTO`` control determines
> +	the detailed behavior.
> +
> +
> +``V4L2_CID_UVC_REGION_OF_INTEREST_AUTO (bitmask)``
> +	This determines which, if any, on board features should track to the
> +	Region of Interest specified by the current value of
> +	``V4L2_CID_UVD__REGION_OF_INTEREST_RECT``.
> +
> +	Max value is a mask indicating all supported Auto
> +	Controls.

No need to wrap this line.

> +
> +.. flat-table::
> +    :header-rows:  0
> +    :stub-columns: 0
> +
> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_EXPOSURE``

s/V4L2/V4L2_UVC/

Same below.

> +      - Setting this to true enables automatic exposure time for the specified

s/exposure time/exposure/ (as it can also include gain)

> +	region.

It's not that it enables automatic exposure for the specified region, is
that it causes the automatic exposure to track the region of interest
instead of the whole image. Same below.

> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_IRIS``
> +      - Setting this to true enables automatic iris aperture for the specified
> +	region.
> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_WHITE_BALANCE``
> +      - Setting this to true enables automatic white balance adjustment for the
> +	specified region.
> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_FOCUS``
> +      - Setting this to true enables automatic focus adjustment for the
> +	specified region.
> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_FACE_DETECT``
> +      - Setting this to true enables automatic face detection for the
> +	specified region.
> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_DETECT_AND_TRACK``
> +      - Setting this to true enables automatic face detection and tracking. The
> +	current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
> +	the driver.
> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_IMAGE_STABILIZATION``
> +      - Setting this to true enables automatic image stabilization. The
> +	current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
> +	the driver.

How so ?

> +    * - ``V4L2_REGION_OF_INTEREST_AUTO_HIGHER_QUALITY``
> +      - Setting this to true enables automatically capture the specified region
> +	with higher quality if possible.

Could you please clarify this one ? I'm not sure to understand what it
means.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 4/7] media: uvcvideo: implement UVC v1.5 ROI
  2022-06-28  7:57 ` [PATCH v7 4/7] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
  2022-06-30  6:42   ` Ricardo Ribalda
@ 2022-08-24 20:36   ` Laurent Pinchart
  2022-08-29  5:48     ` Yunke Cao
  1 sibling, 1 reply; 32+ messages in thread
From: Laurent Pinchart @ 2022-08-24 20:36 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Hans Verkuil, Nicolas Dufresne, Mauro Carvalho Chehab,
	Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Yunke,

Thank you for the patch.

On Tue, Jun 28, 2022 at 04:57:02PM +0900, Yunke Cao wrote:
> Implement support for ROI as described in UVC 1.5:
> 4.2.2.1.20 Digital Region of Interest (ROI) Control
> 
> ROI control is implemented using V4L2 control API as
> two uvc-specific controls:

s/uvc/UVC/

> V4L2_CID_UVC_REGION_OF_INTEREST_RECT and
> V4L2_CID_UVC_REGION_OF_INTEREST_AUTO.
> 
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
>  drivers/media/usb/uvc/uvc_ctrl.c   | 146 ++++++++++++++++++++++++++++-
>  drivers/media/usb/uvc/uvc_v4l2.c   |   5 +-
>  drivers/media/usb/uvc/uvcvideo.h   |   7 ++
>  include/uapi/linux/usb/video.h     |   1 +
>  include/uapi/linux/uvcvideo.h      |  13 +++
>  include/uapi/linux/v4l2-controls.h |   8 ++
>  6 files changed, 178 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index 508ee04afbcd..00482269233a 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -355,6 +355,23 @@ static const struct uvc_control_info uvc_ctrls[] = {
>  		.flags		= UVC_CTRL_FLAG_GET_CUR
>  				| UVC_CTRL_FLAG_AUTO_UPDATE,
>  	},
> +	/* UVC_CTRL_FLAG_AUTO_UPDATE is needed because the RoI may get updated

	/*
	 * UVC_CTRL_...

> +	 * by sensors.
> +	 * "This RoI should be the same as specified in most recent SET_CUR
> +	 * except in the case where the ‘Auto Detect and Track’ and/or
> +	 * ‘Image Stabilization’ bit have been set."
> +	 * 4.2.2.1.20 Digital Region of Interest (ROI) Control
> +	 */
> +	{
> +		.entity		= UVC_GUID_UVC_CAMERA,
> +		.selector	= UVC_CT_REGION_OF_INTEREST_CONTROL,
> +		.index		= 21,
> +		.size		= 10,
> +		.flags		= UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
> +				| UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
> +				| UVC_CTRL_FLAG_GET_DEF
> +				| UVC_CTRL_FLAG_AUTO_UPDATE,
> +	},
>  };
>  
>  static const u32 uvc_control_classes[] = {
> @@ -429,6 +446,64 @@ static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping,
>  	data[first+1] = min_t(int, abs(value), 0xff);
>  }
>  
> +static void uvc_to_v4l2_rect(struct v4l2_rect *v4l2_rect,
> +			     const struct uvc_rect *uvc_rect)
> +{
> +	v4l2_rect->top = uvc_rect->top;
> +	v4l2_rect->left = uvc_rect->left;
> +	v4l2_rect->height = uvc_rect->bottom - uvc_rect->top + 1;
> +	v4l2_rect->width = uvc_rect->right - uvc_rect->left + 1;

What happens if the device sends a bogus value with top > bottom or left
> right ?

> +}
> +
> +static int v4l2_to_uvc_rect(struct uvc_rect *uvc_rect,
> +			    const struct v4l2_rect *v4l2_rect)
> +{
> +	// Safely converts s32 and u32 to u16.

C-style comments.

> +	if (v4l2_rect->top > U16_MAX || v4l2_rect->top < 0 ||
> +	    v4l2_rect->left > U16_MAX || v4l2_rect->left < 0 ||
> +	    v4l2_rect->height > U16_MAX || v4l2_rect->height == 0 ||
> +	    v4l2_rect->width > U16_MAX || v4l2_rect->width == 0 ||
> +	    v4l2_rect->height + v4l2_rect->top - 1 > U16_MAX ||
> +	    v4l2_rect->width + v4l2_rect->left - 1 > U16_MAX)
> +		return -ERANGE;

Doesn't V4L2 adjust the control value instead ?

> +
> +	uvc_rect->top = v4l2_rect->top;
> +	uvc_rect->left = v4l2_rect->left;
> +	uvc_rect->bottom = v4l2_rect->height + v4l2_rect->top - 1;
> +	uvc_rect->right = v4l2_rect->width + v4l2_rect->left - 1;
> +	return 0;
> +}
> +
> +static int uvc_get_array_rect(struct uvc_control_mapping *mapping,
> +			      const u8 *data,  u8 *data_out)
> +{
> +	struct uvc_rect *uvc_rect;
> +
> +	if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> +		return -EINVAL;
> +	if (WARN_ON(mapping->size / 8 != sizeof(struct uvc_rect)))
> +		return -EINVAL;
> +

These checks should go to mapping creation time.

> +	uvc_rect = (struct uvc_rect *)(data + mapping->offset / 8);

If the offset happens to not be a multiple of 2 bytes this will cause
unaligned accesses on some platforms.

> +	uvc_to_v4l2_rect((struct v4l2_rect *)data_out, uvc_rect);

Same here if not aligned on 4 bytes.

> +
> +	return 0;
> +}
> +
> +static int uvc_set_array_rect(struct uvc_control_mapping *mapping,
> +			      const u8 *data_in, u8 *data)
> +{
> +	struct uvc_rect *uvc_rect;
> +
> +	if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> +		return -EINVAL;
> +	if (WARN_ON(mapping->size / 8 != sizeof(struct uvc_rect)))
> +		return -EINVAL;
> +

Same here.

> +	uvc_rect = (struct uvc_rect *)(data + mapping->offset / 8);
> +	return v4l2_to_uvc_rect(uvc_rect, (struct v4l2_rect *)data_in);
> +}
> +
>  static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  	{
>  		.id		= V4L2_CID_BRIGHTNESS,
> @@ -728,6 +803,26 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
>  		.v4l2_type	= V4L2_CTRL_TYPE_BOOLEAN,
>  		.data_type	= UVC_CTRL_DATA_TYPE_BOOLEAN,
>  	},
> +	{
> +		.id		= V4L2_CID_UVC_REGION_OF_INTEREST_RECT,
> +		.entity		= UVC_GUID_UVC_CAMERA,
> +		.selector	= UVC_CT_REGION_OF_INTEREST_CONTROL,
> +		.size		= 64,

sizeof(struct uvc_rect)

> +		.offset		= 0,
> +		.v4l2_type	= V4L2_CTRL_TYPE_RECT,
> +		.data_type	= UVC_CTRL_DATA_TYPE_RECT,
> +		.get_array	= uvc_get_array_rect,
> +		.set_array	= uvc_set_array_rect,
> +	},
> +	{
> +		.id		= V4L2_CID_UVC_REGION_OF_INTEREST_AUTO,
> +		.entity		= UVC_GUID_UVC_CAMERA,
> +		.selector	= UVC_CT_REGION_OF_INTEREST_CONTROL,
> +		.size		= 16,
> +		.offset		= 64,
> +		.v4l2_type	= V4L2_CTRL_TYPE_BITMASK,
> +		.data_type	= UVC_CTRL_DATA_TYPE_BITMASK,
> +	},
>  };
>  
>  /* ------------------------------------------------------------------------
> @@ -1061,7 +1156,16 @@ static int __uvc_ctrl_get_compound_to_user(struct uvc_control_mapping *mapping,
>  	if (WARN_ON(!mapping->size % 8))
>  		return -EINVAL;
>  
> -	size = mapping->size / 8;
> +	switch (mapping->v4l2_type) {
> +	case V4L2_CTRL_TYPE_RECT:
> +		size = sizeof(struct v4l2_rect);

Oohhh that sounds fragile. I'm worried we may be using mapping->size as
the V4L2 control size somewhere else.

It would be safer to split the size field of uvc_control_mapping into a
v4l2_size and a uvc_size (or possibly data_size to match the v4l2_type
and data_type names) in a patch before this. This will force reviewing
all the users of the size field to check which size they need.

> +		break;
> +
> +	default:
> +		size = mapping->size / 8;
> +		break;
> +	}
> +
>  	if (xctrl->size < size) {
>  		xctrl->size = size;
>  		return -ENOSPC;
> @@ -2578,6 +2682,40 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev,
>  	}
>  }
>  
> +static int uvc_ctrl_init_roi(struct uvc_device *dev, struct uvc_control *ctrl)
> +{
> +	int ret;
> +
> +	ret = uvc_query_ctrl(dev, UVC_GET_DEF, ctrl->entity->id, dev->intfnum,
> +			     UVC_CT_REGION_OF_INTEREST_CONTROL,
> +			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
> +			     ctrl->info.size);
> +	if (ret)
> +		goto out;
> +
> +	/*
> +	 * Most firmwares have wrong GET_CURRENT configuration. E.g. it's

s/GET_CURRENT/GET_CUR/

> +	 * below GET_MIN, or have rectangle coordinates mixed up. This
> +	 * causes problems sometimes, because we are unable to set
> +	 * auto-controls value without first setting ROI rectangle to
> +	 * valid configuration.
> +	 *
> +	 * We expect that default configuration is always correct and
> +	 * is within the GET_MIN / GET_MAX boundaries.

A dangerous expectation to make given the quality of most UVC camera
firmwares...

> +	 *
> +	 * Set current ROI configuration to GET_DEF, so that we will
> +	 * always have properly configured ROI.
> +	 */
> +	ret = uvc_query_ctrl(dev, UVC_SET_CUR, 1, dev->intfnum,
> +			     UVC_CT_REGION_OF_INTEREST_CONTROL,
> +			     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
> +			     ctrl->info.size);
> +out:
> +	if (ret)
> +		dev_err(&dev->udev->dev, "Failed to fixup ROI (%d).\n", ret);

s/(%d)./(%d)/

> +	return ret;
> +}
> +
>  /*
>   * Add control information and hardcoded stock control mappings to the given
>   * device.
> @@ -2590,6 +2728,7 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
>  	const struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
>  	const struct uvc_control_mapping *mend =
>  		mapping + ARRAY_SIZE(uvc_ctrl_mappings);
> +	const u8 camera_entity[16] = UVC_GUID_UVC_CAMERA;
>  
>  	/* XU controls initialization requires querying the device for control
>  	 * information. As some buggy UVC devices will crash when queried
> @@ -2610,6 +2749,11 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
>  			 * GET_INFO on standard controls.
>  			 */
>  			uvc_ctrl_get_flags(chain->dev, ctrl, &ctrl->info);
> +
> +			if (ctrl->info.selector ==
> +				UVC_CT_REGION_OF_INTEREST_CONTROL &&
> +			    uvc_entity_match_guid(ctrl->entity, camera_entity))
> +				uvc_ctrl_init_roi(chain->dev, ctrl);

Should we instead add an init operation to the uvc_control_info
structure ?

>  			break;
>  		 }
>  	}
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index 0366d05895a9..aad61af36271 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -998,7 +998,10 @@ static int uvc_ioctl_query_ext_ctrl(struct file *file, void *fh,
>  	qec->step = qc.step;
>  	qec->default_value = qc.default_value;
>  	qec->flags = qc.flags;
> -	qec->elem_size = 4;
> +	if (qc.type == V4L2_CTRL_TYPE_RECT)
> +		qec->elem_size = sizeof(struct v4l2_rect);
> +	else
> +		qec->elem_size = 4;
>  	qec->elems = 1;
>  	qec->nr_of_dims = 0;
>  	memset(qec->dims, 0, sizeof(qec->dims));
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index 2f9b75faae83..9ff95bbad251 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -431,6 +431,13 @@ struct uvc_streaming_header {
>  	u8 bTriggerUsage;
>  };
>  
> +struct uvc_rect {
> +	u16 top;
> +	u16 left;
> +	u16 bottom;
> +	u16 right;
> +} __packed;
> +
>  enum uvc_buffer_state {
>  	UVC_BUF_STATE_IDLE	= 0,
>  	UVC_BUF_STATE_QUEUED	= 1,
> diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h
> index bfdae12cdacf..9076a444758a 100644
> --- a/include/uapi/linux/usb/video.h
> +++ b/include/uapi/linux/usb/video.h
> @@ -104,6 +104,7 @@
>  #define UVC_CT_ROLL_ABSOLUTE_CONTROL			0x0f
>  #define UVC_CT_ROLL_RELATIVE_CONTROL			0x10
>  #define UVC_CT_PRIVACY_CONTROL				0x11
> +#define UVC_CT_REGION_OF_INTEREST_CONTROL		0x14
>  
>  /* A.9.5. Processing Unit Control Selectors */
>  #define UVC_PU_CONTROL_UNDEFINED			0x00
> diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h
> index 8288137387c0..e2feee15cb1b 100644
> --- a/include/uapi/linux/uvcvideo.h
> +++ b/include/uapi/linux/uvcvideo.h
> @@ -16,6 +16,7 @@
>  #define UVC_CTRL_DATA_TYPE_BOOLEAN	3
>  #define UVC_CTRL_DATA_TYPE_ENUM		4
>  #define UVC_CTRL_DATA_TYPE_BITMASK	5
> +#define UVC_CTRL_DATA_TYPE_RECT		6
>  
>  /* Control flags */
>  #define UVC_CTRL_FLAG_SET_CUR		(1 << 0)
> @@ -36,6 +37,18 @@
>  	 UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES | \
>  	 UVC_CTRL_FLAG_GET_DEF)
>  
> +/* V4L2 private controls */
> +#define V4L2_CID_UVC_REGION_OF_INTEREST_RECT	(V4L2_CID_CAMERA_UVC_BASE + 1)
> +#define V4L2_CID_UVC_REGION_OF_INTEREST_AUTO	(V4L2_CID_CAMERA_UVC_BASE + 2)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_EXPOSURE		(1 << 0)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_IRIS			(1 << 1)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_WHITE_BALANCE		(1 << 2)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_FOCUS			(1 << 3)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_FACE_DETECT		(1 << 4)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_DETECT_AND_TRACK	(1 << 5)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_IMAGE_STABILIZATION	(1 << 6)
> +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_HIGHER_QUALITY		(1 << 7)
> +
>  struct uvc_menu_info {
>  	__u32 value;
>  	__u8 name[32];
> diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
> index dfff69ed88f7..d85be46e46f2 100644
> --- a/include/uapi/linux/v4l2-controls.h
> +++ b/include/uapi/linux/v4l2-controls.h
> @@ -1013,6 +1013,14 @@ enum v4l2_auto_focus_range {
>  
>  #define V4L2_CID_CAMERA_SENSOR_ROTATION		(V4L2_CID_CAMERA_CLASS_BASE+35)
>  
> +/* CAMERA-class private control IDs */
> +

Why doesn't this go in the driver-specific controls range ?

> +/*
> + * The base for the uvc driver controls. See linux/uvcvideo.h for the list
> + * of controls. We reserve 16 controls for this driver.

I'd reserve more just in case, UVC 1.5 has quite a few controls that may
not be exposed through standard V4L2 controls, especially for encoding
units.

> + */
> +#define V4L2_CID_CAMERA_UVC_BASE		(V4L2_CID_CAMERA_CLASS_BASE + 0x1000)
> +
>  /* FM Modulator class control IDs */
>  
>  #define V4L2_CID_FM_TX_CLASS_BASE		(V4L2_CTRL_CLASS_FM_TX | 0x900)

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 1/7] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
  2022-08-24  8:50   ` Hans Verkuil
  2022-08-24  8:51     ` Hans Verkuil
@ 2022-08-25  6:15     ` Yunke Cao
  1 sibling, 0 replies; 32+ messages in thread
From: Yunke Cao @ 2022-08-25  6:15 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Laurent Pinchart, Nicolas Dufresne, Mauro Carvalho Chehab,
	Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Hans, Hi Laurent,

Thank you for the review!

On Wed, Aug 24, 2022 at 5:50 PM Hans Verkuil <hverkuil-cisco@xs4all.nl> wrote:
>
> Hi Yunke,
>
> You will need to rebase this since some of the v4l2-ctrl internals
> have changed.
>

Thanks, I will rebase for the next version.

> On 28/06/2022 09:56, Yunke Cao wrote:
> > Add p_rect to struct v4l2_ext_control with basic support in
> > v4l2-ctrls.
> >
> > Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
> > Signed-off-by: Yunke Cao <yunkec@google.com>
> > ---
> >  .../media/v4l/vidioc-g-ext-ctrls.rst          |  4 ++++
> >  .../media/videodev2.h.rst.exceptions          |  1 +
> >  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 20 +++++++++++++++++++
> >  include/media/v4l2-ctrls.h                    |  2 ++
> >  include/uapi/linux/videodev2.h                |  2 ++
> >  5 files changed, 29 insertions(+)
> >
> > diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > index 29971a45a2d4..7473baa4e977 100644
> > --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > @@ -189,6 +189,10 @@ still cause this situation.
> >        - ``p_area``
> >        - A pointer to a struct :c:type:`v4l2_area`. Valid if this control is
> >          of type ``V4L2_CTRL_TYPE_AREA``.
> > +    * - struct :c:type:`v4l2_rect` *
> > +      - ``p_rect``
> > +      - A pointer to a struct :c:type:`v4l2_rect`. Valid if this control is
> > +        of type ``V4L2_CTRL_TYPE_RECT``.
> >      * - struct :c:type:`v4l2_ctrl_h264_sps` *
> >        - ``p_h264_sps``
> >        - A pointer to a struct :c:type:`v4l2_ctrl_h264_sps`. Valid if this control is
>
> You also need to update Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst.
>

Ah, I missed that. Will add it in v8.

> > diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > index 9cbb7a0c354a..7b423475281d 100644
> > --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > @@ -147,6 +147,7 @@ replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type`
> >  replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type`
> >  replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
> >  replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type`
> > +replace symbol V4L2_CTRL_TYPE_RECT :c:type:`v4l2_ctrl_type`
> >  replace symbol V4L2_CTRL_TYPE_FWHT_PARAMS :c:type:`v4l2_ctrl_type`
> >  replace symbol V4L2_CTRL_TYPE_VP8_FRAME :c:type:`v4l2_ctrl_type`
> >  replace symbol V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR :c:type:`v4l2_ctrl_type`
> > diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > index 949c1884d9c1..35d43ba650db 100644
> > --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > @@ -84,6 +84,11 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx,
> >               return ptr1.p_u16[idx] == ptr2.p_u16[idx];
> >       case V4L2_CTRL_TYPE_U32:
> >               return ptr1.p_u32[idx] == ptr2.p_u32[idx];
> > +     case V4L2_CTRL_TYPE_RECT:
> > +             return ptr1.p_rect->top == ptr2.p_rect->top &&
> > +                    ptr1.p_rect->left == ptr2.p_rect->left &&
> > +                    ptr1.p_rect->height == ptr2.p_rect->height &&
> > +                    ptr1.p_rect->width == ptr2.p_rect->width;
>
> You don't need to do anything here, it will fallback to a memcmp, and
> that's fine for struct v4l2_rect.
>

Thanks! Will remove it in v8.

Best,
Yunke

> >       default:
> >               if (ctrl->is_int)
> >                       return ptr1.p_s32[idx] == ptr2.p_s32[idx];
> > @@ -307,6 +312,11 @@ static void std_log(const struct v4l2_ctrl *ctrl)
> >       case V4L2_CTRL_TYPE_VP9_FRAME:
> >               pr_cont("VP9_FRAME");
> >               break;
> > +     case V4L2_CTRL_TYPE_RECT:
> > +             pr_cont("%ux%u@%dx%d",
> > +                     ptr.p_rect->width, ptr.p_rect->height,
> > +                     ptr.p_rect->left, ptr.p_rect->top);
> > +             break;
> >       default:
> >               pr_cont("unknown type %d", ctrl->type);
> >               break;
> > @@ -525,6 +535,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> >       struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
> >       struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params;
> >       struct v4l2_area *area;
> > +     struct v4l2_rect *rect;
> >       void *p = ptr.p + idx * ctrl->elem_size;
> >       unsigned int i;
> >
> > @@ -888,6 +899,12 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> >                       return -EINVAL;
> >               break;
> >
> > +     case V4L2_CTRL_TYPE_RECT:
> > +             rect = p;
> > +             if (!rect->width || !rect->height)
> > +                     return -EINVAL;
> > +             break;
> > +
> >       default:
> >               return -EINVAL;
> >       }
> > @@ -1455,6 +1472,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >       case V4L2_CTRL_TYPE_AREA:
> >               elem_size = sizeof(struct v4l2_area);
> >               break;
> > +     case V4L2_CTRL_TYPE_RECT:
> > +             elem_size = sizeof(struct v4l2_rect);
> > +             break;
> >       default:
> >               if (type < V4L2_CTRL_COMPOUND_TYPES)
> >                       elem_size = sizeof(s32);
> > diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> > index b3ce438f1329..919e104de50b 100644
> > --- a/include/media/v4l2-ctrls.h
> > +++ b/include/media/v4l2-ctrls.h
> > @@ -58,6 +58,7 @@ struct video_device;
> >   * @p_hdr10_cll:             Pointer to an HDR10 Content Light Level structure.
> >   * @p_hdr10_mastering:               Pointer to an HDR10 Mastering Display structure.
> >   * @p_area:                  Pointer to an area.
> > + * @p_rect:                  Pointer to a rectangle.
> >   * @p:                               Pointer to a compound value.
> >   * @p_const:                 Pointer to a constant compound value.
> >   */
> > @@ -87,6 +88,7 @@ union v4l2_ctrl_ptr {
> >       struct v4l2_ctrl_hdr10_cll_info *p_hdr10_cll;
> >       struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
> >       struct v4l2_area *p_area;
> > +     struct v4l2_rect *p_rect;
> >       void *p;
> >       const void *p_const;
> >  };
> > diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> > index 343b95107fce..2e36bb610ea6 100644
> > --- a/include/uapi/linux/videodev2.h
> > +++ b/include/uapi/linux/videodev2.h
> > @@ -1754,6 +1754,7 @@ struct v4l2_ext_control {
> >               __u16 __user *p_u16;
> >               __u32 __user *p_u32;
> >               struct v4l2_area __user *p_area;
> > +             struct v4l2_rect __user *p_rect;
> >               struct v4l2_ctrl_h264_sps __user *p_h264_sps;
> >               struct v4l2_ctrl_h264_pps *p_h264_pps;
> >               struct v4l2_ctrl_h264_scaling_matrix __user *p_h264_scaling_matrix;
> > @@ -1813,6 +1814,7 @@ enum v4l2_ctrl_type {
> >       V4L2_CTRL_TYPE_U16           = 0x0101,
> >       V4L2_CTRL_TYPE_U32           = 0x0102,
> >       V4L2_CTRL_TYPE_AREA          = 0x0106,
> > +     V4L2_CTRL_TYPE_RECT          = 0x0107,
> >
> >       V4L2_CTRL_TYPE_HDR10_CLL_INFO           = 0x0110,
> >       V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY  = 0x0111,
>
> Regards,
>
>         Hans

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 3/7] media: uvcvideo: Add support for compound controls
  2022-08-24 19:56   ` Laurent Pinchart
@ 2022-08-29  5:39     ` Yunke Cao
  0 siblings, 0 replies; 32+ messages in thread
From: Yunke Cao @ 2022-08-29  5:39 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Hans Verkuil, Nicolas Dufresne, Mauro Carvalho Chehab,
	Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Laurent,

Thanks for the review!

On Thu, Aug 25, 2022 at 4:56 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Yunke,
>
> Thank you for the patch.
>
> On Tue, Jun 28, 2022 at 04:57:01PM +0900, Yunke Cao wrote:
> > Supports getting/setting current value.
> > Supports getting default value.
> > Handles V4L2_CTRL_FLAG_NEXT_COMPOUND.
> >
> > Signed-off-by: Yunke Cao <yunkec@google.com>
> > ---
> >  drivers/media/usb/uvc/uvc_ctrl.c | 279 ++++++++++++++++++++++++++-----
> >  drivers/media/usb/uvc/uvcvideo.h |   4 +
> >  2 files changed, 238 insertions(+), 45 deletions(-)
> >
> > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > index 772d9d28a520..508ee04afbcd 100644
> > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > @@ -815,6 +815,34 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
> >       }
> >  }
> >
> > +/* Extract the byte array specified by mapping->offset and mapping->size
> > + * stored at 'data' to the output array 'data_out'.
> > + */
>
> Please use the
>
> /*
>  * multi
>  * line
>  */
>
> style instead of
>
> /* multi
>  * line
>  */
>
> The uvcvideo driver has recently switched to the former in commit
> 699b9a86a3f0 ("media: uvcvideo: Fix comment blocks style").
>
> > +static int uvc_get_array(struct uvc_control_mapping *mapping, const u8 *data,
> > +                      u8 *data_out)
> > +{
> > +     // Only supports byte-aligned data.
>
> And no C++-style comments either please.
>

Will fix the comments style in the next version.

> > +     if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> > +             return -EINVAL;
> > +
> > +     memcpy(data_out, data + mapping->offset / 8, mapping->size / 8);
> > +     return 0;
> > +}
> > +
> > +/* Copy the byte array 'data_in' to the destination specified by mapping->offset
> > + * and mapping->size stored at 'data'.
> > + */
> > +static int uvc_set_array(struct uvc_control_mapping *mapping, const u8 *data_in,
> > +                      u8 *data)
> > +{
> > +     // Only supports byte-aligned data.
> > +     if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> > +             return -EINVAL;
> > +
> > +     memcpy(data + mapping->offset / 8, data_in, mapping->size / 8);
> > +     return 0;
> > +}
> > +
>
> Could you add here a
>
> static bool
> uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
> {
>         return mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES;
> }
>
> and use it below ? That would be clearer I think.
>
> >  /* ------------------------------------------------------------------------
> >   * Terminal and unit management
> >   */
> > @@ -831,7 +859,7 @@ static int uvc_entity_match_guid(const struct uvc_entity *entity,
> >
> >  static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
> >       struct uvc_control_mapping **mapping, struct uvc_control **control,
> > -     int next)
> > +     int next, int next_compound)
> >  {
> >       struct uvc_control *ctrl;
> >       struct uvc_control_mapping *map;
> > @@ -846,14 +874,18 @@ static void __uvc_find_control(struct uvc_entity *entity, u32 v4l2_id,
> >                       continue;
> >
> >               list_for_each_entry(map, &ctrl->info.mappings, list) {
> > -                     if ((map->id == v4l2_id) && !next) {
> > +                     if (map->id == v4l2_id && !next && !next_compound) {
> >                               *control = ctrl;
> >                               *mapping = map;
> >                               return;
> >                       }
> >
> >                       if ((*mapping == NULL || (*mapping)->id > map->id) &&
> > -                         (map->id > v4l2_id) && next) {
> > +                         (map->id > v4l2_id) &&
> > +                         ((map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES &&
> > +                           next) ||
> > +                          (map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES &&
> > +                           next_compound))) {
> >                               *control = ctrl;
> >                               *mapping = map;
> >                       }
>
> This could become for instance
>
>                         if ((*mapping == NULL || (*mapping)->id > map->id) &&
>                             (map->id > v4l2_id) &&
>                             ((!uvc_ctrl_mapping_is_compound(map) && next) ||
>                              (uvc_ctrl_mapping_is_compound(map) && next_compound))) {
>                                 *control = ctrl;
>                                 *mapping = map;
>                         }
>

Sure! That does look much better.

> > @@ -867,6 +899,7 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
> >       struct uvc_control *ctrl = NULL;
> >       struct uvc_entity *entity;
> >       int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;
> > +     int next_compound = v4l2_id & V4L2_CTRL_FLAG_NEXT_COMPOUND;
> >
> >       *mapping = NULL;
> >
> > @@ -875,12 +908,13 @@ static struct uvc_control *uvc_find_control(struct uvc_video_chain *chain,
> >
> >       /* Find the control. */
> >       list_for_each_entry(entity, &chain->entities, chain) {
> > -             __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
> > -             if (ctrl && !next)
> > +             __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next,
> > +                                next_compound);
> > +             if (ctrl && !next && !next_compound)
> >                       return ctrl;
> >       }
> >
> > -     if (ctrl == NULL && !next)
> > +     if (!ctrl && !next && !next_compound)
> >               uvc_dbg(chain->dev, CONTROL, "Control 0x%08x not found\n",
> >                       v4l2_id);
> >
> > @@ -943,6 +977,39 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
> >       return 0;
> >  }
> >
> > +static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain,
> > +                            struct uvc_control *ctrl)
> > +{
>
> I would declare
>
>         u8 data;
>
> here, and
>
> > +     int ret = 0;
> > +
> > +     if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
> > +             return -EACCES;
> > +
> > +     if (ctrl->loaded)
> > +             return 0;
> > +
>
> set
>
>         data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT);
>
> here, and
>
> > +     if (ctrl->entity->get_cur) {
> > +             ret = ctrl->entity->get_cur(chain->dev,
> > +                     ctrl->entity,
> > +                     ctrl->info.selector,
> > +                     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> > +                     ctrl->info.size);
>
> improve indentation here
>
>                 ret = ctrl->entity->get_cur(chain->dev, ctrl->entity,
>                                             ctrl->info.selector, data,
>                                             ctrl->info.size);
>
>
> > +     } else {
>
> and also use data here
>
> > +             ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
> > +                                  ctrl->entity->id, chain->dev->intfnum,
> > +                                  ctrl->info.selector,
> > +                                  uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> > +                                  ctrl->info.size);
>
>                 ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
>                                      ctrl->entity->id, chain->dev->intfnum,
>                                      ctrl->info.selector, data,
>                                      ctrl->info.size);
>
> > +     }
> > +
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     ctrl->loaded = 1;
> > +
> > +     return ret;
> > +}
> > +
> >  static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping,
> >                               const u8 *data)
> >  {
> > @@ -963,35 +1030,19 @@ static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping,
> >       return value;
> >  }
> >
> > -static int __uvc_ctrl_get(struct uvc_video_chain *chain,
> > -     struct uvc_control *ctrl, struct uvc_control_mapping *mapping,
> > -     s32 *value)
> > +static int __uvc_ctrl_get_std(struct uvc_video_chain *chain,
> > +                           struct uvc_control *ctrl,
> > +                           struct uvc_control_mapping *mapping,
> > +                           s32 *value)
> >  {
> >       int ret;
> >
> > -     if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
> > -             return -EACCES;
> > -
> > -     if (!ctrl->loaded) {
> > -             if (ctrl->entity->get_cur) {
> > -                     ret = ctrl->entity->get_cur(chain->dev,
> > -                             ctrl->entity,
> > -                             ctrl->info.selector,
> > -                             uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> > -                             ctrl->info.size);
> > -             } else {
> > -                     ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR,
> > -                             ctrl->entity->id,
> > -                             chain->dev->intfnum,
> > -                             ctrl->info.selector,
> > -                             uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
> > -                             ctrl->info.size);
> > -             }
> > -             if (ret < 0)
> > -                     return ret;
> > +     if (mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> > +             return -EINVAL;
> >
> > -             ctrl->loaded = 1;
> > -     }
> > +     ret = __uvc_ctrl_load_cur(chain, ctrl);
> > +     if (ret < 0)
> > +             return ret;
> >
> >       *value = __uvc_ctrl_get_value(mapping,
> >                               uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> > @@ -999,6 +1050,57 @@ static int __uvc_ctrl_get(struct uvc_video_chain *chain,
> >       return 0;
> >  }
> >
> > +static int __uvc_ctrl_get_compound_to_user(struct uvc_control_mapping *mapping,
>
> I don't like this name much. One option would be to rename the
> __uvc_ctrl_get_compound() function below to
> __uvc_ctrl_get_compound_cur() and call this function
> __uvc_ctrl_get_compound(). Better options are likely possible.
>
> > +                                        struct uvc_control *ctrl,
> > +                                        int id,
> > +                                        struct v4l2_ext_control *xctrl)
> > +{
> > +     int ret, size;
>
> size is never negative, make it an unsigned int.
>
> > +     u8 *data;
> > +
> > +     if (WARN_ON(!mapping->size % 8))
> > +             return -EINVAL;
>
> This duplicates a check in the get_array function. Let's move all those
> checks to the function that adds mappings.
>
> > +
> > +     size = mapping->size / 8;
> > +     if (xctrl->size < size) {
> > +             xctrl->size = size;
> > +             return -ENOSPC;
> > +     }
> > +
> > +     data = kmalloc(size, GFP_KERNEL);
> > +     if (!data)
> > +             return -ENOMEM;
> > +
> > +     ret = mapping->get_array(mapping, uvc_ctrl_data(ctrl, id), data);
> > +     if (ret < 0)
> > +             goto out;
> > +
> > +     ret = copy_to_user(xctrl->ptr, data, size) ? -EFAULT : 0;
> > +
> > +out:
> > +     kfree(data);
> > +     return ret;
> > +}
> > +
> > +static int __uvc_ctrl_get_compound(struct uvc_video_chain *chain,
> > +                                struct uvc_control *ctrl,
> > +                                struct uvc_control_mapping *mapping,
> > +                                struct v4l2_ext_control *xctrl)
> > +{
> > +     int ret;
> > +
> > +     if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> > +             return -EINVAL;
> > +
> > +     ret = __uvc_ctrl_load_cur(chain, ctrl);
> > +     if (ret < 0)
> > +             return ret;
> > +
> > +     return __uvc_ctrl_get_compound_to_user(mapping, ctrl,
> > +                                            UVC_CTRL_DATA_CURRENT,
> > +                                            xctrl);
> > +}
> > +
> >  static int __uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
> >                                 u32 found_id)
> >  {
> > @@ -1102,10 +1204,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
> >
> >       if (mapping->master_id)
> >               __uvc_find_control(ctrl->entity, mapping->master_id,
> > -                                &master_map, &master_ctrl, 0);
> > +                                &master_map, &master_ctrl, 0, 0);
> >       if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
> > -             s32 val;
> > -             int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
> > +             int ret;
> > +             s32 val = 0;
>
> Move ret after val.
>
> > +
> > +             if (master_map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> > +                     return -EINVAL;
> > +
> > +             ret = __uvc_ctrl_get_std(chain, master_ctrl, master_map, &val);
> >               if (ret < 0)
> >                       return ret;
> >
> > @@ -1113,6 +1220,15 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
> >                               v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
> >       }
> >
> > +     if (v4l2_ctrl->type >= V4L2_CTRL_COMPOUND_TYPES) {
> > +             v4l2_ctrl->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD;
> > +             v4l2_ctrl->default_value = 0;
> > +             v4l2_ctrl->minimum = 0;
> > +             v4l2_ctrl->maximum = 0;
> > +             v4l2_ctrl->step = 0;
> > +             return 0;
> > +     }
> > +
> >       if (!ctrl->cached) {
> >               int ret = uvc_ctrl_populate_cache(chain, ctrl);
> >               if (ret < 0)
> > @@ -1346,11 +1462,12 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
> >       u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
> >       s32 val = 0;
> >
> > -     __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0);
> > +     __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0, 0);
> >       if (ctrl == NULL)
> >               return;
> >
> > -     if (__uvc_ctrl_get(chain, ctrl, mapping, &val) == 0)
> > +     if (mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES ||
> > +         __uvc_ctrl_get_std(chain, ctrl, mapping, &val) == 0)
> >               changes |= V4L2_EVENT_CTRL_CH_VALUE;
> >
> >       uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
> > @@ -1517,7 +1634,8 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
> >               u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
> >               s32 val = 0;
> >
> > -             if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0)
> > +             if (mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES ||
> > +                 __uvc_ctrl_get_std(handle->chain, ctrl, mapping, &val) == 0)
> >                       changes |= V4L2_EVENT_CTRL_CH_VALUE;
> >
> >               uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val,
> > @@ -1647,7 +1765,7 @@ static int uvc_ctrl_find_ctrl_idx(struct uvc_entity *entity,
> >
> >       for (i = 0; i < ctrls->count; i++) {
> >               __uvc_find_control(entity, ctrls->controls[i].id, &mapping,
> > -                                &ctrl_found, 0);
> > +                                &ctrl_found, 0, 0);
> >               if (uvc_control == ctrl_found)
> >                       return i;
> >       }
> > @@ -1694,11 +1812,14 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
> >       if (ctrl == NULL)
> >               return -EINVAL;
> >
> > -     return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
> > +     if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> > +             return __uvc_ctrl_get_std(chain, ctrl, mapping, &xctrl->value);
> > +     else
> > +             return __uvc_ctrl_get_compound(chain, ctrl, mapping, xctrl);
> >  }
> >
> > -int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> > -                    struct v4l2_ext_control *xctrl)
> > +int __uvc_ctrl_get_fixed_std(struct uvc_video_chain *chain,
> > +                          struct v4l2_ext_control *xctrl)
> >  {
> >       struct v4l2_queryctrl qc = { .id = xctrl->id };
> >       int ret = uvc_query_v4l2_ctrl(chain, &qc);
> > @@ -1710,6 +1831,56 @@ int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> >       return 0;
> >  }
> >
> > +int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> > +                    struct v4l2_ext_control *xctrl)
> > +{
> > +     struct uvc_control *ctrl;
> > +     struct uvc_control_mapping *mapping;
> > +     int ret;
> > +
> > +     if (__uvc_query_v4l2_class(chain, xctrl->id, 0) >= 0)
> > +             return -EACCES;
> > +
>
> This check comes a bit out of the blue, I think it belongs to another
> patch, with a commit message that clearly explain the rationale.
>
> > +     ctrl = uvc_find_control(chain, xctrl->id, &mapping);
> > +     if (!ctrl)
> > +             return -EINVAL;
>
> And here, you're duplicating the uvc_find_control() call from
> uvc_query_v4l2_ctrl(), called in __uvc_ctrl_get_fixed_std().
> Furthermore, you're not holding the ctrl_mutex lock here, which I think
> is wrong.
>

Thanks for catching this!
I'm thinking of fixing it by calling __uvc_query_v4l2_ctrl()
in __uvc_ctrl_get_fixed_std(). And acquire the ctrl_mutex lock here
in uvc_ctrl_get_fixed(). Does that sound right to you?

> I'd like to see a patch before this one that refactors what needs to be
> refactored first, and then the introduction of compound types support
> without any extra change like this. Splitting code out to
> __uvc_ctrl_load_cur() could also be moved to a preparatory patch to
> simplify this one, it's quite hard to review.
>

I see. For __uvc_ctrl_load_cur(), I guess I can cherry-pick
"[PATCH v4] media: uvcvideo: Use entity get_cur in uvc_ctrl_set"
before this.

I am trying to split the refactoring in 2/7. Should I introduce the
"get/set_std()" functions before this as well?

> > +
> > +     if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> > +             return __uvc_ctrl_get_fixed_std(chain, xctrl);
> > +
> > +     if (!ctrl->cached) {
> > +             ret = uvc_ctrl_populate_cache(chain, ctrl);
> > +             if (ret < 0)
> > +                     return ret;
> > +     }
> > +
> > +     return __uvc_ctrl_get_compound_to_user(mapping, ctrl, UVC_CTRL_DATA_DEF,
> > +                                            xctrl);
> > +}
> > +
> > +int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
> > +                         struct v4l2_ext_control *xctrl,
> > +                         struct uvc_control *ctrl)
> > +{
> > +     int ret;
> > +     u8 *data;
>
> Please move ret after data.
>
> > +
> > +     data = kmalloc(xctrl->size, GFP_KERNEL);
>
> A limit on the size is needed to avoid denial of service attacks.
>
> > +     if (!data)
> > +             return -ENOMEM;
> > +
> > +     ret = copy_from_user(data, xctrl->ptr, xctrl->size);
> > +     if (ret < 0)
> > +             goto out;
> > +
> > +     ret = mapping->set_array(mapping, data,
> > +                     uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
>
>         ret = mapping->set_array(mapping, data,
>                                  uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
>
> The V4L2 control model is that the kernel can update the value of a
> control if the requested value is out of range or otherwise not
> acceptable. You should here copy the data back to the user.

Ah okay. Will call "__uvc_ctrl_get_compound_to_user()" here.

>
> > +
> > +out:
> > +     kfree(data);
> > +     return ret;
> > +}
> > +
> >  int uvc_ctrl_set(struct uvc_fh *handle,
> >       struct v4l2_ext_control *xctrl)
> >  {
> > @@ -1820,8 +1991,14 @@ int uvc_ctrl_set(struct uvc_fh *handle,
> >                      ctrl->info.size);
> >       }
> >
> > -     mapping->set(mapping, value,
> > -             uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> > +     if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES) {
> > +             mapping->set(mapping, value,
> > +                          uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
> > +     } else {
> > +             ret = __uvc_ctrl_set_compound(mapping, xctrl, ctrl);
> > +             if (ret < 0)
> > +                     return ret;
> > +     }
> >
> >       if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
> >               ctrl->handle = handle;
> > @@ -2220,10 +2397,14 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
> >               return -ENOMEM;
> >       }
> >
> > -     if (map->get == NULL)
> > +     if (!map->get && map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> >               map->get = uvc_get_le_value;
> > -     if (map->set == NULL)
> > +     if (!map->set && map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> >               map->set = uvc_set_le_value;
> > +     if (!map->get_array && map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> > +             map->get_array = uvc_get_array;
> > +     if (!map->set_array && map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES)
> > +             map->set_array = uvc_set_array;
> >
> >       for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
> >               if (V4L2_CTRL_ID2WHICH(uvc_control_classes[i]) ==
> > @@ -2233,6 +2414,14 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
> >               }
> >       }
> >
> > +     if (map->v4l2_type < V4L2_CTRL_COMPOUND_TYPES &&
> > +         WARN_ON(!map->get || !map->set))
> > +             return -EINVAL;
> > +
> > +     if (map->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES &&
> > +         WARN_ON(!map->get_array || !map->set_array))
> > +             return -EINVAL;
> > +
>
> Can this happen, given that you set them above if they're null ?
>

It cannot happen. Will remove this in v8.

> >       list_add_tail(&map->list, &ctrl->info.mappings);
> >       uvc_dbg(chain->dev, CONTROL, "Adding mapping '%s' to control %pUl/%u\n",
> >               uvc_map_get_name(map), ctrl->info.entity,
> > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > index ba028ba7c34e..2f9b75faae83 100644
> > --- a/drivers/media/usb/uvc/uvcvideo.h
> > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > @@ -262,8 +262,12 @@ struct uvc_control_mapping {
> >
> >       s32 (*get)(struct uvc_control_mapping *mapping, u8 query,
> >                  const u8 *data);
> > +     int (*get_array)(struct uvc_control_mapping *mapping, const u8 *data,
> > +                      u8 *data_out);
> >       void (*set)(struct uvc_control_mapping *mapping, s32 value,
> >                   u8 *data);
> > +     int (*set_array)(struct uvc_control_mapping *mapping, const u8 *data_in,
> > +                      u8 *data);
>
> Wouldn't those operations be better names get_compound and set_compound ?
>

Agreed. Will rename to get_compound and set_compound.

Best,
Yunke

> >  };
> >
> >  struct uvc_control {
>
> --
> Regards,
>
> Laurent Pinchart

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 4/7] media: uvcvideo: implement UVC v1.5 ROI
  2022-08-24 20:36   ` Laurent Pinchart
@ 2022-08-29  5:48     ` Yunke Cao
  2022-09-28  8:13       ` Ricardo Ribalda
  0 siblings, 1 reply; 32+ messages in thread
From: Yunke Cao @ 2022-08-29  5:48 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Hans Verkuil, Nicolas Dufresne, Mauro Carvalho Chehab,
	Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Laurent,

Thanks for the review!

On Thu, Aug 25, 2022 at 5:37 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Yunke,
>
> Thank you for the patch.
>
> On Tue, Jun 28, 2022 at 04:57:02PM +0900, Yunke Cao wrote:
> > Implement support for ROI as described in UVC 1.5:
> > 4.2.2.1.20 Digital Region of Interest (ROI) Control
> >
> > ROI control is implemented using V4L2 control API as
> > two uvc-specific controls:
>
> s/uvc/UVC/
>
> > V4L2_CID_UVC_REGION_OF_INTEREST_RECT and
> > V4L2_CID_UVC_REGION_OF_INTEREST_AUTO.
> >
> > Signed-off-by: Yunke Cao <yunkec@google.com>
> > ---
> >  drivers/media/usb/uvc/uvc_ctrl.c   | 146 ++++++++++++++++++++++++++++-
> >  drivers/media/usb/uvc/uvc_v4l2.c   |   5 +-
> >  drivers/media/usb/uvc/uvcvideo.h   |   7 ++
> >  include/uapi/linux/usb/video.h     |   1 +
> >  include/uapi/linux/uvcvideo.h      |  13 +++
> >  include/uapi/linux/v4l2-controls.h |   8 ++
> >  6 files changed, 178 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > index 508ee04afbcd..00482269233a 100644
> > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > @@ -355,6 +355,23 @@ static const struct uvc_control_info uvc_ctrls[] = {
> >               .flags          = UVC_CTRL_FLAG_GET_CUR
> >                               | UVC_CTRL_FLAG_AUTO_UPDATE,
> >       },
> > +     /* UVC_CTRL_FLAG_AUTO_UPDATE is needed because the RoI may get updated
>
>         /*
>          * UVC_CTRL_...
>
> > +      * by sensors.
> > +      * "This RoI should be the same as specified in most recent SET_CUR
> > +      * except in the case where the ‘Auto Detect and Track’ and/or
> > +      * ‘Image Stabilization’ bit have been set."
> > +      * 4.2.2.1.20 Digital Region of Interest (ROI) Control
> > +      */
> > +     {
> > +             .entity         = UVC_GUID_UVC_CAMERA,
> > +             .selector       = UVC_CT_REGION_OF_INTEREST_CONTROL,
> > +             .index          = 21,
> > +             .size           = 10,
> > +             .flags          = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
> > +                             | UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
> > +                             | UVC_CTRL_FLAG_GET_DEF
> > +                             | UVC_CTRL_FLAG_AUTO_UPDATE,
> > +     },
> >  };
> >
> >  static const u32 uvc_control_classes[] = {
> > @@ -429,6 +446,64 @@ static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping,
> >       data[first+1] = min_t(int, abs(value), 0xff);
> >  }
> >
> > +static void uvc_to_v4l2_rect(struct v4l2_rect *v4l2_rect,
> > +                          const struct uvc_rect *uvc_rect)
> > +{
> > +     v4l2_rect->top = uvc_rect->top;
> > +     v4l2_rect->left = uvc_rect->left;
> > +     v4l2_rect->height = uvc_rect->bottom - uvc_rect->top + 1;
> > +     v4l2_rect->width = uvc_rect->right - uvc_rect->left + 1;
>
> What happens if the device sends a bogus value with top > bottom or left
> > right ?
>

Hmm. I didn't think of this.  I guess we should check and return -EINVAL.

> > +}
> > +
> > +static int v4l2_to_uvc_rect(struct uvc_rect *uvc_rect,
> > +                         const struct v4l2_rect *v4l2_rect)
> > +{
> > +     // Safely converts s32 and u32 to u16.
>
> C-style comments.
>
> > +     if (v4l2_rect->top > U16_MAX || v4l2_rect->top < 0 ||
> > +         v4l2_rect->left > U16_MAX || v4l2_rect->left < 0 ||
> > +         v4l2_rect->height > U16_MAX || v4l2_rect->height == 0 ||
> > +         v4l2_rect->width > U16_MAX || v4l2_rect->width == 0 ||
> > +         v4l2_rect->height + v4l2_rect->top - 1 > U16_MAX ||
> > +         v4l2_rect->width + v4l2_rect->left - 1 > U16_MAX)
> > +             return -ERANGE;
>
> Doesn't V4L2 adjust the control value instead ?
>

videoc-g-ext-ctrls.rst says
" When the value is out of bounds drivers can choose to take the closest
valid value or return an ERANGE error code, whatever seems more appropriate."

I chose to return ERANGE because it's a bit easier. Is it acceptable?

> > +
> > +     uvc_rect->top = v4l2_rect->top;
> > +     uvc_rect->left = v4l2_rect->left;
> > +     uvc_rect->bottom = v4l2_rect->height + v4l2_rect->top - 1;
> > +     uvc_rect->right = v4l2_rect->width + v4l2_rect->left - 1;
> > +     return 0;
> > +}
> > +
> > +static int uvc_get_array_rect(struct uvc_control_mapping *mapping,
> > +                           const u8 *data,  u8 *data_out)
> > +{
> > +     struct uvc_rect *uvc_rect;
> > +
> > +     if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> > +             return -EINVAL;
> > +     if (WARN_ON(mapping->size / 8 != sizeof(struct uvc_rect)))
> > +             return -EINVAL;
> > +
>
> These checks should go to mapping creation time.
>
> > +     uvc_rect = (struct uvc_rect *)(data + mapping->offset / 8);
>
> If the offset happens to not be a multiple of 2 bytes this will cause
> unaligned accesses on some platforms.
>
> > +     uvc_to_v4l2_rect((struct v4l2_rect *)data_out, uvc_rect);
>
> Same here if not aligned on 4 bytes.
>
> > +
> > +     return 0;
> > +}
> > +
> > +static int uvc_set_array_rect(struct uvc_control_mapping *mapping,
> > +                           const u8 *data_in, u8 *data)
> > +{
> > +     struct uvc_rect *uvc_rect;
> > +
> > +     if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> > +             return -EINVAL;
> > +     if (WARN_ON(mapping->size / 8 != sizeof(struct uvc_rect)))
> > +             return -EINVAL;
> > +
>
> Same here.
>
> > +     uvc_rect = (struct uvc_rect *)(data + mapping->offset / 8);
> > +     return v4l2_to_uvc_rect(uvc_rect, (struct v4l2_rect *)data_in);
> > +}
> > +
> >  static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >       {
> >               .id             = V4L2_CID_BRIGHTNESS,
> > @@ -728,6 +803,26 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> >       },
> > +     {
> > +             .id             = V4L2_CID_UVC_REGION_OF_INTEREST_RECT,
> > +             .entity         = UVC_GUID_UVC_CAMERA,
> > +             .selector       = UVC_CT_REGION_OF_INTEREST_CONTROL,
> > +             .size           = 64,
>
> sizeof(struct uvc_rect)
>
> > +             .offset         = 0,
> > +             .v4l2_type      = V4L2_CTRL_TYPE_RECT,
> > +             .data_type      = UVC_CTRL_DATA_TYPE_RECT,
> > +             .get_array      = uvc_get_array_rect,
> > +             .set_array      = uvc_set_array_rect,
> > +     },
> > +     {
> > +             .id             = V4L2_CID_UVC_REGION_OF_INTEREST_AUTO,
> > +             .entity         = UVC_GUID_UVC_CAMERA,
> > +             .selector       = UVC_CT_REGION_OF_INTEREST_CONTROL,
> > +             .size           = 16,
> > +             .offset         = 64,
> > +             .v4l2_type      = V4L2_CTRL_TYPE_BITMASK,
> > +             .data_type      = UVC_CTRL_DATA_TYPE_BITMASK,
> > +     },
> >  };
> >
> >  /* ------------------------------------------------------------------------
> > @@ -1061,7 +1156,16 @@ static int __uvc_ctrl_get_compound_to_user(struct uvc_control_mapping *mapping,
> >       if (WARN_ON(!mapping->size % 8))
> >               return -EINVAL;
> >
> > -     size = mapping->size / 8;
> > +     switch (mapping->v4l2_type) {
> > +     case V4L2_CTRL_TYPE_RECT:
> > +             size = sizeof(struct v4l2_rect);
>
> Oohhh that sounds fragile. I'm worried we may be using mapping->size as
> the V4L2 control size somewhere else.
>
> It would be safer to split the size field of uvc_control_mapping into a
> v4l2_size and a uvc_size (or possibly data_size to match the v4l2_type
> and data_type names) in a patch before this. This will force reviewing
> all the users of the size field to check which size they need.
>

Thanks for the suggestion! That makes a lot of sense. I will try to implement
this in the next version, and fix other places you pointed out.

Best,
Yunke

> > +             break;
> > +
> > +     default:
> > +             size = mapping->size / 8;
> > +             break;
> > +     }
> > +
> >       if (xctrl->size < size) {
> >               xctrl->size = size;
> >               return -ENOSPC;
> > @@ -2578,6 +2682,40 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev,
> >       }
> >  }
> >
> > +static int uvc_ctrl_init_roi(struct uvc_device *dev, struct uvc_control *ctrl)
> > +{
> > +     int ret;
> > +
> > +     ret = uvc_query_ctrl(dev, UVC_GET_DEF, ctrl->entity->id, dev->intfnum,
> > +                          UVC_CT_REGION_OF_INTEREST_CONTROL,
> > +                          uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
> > +                          ctrl->info.size);
> > +     if (ret)
> > +             goto out;
> > +
> > +     /*
> > +      * Most firmwares have wrong GET_CURRENT configuration. E.g. it's
>
> s/GET_CURRENT/GET_CUR/
>
> > +      * below GET_MIN, or have rectangle coordinates mixed up. This
> > +      * causes problems sometimes, because we are unable to set
> > +      * auto-controls value without first setting ROI rectangle to
> > +      * valid configuration.
> > +      *
> > +      * We expect that default configuration is always correct and
> > +      * is within the GET_MIN / GET_MAX boundaries.
>
> A dangerous expectation to make given the quality of most UVC camera
> firmwares...
>
> > +      *
> > +      * Set current ROI configuration to GET_DEF, so that we will
> > +      * always have properly configured ROI.
> > +      */
> > +     ret = uvc_query_ctrl(dev, UVC_SET_CUR, 1, dev->intfnum,
> > +                          UVC_CT_REGION_OF_INTEREST_CONTROL,
> > +                          uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
> > +                          ctrl->info.size);
> > +out:
> > +     if (ret)
> > +             dev_err(&dev->udev->dev, "Failed to fixup ROI (%d).\n", ret);
>
> s/(%d)./(%d)/
>
> > +     return ret;
> > +}
> > +
> >  /*
> >   * Add control information and hardcoded stock control mappings to the given
> >   * device.
> > @@ -2590,6 +2728,7 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
> >       const struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
> >       const struct uvc_control_mapping *mend =
> >               mapping + ARRAY_SIZE(uvc_ctrl_mappings);
> > +     const u8 camera_entity[16] = UVC_GUID_UVC_CAMERA;
> >
> >       /* XU controls initialization requires querying the device for control
> >        * information. As some buggy UVC devices will crash when queried
> > @@ -2610,6 +2749,11 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
> >                        * GET_INFO on standard controls.
> >                        */
> >                       uvc_ctrl_get_flags(chain->dev, ctrl, &ctrl->info);
> > +
> > +                     if (ctrl->info.selector ==
> > +                             UVC_CT_REGION_OF_INTEREST_CONTROL &&
> > +                         uvc_entity_match_guid(ctrl->entity, camera_entity))
> > +                             uvc_ctrl_init_roi(chain->dev, ctrl);
>
> Should we instead add an init operation to the uvc_control_info
> structure ?
>
> >                       break;
> >                }
> >       }
> > diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> > index 0366d05895a9..aad61af36271 100644
> > --- a/drivers/media/usb/uvc/uvc_v4l2.c
> > +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> > @@ -998,7 +998,10 @@ static int uvc_ioctl_query_ext_ctrl(struct file *file, void *fh,
> >       qec->step = qc.step;
> >       qec->default_value = qc.default_value;
> >       qec->flags = qc.flags;
> > -     qec->elem_size = 4;
> > +     if (qc.type == V4L2_CTRL_TYPE_RECT)
> > +             qec->elem_size = sizeof(struct v4l2_rect);
> > +     else
> > +             qec->elem_size = 4;
> >       qec->elems = 1;
> >       qec->nr_of_dims = 0;
> >       memset(qec->dims, 0, sizeof(qec->dims));
> > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > index 2f9b75faae83..9ff95bbad251 100644
> > --- a/drivers/media/usb/uvc/uvcvideo.h
> > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > @@ -431,6 +431,13 @@ struct uvc_streaming_header {
> >       u8 bTriggerUsage;
> >  };
> >
> > +struct uvc_rect {
> > +     u16 top;
> > +     u16 left;
> > +     u16 bottom;
> > +     u16 right;
> > +} __packed;
> > +
> >  enum uvc_buffer_state {
> >       UVC_BUF_STATE_IDLE      = 0,
> >       UVC_BUF_STATE_QUEUED    = 1,
> > diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h
> > index bfdae12cdacf..9076a444758a 100644
> > --- a/include/uapi/linux/usb/video.h
> > +++ b/include/uapi/linux/usb/video.h
> > @@ -104,6 +104,7 @@
> >  #define UVC_CT_ROLL_ABSOLUTE_CONTROL                 0x0f
> >  #define UVC_CT_ROLL_RELATIVE_CONTROL                 0x10
> >  #define UVC_CT_PRIVACY_CONTROL                               0x11
> > +#define UVC_CT_REGION_OF_INTEREST_CONTROL            0x14
> >
> >  /* A.9.5. Processing Unit Control Selectors */
> >  #define UVC_PU_CONTROL_UNDEFINED                     0x00
> > diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h
> > index 8288137387c0..e2feee15cb1b 100644
> > --- a/include/uapi/linux/uvcvideo.h
> > +++ b/include/uapi/linux/uvcvideo.h
> > @@ -16,6 +16,7 @@
> >  #define UVC_CTRL_DATA_TYPE_BOOLEAN   3
> >  #define UVC_CTRL_DATA_TYPE_ENUM              4
> >  #define UVC_CTRL_DATA_TYPE_BITMASK   5
> > +#define UVC_CTRL_DATA_TYPE_RECT              6
> >
> >  /* Control flags */
> >  #define UVC_CTRL_FLAG_SET_CUR                (1 << 0)
> > @@ -36,6 +37,18 @@
> >        UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES | \
> >        UVC_CTRL_FLAG_GET_DEF)
> >
> > +/* V4L2 private controls */
> > +#define V4L2_CID_UVC_REGION_OF_INTEREST_RECT (V4L2_CID_CAMERA_UVC_BASE + 1)
> > +#define V4L2_CID_UVC_REGION_OF_INTEREST_AUTO (V4L2_CID_CAMERA_UVC_BASE + 2)
> > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_EXPOSURE            (1 << 0)
> > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_IRIS                        (1 << 1)
> > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_WHITE_BALANCE               (1 << 2)
> > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_FOCUS                       (1 << 3)
> > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_FACE_DETECT         (1 << 4)
> > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_DETECT_AND_TRACK    (1 << 5)
> > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_IMAGE_STABILIZATION (1 << 6)
> > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_HIGHER_QUALITY              (1 << 7)
> > +
> >  struct uvc_menu_info {
> >       __u32 value;
> >       __u8 name[32];
> > diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
> > index dfff69ed88f7..d85be46e46f2 100644
> > --- a/include/uapi/linux/v4l2-controls.h
> > +++ b/include/uapi/linux/v4l2-controls.h
> > @@ -1013,6 +1013,14 @@ enum v4l2_auto_focus_range {
> >
> >  #define V4L2_CID_CAMERA_SENSOR_ROTATION              (V4L2_CID_CAMERA_CLASS_BASE+35)
> >
> > +/* CAMERA-class private control IDs */
> > +
>
> Why doesn't this go in the driver-specific controls range ?
>
> > +/*
> > + * The base for the uvc driver controls. See linux/uvcvideo.h for the list
> > + * of controls. We reserve 16 controls for this driver.
>
> I'd reserve more just in case, UVC 1.5 has quite a few controls that may
> not be exposed through standard V4L2 controls, especially for encoding
> units.
>
> > + */
> > +#define V4L2_CID_CAMERA_UVC_BASE             (V4L2_CID_CAMERA_CLASS_BASE + 0x1000)
> > +
> >  /* FM Modulator class control IDs */
> >
> >  #define V4L2_CID_FM_TX_CLASS_BASE            (V4L2_CTRL_CLASS_FM_TX | 0x900)
>
> --
> Regards,
>
> Laurent Pinchart

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 7/7] media: uvcvideo: document UVC v1.5 ROI
  2022-08-24 20:17   ` Laurent Pinchart
@ 2022-09-20  7:46     ` Yunke Cao
  0 siblings, 0 replies; 32+ messages in thread
From: Yunke Cao @ 2022-09-20  7:46 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Hans Verkuil, Nicolas Dufresne, Mauro Carvalho Chehab,
	Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Laurent,

Thanks for the review!

On Thu, Aug 25, 2022 at 5:17 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Yunke,
>
> Thank you for the patch.
>
> On Tue, Jun 28, 2022 at 04:57:05PM +0900, Yunke Cao wrote:
> > Added documentation of V4L2_CID_UVC_REGION_OF_INTEREST_RECT and
> > V4L2_CID_UVC_REGION_OF_INTEREST_AUTO.
> >
> > Signed-off-by: Yunke Cao <yunkec@google.com>
> > ---
> >  .../userspace-api/media/drivers/uvcvideo.rst  | 61 +++++++++++++++++++
> >  1 file changed, 61 insertions(+)
> >
> > diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> > index a290f9fadae9..ee4c182aa274 100644
> > --- a/Documentation/userspace-api/media/drivers/uvcvideo.rst
> > +++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst
> > @@ -181,6 +181,7 @@ Argument: struct uvc_xu_control_mapping
> >       UVC_CTRL_DATA_TYPE_BOOLEAN      Boolean
> >       UVC_CTRL_DATA_TYPE_ENUM         Enumeration
> >       UVC_CTRL_DATA_TYPE_BITMASK      Bitmask
> > +     UVC_CTRL_DATA_TYPE_RECT         Rectangular area
> >
> >
> >  UVCIOC_CTRL_QUERY - Query a UVC XU control
> > @@ -255,3 +256,63 @@ Argument: struct uvc_xu_control_query
> >       __u8    query           Request code to send to the device
> >       __u16   size            Control data size (in bytes)
> >       __u8    *data           Control value
> > +
>
> You can add a second blank line here, as above the "Extension Unit (XU)
> support" title.
>
> > +Private V4L2 controls
>
> "Driver-specific V4L2 controls" would be better, those controls are not
> private.
>
> > +---------------------
> > +
> > +A few UVC specific V4L2 control IDs are listed below.
>
> s/UVC specific/UVC-specific/
>
> But I'd write
>
> The uvcvideo driver implements the following UVC-specific controls:
>
> > +
> > +``V4L2_CID_UVC_REGION_OF_INTEREST_RECT (struct)``
> > +     This control determines the region of interest (ROI). ROI is an
>
> s/is an/is a/
>
> > +     rectangular area represented by a struct :c:type:`v4l2_rect`. The
> > +     rectangle is in global sensor coordinates and pixel units. It is
> > +     independent of the field of view, not impacted by any cropping or
> > +     scaling.
> > +
> > +     Use ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` to query
> > +     the range of rectangle sizes. For example, a device can have a minimum
> > +     ROI rectangle of 1x1@0x0 and a maximum of 640x480@0x0.
>
> Minimum and maximum values for rectangles are ill-defined. Are the top
> and left coordinates always 0 ? If so that should be documented.
>
> > +
> > +     Setting a ROI allows the camera to optimize the capture for the region.
> > +     The value of ``V4L2_CID_REGION_OF_INTEREST_AUTO`` control determines
> > +     the detailed behavior.
> > +
> > +
> > +``V4L2_CID_UVC_REGION_OF_INTEREST_AUTO (bitmask)``
> > +     This determines which, if any, on board features should track to the
> > +     Region of Interest specified by the current value of
> > +     ``V4L2_CID_UVD__REGION_OF_INTEREST_RECT``.
> > +
> > +     Max value is a mask indicating all supported Auto
> > +     Controls.
>
> No need to wrap this line.
>
> > +
> > +.. flat-table::
> > +    :header-rows:  0
> > +    :stub-columns: 0
> > +
> > +    * - ``V4L2_REGION_OF_INTEREST_AUTO_EXPOSURE``
>
> s/V4L2/V4L2_UVC/
>
> Same below.
>
> > +      - Setting this to true enables automatic exposure time for the specified
>
> s/exposure time/exposure/ (as it can also include gain)
>
> > +     region.
>
> It's not that it enables automatic exposure for the specified region, is
> that it causes the automatic exposure to track the region of interest
> instead of the whole image. Same below.
>

I see. Thanks for clarifying. I will update this and the comments above in the
next version.

> > +    * - ``V4L2_REGION_OF_INTEREST_AUTO_IRIS``
> > +      - Setting this to true enables automatic iris aperture for the specified
> > +     region.
> > +    * - ``V4L2_REGION_OF_INTEREST_AUTO_WHITE_BALANCE``
> > +      - Setting this to true enables automatic white balance adjustment for the
> > +     specified region.
> > +    * - ``V4L2_REGION_OF_INTEREST_AUTO_FOCUS``
> > +      - Setting this to true enables automatic focus adjustment for the
> > +     specified region.
> > +    * - ``V4L2_REGION_OF_INTEREST_AUTO_FACE_DETECT``
> > +      - Setting this to true enables automatic face detection for the
> > +     specified region.
> > +    * - ``V4L2_REGION_OF_INTEREST_AUTO_DETECT_AND_TRACK``
> > +      - Setting this to true enables automatic face detection and tracking. The
> > +     current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
> > +     the driver.
> > +    * - ``V4L2_REGION_OF_INTEREST_AUTO_IMAGE_STABILIZATION``
> > +      - Setting this to true enables automatic image stabilization. The
> > +     current value of ``V4L2_CID_REGION_OF_INTEREST_RECT`` may be updated by
> > +     the driver.
>
> How so ?


If I read the UVC specification correctly, the ROI rectangles are
volatile when these two
flags are set.
"This RoI should be the same as specified in most recent SET_CUR except in the
case where the ‘Auto Detect and Track’ and/or ‘Image Stabilization’
bit have been set."
(4.2.2.1.20 Digital Region of Interest (ROI) Control)

Maybe it's better to say "updated by the hardware" instead of driver?

>
>
> > +    * - ``V4L2_REGION_OF_INTEREST_AUTO_HIGHER_QUALITY``
> > +      - Setting this to true enables automatically capture the specified region
> > +     with higher quality if possible.
>
> Could you please clarify this one ? I'm not sure to understand what it
> means.

Sorry, it's not very clear to me either... I just added it because there's a
"Higher Quality" in the specification.

Best,
Yunke

>
> --
> Regards,
>
> Laurent Pinchart

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 6/7] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL
  2022-08-24 20:03   ` Laurent Pinchart
@ 2022-09-20  7:52     ` Yunke Cao
  0 siblings, 0 replies; 32+ messages in thread
From: Yunke Cao @ 2022-09-20  7:52 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Hans Verkuil, Nicolas Dufresne, Mauro Carvalho Chehab,
	Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Laurent,

Thanks for the review.

On Thu, Aug 25, 2022 at 5:04 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Yunke,
>
> Thank you for the patch.
>
> On Tue, Jun 28, 2022 at 04:57:04PM +0900, Yunke Cao wrote:
> > Add support for V4L2_CTRL_WHICH_MIN/MAX_VAL in uvc driver.
> > It is useful for the V4L2_CID_UVC_REGION_OF_INTEREST_RECT control.
> >
> > Signed-off-by: Yunke Cao <yunkec@google.com>
> > ---
> >  drivers/media/usb/uvc/uvc_ctrl.c | 73 ++++++++++++++++++++++++++------
> >  drivers/media/usb/uvc/uvc_v4l2.c | 11 +++--
> >  drivers/media/usb/uvc/uvcvideo.h |  3 +-
> >  3 files changed, 70 insertions(+), 17 deletions(-)
> >
> > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > index 00482269233a..b569d6824ac1 100644
> > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > @@ -1923,7 +1923,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
> >  }
> >
> >  int __uvc_ctrl_get_fixed_std(struct uvc_video_chain *chain,
> > -                          struct v4l2_ext_control *xctrl)
> > +                          struct v4l2_ext_control *xctrl, u32 v4l2_which)
> >  {
> >       struct v4l2_queryctrl qc = { .id = xctrl->id };
> >       int ret = uvc_query_v4l2_ctrl(chain, &qc);
> > @@ -1931,16 +1931,69 @@ int __uvc_ctrl_get_fixed_std(struct uvc_video_chain *chain,
> >       if (ret < 0)
> >               return ret;
> >
> > -     xctrl->value = qc.default_value;
> > +     switch (v4l2_which) {
> > +     case V4L2_CTRL_WHICH_DEF_VAL:
> > +             xctrl->value = qc.default_value;
> > +             break;
> > +     case V4L2_CTRL_WHICH_MIN_VAL:
> > +             xctrl->value = qc.minimum;
> > +             break;
> > +     case V4L2_CTRL_WHICH_MAX_VAL:
> > +             xctrl->value = qc.maximum;
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> >       return 0;
> >  }
> >
> > +static int __uvc_ctrl_get_fixed_compound(struct uvc_video_chain *chain,
> > +                                      struct uvc_control_mapping *mapping,
> > +                                      struct uvc_control *ctrl,
> > +                                      u32 v4l2_which,
> > +                                      struct v4l2_ext_control *xctrl)
> > +{
> > +     int ret;
> > +     u32 flag, id;
> > +
> > +     if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> > +             return -EINVAL;
> > +
>
> This can't happen.
>
> > +     switch (v4l2_which) {
> > +     case V4L2_CTRL_WHICH_DEF_VAL:
> > +             flag = UVC_CTRL_FLAG_GET_DEF;
> > +             id = UVC_CTRL_DATA_DEF;
> > +             break;
> > +     case V4L2_CTRL_WHICH_MIN_VAL:
> > +             flag = UVC_CTRL_FLAG_GET_MIN;
> > +             id = UVC_CTRL_DATA_MIN;
> > +             break;
> > +     case V4L2_CTRL_WHICH_MAX_VAL:
> > +             flag = UVC_CTRL_FLAG_GET_MAX;
> > +             id = UVC_CTRL_DATA_MAX;
> > +             break;
> > +     default:
> > +             return -EINVAL;
>
> Can this happen ?
>

Will remove.

> > +     }
> > +
> > +     if (!(ctrl->info.flags & flag) && flag != UVC_CTRL_FLAG_GET_DEF)
> > +             return -EACCES;
> > +
> > +     if (!ctrl->cached) {
> > +             ret = uvc_ctrl_populate_cache(chain, ctrl);
> > +             if (ret < 0)
> > +                     return ret;
> > +     }
> > +
> > +     return __uvc_ctrl_get_compound_to_user(mapping, ctrl, id, xctrl);
> > +}
> > +
> >  int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> > -                    struct v4l2_ext_control *xctrl)
> > +                    struct v4l2_ext_control *xctrl, u32 v4l2_which)
> >  {
> >       struct uvc_control *ctrl;
> >       struct uvc_control_mapping *mapping;
> > -     int ret;
> >
> >       if (__uvc_query_v4l2_class(chain, xctrl->id, 0) >= 0)
> >               return -EACCES;
> > @@ -1950,16 +2003,10 @@ int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> >               return -EINVAL;
> >
> >       if (mapping->v4l2_type < V4L2_CTRL_COMPOUND_TYPES)
> > -             return __uvc_ctrl_get_fixed_std(chain, xctrl);
> > +             return __uvc_ctrl_get_fixed_std(chain, xctrl, v4l2_which);
> >
> > -     if (!ctrl->cached) {
> > -             ret = uvc_ctrl_populate_cache(chain, ctrl);
> > -             if (ret < 0)
> > -                     return ret;
> > -     }
> > -
> > -     return __uvc_ctrl_get_compound_to_user(mapping, ctrl, UVC_CTRL_DATA_DEF,
> > -                                            xctrl);
> > +     return __uvc_ctrl_get_fixed_compound(chain, mapping, ctrl, v4l2_which,
> > +                                          xctrl);
> >  }
> >
> >  int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
> > diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> > index aad61af36271..004e3b764737 100644
> > --- a/drivers/media/usb/uvc/uvc_v4l2.c
> > +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> > @@ -1043,16 +1043,21 @@ static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
> >       if (ret < 0)
> >               return ret;
> >
> > -     if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
> > +     switch (ctrls->which) {
> > +     case V4L2_CTRL_WHICH_DEF_VAL:
> > +     case V4L2_CTRL_WHICH_MIN_VAL:
> > +     case V4L2_CTRL_WHICH_MAX_VAL:
> >               for (i = 0; i < ctrls->count; ++ctrl, ++i) {
> > -                     ret = uvc_ctrl_get_fixed(chain, ctrl);
> > +                     ret = uvc_ctrl_get_fixed(chain, ctrl, ctrls->which);
> >                       if (ret < 0) {
> >                               ctrls->error_idx = i;
> >                               return ret;
> >                       }
> >               }
> > -
> >               return 0;
> > +
> > +     default:
>
>         case V4L2_CTRL_WHICH_CUR_VAL:
>
> > +             break;
>
> and
>
>         default:
>                 return -EINVAL;
>
> (please double-check that the error value is the right one). This should
> be mentioned in the commit message.
>

Thanks for catching this!
What specifictly should be in the commit message? Sorry, I didn't get it.

Best,
Yunke

> >       }
> >
> >       ret = uvc_ctrl_begin(chain);
> > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > index 9ff95bbad251..54cc47bc2d33 100644
> > --- a/drivers/media/usb/uvc/uvcvideo.h
> > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > @@ -913,7 +913,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
> >
> >  int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
> >  int uvc_ctrl_get_fixed(struct uvc_video_chain *chain,
> > -                    struct v4l2_ext_control *xctrl);
> > +                    struct v4l2_ext_control *xctrl,
> > +                    u32 v4l2_which);
> >  int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
> >  int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
> >                          bool read);
>
> --
> Regards,
>
> Laurent Pinchart

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 5/7] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2022-08-24 16:20   ` Laurent Pinchart
@ 2022-09-20 10:01     ` Yunke Cao
  2022-09-30  7:59     ` Hans Verkuil
  1 sibling, 0 replies; 32+ messages in thread
From: Yunke Cao @ 2022-09-20 10:01 UTC (permalink / raw)
  To: Laurent Pinchart, Hans Verkuil
  Cc: Nicolas Dufresne, Mauro Carvalho Chehab, Tomasz Figa,
	Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Laurent, Hans,

Thank you Laurent for the review.

On Thu, Aug 25, 2022 at 1:20 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Yunke and Hans,
>
> Thank you for the patch.
>
> On Tue, Jun 28, 2022 at 04:57:03PM +0900, Yunke Cao wrote:
> > From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> >
> > Add the capability of retrieving the min and max values of a
> > compound control.
> >
> > Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
> > Signed-off-by: Yunke Cao <yunkec@google.com>
> > ---
> > git am from https://lore.kernel.org/all/20191119113457.57833-3-hverkuil-cisco@xs4all.nl/
> > -Fixed some merge conflits.
> > -Fixed the build error in drivers/media/platform/qcom/venus.
> >
> >  .../media/v4l/vidioc-g-ext-ctrls.rst          |   7 +-
> >  .../media/videodev2.h.rst.exceptions          |   2 +
> >  drivers/media/i2c/imx214.c                    |   5 +-
> >  .../media/platform/qcom/venus/venc_ctrls.c    |   4 +
> >  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  51 +++++--
> >  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 135 ++++++++++++++++--
> >  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
> >  include/media/v4l2-ctrls.h                    |  32 ++++-
> >  include/uapi/linux/videodev2.h                |   2 +
> >  9 files changed, 214 insertions(+), 28 deletions(-)
> >
> > diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > index 7473baa4e977..65a5f878cc28 100644
> > --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > @@ -284,14 +284,17 @@ still cause this situation.
> >        - Which value of the control to get/set/try.
> >      * - :cspan:`2` ``V4L2_CTRL_WHICH_CUR_VAL`` will return the current value of
> >       the control, ``V4L2_CTRL_WHICH_DEF_VAL`` will return the default
> > +     value of the control, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
> > +     value of the control, ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
>
> The concept of minimum and maximum values for compound controls can be
> quite ill-defined. Could we document here that the definition of the
> minimum and maximum values are provided by the control documentation for
> compound controls ?
>

Sure, I can do that.

> >       value of the control and ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
> >       these controls have to be retrieved from a request or tried/set for
> >       a request. In the latter case the ``request_fd`` field contains the
> >       file descriptor of the request that should be used. If the device
> >       does not support requests, then ``EACCES`` will be returned.
> >
> > -     When using ``V4L2_CTRL_WHICH_DEF_VAL`` be aware that you can only
> > -     get the default value of the control, you cannot set or try it.
> > +     When using ``V4L2_CTRL_WHICH_DEF_VAL``, ``V4L2_CTRL_WHICH_MIN_VAL``
> > +     or ``V4L2_CTRL_WHICH_MAX_VAL`` be aware that you can only get the
> > +     default/minimum/maximum value of the control, you cannot set or try it.
> >
> >       For backwards compatibility you can also use a control class here
> >       (see :ref:`ctrl-class`). In that case all controls have to
> > diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > index 7b423475281d..e2dde31d76df 100644
> > --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > @@ -553,6 +553,8 @@ ignore define V4L2_CTRL_DRIVER_PRIV
> >  ignore define V4L2_CTRL_MAX_DIMS
> >  ignore define V4L2_CTRL_WHICH_CUR_VAL
> >  ignore define V4L2_CTRL_WHICH_DEF_VAL
> > +ignore define V4L2_CTRL_WHICH_MIN_VAL
> > +ignore define V4L2_CTRL_WHICH_MAX_VAL
> >  ignore define V4L2_CTRL_WHICH_REQUEST_VAL
> >  ignore define V4L2_OUT_CAP_CUSTOM_TIMINGS
> >  ignore define V4L2_CID_MAX_CTRLS
> > diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c
> > index 83c1737abeec..aa0bfd18f7b7 100644
> > --- a/drivers/media/i2c/imx214.c
> > +++ b/drivers/media/i2c/imx214.c
> > @@ -1037,7 +1037,10 @@ static int imx214_probe(struct i2c_client *client)
> >       imx214->unit_size = v4l2_ctrl_new_std_compound(&imx214->ctrls,
> >                               NULL,
> >                               V4L2_CID_UNIT_CELL_SIZE,
> > -                             v4l2_ctrl_ptr_create((void *)&unit_size));
> > +                             v4l2_ctrl_ptr_create((void *)&unit_size),
> > +                             v4l2_ctrl_ptr_create(NULL),
> > +                             v4l2_ctrl_ptr_create(NULL));
>
> Would it make sense to set min = max = default for read-only controls ?
> You should also update the documentation of V4L2_CID_UNIT_CELL_SIZE (and
> other controls below) to indicate how their minimum and maximum are
> defined.
>

I only modified imx214.c and venc_ctrls.c to fix the build errors
caused by the change
of v4l2_ctrl_ptr_create().

Can I leave the min/max for these controls undefined in this patch (by
returning error
as we discussed below)?

setting min = max = default for V4L2_CID_UNIT_CELL_SIZE makes sense to me.
I can implement and document V4L2_CID_UNIT_CELL_SIZE in a followup patch.
I'm not familiar with the other two controls though, so I prefer to
leave them undefined.

> > +
> >       ret = imx214->ctrls.error;
> >       if (ret) {
> >               dev_err(&client->dev, "%s control init failed (%d)\n",
> > diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
> > index ed44e5800759..4af8f9ca12a6 100644
> > --- a/drivers/media/platform/qcom/venus/venc_ctrls.c
> > +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
> > @@ -579,10 +579,14 @@ int venc_ctrl_init(struct venus_inst *inst)
> >
> >       v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
> >                                  V4L2_CID_COLORIMETRY_HDR10_CLL_INFO,
> > +                                v4l2_ctrl_ptr_create(NULL),
> > +                                v4l2_ctrl_ptr_create(NULL),
> >                                  v4l2_ctrl_ptr_create(NULL));
> >
> >       v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
> >                                  V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY,
> > +                                v4l2_ctrl_ptr_create(NULL),
> > +                                v4l2_ctrl_ptr_create(NULL),
> >                                  v4l2_ctrl_ptr_create(NULL));
> >
> >       v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
> > diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> > index db9baa0bd05f..8a9c816b0dab 100644
> > --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
> > +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> > @@ -97,6 +97,28 @@ static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> >       return ptr_to_user(c, ctrl, ctrl->p_new);
> >  }
> >
> > +/* Helper function: copy the minimum control value back to the caller */
> > +static int min_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> > +{
> > +     int idx;
> > +
> > +     for (idx = 0; idx < ctrl->elems; idx++)
> > +             ctrl->type_ops->minimum(ctrl, idx, ctrl->p_new);
> > +
> > +     return ptr_to_user(c, ctrl, ctrl->p_new);
> > +}
> > +
> > +/* Helper function: copy the maximum control value back to the caller */
> > +static int max_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> > +{
> > +     int idx;
> > +
> > +     for (idx = 0; idx < ctrl->elems; idx++)
> > +             ctrl->type_ops->maximum(ctrl, idx, ctrl->p_new);
> > +
> > +     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,
> > @@ -220,8 +242,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
> >               cs->error_idx = i;
> >
> >               if (cs->which &&
> > -                 cs->which != V4L2_CTRL_WHICH_DEF_VAL &&
> > -                 cs->which != V4L2_CTRL_WHICH_REQUEST_VAL &&
> > +                 (cs->which < V4L2_CTRL_WHICH_DEF_VAL ||
> > +                  cs->which > V4L2_CTRL_WHICH_MAX_VAL) &&
> >                   V4L2_CTRL_ID2WHICH(id) != cs->which) {
> >                       dprintk(vdev,
> >                               "invalid which 0x%x or control id 0x%x\n",
> > @@ -335,8 +357,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
> >   */
> >  static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
> >  {
> > -     if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL ||
> > -         which == V4L2_CTRL_WHICH_REQUEST_VAL)
> > +     if (which == 0 || (which >= V4L2_CTRL_WHICH_DEF_VAL &&
> > +                        which <= V4L2_CTRL_WHICH_MAX_VAL))
> >               return 0;
> >       return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
> >  }
> > @@ -356,10 +378,12 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
> >       struct v4l2_ctrl_helper *helpers = helper;
> >       int ret;
> >       int i, j;
> > -     bool is_default, is_request;
> > +     bool is_default, is_request, is_min, is_max;
> >
> >       is_default = (cs->which == V4L2_CTRL_WHICH_DEF_VAL);
> >       is_request = (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL);
> > +     is_min = (cs->which == V4L2_CTRL_WHICH_MIN_VAL);
> > +     is_max = (cs->which == V4L2_CTRL_WHICH_MAX_VAL);
> >
> >       cs->error_idx = cs->count;
> >       cs->which = V4L2_CTRL_ID2WHICH(cs->which);
> > @@ -399,13 +423,14 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
> >
> >               /*
> >                * g_volatile_ctrl will update the new control values.
> > -              * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL and
> > +              * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL,
> > +              * V4L2_CTRL_WHICH_MIN_VAL, V4L2_CTRL_WHICH_MAX_VAL and
> >                * V4L2_CTRL_WHICH_REQUEST_VAL. In the case of requests
> >                * it is v4l2_ctrl_request_complete() that copies the
> >                * volatile controls at the time of request completion
> >                * to the request, so you don't want to do that again.
> >                */
> > -             if (!is_default && !is_request &&
> > +             if (!is_default && !is_request && !is_min && !is_max &&
> >                   ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
> >                   (master->has_volatiles && !is_cur_manual(master)))) {
> >                       for (j = 0; j < master->ncontrols; j++)
> > @@ -432,6 +457,10 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
> >                               ret = def_to_user(cs->controls + idx, ref->ctrl);
> >                       else if (is_request && ref->valid_p_req)
> >                               ret = req_to_user(cs->controls + idx, ref);
> > +                     else if (is_min)
> > +                             ret = min_to_user(cs->controls + idx, ref->ctrl);
> > +                     else if (is_max)
> > +                             ret = max_to_user(cs->controls + idx, ref->ctrl);
> >                       else if (is_volatile)
> >                               ret = new_to_user(cs->controls + idx, ref->ctrl);
> >                       else
> > @@ -523,9 +552,11 @@ int try_set_ext_ctrls_common(struct v4l2_fh *fh,
> >
> >       cs->error_idx = cs->count;
> >
> > -     /* Default value cannot be changed */
> > -     if (cs->which == V4L2_CTRL_WHICH_DEF_VAL) {
> > -             dprintk(vdev, "%s: cannot change default value\n",
> > +     /* Default/minimum/maximum values cannot be changed */
> > +     if (cs->which == V4L2_CTRL_WHICH_DEF_VAL ||
> > +         cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
> > +         cs->which == V4L2_CTRL_WHICH_MAX_VAL) {
> > +             dprintk(vdev, "%s: cannot change default/min/max value\n",
> >                       video_device_node_name(vdev));
> >               return -EINVAL;
> >       }
> > diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > index 35d43ba650db..8c5828c7c6d3 100644
> > --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > @@ -186,6 +186,28 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> >       }
> >  }
> >
> > +static void std_min_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> > +                          union v4l2_ctrl_ptr ptr)
> > +{
> > +     void *p = ptr.p + idx * ctrl->elem_size;
> > +
> > +     if (ctrl->p_min.p_const)
> > +             memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
> > +     else
> > +             memset(p, 0, ctrl->elem_size);
> > +}
> > +
> > +static void std_max_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> > +                          union v4l2_ctrl_ptr ptr)
> > +{
> > +     void *p = ptr.p + idx * ctrl->elem_size;
> > +
> > +     if (ctrl->p_max.p_const)
> > +             memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
> > +     else
> > +             memset(p, 0, ctrl->elem_size);
> > +}
> > +
> >  static void std_init(const struct v4l2_ctrl *ctrl, u32 idx,
> >                    union v4l2_ctrl_ptr ptr)
> >  {
> > @@ -224,6 +246,82 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx,
> >       }
> >  }
> >
> > +static void std_minimum(const struct v4l2_ctrl *ctrl, u32 idx,
> > +                     union v4l2_ctrl_ptr ptr)
> > +{
> > +     switch (ctrl->type) {
> > +     case V4L2_CTRL_TYPE_STRING:
> > +             idx *= ctrl->elem_size;
> > +             memset(ptr.p_char + idx, ' ', ctrl->minimum);
> > +             ptr.p_char[idx + ctrl->minimum] = '\0';
> > +             break;
> > +     case V4L2_CTRL_TYPE_INTEGER64:
> > +             ptr.p_s64[idx] = ctrl->minimum;
> > +             break;
> > +     case V4L2_CTRL_TYPE_INTEGER:
> > +     case V4L2_CTRL_TYPE_INTEGER_MENU:
> > +     case V4L2_CTRL_TYPE_MENU:
> > +     case V4L2_CTRL_TYPE_BITMASK:
> > +     case V4L2_CTRL_TYPE_BOOLEAN:
> > +             ptr.p_s32[idx] = ctrl->minimum;
> > +             break;
> > +     case V4L2_CTRL_TYPE_BUTTON:
> > +     case V4L2_CTRL_TYPE_CTRL_CLASS:
> > +             ptr.p_s32[idx] = 0;
> > +             break;
> > +     case V4L2_CTRL_TYPE_U8:
> > +             ptr.p_u8[idx] = ctrl->minimum;
> > +             break;
> > +     case V4L2_CTRL_TYPE_U16:
> > +             ptr.p_u16[idx] = ctrl->minimum;
> > +             break;
> > +     case V4L2_CTRL_TYPE_U32:
> > +             ptr.p_u32[idx] = ctrl->minimum;
> > +             break;
> > +     default:
> > +             std_min_compound(ctrl, idx, ptr);
> > +             break;
> > +     }
> > +}
> > +
> > +static void std_maximum(const struct v4l2_ctrl *ctrl, u32 idx,
> > +                     union v4l2_ctrl_ptr ptr)
> > +{
> > +     switch (ctrl->type) {
> > +     case V4L2_CTRL_TYPE_STRING:
> > +             idx *= ctrl->elem_size;
> > +             memset(ptr.p_char + idx, ' ', ctrl->maximum);
> > +             ptr.p_char[idx + ctrl->maximum] = '\0';
> > +             break;
> > +     case V4L2_CTRL_TYPE_INTEGER64:
> > +             ptr.p_s64[idx] = ctrl->maximum;
> > +             break;
> > +     case V4L2_CTRL_TYPE_INTEGER:
> > +     case V4L2_CTRL_TYPE_INTEGER_MENU:
> > +     case V4L2_CTRL_TYPE_MENU:
> > +     case V4L2_CTRL_TYPE_BITMASK:
> > +     case V4L2_CTRL_TYPE_BOOLEAN:
> > +             ptr.p_s32[idx] = ctrl->maximum;
> > +             break;
> > +     case V4L2_CTRL_TYPE_BUTTON:
> > +     case V4L2_CTRL_TYPE_CTRL_CLASS:
> > +             ptr.p_s32[idx] = 0;
> > +             break;
> > +     case V4L2_CTRL_TYPE_U8:
> > +             ptr.p_u8[idx] = ctrl->maximum;
> > +             break;
> > +     case V4L2_CTRL_TYPE_U16:
> > +             ptr.p_u16[idx] = ctrl->maximum;
> > +             break;
> > +     case V4L2_CTRL_TYPE_U32:
> > +             ptr.p_u32[idx] = ctrl->maximum;
> > +             break;
> > +     default:
> > +             std_max_compound(ctrl, idx, ptr);
> > +             break;
> > +     }
> > +}
> > +
> >  static void std_log(const struct v4l2_ctrl *ctrl)
> >  {
> >       union v4l2_ctrl_ptr ptr = ctrl->p_cur;
> > @@ -986,6 +1084,8 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
> >  static const struct v4l2_ctrl_type_ops std_type_ops = {
> >       .equal = std_equal,
> >       .init = std_init,
> > +     .minimum = std_minimum,
> > +     .maximum = std_maximum,
> >       .log = std_log,
> >       .validate = std_validate,
> >  };
> > @@ -1368,7 +1468,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >                       s64 min, s64 max, u64 step, s64 def,
> >                       const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size,
> >                       u32 flags, const char * const *qmenu,
> > -                     const s64 *qmenu_int, const union v4l2_ctrl_ptr p_def,
> > +                     const s64 *qmenu_int,
> > +                     const union v4l2_ctrl_ptr p_def,
> > +                     const union v4l2_ctrl_ptr p_min,
> > +                     const union v4l2_ctrl_ptr p_max,
> >                       void *priv)
> >  {
> >       struct v4l2_ctrl *ctrl;
> > @@ -1515,7 +1618,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >               sz_extra += 2 * tot_ctrl_size;
> >
> >       if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const)
> > -             sz_extra += elem_size;
> > +             sz_extra += elem_size * 3;
> >
> >       ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
> >       if (ctrl == NULL) {
> > @@ -1565,6 +1668,13 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >               ctrl->p_def.p = ctrl->p_cur.p + tot_ctrl_size;
> >               memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
> >       }
> > +     if (type >= V4L2_CTRL_COMPOUND_TYPES &&
> > +         p_min.p_const && p_max.p_const) {
> > +             ctrl->p_min.p = ctrl->p_cur.p + 2 * tot_ctrl_size;
> > +             memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
> > +             ctrl->p_max.p = ctrl->p_cur.p + 3 * tot_ctrl_size;
> > +             memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
> > +     }
> >
> >       for (idx = 0; idx < elems; idx++) {
> >               ctrl->type_ops->init(ctrl, idx, ctrl->p_cur);
> > @@ -1617,7 +1727,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
> >                       type, min, max,
> >                       is_menu ? cfg->menu_skip_mask : step, def,
> >                       cfg->dims, cfg->elem_size,
> > -                     flags, qmenu, qmenu_int, cfg->p_def, priv);
> > +                     flags, qmenu, qmenu_int, cfg->p_def, cfg->p_min,
> > +                     cfg->p_max, priv);
> >       if (ctrl)
> >               ctrl->is_private = cfg->is_private;
> >       return ctrl;
> > @@ -1642,7 +1753,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
> >       }
> >       return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
> >                            min, max, step, def, NULL, 0,
> > -                          flags, NULL, NULL, ptr_null, NULL);
> > +                          flags, NULL, NULL, ptr_null, ptr_null,
> > +                          ptr_null, NULL);
> >  }
> >  EXPORT_SYMBOL(v4l2_ctrl_new_std);
> >
> > @@ -1675,7 +1787,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
> >       }
> >       return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
> >                            0, max, mask, def, NULL, 0,
> > -                          flags, qmenu, qmenu_int, ptr_null, NULL);
> > +                          flags, qmenu, qmenu_int, ptr_null, ptr_null,
> > +                          ptr_null, NULL);
> >  }
> >  EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
> >
> > @@ -1707,7 +1820,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
> >       }
> >       return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
> >                            0, max, mask, def, NULL, 0,
> > -                          flags, qmenu, NULL, ptr_null, NULL);
> > +                          flags, qmenu, NULL, ptr_null, ptr_null,
> > +                          ptr_null, NULL);
> >
> >  }
> >  EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
> > @@ -1715,7 +1829,9 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
> >  /* Helper function for standard compound controls */
> >  struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
> >                               const struct v4l2_ctrl_ops *ops, u32 id,
> > -                             const union v4l2_ctrl_ptr p_def)
> > +                             const union v4l2_ctrl_ptr p_def,
> > +                             const union v4l2_ctrl_ptr p_min,
> > +                             const union v4l2_ctrl_ptr p_max)
> >  {
> >       const char *name;
> >       enum v4l2_ctrl_type type;
> > @@ -1729,7 +1845,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
> >       }
> >       return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
> >                            min, max, step, def, NULL, 0,
> > -                          flags, NULL, NULL, p_def, NULL);
> > +                          flags, NULL, NULL, p_def, p_min, p_max, NULL);
> >  }
> >  EXPORT_SYMBOL(v4l2_ctrl_new_std_compound);
> >
> > @@ -1753,7 +1869,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
> >       }
> >       return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
> >                            0, max, 0, def, NULL, 0,
> > -                          flags, NULL, qmenu_int, ptr_null, NULL);
> > +                          flags, NULL, qmenu_int, ptr_null, ptr_null,
> > +                          ptr_null, NULL);
> >  }
> >  EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
> >
> > diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> > index 21470de62d72..0f713b9a95f9 100644
> > --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> > +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> > @@ -893,7 +893,9 @@ static bool check_ext_ctrls(struct v4l2_ext_controls *c, unsigned long ioctl)
> >                       return false;
> >               break;
> >       case V4L2_CTRL_WHICH_DEF_VAL:
> > -             /* Default value cannot be changed */
> > +     case V4L2_CTRL_WHICH_MIN_VAL:
> > +     case V4L2_CTRL_WHICH_MAX_VAL:
> > +             /* Default, minimum or maximum value cannot be changed */
> >               if (ioctl == VIDIOC_S_EXT_CTRLS ||
> >                   ioctl == VIDIOC_TRY_EXT_CTRLS) {
> >                       c->error_idx = c->count;
> > diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> > index 919e104de50b..c8ab8c44d7c6 100644
> > --- a/include/media/v4l2-ctrls.h
> > +++ b/include/media/v4l2-ctrls.h
> > @@ -131,6 +131,8 @@ struct v4l2_ctrl_ops {
> >   *
> >   * @equal: return true if both values are equal.
> >   * @init: initialize the value.
> > + * @minimum: set the value to the minimum value of the control.
> > + * @maximum: set the value to the maximum value of the control.
> >   * @log: log the value.
> >   * @validate: validate the value. Return 0 on success and a negative value
> >   *   otherwise.
> > @@ -141,6 +143,10 @@ struct v4l2_ctrl_type_ops {
> >                     union v4l2_ctrl_ptr ptr2);
> >       void (*init)(const struct v4l2_ctrl *ctrl, u32 idx,
> >                    union v4l2_ctrl_ptr ptr);
> > +     void (*minimum)(const struct v4l2_ctrl *ctrl, u32 idx,
> > +                     union v4l2_ctrl_ptr ptr);
> > +     void (*maximum)(const struct v4l2_ctrl *ctrl, u32 idx,
> > +                     union v4l2_ctrl_ptr ptr);
> >       void (*log)(const struct v4l2_ctrl *ctrl);
> >       int (*validate)(const struct v4l2_ctrl *ctrl, u32 idx,
> >                       union v4l2_ctrl_ptr ptr);
> > @@ -237,6 +243,12 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv);
> >   * @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).
> > + * @p_min:   The control's minimum value represented via a union which
> > + *           provides a standard way of accessing control types
> > + *           through a pointer (for compound controls only).
> > + * @p_max:   The control's maximum value represented via a union which
> > + *           provides a standard way of accessing control types
> > + *           through a pointer (for compound controls only).
> >   * @p_cur:   The control's current value represented via a union which
> >   *           provides a standard way of accessing control types
> >   *           through a pointer.
> > @@ -292,6 +304,8 @@ struct v4l2_ctrl {
> >       } cur;
> >
> >       union v4l2_ctrl_ptr p_def;
> > +     union v4l2_ctrl_ptr p_min;
> > +     union v4l2_ctrl_ptr p_max;
> >       union v4l2_ctrl_ptr p_new;
> >       union v4l2_ctrl_ptr p_cur;
> >  };
> > @@ -398,6 +412,8 @@ struct v4l2_ctrl_handler {
> >   * @step:    The control's step value for non-menu controls.
> >   * @def:     The control's default value.
> >   * @p_def:   The control's default value for compound controls.
> > + * @p_min:   The control's minimum value for compound controls.
> > + * @p_max:   The control's maximum value for compound controls.
> >   * @dims:    The size of each dimension.
> >   * @elem_size:       The size in bytes of the control.
> >   * @flags:   The control's flags.
> > @@ -427,6 +443,8 @@ struct v4l2_ctrl_config {
> >       u64 step;
> >       s64 def;
> >       union v4l2_ctrl_ptr p_def;
> > +     union v4l2_ctrl_ptr p_min;
> > +     union v4l2_ctrl_ptr p_max;
> >       u32 dims[V4L2_CTRL_MAX_DIMS];
> >       u32 elem_size;
> >       u32 flags;
> > @@ -696,17 +714,21 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
> >   * @ops:       The control ops.
> >   * @id:        The control ID.
> >   * @p_def:     The control's default value.
> > + * @p_min:     The control's minimum value.
> > + * @p_max:     The control's maximum value.
> >   *
> > - * Sames as v4l2_ctrl_new_std(), but with support to compound controls, thanks
> > - * to the @p_def field. Use v4l2_ctrl_ptr_create() to create @p_def from a
> > - * pointer. Use v4l2_ctrl_ptr_create(NULL) if the default value of the
> > - * compound control should be all zeroes.
> > + * Same as v4l2_ctrl_new_std(), but with support to compound controls, thanks
> > + * to the @p_def/min/max field. Use v4l2_ctrl_ptr_create() to create
>
> s/field/fields/
>
> > + * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
> > + * default/min/max value of the compound control should be all zeroes.
>
> Hmmm... I expect that, for some compound control types, the concept of
> minimum and maximum won't be defined. Wouldn't it better for a get
> control operation on V4L2_CTRL_WHICH_{MIN,MAX}_VAL to return an error in
> that case, instead of zeroed memory ?
>

Returning error for undefined cases sounds good to me.
What about changing std_min_compound to return error for null ptrs?

+static int std_min_compound(const struct v4l2_ctrl *ctrl, u32 idx,
+                            union v4l2_ctrl_ptr ptr)
+{
+       void *p = ptr.p + idx * ctrl->elem_size;
+
+       if (ctrl->p_min.p_const)
+               memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
+       else
+               return -EINVAL;
+}

Best,
Yunke

>
> >   *
> >   */
> >  struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
> >                                            const struct v4l2_ctrl_ops *ops,
> >                                            u32 id,
> > -                                          const union v4l2_ctrl_ptr p_def);
> > +                                          const union v4l2_ctrl_ptr p_def,
> > +                                          const union v4l2_ctrl_ptr p_min,
> > +                                          const union v4l2_ctrl_ptr p_max);
> >
> >  /**
> >   * v4l2_ctrl_new_int_menu() - Create a new standard V4L2 integer menu control.
> > diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> > index 2e36bb610ea6..134f6a65eacc 100644
> > --- a/include/uapi/linux/videodev2.h
> > +++ b/include/uapi/linux/videodev2.h
> > @@ -1796,6 +1796,8 @@ struct v4l2_ext_controls {
> >  #define V4L2_CTRL_WHICH_CUR_VAL   0
> >  #define V4L2_CTRL_WHICH_DEF_VAL   0x0f000000
> >  #define V4L2_CTRL_WHICH_REQUEST_VAL 0x0f010000
> > +#define V4L2_CTRL_WHICH_MIN_VAL   0x0f020000
> > +#define V4L2_CTRL_WHICH_MAX_VAL   0x0f030000
> >
> >  enum v4l2_ctrl_type {
> >       V4L2_CTRL_TYPE_INTEGER       = 1,
>
> --
> Regards,
>
> Laurent Pinchart

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 0/7] media: Implement UVC v1.5 ROI
  2022-07-15  0:52   ` Laurent Pinchart
@ 2022-09-27  4:32     ` Yunke Cao
  0 siblings, 0 replies; 32+ messages in thread
From: Yunke Cao @ 2022-09-27  4:32 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Hans Verkuil, Nicolas Dufresne, Mauro Carvalho Chehab,
	Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Laurent,

Thanks for the review! I've been working on v8 based on your feedback.

I have some questions about your comments and replied in some of the patches.
Would you mind taking another look?

Best,
Yunke

On Fri, Jul 15, 2022 at 9:52 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Yunke,
>
> On Fri, Jul 15, 2022 at 08:25:20AM +0900, Yunke Cao wrote:
> > Hi Laurent,
> >
> > Do you have time to review this version? Ricardo has already reviewed
> > it, I hope it is easier to review now.
>
> I'll try find time, but I doubt it will be before a couple of weeks.
>
> > On Tue, Jun 28, 2022 at 4:57 PM Yunke Cao <yunkec@google.com> wrote:
> > >
> > > This patch set implements UVC v1.5 region of interest using V4L2
> > > control API.
> > >
> > > ROI control is consisted two uvc specific controls.
> > > 1. A rectangle control with a newly added type V4L2_CTRL_TYPE_RECT.
> > > 2. An auto control with type bitmask.
> > >
> > > V4L2_CTRL_WHICH_MIN/MAX_VAL is added to support the rectangle control.
> > >
> > > Tested on two different usb cameras using v4l2-compliance, v4l2-ctl
> > > and calling ioctls.
> > >
> > > 1/7 add V4L2_CTRL_TYPE_RECT.
> > > 2/7 and 3/7 support compound types in UVC.
> > > 4/7 implement ROI in UVC.
> > > 5/7 is a cherry-pick for Hans' implementation of
> > > V4L2_CTRL_WHICH_MIN/MAX_VAL in v4l2-core.
> > > 6/7 support MIN/MAX in UVC.
> > > 7/7 document the changes.
> > >
> > > Changelog since v6:
> > > -Add patch 2 and 3 to support compound types properly in UVC and
> > > implement ROI on top of them.
> > > -Reorder the patches.
> > >
> > > Changelog since v5:
> > > -Add a __uvc_ctrl_get_p_rect_to_user instead of modifying
> > >  __uvc_ctrl_get.
> > > -Support V4L2_CTRL_FLAG_NEXT_COMPOUND correctly.
> > > -Fix formats.
> > >
> > > Changelog since v4:
> > > -Cherry-pick the original patch
> > >  "v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL".
> > > -Split patch "media: uvcvideo: implement UVC v1.5 ROI" into two patches.
> > >  The codes for supporting min/max in uvc are in patch 4/5 now.
> > > -Minor fixes. Detailed changelog in patches
> > >
> > > Changelog since v3:
> > > - Reordered/sliced the patches.
> > >   1. Add rect type.
> > >   2. Add min/max.
> > >   3. Add the roi controls (including init to default).
> > >   4. Document the roi controls.
> > > - Define the roi controls as uvc-specific in uvcvideo.h.
> > > - Modified documentation.
> > > - Removed the vivid change. Given the controls are now uvc-specific.
> > >   I'm not sure how valuable it is to add it in vivid. Let me know
> > >   otherwise.
> > >
> > > Hans Verkuil (1):
> > >   v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
> > >
> > > Yunke Cao (6):
> > >   media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
> > >   media: uvcvideo: add uvc_ctrl_get_fixed for getting default value
> > >   media: uvcvideo: Add support for compound controls
> > >   media: uvcvideo: implement UVC v1.5 ROI
> > >   media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL
> > >   media: uvcvideo: document UVC v1.5 ROI
> > >
> > >  .../userspace-api/media/drivers/uvcvideo.rst  |  61 +++
> > >  .../media/v4l/vidioc-g-ext-ctrls.rst          |  11 +-
> > >  .../media/videodev2.h.rst.exceptions          |   3 +
> > >  drivers/media/i2c/imx214.c                    |   5 +-
> > >  .../media/platform/qcom/venus/venc_ctrls.c    |   4 +
> > >  drivers/media/usb/uvc/uvc_ctrl.c              | 479 ++++++++++++++++--
> > >  drivers/media/usb/uvc/uvc_v4l2.c              |  20 +-
> > >  drivers/media/usb/uvc/uvcvideo.h              |  14 +
> > >  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  51 +-
> > >  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 155 +++++-
> > >  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
> > >  include/media/v4l2-ctrls.h                    |  34 +-
> > >  include/uapi/linux/usb/video.h                |   1 +
> > >  include/uapi/linux/uvcvideo.h                 |  13 +
> > >  include/uapi/linux/v4l2-controls.h            |   8 +
> > >  include/uapi/linux/videodev2.h                |   4 +
> > >  16 files changed, 788 insertions(+), 79 deletions(-)
>
> --
> Regards,
>
> Laurent Pinchart

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 4/7] media: uvcvideo: implement UVC v1.5 ROI
  2022-08-29  5:48     ` Yunke Cao
@ 2022-09-28  8:13       ` Ricardo Ribalda
  0 siblings, 0 replies; 32+ messages in thread
From: Ricardo Ribalda @ 2022-09-28  8:13 UTC (permalink / raw)
  To: Yunke Cao
  Cc: Laurent Pinchart, Hans Verkuil, Nicolas Dufresne,
	Mauro Carvalho Chehab, Tomasz Figa, Sergey Senozhatsky,
	linux-media

On Mon, 29 Aug 2022 at 07:48, Yunke Cao <yunkec@google.com> wrote:
>
> Hi Laurent,
>
> Thanks for the review!
>
> On Thu, Aug 25, 2022 at 5:37 AM Laurent Pinchart
> <laurent.pinchart@ideasonboard.com> wrote:
> >
> > Hi Yunke,
> >
> > Thank you for the patch.
> >
> > On Tue, Jun 28, 2022 at 04:57:02PM +0900, Yunke Cao wrote:
> > > Implement support for ROI as described in UVC 1.5:
> > > 4.2.2.1.20 Digital Region of Interest (ROI) Control
> > >
> > > ROI control is implemented using V4L2 control API as
> > > two uvc-specific controls:
> >
> > s/uvc/UVC/
> >
> > > V4L2_CID_UVC_REGION_OF_INTEREST_RECT and
> > > V4L2_CID_UVC_REGION_OF_INTEREST_AUTO.
> > >
> > > Signed-off-by: Yunke Cao <yunkec@google.com>
> > > ---
> > >  drivers/media/usb/uvc/uvc_ctrl.c   | 146 ++++++++++++++++++++++++++++-
> > >  drivers/media/usb/uvc/uvc_v4l2.c   |   5 +-
> > >  drivers/media/usb/uvc/uvcvideo.h   |   7 ++
> > >  include/uapi/linux/usb/video.h     |   1 +
> > >  include/uapi/linux/uvcvideo.h      |  13 +++
> > >  include/uapi/linux/v4l2-controls.h |   8 ++
> > >  6 files changed, 178 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> > > index 508ee04afbcd..00482269233a 100644
> > > --- a/drivers/media/usb/uvc/uvc_ctrl.c
> > > +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> > > @@ -355,6 +355,23 @@ static const struct uvc_control_info uvc_ctrls[] = {
> > >               .flags          = UVC_CTRL_FLAG_GET_CUR
> > >                               | UVC_CTRL_FLAG_AUTO_UPDATE,
> > >       },
> > > +     /* UVC_CTRL_FLAG_AUTO_UPDATE is needed because the RoI may get updated
> >
> >         /*
> >          * UVC_CTRL_...
> >
> > > +      * by sensors.
> > > +      * "This RoI should be the same as specified in most recent SET_CUR
> > > +      * except in the case where the ‘Auto Detect and Track’ and/or
> > > +      * ‘Image Stabilization’ bit have been set."
> > > +      * 4.2.2.1.20 Digital Region of Interest (ROI) Control
> > > +      */
> > > +     {
> > > +             .entity         = UVC_GUID_UVC_CAMERA,
> > > +             .selector       = UVC_CT_REGION_OF_INTEREST_CONTROL,
> > > +             .index          = 21,
> > > +             .size           = 10,
> > > +             .flags          = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
> > > +                             | UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
> > > +                             | UVC_CTRL_FLAG_GET_DEF
> > > +                             | UVC_CTRL_FLAG_AUTO_UPDATE,
> > > +     },
> > >  };
> > >
> > >  static const u32 uvc_control_classes[] = {
> > > @@ -429,6 +446,64 @@ static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping,
> > >       data[first+1] = min_t(int, abs(value), 0xff);
> > >  }
> > >
> > > +static void uvc_to_v4l2_rect(struct v4l2_rect *v4l2_rect,
> > > +                          const struct uvc_rect *uvc_rect)
> > > +{
> > > +     v4l2_rect->top = uvc_rect->top;
> > > +     v4l2_rect->left = uvc_rect->left;
> > > +     v4l2_rect->height = uvc_rect->bottom - uvc_rect->top + 1;
> > > +     v4l2_rect->width = uvc_rect->right - uvc_rect->left + 1;
> >
> > What happens if the device sends a bogus value with top > bottom or left
> > > right ?
> >
>
> Hmm. I didn't think of this.  I guess we should check and return -EINVAL.
>
> > > +}
> > > +
> > > +static int v4l2_to_uvc_rect(struct uvc_rect *uvc_rect,
> > > +                         const struct v4l2_rect *v4l2_rect)
> > > +{
> > > +     // Safely converts s32 and u32 to u16.
> >
> > C-style comments.
> >
> > > +     if (v4l2_rect->top > U16_MAX || v4l2_rect->top < 0 ||
> > > +         v4l2_rect->left > U16_MAX || v4l2_rect->left < 0 ||
> > > +         v4l2_rect->height > U16_MAX || v4l2_rect->height == 0 ||
> > > +         v4l2_rect->width > U16_MAX || v4l2_rect->width == 0 ||
> > > +         v4l2_rect->height + v4l2_rect->top - 1 > U16_MAX ||
> > > +         v4l2_rect->width + v4l2_rect->left - 1 > U16_MAX)
> > > +             return -ERANGE;
> >
> > Doesn't V4L2 adjust the control value instead ?
> >
>
> videoc-g-ext-ctrls.rst says
> " When the value is out of bounds drivers can choose to take the closest
> valid value or return an ERANGE error code, whatever seems more appropriate."
>
> I chose to return ERANGE because it's a bit easier. Is it acceptable?
>
> > > +
> > > +     uvc_rect->top = v4l2_rect->top;
> > > +     uvc_rect->left = v4l2_rect->left;
> > > +     uvc_rect->bottom = v4l2_rect->height + v4l2_rect->top - 1;
> > > +     uvc_rect->right = v4l2_rect->width + v4l2_rect->left - 1;
> > > +     return 0;
> > > +}
> > > +
> > > +static int uvc_get_array_rect(struct uvc_control_mapping *mapping,
> > > +                           const u8 *data,  u8 *data_out)
> > > +{
> > > +     struct uvc_rect *uvc_rect;
> > > +
> > > +     if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> > > +             return -EINVAL;
> > > +     if (WARN_ON(mapping->size / 8 != sizeof(struct uvc_rect)))
> > > +             return -EINVAL;
> > > +
> >
> > These checks should go to mapping creation time.
> >
> > > +     uvc_rect = (struct uvc_rect *)(data + mapping->offset / 8);
> >
> > If the offset happens to not be a multiple of 2 bytes this will cause
> > unaligned accesses on some platforms.
> >
> > > +     uvc_to_v4l2_rect((struct v4l2_rect *)data_out, uvc_rect);
> >
> > Same here if not aligned on 4 bytes.
> >
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +static int uvc_set_array_rect(struct uvc_control_mapping *mapping,
> > > +                           const u8 *data_in, u8 *data)
> > > +{
> > > +     struct uvc_rect *uvc_rect;
> > > +
> > > +     if (WARN_ON(mapping->offset % 8 || mapping->size % 8))
> > > +             return -EINVAL;
> > > +     if (WARN_ON(mapping->size / 8 != sizeof(struct uvc_rect)))
> > > +             return -EINVAL;
> > > +
> >
> > Same here.
> >
> > > +     uvc_rect = (struct uvc_rect *)(data + mapping->offset / 8);
> > > +     return v4l2_to_uvc_rect(uvc_rect, (struct v4l2_rect *)data_in);
> > > +}
> > > +
> > >  static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >       {
> > >               .id             = V4L2_CID_BRIGHTNESS,
> > > @@ -728,6 +803,26 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
> > >               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
> > >               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
> > >       },
> > > +     {
> > > +             .id             = V4L2_CID_UVC_REGION_OF_INTEREST_RECT,
> > > +             .entity         = UVC_GUID_UVC_CAMERA,
> > > +             .selector       = UVC_CT_REGION_OF_INTEREST_CONTROL,
> > > +             .size           = 64,
> >
> > sizeof(struct uvc_rect)
> >
> > > +             .offset         = 0,
> > > +             .v4l2_type      = V4L2_CTRL_TYPE_RECT,
> > > +             .data_type      = UVC_CTRL_DATA_TYPE_RECT,
> > > +             .get_array      = uvc_get_array_rect,
> > > +             .set_array      = uvc_set_array_rect,
> > > +     },
> > > +     {
> > > +             .id             = V4L2_CID_UVC_REGION_OF_INTEREST_AUTO,
> > > +             .entity         = UVC_GUID_UVC_CAMERA,
> > > +             .selector       = UVC_CT_REGION_OF_INTEREST_CONTROL,
> > > +             .size           = 16,
> > > +             .offset         = 64,
> > > +             .v4l2_type      = V4L2_CTRL_TYPE_BITMASK,
> > > +             .data_type      = UVC_CTRL_DATA_TYPE_BITMASK,

Now that we have a private control, we need to setup the name here for
both controls.


> > > +     },
> > >  };
> > >
> > >  /* ------------------------------------------------------------------------
> > > @@ -1061,7 +1156,16 @@ static int __uvc_ctrl_get_compound_to_user(struct uvc_control_mapping *mapping,
> > >       if (WARN_ON(!mapping->size % 8))
> > >               return -EINVAL;
> > >
> > > -     size = mapping->size / 8;
> > > +     switch (mapping->v4l2_type) {
> > > +     case V4L2_CTRL_TYPE_RECT:
> > > +             size = sizeof(struct v4l2_rect);
> >
> > Oohhh that sounds fragile. I'm worried we may be using mapping->size as
> > the V4L2 control size somewhere else.
> >
> > It would be safer to split the size field of uvc_control_mapping into a
> > v4l2_size and a uvc_size (or possibly data_size to match the v4l2_type
> > and data_type names) in a patch before this. This will force reviewing
> > all the users of the size field to check which size they need.
> >
>
> Thanks for the suggestion! That makes a lot of sense. I will try to implement
> this in the next version, and fix other places you pointed out.
>
> Best,
> Yunke
>
> > > +             break;
> > > +
> > > +     default:
> > > +             size = mapping->size / 8;
> > > +             break;
> > > +     }
> > > +
> > >       if (xctrl->size < size) {
> > >               xctrl->size = size;
> > >               return -ENOSPC;
> > > @@ -2578,6 +2682,40 @@ static void uvc_ctrl_prune_entity(struct uvc_device *dev,
> > >       }
> > >  }
> > >
> > > +static int uvc_ctrl_init_roi(struct uvc_device *dev, struct uvc_control *ctrl)
> > > +{
> > > +     int ret;
> > > +
> > > +     ret = uvc_query_ctrl(dev, UVC_GET_DEF, ctrl->entity->id, dev->intfnum,
> > > +                          UVC_CT_REGION_OF_INTEREST_CONTROL,
> > > +                          uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
> > > +                          ctrl->info.size);
> > > +     if (ret)
> > > +             goto out;
> > > +
> > > +     /*
> > > +      * Most firmwares have wrong GET_CURRENT configuration. E.g. it's
> >
> > s/GET_CURRENT/GET_CUR/
> >
> > > +      * below GET_MIN, or have rectangle coordinates mixed up. This
> > > +      * causes problems sometimes, because we are unable to set
> > > +      * auto-controls value without first setting ROI rectangle to
> > > +      * valid configuration.
> > > +      *
> > > +      * We expect that default configuration is always correct and
> > > +      * is within the GET_MIN / GET_MAX boundaries.
> >
> > A dangerous expectation to make given the quality of most UVC camera
> > firmwares...
> >
> > > +      *
> > > +      * Set current ROI configuration to GET_DEF, so that we will
> > > +      * always have properly configured ROI.
> > > +      */
> > > +     ret = uvc_query_ctrl(dev, UVC_SET_CUR, 1, dev->intfnum,
> > > +                          UVC_CT_REGION_OF_INTEREST_CONTROL,
> > > +                          uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF),
> > > +                          ctrl->info.size);
> > > +out:
> > > +     if (ret)
> > > +             dev_err(&dev->udev->dev, "Failed to fixup ROI (%d).\n", ret);
> >
> > s/(%d)./(%d)/
> >
> > > +     return ret;
> > > +}
> > > +
> > >  /*
> > >   * Add control information and hardcoded stock control mappings to the given
> > >   * device.
> > > @@ -2590,6 +2728,7 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
> > >       const struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
> > >       const struct uvc_control_mapping *mend =
> > >               mapping + ARRAY_SIZE(uvc_ctrl_mappings);
> > > +     const u8 camera_entity[16] = UVC_GUID_UVC_CAMERA;
> > >
> > >       /* XU controls initialization requires querying the device for control
> > >        * information. As some buggy UVC devices will crash when queried
> > > @@ -2610,6 +2749,11 @@ static void uvc_ctrl_init_ctrl(struct uvc_video_chain *chain,
> > >                        * GET_INFO on standard controls.
> > >                        */
> > >                       uvc_ctrl_get_flags(chain->dev, ctrl, &ctrl->info);
> > > +
> > > +                     if (ctrl->info.selector ==
> > > +                             UVC_CT_REGION_OF_INTEREST_CONTROL &&
> > > +                         uvc_entity_match_guid(ctrl->entity, camera_entity))
> > > +                             uvc_ctrl_init_roi(chain->dev, ctrl);
> >
> > Should we instead add an init operation to the uvc_control_info
> > structure ?
> >
> > >                       break;
> > >                }
> > >       }
> > > diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> > > index 0366d05895a9..aad61af36271 100644
> > > --- a/drivers/media/usb/uvc/uvc_v4l2.c
> > > +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> > > @@ -998,7 +998,10 @@ static int uvc_ioctl_query_ext_ctrl(struct file *file, void *fh,
> > >       qec->step = qc.step;
> > >       qec->default_value = qc.default_value;
> > >       qec->flags = qc.flags;
> > > -     qec->elem_size = 4;
> > > +     if (qc.type == V4L2_CTRL_TYPE_RECT)
> > > +             qec->elem_size = sizeof(struct v4l2_rect);
> > > +     else
> > > +             qec->elem_size = 4;
> > >       qec->elems = 1;
> > >       qec->nr_of_dims = 0;
> > >       memset(qec->dims, 0, sizeof(qec->dims));
> > > diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> > > index 2f9b75faae83..9ff95bbad251 100644
> > > --- a/drivers/media/usb/uvc/uvcvideo.h
> > > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > > @@ -431,6 +431,13 @@ struct uvc_streaming_header {
> > >       u8 bTriggerUsage;
> > >  };
> > >
> > > +struct uvc_rect {
> > > +     u16 top;
> > > +     u16 left;
> > > +     u16 bottom;
> > > +     u16 right;
> > > +} __packed;
> > > +
> > >  enum uvc_buffer_state {
> > >       UVC_BUF_STATE_IDLE      = 0,
> > >       UVC_BUF_STATE_QUEUED    = 1,
> > > diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h
> > > index bfdae12cdacf..9076a444758a 100644
> > > --- a/include/uapi/linux/usb/video.h
> > > +++ b/include/uapi/linux/usb/video.h
> > > @@ -104,6 +104,7 @@
> > >  #define UVC_CT_ROLL_ABSOLUTE_CONTROL                 0x0f
> > >  #define UVC_CT_ROLL_RELATIVE_CONTROL                 0x10
> > >  #define UVC_CT_PRIVACY_CONTROL                               0x11
> > > +#define UVC_CT_REGION_OF_INTEREST_CONTROL            0x14
> > >
> > >  /* A.9.5. Processing Unit Control Selectors */
> > >  #define UVC_PU_CONTROL_UNDEFINED                     0x00
> > > diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h
> > > index 8288137387c0..e2feee15cb1b 100644
> > > --- a/include/uapi/linux/uvcvideo.h
> > > +++ b/include/uapi/linux/uvcvideo.h
> > > @@ -16,6 +16,7 @@
> > >  #define UVC_CTRL_DATA_TYPE_BOOLEAN   3
> > >  #define UVC_CTRL_DATA_TYPE_ENUM              4
> > >  #define UVC_CTRL_DATA_TYPE_BITMASK   5
> > > +#define UVC_CTRL_DATA_TYPE_RECT              6
> > >
> > >  /* Control flags */
> > >  #define UVC_CTRL_FLAG_SET_CUR                (1 << 0)
> > > @@ -36,6 +37,18 @@
> > >        UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES | \
> > >        UVC_CTRL_FLAG_GET_DEF)
> > >
> > > +/* V4L2 private controls */
> > > +#define V4L2_CID_UVC_REGION_OF_INTEREST_RECT (V4L2_CID_CAMERA_UVC_BASE + 1)
> > > +#define V4L2_CID_UVC_REGION_OF_INTEREST_AUTO (V4L2_CID_CAMERA_UVC_BASE + 2)
> > > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_EXPOSURE            (1 << 0)
> > > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_IRIS                        (1 << 1)
> > > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_WHITE_BALANCE               (1 << 2)
> > > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_FOCUS                       (1 << 3)
> > > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_FACE_DETECT         (1 << 4)
> > > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_DETECT_AND_TRACK    (1 << 5)
> > > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_IMAGE_STABILIZATION (1 << 6)
> > > +#define V4L2_UVC_REGION_OF_INTEREST_AUTO_HIGHER_QUALITY              (1 << 7)
> > > +
> > >  struct uvc_menu_info {
> > >       __u32 value;
> > >       __u8 name[32];
> > > diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
> > > index dfff69ed88f7..d85be46e46f2 100644
> > > --- a/include/uapi/linux/v4l2-controls.h
> > > +++ b/include/uapi/linux/v4l2-controls.h
> > > @@ -1013,6 +1013,14 @@ enum v4l2_auto_focus_range {
> > >
> > >  #define V4L2_CID_CAMERA_SENSOR_ROTATION              (V4L2_CID_CAMERA_CLASS_BASE+35)
> > >
> > > +/* CAMERA-class private control IDs */
> > > +
> >
> > Why doesn't this go in the driver-specific controls range ?
> >
> > > +/*
> > > + * The base for the uvc driver controls. See linux/uvcvideo.h for the list
> > > + * of controls. We reserve 16 controls for this driver.
> >
> > I'd reserve more just in case, UVC 1.5 has quite a few controls that may
> > not be exposed through standard V4L2 controls, especially for encoding
> > units.
> >
> > > + */
> > > +#define V4L2_CID_CAMERA_UVC_BASE             (V4L2_CID_CAMERA_CLASS_BASE + 0x1000)
> > > +
> > >  /* FM Modulator class control IDs */
> > >
> > >  #define V4L2_CID_FM_TX_CLASS_BASE            (V4L2_CTRL_CLASS_FM_TX | 0x900)
> >
> > --
> > Regards,
> >
> > Laurent Pinchart



-- 
Ricardo Ribalda

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v7 5/7] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
  2022-08-24 16:20   ` Laurent Pinchart
  2022-09-20 10:01     ` Yunke Cao
@ 2022-09-30  7:59     ` Hans Verkuil
  1 sibling, 0 replies; 32+ messages in thread
From: Hans Verkuil @ 2022-09-30  7:59 UTC (permalink / raw)
  To: Laurent Pinchart, Yunke Cao
  Cc: Nicolas Dufresne, Mauro Carvalho Chehab, Tomasz Figa,
	Sergey Senozhatsky, Ricardo Ribalda, linux-media

Hi Yunke, Laurent,

On 8/24/22 18:20, Laurent Pinchart wrote:
> Hi Yunke and Hans,
> 
> Thank you for the patch.
> 
> On Tue, Jun 28, 2022 at 04:57:03PM +0900, Yunke Cao wrote:
>> From: Hans Verkuil <hverkuil-cisco@xs4all.nl>
>>
>> Add the capability of retrieving the min and max values of a
>> compound control.
>>
>> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
>> Signed-off-by: Yunke Cao <yunkec@google.com>
>> ---
>> git am from https://lore.kernel.org/all/20191119113457.57833-3-hverkuil-cisco@xs4all.nl/
>> -Fixed some merge conflits.
>> -Fixed the build error in drivers/media/platform/qcom/venus.
>>
>>  .../media/v4l/vidioc-g-ext-ctrls.rst          |   7 +-
>>  .../media/videodev2.h.rst.exceptions          |   2 +
>>  drivers/media/i2c/imx214.c                    |   5 +-
>>  .../media/platform/qcom/venus/venc_ctrls.c    |   4 +
>>  drivers/media/v4l2-core/v4l2-ctrls-api.c      |  51 +++++--
>>  drivers/media/v4l2-core/v4l2-ctrls-core.c     | 135 ++++++++++++++++--
>>  drivers/media/v4l2-core/v4l2-ioctl.c          |   4 +-
>>  include/media/v4l2-ctrls.h                    |  32 ++++-
>>  include/uapi/linux/videodev2.h                |   2 +
>>  9 files changed, 214 insertions(+), 28 deletions(-)
>>
>> diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>> index 7473baa4e977..65a5f878cc28 100644
>> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
>> @@ -284,14 +284,17 @@ still cause this situation.
>>        - Which value of the control to get/set/try.
>>      * - :cspan:`2` ``V4L2_CTRL_WHICH_CUR_VAL`` will return the current value of
>>  	the control, ``V4L2_CTRL_WHICH_DEF_VAL`` will return the default
>> +	value of the control, ``V4L2_CTRL_WHICH_MIN_VAL`` will return the minimum
>> +	value of the control, ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
> 
> The concept of minimum and maximum values for compound controls can be
> quite ill-defined. Could we document here that the definition of the
> minimum and maximum values are provided by the control documentation for
> compound controls ?

Makes sense! I agree with the updated text in v8.

> 
>>  	value of the control and ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
>>  	these controls have to be retrieved from a request or tried/set for
>>  	a request. In the latter case the ``request_fd`` field contains the
>>  	file descriptor of the request that should be used. If the device
>>  	does not support requests, then ``EACCES`` will be returned.
>>  
>> -	When using ``V4L2_CTRL_WHICH_DEF_VAL`` be aware that you can only
>> -	get the default value of the control, you cannot set or try it.
>> +	When using ``V4L2_CTRL_WHICH_DEF_VAL``, ``V4L2_CTRL_WHICH_MIN_VAL``
>> +	or ``V4L2_CTRL_WHICH_MAX_VAL`` be aware that you can only get the
>> +	default/minimum/maximum value of the control, you cannot set or try it.
>>  
>>  	For backwards compatibility you can also use a control class here
>>  	(see :ref:`ctrl-class`). In that case all controls have to
>> diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
>> index 7b423475281d..e2dde31d76df 100644
>> --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
>> +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
>> @@ -553,6 +553,8 @@ ignore define V4L2_CTRL_DRIVER_PRIV
>>  ignore define V4L2_CTRL_MAX_DIMS
>>  ignore define V4L2_CTRL_WHICH_CUR_VAL
>>  ignore define V4L2_CTRL_WHICH_DEF_VAL
>> +ignore define V4L2_CTRL_WHICH_MIN_VAL
>> +ignore define V4L2_CTRL_WHICH_MAX_VAL
>>  ignore define V4L2_CTRL_WHICH_REQUEST_VAL
>>  ignore define V4L2_OUT_CAP_CUSTOM_TIMINGS
>>  ignore define V4L2_CID_MAX_CTRLS
>> diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c
>> index 83c1737abeec..aa0bfd18f7b7 100644
>> --- a/drivers/media/i2c/imx214.c
>> +++ b/drivers/media/i2c/imx214.c
>> @@ -1037,7 +1037,10 @@ static int imx214_probe(struct i2c_client *client)
>>  	imx214->unit_size = v4l2_ctrl_new_std_compound(&imx214->ctrls,
>>  				NULL,
>>  				V4L2_CID_UNIT_CELL_SIZE,
>> -				v4l2_ctrl_ptr_create((void *)&unit_size));
>> +				v4l2_ctrl_ptr_create((void *)&unit_size),
>> +				v4l2_ctrl_ptr_create(NULL),
>> +				v4l2_ctrl_ptr_create(NULL));
> 
> Would it make sense to set min = max = default for read-only controls ?

No, that doesn't make sense. Read-only controls are only read-only from the PoV of
userspace. The kernel *does* set them, so you still want to have a range of values
that the control can take.

> You should also update the documentation of V4L2_CID_UNIT_CELL_SIZE (and
> other controls below) to indicate how their minimum and maximum are
> defined.
> 
>> +
>>  	ret = imx214->ctrls.error;
>>  	if (ret) {
>>  		dev_err(&client->dev, "%s control init failed (%d)\n",
>> diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
>> index ed44e5800759..4af8f9ca12a6 100644
>> --- a/drivers/media/platform/qcom/venus/venc_ctrls.c
>> +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
>> @@ -579,10 +579,14 @@ int venc_ctrl_init(struct venus_inst *inst)
>>  
>>  	v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
>>  				   V4L2_CID_COLORIMETRY_HDR10_CLL_INFO,
>> +				   v4l2_ctrl_ptr_create(NULL),
>> +				   v4l2_ctrl_ptr_create(NULL),
>>  				   v4l2_ctrl_ptr_create(NULL));
>>  
>>  	v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
>>  				   V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY,
>> +				   v4l2_ctrl_ptr_create(NULL),
>> +				   v4l2_ctrl_ptr_create(NULL),
>>  				   v4l2_ctrl_ptr_create(NULL));
>>  
>>  	v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
>> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
>> index db9baa0bd05f..8a9c816b0dab 100644
>> --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
>> +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
>> @@ -97,6 +97,28 @@ static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
>>  	return ptr_to_user(c, ctrl, ctrl->p_new);
>>  }
>>  
>> +/* Helper function: copy the minimum control value back to the caller */
>> +static int min_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
>> +{
>> +	int idx;
>> +
>> +	for (idx = 0; idx < ctrl->elems; idx++)
>> +		ctrl->type_ops->minimum(ctrl, idx, ctrl->p_new);
>> +
>> +	return ptr_to_user(c, ctrl, ctrl->p_new);
>> +}
>> +
>> +/* Helper function: copy the maximum control value back to the caller */
>> +static int max_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
>> +{
>> +	int idx;
>> +
>> +	for (idx = 0; idx < ctrl->elems; idx++)
>> +		ctrl->type_ops->maximum(ctrl, idx, ctrl->p_new);
>> +
>> +	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,
>> @@ -220,8 +242,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>>  		cs->error_idx = i;
>>  
>>  		if (cs->which &&
>> -		    cs->which != V4L2_CTRL_WHICH_DEF_VAL &&
>> -		    cs->which != V4L2_CTRL_WHICH_REQUEST_VAL &&
>> +		    (cs->which < V4L2_CTRL_WHICH_DEF_VAL ||
>> +		     cs->which > V4L2_CTRL_WHICH_MAX_VAL) &&
>>  		    V4L2_CTRL_ID2WHICH(id) != cs->which) {
>>  			dprintk(vdev,
>>  				"invalid which 0x%x or control id 0x%x\n",
>> @@ -335,8 +357,8 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
>>   */
>>  static int class_check(struct v4l2_ctrl_handler *hdl, u32 which)
>>  {
>> -	if (which == 0 || which == V4L2_CTRL_WHICH_DEF_VAL ||
>> -	    which == V4L2_CTRL_WHICH_REQUEST_VAL)
>> +	if (which == 0 || (which >= V4L2_CTRL_WHICH_DEF_VAL &&
>> +			   which <= V4L2_CTRL_WHICH_MAX_VAL))
>>  		return 0;
>>  	return find_ref_lock(hdl, which | 1) ? 0 : -EINVAL;
>>  }
>> @@ -356,10 +378,12 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
>>  	struct v4l2_ctrl_helper *helpers = helper;
>>  	int ret;
>>  	int i, j;
>> -	bool is_default, is_request;
>> +	bool is_default, is_request, is_min, is_max;
>>  
>>  	is_default = (cs->which == V4L2_CTRL_WHICH_DEF_VAL);
>>  	is_request = (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL);
>> +	is_min = (cs->which == V4L2_CTRL_WHICH_MIN_VAL);
>> +	is_max = (cs->which == V4L2_CTRL_WHICH_MAX_VAL);
>>  
>>  	cs->error_idx = cs->count;
>>  	cs->which = V4L2_CTRL_ID2WHICH(cs->which);
>> @@ -399,13 +423,14 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
>>  
>>  		/*
>>  		 * g_volatile_ctrl will update the new control values.
>> -		 * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL and
>> +		 * This makes no sense for V4L2_CTRL_WHICH_DEF_VAL,
>> +		 * V4L2_CTRL_WHICH_MIN_VAL, V4L2_CTRL_WHICH_MAX_VAL and
>>  		 * V4L2_CTRL_WHICH_REQUEST_VAL. In the case of requests
>>  		 * it is v4l2_ctrl_request_complete() that copies the
>>  		 * volatile controls at the time of request completion
>>  		 * to the request, so you don't want to do that again.
>>  		 */
>> -		if (!is_default && !is_request &&
>> +		if (!is_default && !is_request && !is_min && !is_max &&
>>  		    ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
>>  		    (master->has_volatiles && !is_cur_manual(master)))) {
>>  			for (j = 0; j < master->ncontrols; j++)
>> @@ -432,6 +457,10 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
>>  				ret = def_to_user(cs->controls + idx, ref->ctrl);
>>  			else if (is_request && ref->valid_p_req)
>>  				ret = req_to_user(cs->controls + idx, ref);
>> +			else if (is_min)
>> +				ret = min_to_user(cs->controls + idx, ref->ctrl);
>> +			else if (is_max)
>> +				ret = max_to_user(cs->controls + idx, ref->ctrl);
>>  			else if (is_volatile)
>>  				ret = new_to_user(cs->controls + idx, ref->ctrl);
>>  			else
>> @@ -523,9 +552,11 @@ int try_set_ext_ctrls_common(struct v4l2_fh *fh,
>>  
>>  	cs->error_idx = cs->count;
>>  
>> -	/* Default value cannot be changed */
>> -	if (cs->which == V4L2_CTRL_WHICH_DEF_VAL) {
>> -		dprintk(vdev, "%s: cannot change default value\n",
>> +	/* Default/minimum/maximum values cannot be changed */
>> +	if (cs->which == V4L2_CTRL_WHICH_DEF_VAL ||
>> +	    cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
>> +	    cs->which == V4L2_CTRL_WHICH_MAX_VAL) {
>> +		dprintk(vdev, "%s: cannot change default/min/max value\n",
>>  			video_device_node_name(vdev));
>>  		return -EINVAL;
>>  	}
>> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
>> index 35d43ba650db..8c5828c7c6d3 100644
>> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
>> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
>> @@ -186,6 +186,28 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
>>  	}
>>  }
>>  
>> +static void std_min_compound(const struct v4l2_ctrl *ctrl, u32 idx,
>> +			     union v4l2_ctrl_ptr ptr)
>> +{
>> +	void *p = ptr.p + idx * ctrl->elem_size;
>> +
>> +	if (ctrl->p_min.p_const)
>> +		memcpy(p, ctrl->p_min.p_const, ctrl->elem_size);
>> +	else
>> +		memset(p, 0, ctrl->elem_size);
>> +}
>> +
>> +static void std_max_compound(const struct v4l2_ctrl *ctrl, u32 idx,
>> +			     union v4l2_ctrl_ptr ptr)
>> +{
>> +	void *p = ptr.p + idx * ctrl->elem_size;
>> +
>> +	if (ctrl->p_max.p_const)
>> +		memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
>> +	else
>> +		memset(p, 0, ctrl->elem_size);
>> +}
>> +
>>  static void std_init(const struct v4l2_ctrl *ctrl, u32 idx,
>>  		     union v4l2_ctrl_ptr ptr)
>>  {
>> @@ -224,6 +246,82 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx,
>>  	}
>>  }
>>  
>> +static void std_minimum(const struct v4l2_ctrl *ctrl, u32 idx,
>> +			union v4l2_ctrl_ptr ptr)
>> +{
>> +	switch (ctrl->type) {
>> +	case V4L2_CTRL_TYPE_STRING:
>> +		idx *= ctrl->elem_size;
>> +		memset(ptr.p_char + idx, ' ', ctrl->minimum);
>> +		ptr.p_char[idx + ctrl->minimum] = '\0';
>> +		break;
>> +	case V4L2_CTRL_TYPE_INTEGER64:
>> +		ptr.p_s64[idx] = ctrl->minimum;
>> +		break;
>> +	case V4L2_CTRL_TYPE_INTEGER:
>> +	case V4L2_CTRL_TYPE_INTEGER_MENU:
>> +	case V4L2_CTRL_TYPE_MENU:
>> +	case V4L2_CTRL_TYPE_BITMASK:
>> +	case V4L2_CTRL_TYPE_BOOLEAN:
>> +		ptr.p_s32[idx] = ctrl->minimum;
>> +		break;
>> +	case V4L2_CTRL_TYPE_BUTTON:
>> +	case V4L2_CTRL_TYPE_CTRL_CLASS:
>> +		ptr.p_s32[idx] = 0;
>> +		break;
>> +	case V4L2_CTRL_TYPE_U8:
>> +		ptr.p_u8[idx] = ctrl->minimum;
>> +		break;
>> +	case V4L2_CTRL_TYPE_U16:
>> +		ptr.p_u16[idx] = ctrl->minimum;
>> +		break;
>> +	case V4L2_CTRL_TYPE_U32:
>> +		ptr.p_u32[idx] = ctrl->minimum;
>> +		break;
>> +	default:
>> +		std_min_compound(ctrl, idx, ptr);
>> +		break;
>> +	}
>> +}
>> +
>> +static void std_maximum(const struct v4l2_ctrl *ctrl, u32 idx,
>> +			union v4l2_ctrl_ptr ptr)
>> +{
>> +	switch (ctrl->type) {
>> +	case V4L2_CTRL_TYPE_STRING:
>> +		idx *= ctrl->elem_size;
>> +		memset(ptr.p_char + idx, ' ', ctrl->maximum);
>> +		ptr.p_char[idx + ctrl->maximum] = '\0';
>> +		break;
>> +	case V4L2_CTRL_TYPE_INTEGER64:
>> +		ptr.p_s64[idx] = ctrl->maximum;
>> +		break;
>> +	case V4L2_CTRL_TYPE_INTEGER:
>> +	case V4L2_CTRL_TYPE_INTEGER_MENU:
>> +	case V4L2_CTRL_TYPE_MENU:
>> +	case V4L2_CTRL_TYPE_BITMASK:
>> +	case V4L2_CTRL_TYPE_BOOLEAN:
>> +		ptr.p_s32[idx] = ctrl->maximum;
>> +		break;
>> +	case V4L2_CTRL_TYPE_BUTTON:
>> +	case V4L2_CTRL_TYPE_CTRL_CLASS:
>> +		ptr.p_s32[idx] = 0;
>> +		break;
>> +	case V4L2_CTRL_TYPE_U8:
>> +		ptr.p_u8[idx] = ctrl->maximum;
>> +		break;
>> +	case V4L2_CTRL_TYPE_U16:
>> +		ptr.p_u16[idx] = ctrl->maximum;
>> +		break;
>> +	case V4L2_CTRL_TYPE_U32:
>> +		ptr.p_u32[idx] = ctrl->maximum;
>> +		break;
>> +	default:
>> +		std_max_compound(ctrl, idx, ptr);
>> +		break;
>> +	}
>> +}
>> +
>>  static void std_log(const struct v4l2_ctrl *ctrl)
>>  {
>>  	union v4l2_ctrl_ptr ptr = ctrl->p_cur;
>> @@ -986,6 +1084,8 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx,
>>  static const struct v4l2_ctrl_type_ops std_type_ops = {
>>  	.equal = std_equal,
>>  	.init = std_init,
>> +	.minimum = std_minimum,
>> +	.maximum = std_maximum,
>>  	.log = std_log,
>>  	.validate = std_validate,
>>  };
>> @@ -1368,7 +1468,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>>  			s64 min, s64 max, u64 step, s64 def,
>>  			const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size,
>>  			u32 flags, const char * const *qmenu,
>> -			const s64 *qmenu_int, const union v4l2_ctrl_ptr p_def,
>> +			const s64 *qmenu_int,
>> +			const union v4l2_ctrl_ptr p_def,
>> +			const union v4l2_ctrl_ptr p_min,
>> +			const union v4l2_ctrl_ptr p_max,
>>  			void *priv)
>>  {
>>  	struct v4l2_ctrl *ctrl;
>> @@ -1515,7 +1618,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>>  		sz_extra += 2 * tot_ctrl_size;
>>  
>>  	if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const)
>> -		sz_extra += elem_size;
>> +		sz_extra += elem_size * 3;
>>  
>>  	ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
>>  	if (ctrl == NULL) {
>> @@ -1565,6 +1668,13 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>>  		ctrl->p_def.p = ctrl->p_cur.p + tot_ctrl_size;
>>  		memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
>>  	}
>> +	if (type >= V4L2_CTRL_COMPOUND_TYPES &&
>> +	    p_min.p_const && p_max.p_const) {
>> +		ctrl->p_min.p = ctrl->p_cur.p + 2 * tot_ctrl_size;
>> +		memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
>> +		ctrl->p_max.p = ctrl->p_cur.p + 3 * tot_ctrl_size;
>> +		memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
>> +	}
>>  
>>  	for (idx = 0; idx < elems; idx++) {
>>  		ctrl->type_ops->init(ctrl, idx, ctrl->p_cur);
>> @@ -1617,7 +1727,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
>>  			type, min, max,
>>  			is_menu ? cfg->menu_skip_mask : step, def,
>>  			cfg->dims, cfg->elem_size,
>> -			flags, qmenu, qmenu_int, cfg->p_def, priv);
>> +			flags, qmenu, qmenu_int, cfg->p_def, cfg->p_min,
>> +			cfg->p_max, priv);
>>  	if (ctrl)
>>  		ctrl->is_private = cfg->is_private;
>>  	return ctrl;
>> @@ -1642,7 +1753,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
>>  	}
>>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>>  			     min, max, step, def, NULL, 0,
>> -			     flags, NULL, NULL, ptr_null, NULL);
>> +			     flags, NULL, NULL, ptr_null, ptr_null,
>> +			     ptr_null, NULL);
>>  }
>>  EXPORT_SYMBOL(v4l2_ctrl_new_std);
>>  
>> @@ -1675,7 +1787,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
>>  	}
>>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>>  			     0, max, mask, def, NULL, 0,
>> -			     flags, qmenu, qmenu_int, ptr_null, NULL);
>> +			     flags, qmenu, qmenu_int, ptr_null, ptr_null,
>> +			     ptr_null, NULL);
>>  }
>>  EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
>>  
>> @@ -1707,7 +1820,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
>>  	}
>>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>>  			     0, max, mask, def, NULL, 0,
>> -			     flags, qmenu, NULL, ptr_null, NULL);
>> +			     flags, qmenu, NULL, ptr_null, ptr_null,
>> +			     ptr_null, NULL);
>>  
>>  }
>>  EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
>> @@ -1715,7 +1829,9 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items);
>>  /* Helper function for standard compound controls */
>>  struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
>>  				const struct v4l2_ctrl_ops *ops, u32 id,
>> -				const union v4l2_ctrl_ptr p_def)
>> +				const union v4l2_ctrl_ptr p_def,
>> +				const union v4l2_ctrl_ptr p_min,
>> +				const union v4l2_ctrl_ptr p_max)
>>  {
>>  	const char *name;
>>  	enum v4l2_ctrl_type type;
>> @@ -1729,7 +1845,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
>>  	}
>>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>>  			     min, max, step, def, NULL, 0,
>> -			     flags, NULL, NULL, p_def, NULL);
>> +			     flags, NULL, NULL, p_def, p_min, p_max, NULL);
>>  }
>>  EXPORT_SYMBOL(v4l2_ctrl_new_std_compound);
>>  
>> @@ -1753,7 +1869,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
>>  	}
>>  	return v4l2_ctrl_new(hdl, ops, NULL, id, name, type,
>>  			     0, max, 0, def, NULL, 0,
>> -			     flags, NULL, qmenu_int, ptr_null, NULL);
>> +			     flags, NULL, qmenu_int, ptr_null, ptr_null,
>> +			     ptr_null, NULL);
>>  }
>>  EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
>>  
>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
>> index 21470de62d72..0f713b9a95f9 100644
>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>> @@ -893,7 +893,9 @@ static bool check_ext_ctrls(struct v4l2_ext_controls *c, unsigned long ioctl)
>>  			return false;
>>  		break;
>>  	case V4L2_CTRL_WHICH_DEF_VAL:
>> -		/* Default value cannot be changed */
>> +	case V4L2_CTRL_WHICH_MIN_VAL:
>> +	case V4L2_CTRL_WHICH_MAX_VAL:
>> +		/* Default, minimum or maximum value cannot be changed */
>>  		if (ioctl == VIDIOC_S_EXT_CTRLS ||
>>  		    ioctl == VIDIOC_TRY_EXT_CTRLS) {
>>  			c->error_idx = c->count;
>> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
>> index 919e104de50b..c8ab8c44d7c6 100644
>> --- a/include/media/v4l2-ctrls.h
>> +++ b/include/media/v4l2-ctrls.h
>> @@ -131,6 +131,8 @@ struct v4l2_ctrl_ops {
>>   *
>>   * @equal: return true if both values are equal.
>>   * @init: initialize the value.
>> + * @minimum: set the value to the minimum value of the control.
>> + * @maximum: set the value to the maximum value of the control.
>>   * @log: log the value.
>>   * @validate: validate the value. Return 0 on success and a negative value
>>   *	otherwise.
>> @@ -141,6 +143,10 @@ struct v4l2_ctrl_type_ops {
>>  		      union v4l2_ctrl_ptr ptr2);
>>  	void (*init)(const struct v4l2_ctrl *ctrl, u32 idx,
>>  		     union v4l2_ctrl_ptr ptr);
>> +	void (*minimum)(const struct v4l2_ctrl *ctrl, u32 idx,
>> +			union v4l2_ctrl_ptr ptr);
>> +	void (*maximum)(const struct v4l2_ctrl *ctrl, u32 idx,
>> +			union v4l2_ctrl_ptr ptr);
>>  	void (*log)(const struct v4l2_ctrl *ctrl);
>>  	int (*validate)(const struct v4l2_ctrl *ctrl, u32 idx,
>>  			union v4l2_ctrl_ptr ptr);
>> @@ -237,6 +243,12 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv);
>>   * @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).
>> + * @p_min:	The control's minimum value represented via a union which
>> + *		provides a standard way of accessing control types
>> + *		through a pointer (for compound controls only).
>> + * @p_max:	The control's maximum value represented via a union which
>> + *		provides a standard way of accessing control types
>> + *		through a pointer (for compound controls only).
>>   * @p_cur:	The control's current value represented via a union which
>>   *		provides a standard way of accessing control types
>>   *		through a pointer.
>> @@ -292,6 +304,8 @@ struct v4l2_ctrl {
>>  	} cur;
>>  
>>  	union v4l2_ctrl_ptr p_def;
>> +	union v4l2_ctrl_ptr p_min;
>> +	union v4l2_ctrl_ptr p_max;
>>  	union v4l2_ctrl_ptr p_new;
>>  	union v4l2_ctrl_ptr p_cur;
>>  };
>> @@ -398,6 +412,8 @@ struct v4l2_ctrl_handler {
>>   * @step:	The control's step value for non-menu controls.
>>   * @def:	The control's default value.
>>   * @p_def:	The control's default value for compound controls.
>> + * @p_min:	The control's minimum value for compound controls.
>> + * @p_max:	The control's maximum value for compound controls.
>>   * @dims:	The size of each dimension.
>>   * @elem_size:	The size in bytes of the control.
>>   * @flags:	The control's flags.
>> @@ -427,6 +443,8 @@ struct v4l2_ctrl_config {
>>  	u64 step;
>>  	s64 def;
>>  	union v4l2_ctrl_ptr p_def;
>> +	union v4l2_ctrl_ptr p_min;
>> +	union v4l2_ctrl_ptr p_max;
>>  	u32 dims[V4L2_CTRL_MAX_DIMS];
>>  	u32 elem_size;
>>  	u32 flags;
>> @@ -696,17 +714,21 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl,
>>   * @ops:       The control ops.
>>   * @id:        The control ID.
>>   * @p_def:     The control's default value.
>> + * @p_min:     The control's minimum value.
>> + * @p_max:     The control's maximum value.
>>   *
>> - * Sames as v4l2_ctrl_new_std(), but with support to compound controls, thanks
>> - * to the @p_def field. Use v4l2_ctrl_ptr_create() to create @p_def from a
>> - * pointer. Use v4l2_ctrl_ptr_create(NULL) if the default value of the
>> - * compound control should be all zeroes.
>> + * Same as v4l2_ctrl_new_std(), but with support to compound controls, thanks
>> + * to the @p_def/min/max field. Use v4l2_ctrl_ptr_create() to create
> 
> s/field/fields/

You forgot to fix this in v8.

> 
>> + * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
>> + * default/min/max value of the compound control should be all zeroes.
> 
> Hmmm... I expect that, for some compound control types, the concept of
> minimum and maximum won't be defined. Wouldn't it better for a get
> control operation on V4L2_CTRL_WHICH_{MIN,MAX}_VAL to return an error in
> that case, instead of zeroed memory ?

I agree with that. The error code needs to be documented, though. And I
don't see that in v8.

If I understand it correctly, then you return -EINVAL in v8 if no min or max
info is available. I'm not so keen on that since it isn't an invalid argument.
Perhaps -ENODATA?

Regards,

	Hans

> 
>>   *
>>   */
>>  struct v4l2_ctrl *v4l2_ctrl_new_std_compound(struct v4l2_ctrl_handler *hdl,
>>  					     const struct v4l2_ctrl_ops *ops,
>>  					     u32 id,
>> -					     const union v4l2_ctrl_ptr p_def);
>> +					     const union v4l2_ctrl_ptr p_def,
>> +					     const union v4l2_ctrl_ptr p_min,
>> +					     const union v4l2_ctrl_ptr p_max);
>>  
>>  /**
>>   * v4l2_ctrl_new_int_menu() - Create a new standard V4L2 integer menu control.
>> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
>> index 2e36bb610ea6..134f6a65eacc 100644
>> --- a/include/uapi/linux/videodev2.h
>> +++ b/include/uapi/linux/videodev2.h
>> @@ -1796,6 +1796,8 @@ struct v4l2_ext_controls {
>>  #define V4L2_CTRL_WHICH_CUR_VAL   0
>>  #define V4L2_CTRL_WHICH_DEF_VAL   0x0f000000
>>  #define V4L2_CTRL_WHICH_REQUEST_VAL 0x0f010000
>> +#define V4L2_CTRL_WHICH_MIN_VAL   0x0f020000
>> +#define V4L2_CTRL_WHICH_MAX_VAL   0x0f030000
>>  
>>  enum v4l2_ctrl_type {
>>  	V4L2_CTRL_TYPE_INTEGER	     = 1,
> 

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2022-09-30  7:59 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-28  7:56 [PATCH v7 0/7] media: Implement UVC v1.5 ROI Yunke Cao
2022-06-28  7:56 ` [PATCH v7 1/7] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
2022-08-24  8:40   ` Laurent Pinchart
2022-08-24  8:50   ` Hans Verkuil
2022-08-24  8:51     ` Hans Verkuil
2022-08-25  6:15     ` Yunke Cao
2022-06-28  7:57 ` [PATCH v7 2/7] media: uvcvideo: add uvc_ctrl_get_fixed for getting default value Yunke Cao
2022-08-24 16:24   ` Laurent Pinchart
2022-06-28  7:57 ` [PATCH v7 3/7] media: uvcvideo: Add support for compound controls Yunke Cao
2022-06-30  6:42   ` Ricardo Ribalda
2022-08-24 19:56   ` Laurent Pinchart
2022-08-29  5:39     ` Yunke Cao
2022-06-28  7:57 ` [PATCH v7 4/7] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
2022-06-30  6:42   ` Ricardo Ribalda
2022-08-24 20:36   ` Laurent Pinchart
2022-08-29  5:48     ` Yunke Cao
2022-09-28  8:13       ` Ricardo Ribalda
2022-06-28  7:57 ` [PATCH v7 5/7] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
2022-08-24 16:20   ` Laurent Pinchart
2022-09-20 10:01     ` Yunke Cao
2022-09-30  7:59     ` Hans Verkuil
2022-06-28  7:57 ` [PATCH v7 6/7] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
2022-06-30  6:41   ` Ricardo Ribalda
2022-08-24 20:03   ` Laurent Pinchart
2022-09-20  7:52     ` Yunke Cao
2022-06-28  7:57 ` [PATCH v7 7/7] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
2022-06-30  6:41   ` Ricardo Ribalda
2022-08-24 20:17   ` Laurent Pinchart
2022-09-20  7:46     ` Yunke Cao
2022-07-14 23:25 ` [PATCH v7 0/7] media: Implement " Yunke Cao
2022-07-15  0:52   ` Laurent Pinchart
2022-09-27  4:32     ` Yunke Cao

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.