* [PATCH v12 00/11] Implement UVC v1.5 ROI
@ 2023-08-17 7:17 Yunke Cao
2023-08-17 7:17 ` [PATCH v12 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
` (11 more replies)
0 siblings, 12 replies; 17+ messages in thread
From: Yunke Cao @ 2023-08-17 7:17 UTC (permalink / raw)
To: Hans Verkuil, Laurent Pinchart, Daniel Scally
Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao
Hi,
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.
A patchset in v4l-utils will be sent separately.
Tested with v4l2-compliance, v4l2-ctl, calling ioctls on usb cameras and
VIVID with a newly added V4L2_CTRL_TYPE_RECT control.
1/11 adds V4L2_CTRL_TYPE_RECT.
2/11, 3/11, 4/11 refactors uvc_ctrl.c.
5/11 adds support for compound controls.
6/11 is a cherry-pick for Hans' implementation of
V4L2_CTRL_WHICH_MIN/MAX_VAL in v4l2-core.
7/11 addes the rectangle control in VIVID.
8/11 supports MIN/MAX in UVC.
9/11 implements ROI in UVC.
10/11 initializes ROI control to default value.
11/11 documents the ROI changes.
Changes since v11
- Git rid of media: v4l2: document the usage of min/max for V4L2_CTRL_TYPE_RECT.
- Added a rectangle control in VIVID.
- Addressed comments in 6/11, details in patch.
Hans Verkuil (1):
v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
Yunke Cao (10):
media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
media: uvcvideo: add uvc_ctrl_get_boundary for getting default value
media: uvcvideo: introduce __uvc_ctrl_get_std()
media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size
media: uvcvideo: Add support for compound controls
media: vivid: Add an rectangle control
media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL
media: uvcvideo: implement UVC v1.5 ROI
media: uvcvideo: initilaize ROI control to default value
media: uvcvideo: document UVC v1.5 ROI
.../userspace-api/media/drivers/uvcvideo.rst | 64 +-
.../media/v4l/vidioc-g-ext-ctrls.rst | 20 +-
.../media/v4l/vidioc-queryctrl.rst | 14 +
.../media/videodev2.h.rst.exceptions | 4 +
drivers/media/i2c/imx214.c | 5 +-
.../media/platform/qcom/venus/venc_ctrls.c | 9 +-
.../media/test-drivers/vivid/vivid-ctrls.c | 34 +
drivers/media/usb/uvc/uvc_ctrl.c | 721 ++++++++++++++----
drivers/media/usb/uvc/uvc_v4l2.c | 18 +-
drivers/media/usb/uvc/uvcvideo.h | 24 +-
drivers/media/v4l2-core/v4l2-ctrls-api.c | 54 +-
drivers/media/v4l2-core/v4l2-ctrls-core.c | 176 ++++-
drivers/media/v4l2-core/v4l2-ioctl.c | 4 +-
include/media/v4l2-ctrls.h | 36 +-
include/uapi/linux/usb/video.h | 1 +
include/uapi/linux/uvcvideo.h | 13 +
include/uapi/linux/v4l2-controls.h | 9 +
include/uapi/linux/videodev2.h | 5 +
18 files changed, 1001 insertions(+), 210 deletions(-)
--
2.41.0.694.ge786442a9b-goog
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v12 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
2023-08-17 7:17 [PATCH v12 00/11] Implement UVC v1.5 ROI Yunke Cao
@ 2023-08-17 7:17 ` Yunke Cao
2023-08-17 7:17 ` [PATCH v12 02/11] media: uvcvideo: add uvc_ctrl_get_boundary for getting default value Yunke Cao
` (10 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Yunke Cao @ 2023-08-17 7:17 UTC (permalink / raw)
To: Hans Verkuil, Laurent Pinchart, Daniel Scally
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: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Signed-off-by: Yunke Cao <yunkec@google.com>
Reviewed-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
Changelog since v11:
- Added reviewed-by from Hans
Changelog since v10:
- Added reviewed-by from Sergey and Daniel.
Changelog since v9:
- No Change.
Changelog since v8:
- No change.
Changelog since v7:
- Document V4L2_CTRL_TYPE_RECT in vidioc-queryctrl.rst.
- Rebased to media-stage master.
- Do not assign each field in std_equal
.../media/v4l/vidioc-g-ext-ctrls.rst | 4 ++++
.../userspace-api/media/v4l/vidioc-queryctrl.rst | 7 +++++++
.../media/videodev2.h.rst.exceptions | 1 +
drivers/media/v4l2-core/v4l2-ctrls-core.c | 16 +++++++++++++++-
include/media/v4l2-ctrls.h | 2 ++
include/uapi/linux/videodev2.h | 2 ++
6 files changed, 31 insertions(+), 1 deletion(-)
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 f9f73530a6be..7b1001d11f9c 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
@@ -199,6 +199,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/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
index 4d38acafe8e1..56d5c8b0b88b 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
@@ -441,6 +441,13 @@ See also the examples in :ref:`control`.
- n/a
- A struct :c:type:`v4l2_area`, containing the width and the height
of a rectangular area. Units depend on the use case.
+ * - ``V4L2_CTRL_TYPE_RECT``
+ - n/a
+ - n/a
+ - n/a
+ - A struct :c:type:`v4l2_rect`, containing a rectangle described by
+ the position of its top-left corner, the width and the height. Units
+ depend on the use case.
* - ``V4L2_CTRL_TYPE_H264_SPS``
- n/a
- n/a
diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
index 3e58aac4ef0b..c46082ef0e4d 100644
--- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
+++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
@@ -150,6 +150,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 a662fb60f73f..f1486ab032cf 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -367,7 +367,11 @@ void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
case V4L2_CTRL_TYPE_AV1_FILM_GRAIN:
pr_cont("AV1_FILM_GRAIN");
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;
@@ -812,6 +816,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;
@@ -1169,6 +1174,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;
}
@@ -1868,6 +1879,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 59679a42b3e7..b0db167a3ac4 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -56,6 +56,7 @@ struct video_device;
* @p_av1_tile_group_entry: Pointer to an AV1 tile group entry structure.
* @p_av1_frame: Pointer to an AV1 frame structure.
* @p_av1_film_grain: Pointer to an AV1 film grain structure.
+ * @p_rect: Pointer to a rectangle.
* @p: Pointer to a compound value.
* @p_const: Pointer to a constant compound value.
*/
@@ -89,6 +90,7 @@ union v4l2_ctrl_ptr {
struct v4l2_ctrl_av1_tile_group_entry *p_av1_tile_group_entry;
struct v4l2_ctrl_av1_frame *p_av1_frame;
struct v4l2_ctrl_av1_film_grain *p_av1_film_grain;
+ 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 3af6a82d0cad..089d553cf736 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -1812,6 +1812,7 @@ struct v4l2_ext_control {
__s32 __user *p_s32;
__s64 __user *p_s64;
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;
@@ -1880,6 +1881,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.41.0.694.ge786442a9b-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v12 02/11] media: uvcvideo: add uvc_ctrl_get_boundary for getting default value
2023-08-17 7:17 [PATCH v12 00/11] Implement UVC v1.5 ROI Yunke Cao
2023-08-17 7:17 ` [PATCH v12 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
@ 2023-08-17 7:17 ` Yunke Cao
2023-08-17 7:17 ` [PATCH v12 03/11] media: uvcvideo: introduce __uvc_ctrl_get_std() Yunke Cao
` (9 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Yunke Cao @ 2023-08-17 7:17 UTC (permalink / raw)
To: Hans Verkuil, Laurent Pinchart, Daniel Scally
Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao
Introduce uvc_ctrl_get_boundary(), making it easier to extend to
support compound controls and V4L2_CTRL_WHICH_MIN/MAX_VAL in the
following patches.
For now, it simply returns EINVAL for compound controls and calls
__query_v4l2_ctrl() for non-compound controls.
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Signed-off-by: Yunke Cao <yunkec@google.com>
---
An alternative name is uvc_ctrl_get_statc(). Let me know if that's better.
Changelog since v11:
- No change.
Changelog since v10:
- Fix __uvc_ctrl_get_boundary_std() typo causing build error.
- Added Reviewed-by from Sergey.
Changelog since v9:
- Make __uvc_ctrl_get_boundary_std() static.
Changelog since v8:
- No Change.
Changelog since v7:
- Rename uvc_ctrl_get_fixed() to uvc_ctrl_get_boundary().
- Move refactor (introduce __uvc_ctrl_get_boundary_std()) in this patch
instead of in the patch where we implement compound control.
- Fix locking. uvc_ctrl_get_boundary() now acquires ctrl_mutex.
__uvc_ctrl_get_boundary_std() calls __uvc_query_v4l2_ctrl() while
holding the mutex.
- Define a uvc_ctrl_mapping_is_compound() for readability.
drivers/media/usb/uvc/uvc_ctrl.c | 48 ++++++++++++++++++++++++++++++++
drivers/media/usb/uvc/uvc_v4l2.c | 6 +---
drivers/media/usb/uvc/uvcvideo.h | 2 ++
3 files changed, 51 insertions(+), 5 deletions(-)
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 5e9d3da862dd..4a4ceb6289f5 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -884,6 +884,12 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
}
}
+static bool
+uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
+{
+ return mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES;
+}
+
/* ------------------------------------------------------------------------
* Terminal and unit management
*/
@@ -1877,6 +1883,48 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
}
+static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
+ struct uvc_control *ctrl,
+ struct uvc_control_mapping *mapping,
+ struct v4l2_ext_control *xctrl)
+{
+ struct v4l2_queryctrl qc = { .id = xctrl->id };
+
+ int ret = __uvc_query_v4l2_ctrl(chain, ctrl, mapping, &qc);
+
+ if (ret < 0)
+ return ret;
+
+ xctrl->value = qc.default_value;
+ return 0;
+}
+
+int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
+ struct v4l2_ext_control *xctrl)
+{
+ struct uvc_control *ctrl;
+ struct uvc_control_mapping *mapping;
+ int ret;
+
+ if (mutex_lock_interruptible(&chain->ctrl_mutex))
+ return -ERESTARTSYS;
+
+ ctrl = uvc_find_control(chain, xctrl->id, &mapping);
+ if (!ctrl) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (uvc_ctrl_mapping_is_compound(mapping))
+ ret = -EINVAL;
+ else
+ ret = __uvc_ctrl_get_boundary_std(chain, ctrl, mapping, xctrl);
+
+done:
+ mutex_unlock(&chain->ctrl_mutex);
+ return ret;
+}
+
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 5ac2a424b13d..656cf0fbf7a3 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1096,15 +1096,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_boundary(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 6fb0a78b1b00..5091085fcfb0 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -781,6 +781,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_boundary(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,
const struct v4l2_ext_controls *ctrls,
--
2.41.0.694.ge786442a9b-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v12 03/11] media: uvcvideo: introduce __uvc_ctrl_get_std()
2023-08-17 7:17 [PATCH v12 00/11] Implement UVC v1.5 ROI Yunke Cao
2023-08-17 7:17 ` [PATCH v12 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
2023-08-17 7:17 ` [PATCH v12 02/11] media: uvcvideo: add uvc_ctrl_get_boundary for getting default value Yunke Cao
@ 2023-08-17 7:17 ` Yunke Cao
2023-08-17 7:17 ` [PATCH v12 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size Yunke Cao
` (8 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Yunke Cao @ 2023-08-17 7:17 UTC (permalink / raw)
To: Hans Verkuil, Laurent Pinchart, Daniel Scally
Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao
Refactor uvc_ctrl to make adding compound control easier.
Currently __uvc_ctrl_get() only work for non-compound controls.
Move the logic into __uvc_ctrl_std(), return error for compound
controls.
Make __uvc_ctrl_get() call __uvc_ctrl_std() inside. Also modify some of the
callers of __uvc_ctrl_get() to call __uvc_ctrl_get_std() instead.
Signed-off-by: Yunke Cao <yunkec@google.com>
---
Changelog since v11:
- Minor change to avoid negative if statement.
Changelog since v10:
- Better commit message.
Changelog since v9:
- No change.
Changelog since v8:
- No change.
Changelog since v7:
- Newly added patch. Split the refactoring of uvc_ctrl_get from v7 3/7.
drivers/media/usb/uvc/uvc_ctrl.c | 42 +++++++++++++++++++++-----------
1 file changed, 28 insertions(+), 14 deletions(-)
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 4a4ceb6289f5..b73a7e3812cf 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1081,15 +1081,15 @@ static int __uvc_ctrl_load_cur(struct uvc_video_chain *chain,
return ret;
}
-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 (uvc_ctrl_mapping_is_compound(mapping))
+ return -EINVAL;
ret = __uvc_ctrl_load_cur(chain, ctrl);
if (ret < 0)
@@ -1199,7 +1199,7 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
return 0;
- ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
+ ret = __uvc_ctrl_get_std(chain, master_ctrl, master_map, &val);
if (ret >= 0 && val != mapping->master_manual)
return -EACCES;
@@ -1264,8 +1264,13 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
__uvc_find_control(ctrl->entity, mapping->master_id,
&master_map, &master_ctrl, 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);
+ s32 val = 0;
+ int ret;
+
+ if (uvc_ctrl_mapping_is_compound(master_map))
+ return -EINVAL;
+
+ ret = __uvc_ctrl_get_std(chain, master_ctrl, master_map, &val);
if (ret < 0)
return ret;
@@ -1529,7 +1534,8 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
if (ctrl == NULL)
return;
- if (__uvc_ctrl_get(chain, ctrl, mapping, &val) == 0)
+ if (uvc_ctrl_mapping_is_compound(mapping) ||
+ __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);
@@ -1700,7 +1706,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 (uvc_ctrl_mapping_is_compound(mapping) ||
+ __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,
@@ -1880,7 +1887,10 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
if (ctrl == NULL)
return -EINVAL;
- return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
+ if (uvc_ctrl_mapping_is_compound(mapping))
+ return -EINVAL;
+ else
+ return __uvc_ctrl_get_std(chain, ctrl, mapping, &xctrl->value);
}
static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
@@ -2039,8 +2049,12 @@ int uvc_ctrl_set(struct uvc_fh *handle,
ctrl->info.size);
}
- mapping->set(mapping, value,
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
+ if (uvc_ctrl_mapping_is_compound(mapping))
+ return -EINVAL;
+ else
+ mapping->set(mapping, value,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
+
if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
ctrl->handle = handle;
--
2.41.0.694.ge786442a9b-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v12 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size
2023-08-17 7:17 [PATCH v12 00/11] Implement UVC v1.5 ROI Yunke Cao
` (2 preceding siblings ...)
2023-08-17 7:17 ` [PATCH v12 03/11] media: uvcvideo: introduce __uvc_ctrl_get_std() Yunke Cao
@ 2023-08-17 7:17 ` Yunke Cao
2023-08-17 7:17 ` [PATCH v12 05/11] media: uvcvideo: Add support for compound controls Yunke Cao
` (7 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Yunke Cao @ 2023-08-17 7:17 UTC (permalink / raw)
To: Hans Verkuil, Laurent Pinchart, Daniel Scally
Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao
Rename the existing size to data_size to represent uvc control data size,
add a separate field for v4l2 control size. v4l2 control size will be
used the compound controls.
Also modify the uvc driver documents to clarify the size in
uvc_xu_control_mapping corresponds to the uvc control data size.
Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>
Signed-off-by: Yunke Cao <yunkec@google.com>
---
Changelog since v11:
- No change.
Changelog since v10:
- Added Reviewed-by from Daniel Scally.
Changelog since v9:
- No change.
Changelog since v8:
- No change.
Changelog since v7:
- Newly added patch.
.../userspace-api/media/drivers/uvcvideo.rst | 2 +-
drivers/media/usb/uvc/uvc_ctrl.c | 80 +++++++++----------
drivers/media/usb/uvc/uvc_v4l2.c | 2 +-
drivers/media/usb/uvc/uvcvideo.h | 6 +-
4 files changed, 47 insertions(+), 43 deletions(-)
diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
index a290f9fadae9..aab4304e6bb5 100644
--- a/Documentation/userspace-api/media/drivers/uvcvideo.rst
+++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst
@@ -157,7 +157,7 @@ Argument: struct uvc_xu_control_mapping
__u8 name[32] V4L2 control name
__u8 entity[16] UVC extension unit GUID
__u8 selector UVC control selector
- __u8 size V4L2 control size (in bits)
+ __u8 size UVC control data size (in bits)
__u8 offset V4L2 control offset (in bits)
enum v4l2_ctrl_type
v4l2_type V4L2 control type
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index b73a7e3812cf..fe1245ccea15 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -464,7 +464,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_BRIGHTNESS,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_BRIGHTNESS_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_SIGNED,
@@ -473,7 +473,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_CONTRAST,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_CONTRAST_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -482,7 +482,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_HUE,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_HUE_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_SIGNED,
@@ -493,7 +493,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_SATURATION,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_SATURATION_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -502,7 +502,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_SHARPNESS,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_SHARPNESS_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -511,7 +511,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_GAMMA,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_GAMMA_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -520,7 +520,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_BACKLIGHT_COMPENSATION,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -529,7 +529,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_GAIN,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_GAIN_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -538,7 +538,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_HUE_AUTO,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_HUE_AUTO_CONTROL,
- .size = 1,
+ .data_size = 1,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
@@ -548,7 +548,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_EXPOSURE_AUTO,
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_AE_MODE_CONTROL,
- .size = 4,
+ .data_size = 4,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_MENU,
.data_type = UVC_CTRL_DATA_TYPE_BITMASK,
@@ -561,7 +561,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_EXPOSURE_AUTO_PRIORITY,
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_AE_PRIORITY_CONTROL,
- .size = 1,
+ .data_size = 1,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
@@ -570,7 +570,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_EXPOSURE_ABSOLUTE,
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
- .size = 32,
+ .data_size = 32,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -581,7 +581,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_AUTO_WHITE_BALANCE,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
- .size = 1,
+ .data_size = 1,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
@@ -591,7 +591,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -602,7 +602,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_AUTO_WHITE_BALANCE,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
- .size = 1,
+ .data_size = 1,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
@@ -613,7 +613,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_BLUE_BALANCE,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_SIGNED,
@@ -624,7 +624,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_RED_BALANCE,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 16,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_SIGNED,
@@ -635,7 +635,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_FOCUS_ABSOLUTE,
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -646,7 +646,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_FOCUS_AUTO,
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_FOCUS_AUTO_CONTROL,
- .size = 1,
+ .data_size = 1,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
@@ -656,7 +656,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_IRIS_ABSOLUTE,
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_IRIS_ABSOLUTE_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -665,7 +665,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_IRIS_RELATIVE,
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_IRIS_RELATIVE_CONTROL,
- .size = 8,
+ .data_size = 8,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_SIGNED,
@@ -674,7 +674,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_ZOOM_ABSOLUTE,
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
@@ -683,7 +683,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_ZOOM_CONTINUOUS,
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_ZOOM_RELATIVE_CONTROL,
- .size = 0,
+ .data_size = 0,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_SIGNED,
@@ -694,7 +694,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_PAN_ABSOLUTE,
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
- .size = 32,
+ .data_size = 32,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_SIGNED,
@@ -703,7 +703,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_TILT_ABSOLUTE,
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
- .size = 32,
+ .data_size = 32,
.offset = 32,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_SIGNED,
@@ -712,7 +712,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_PAN_SPEED,
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_RELATIVE_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_SIGNED,
@@ -723,7 +723,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_TILT_SPEED,
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_RELATIVE_CONTROL,
- .size = 16,
+ .data_size = 16,
.offset = 16,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_SIGNED,
@@ -734,7 +734,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_PRIVACY,
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PRIVACY_CONTROL,
- .size = 1,
+ .data_size = 1,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
@@ -743,7 +743,7 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.id = V4L2_CID_PRIVACY,
.entity = UVC_GUID_EXT_GPIO_CONTROLLER,
.selector = UVC_CT_PRIVACY_CONTROL,
- .size = 1,
+ .data_size = 1,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
@@ -754,7 +754,7 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
.id = V4L2_CID_POWER_LINE_FREQUENCY,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
- .size = 2,
+ .data_size = 2,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_MENU,
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
@@ -766,7 +766,7 @@ const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc11 = {
.id = V4L2_CID_POWER_LINE_FREQUENCY,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
- .size = 2,
+ .data_size = 2,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_MENU,
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
@@ -783,7 +783,7 @@ static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_uvc15 = {
.id = V4L2_CID_POWER_LINE_FREQUENCY,
.entity = UVC_GUID_UVC_PROCESSING,
.selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL,
- .size = 2,
+ .data_size = 2,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_MENU,
.data_type = UVC_CTRL_DATA_TYPE_ENUM,
@@ -816,7 +816,7 @@ static inline void uvc_clear_bit(u8 *data, int bit)
}
/*
- * Extract the bit string specified by mapping->offset and mapping->size
+ * Extract the bit string specified by mapping->offset and mapping->data_size
* from the little-endian data stored at 'data' and return the result as
* a signed 32bit integer. Sign extension will be performed if the mapping
* references a signed data type.
@@ -824,7 +824,7 @@ static inline void uvc_clear_bit(u8 *data, int bit)
static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
u8 query, const u8 *data)
{
- int bits = mapping->size;
+ int bits = mapping->data_size;
int offset = mapping->offset;
s32 value = 0;
u8 mask;
@@ -847,19 +847,19 @@ static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
/* Sign-extend the value if needed. */
if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
- value |= -(value & (1 << (mapping->size - 1)));
+ value |= -(value & (1 << (mapping->data_size - 1)));
return value;
}
/*
- * Set the bit string specified by mapping->offset and mapping->size
+ * Set the bit string specified by mapping->offset and mapping->data_size
* in the little-endian data stored at 'data' to the value 'value'.
*/
static void uvc_set_le_value(struct uvc_control_mapping *mapping,
s32 value, u8 *data)
{
- int bits = mapping->size;
+ int bits = mapping->data_size;
int offset = mapping->offset;
u8 mask;
@@ -2036,7 +2036,7 @@ int uvc_ctrl_set(struct uvc_fh *handle,
* needs to be loaded from the device to perform the read-modify-write
* operation.
*/
- if ((ctrl->info.size * 8) != mapping->size) {
+ if ((ctrl->info.size * 8) != mapping->data_size) {
ret = __uvc_ctrl_load_cur(chain, ctrl);
if (ret < 0)
return ret;
@@ -2543,8 +2543,8 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
}
/* Validate the user-provided bit-size and offset */
- if (mapping->size > 32 ||
- mapping->offset + mapping->size > ctrl->info.size * 8) {
+ if (mapping->data_size > 32 ||
+ mapping->offset + mapping->data_size > ctrl->info.size * 8) {
ret = -EINVAL;
goto done;
}
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 656cf0fbf7a3..dee3151ff6c4 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -122,7 +122,7 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
}
memcpy(map->entity, xmap->entity, sizeof(map->entity));
map->selector = xmap->selector;
- map->size = xmap->size;
+ map->data_size = xmap->size;
map->offset = xmap->offset;
map->v4l2_type = xmap->v4l2_type;
map->data_type = xmap->data_type;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 5091085fcfb0..7bc41270ed94 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -110,7 +110,11 @@ struct uvc_control_mapping {
u8 entity[16];
u8 selector;
- u8 size;
+ /* Size of the v4l2 control. Required for compound controls. */
+ u8 v4l2_size;
+ /* UVC data size. Required for all controls. */
+ u8 data_size;
+
u8 offset;
enum v4l2_ctrl_type v4l2_type;
u32 data_type;
--
2.41.0.694.ge786442a9b-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v12 05/11] media: uvcvideo: Add support for compound controls
2023-08-17 7:17 [PATCH v12 00/11] Implement UVC v1.5 ROI Yunke Cao
` (3 preceding siblings ...)
2023-08-17 7:17 ` [PATCH v12 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size Yunke Cao
@ 2023-08-17 7:17 ` Yunke Cao
2023-08-17 7:17 ` [PATCH v12 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
` (6 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Yunke Cao @ 2023-08-17 7:17 UTC (permalink / raw)
To: Hans Verkuil, Laurent Pinchart, Daniel Scally
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.
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
Signed-off-by: Yunke Cao <yunkec@google.com>
---
Changelog since v11:
- No change.
Changelog since v10:
- Rewrite some logic in __uvc_find_control().
- Remove a duplicate check in __uvc_ctrl_get_compound_cur().
- Thanks, Daniel!
Changelog since v9:
- Make __uvc_ctrl_set_compound() static.
Changelog since v8:
- No change.
Changelog since v7:
- Fixed comments styles, indentation and a few other style issues.
- Renamed uvc_g/set_array() to uvc_g/set_compound().
- Moved size check to __uvc_ctrl_add_mapping().
- After setting a new value, copy it back to user.
- In __uvc_ctrl_set_compound(), check size before allocating.
drivers/media/usb/uvc/uvc_ctrl.c | 179 +++++++++++++++++++++++++++----
drivers/media/usb/uvc/uvcvideo.h | 4 +
2 files changed, 164 insertions(+), 19 deletions(-)
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index fe1245ccea15..197e00e146b4 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -884,6 +884,28 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
}
}
+/*
+ * Extract the byte array specified by mapping->offset and mapping->data_size
+ * stored at 'data' to the output array 'data_out'.
+ */
+static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data,
+ u8 *data_out)
+{
+ memcpy(data_out, data + mapping->offset / 8, mapping->data_size / 8);
+ return 0;
+}
+
+/*
+ * Copy the byte array 'data_in' to the destination specified by mapping->offset
+ * and mapping->data_size stored at 'data'.
+ */
+static int uvc_set_compound(struct uvc_control_mapping *mapping,
+ const u8 *data_in, u8 *data)
+{
+ memcpy(data + mapping->offset / 8, data_in, mapping->data_size / 8);
+ return 0;
+}
+
static bool
uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
{
@@ -906,7 +928,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;
@@ -921,14 +943,16 @@ 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) &&
+ (uvc_ctrl_mapping_is_compound(map) ?
+ next_compound : next)) {
*control = ctrl;
*mapping = map;
}
@@ -942,6 +966,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;
@@ -950,12 +975,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);
@@ -1101,12 +1127,59 @@ static int __uvc_ctrl_get_std(struct uvc_video_chain *chain,
return 0;
}
+static int __uvc_ctrl_get_compound(struct uvc_control_mapping *mapping,
+ struct uvc_control *ctrl,
+ int id,
+ struct v4l2_ext_control *xctrl)
+{
+ u8 size;
+ u8 *data;
+ int ret;
+
+ size = mapping->v4l2_size / 8;
+ if (xctrl->size < size) {
+ xctrl->size = size;
+ return -ENOSPC;
+ }
+
+ data = kmalloc(size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = mapping->get_compound(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_cur(struct uvc_video_chain *chain,
+ struct uvc_control *ctrl,
+ struct uvc_control_mapping *mapping,
+ struct v4l2_ext_control *xctrl)
+{
+ int ret;
+
+ ret = __uvc_ctrl_load_cur(chain, ctrl);
+ if (ret < 0)
+ return ret;
+
+ return __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_CURRENT,
+ xctrl);
+}
+
static int __uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
u32 found_id)
{
- bool find_next = req_id & V4L2_CTRL_FLAG_NEXT_CTRL;
unsigned int i;
+ bool find_next = req_id &
+ (V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND);
+
req_id &= V4L2_CTRL_ID_MASK;
for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
@@ -1194,7 +1267,7 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
}
__uvc_find_control(ctrl->entity, mapping->master_id, &master_map,
- &master_ctrl, 0);
+ &master_ctrl, 0, 0);
if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
return 0;
@@ -1262,7 +1335,7 @@ 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 = 0;
int ret;
@@ -1278,6 +1351,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)
@@ -1530,7 +1612,7 @@ 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;
@@ -1840,7 +1922,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;
}
@@ -1888,7 +1970,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
return -EINVAL;
if (uvc_ctrl_mapping_is_compound(mapping))
- return -EINVAL;
+ return __uvc_ctrl_get_compound_cur(chain, ctrl, mapping, xctrl);
else
return __uvc_ctrl_get_std(chain, ctrl, mapping, &xctrl->value);
}
@@ -1909,6 +1991,22 @@ static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
return 0;
}
+static int __uvc_ctrl_get_boundary_compound(struct uvc_video_chain *chain,
+ struct uvc_control *ctrl,
+ struct uvc_control_mapping *mapping,
+ struct v4l2_ext_control *xctrl)
+{
+ int ret;
+
+ if (!ctrl->cached) {
+ ret = uvc_ctrl_populate_cache(chain, ctrl);
+ if (ret < 0)
+ return ret;
+ }
+
+ return __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_DEF, xctrl);
+}
+
int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
struct v4l2_ext_control *xctrl)
{
@@ -1926,7 +2024,8 @@ int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
}
if (uvc_ctrl_mapping_is_compound(mapping))
- ret = -EINVAL;
+ ret = __uvc_ctrl_get_boundary_compound(chain, ctrl, mapping,
+ xctrl);
else
ret = __uvc_ctrl_get_boundary_std(chain, ctrl, mapping, xctrl);
@@ -1935,6 +2034,34 @@ int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
return ret;
}
+static int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
+ struct v4l2_ext_control *xctrl,
+ struct uvc_control *ctrl)
+{
+ u8 *data;
+ int ret;
+
+ if (xctrl->size != mapping->v4l2_size / 8)
+ return -EINVAL;
+
+ 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_compound(mapping, data,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
+
+ __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_CURRENT, xctrl);
+
+out:
+ kfree(data);
+ return ret;
+}
+
int uvc_ctrl_set(struct uvc_fh *handle,
struct v4l2_ext_control *xctrl)
{
@@ -2049,13 +2176,14 @@ int uvc_ctrl_set(struct uvc_fh *handle,
ctrl->info.size);
}
- if (uvc_ctrl_mapping_is_compound(mapping))
- return -EINVAL;
- else
+ if (uvc_ctrl_mapping_is_compound(mapping)) {
+ ret = __uvc_ctrl_set_compound(mapping, xctrl, ctrl);
+ if (ret < 0)
+ return ret;
+ } else
mapping->set(mapping, value,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
-
if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
ctrl->handle = handle;
@@ -2465,10 +2593,23 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
goto err_nomem;
}
- if (map->get == NULL)
+ if (uvc_ctrl_mapping_is_compound(map)) {
+ if (map->data_size != map->v4l2_size)
+ return -EINVAL;
+
+ /* Only supports byte-aligned data. */
+ if (WARN_ON(map->offset % 8 || map->data_size % 8))
+ return -EINVAL;
+ }
+
+ if (!map->get && !uvc_ctrl_mapping_is_compound(map))
map->get = uvc_get_le_value;
- if (map->set == NULL)
+ if (!map->set && !uvc_ctrl_mapping_is_compound(map))
map->set = uvc_set_le_value;
+ if (!map->get_compound && uvc_ctrl_mapping_is_compound(map))
+ map->get_compound = uvc_get_compound;
+ if (!map->set_compound && uvc_ctrl_mapping_is_compound(map))
+ map->set_compound = uvc_set_compound;
for (i = 0; i < ARRAY_SIZE(uvc_control_classes); i++) {
if (V4L2_CTRL_ID2WHICH(uvc_control_classes[i]) ==
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 7bc41270ed94..11805b729c22 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -129,8 +129,12 @@ struct uvc_control_mapping {
s32 (*get)(struct uvc_control_mapping *mapping, u8 query,
const u8 *data);
+ int (*get_compound)(struct uvc_control_mapping *mapping, const u8 *data,
+ u8 *data_out);
void (*set)(struct uvc_control_mapping *mapping, s32 value,
u8 *data);
+ int (*set_compound)(struct uvc_control_mapping *mapping, const u8 *data_in,
+ u8 *data);
};
struct uvc_control {
--
2.41.0.694.ge786442a9b-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v12 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
2023-08-17 7:17 [PATCH v12 00/11] Implement UVC v1.5 ROI Yunke Cao
` (4 preceding siblings ...)
2023-08-17 7:17 ` [PATCH v12 05/11] media: uvcvideo: Add support for compound controls Yunke Cao
@ 2023-08-17 7:17 ` Yunke Cao
2023-08-18 1:48 ` Yunke Cao
2023-09-13 9:26 ` Hans Verkuil
2023-08-17 7:17 ` [PATCH v12 07/11] media: vivid: Add an rectangle control Yunke Cao
` (5 subsequent siblings)
11 siblings, 2 replies; 17+ messages in thread
From: Yunke Cao @ 2023-08-17 7:17 UTC (permalink / raw)
To: Hans Verkuil, Laurent Pinchart, Daniel Scally
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>
---
Changelog since v11:
- Added a flag V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX.
- Modified std_min/max_compound() to be void function. Moved the check of
whether WHICH_MIN/MAX_VAL into prepare_ext_ctrls(), and return EINVAL.
- Modified documentations to reflect this change.
Changelog since v10:
- No change.
Changelog since v9:
- No change.
Changelog since v8:
- Return ENODATA when min/max is not implemented. Document this behavior.
- Created a shared helper function __v4l2_ctrl_type_op_init that takes "which"
as a parameter. Call it in def, min and max operations.
Changelog since v7:
- Document that the definition of the min/max are provided by compound controls
are defined in control documentation.
- Return error, instead of zeroed memory for v4l2_ctrl_ptr_create(NULL).
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 | 16 +-
.../media/v4l/vidioc-queryctrl.rst | 9 +-
.../media/videodev2.h.rst.exceptions | 3 +
drivers/media/i2c/imx214.c | 5 +-
.../media/platform/qcom/venus/venc_ctrls.c | 9 +-
drivers/media/v4l2-core/v4l2-ctrls-api.c | 54 ++++--
drivers/media/v4l2-core/v4l2-ctrls-core.c | 160 +++++++++++++++---
drivers/media/v4l2-core/v4l2-ioctl.c | 4 +-
include/media/v4l2-ctrls.h | 34 +++-
include/uapi/linux/videodev2.h | 3 +
10 files changed, 252 insertions(+), 45 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 7b1001d11f9c..b4bf016411d3 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
@@ -330,14 +330,26 @@ 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.
+
+ Whether a control supports querying the minimum and maximum values using
+ ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` is indicated
+ by the ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. Most non-compound
+ control types support this. For controls with compound types, the
+ definition of minimum/maximum values are provided by
+ the control documentation. If a compound control does not document the
+ meaning of minimum/maximum value, then querying the minimum or maximum
+ value will result in the error code -EINVAL.
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/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
index 56d5c8b0b88b..b39f7e27bbbe 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
@@ -447,7 +447,10 @@ See also the examples in :ref:`control`.
- n/a
- A struct :c:type:`v4l2_rect`, containing a rectangle described by
the position of its top-left corner, the width and the height. Units
- depend on the use case.
+ depend on the use case. Support for ``V4L2_CTRL_WHICH_MIN_VAL`` and
+ ``V4L2_CTRL_WHICH_MAX_VAL`` is optional and depends on the
+ ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. See the documentation of
+ the specific control on how to interpret the minimum and maximum values.
* - ``V4L2_CTRL_TYPE_H264_SPS``
- n/a
- n/a
@@ -664,6 +667,10 @@ See also the examples in :ref:`control`.
``dims[0]``. So setting the control with a differently sized
array will change the ``elems`` field when the control is
queried afterwards.
+ * - ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX``
+ - 0x1000
+ - This control supports getting minimum and maximum values using
+ vidioc_g_ext_ctrls with V4L2_CTRL_WHICH_MIN/MAX_VAL.
Return Value
============
diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
index c46082ef0e4d..a417af25e9a4 100644
--- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
+++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
@@ -393,6 +393,7 @@ replace define V4L2_CTRL_FLAG_HAS_PAYLOAD control-flags
replace define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE control-flags
replace define V4L2_CTRL_FLAG_MODIFY_LAYOUT control-flags
replace define V4L2_CTRL_FLAG_DYNAMIC_ARRAY control-flags
+replace define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX control-flags
replace define V4L2_CTRL_FLAG_NEXT_CTRL control
replace define V4L2_CTRL_FLAG_NEXT_COMPOUND control
@@ -567,6 +568,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 2f9c8582f940..8db4a5eb1737 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 7468e43800a9..28eca8f9d148 100644
--- a/drivers/media/platform/qcom/venus/venc_ctrls.c
+++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
@@ -607,11 +607,16 @@ 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(&p_hdr10_cll));
+ v4l2_ctrl_ptr_create(&p_hdr10_cll),
+ 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((void *)&p_hdr10_mastering));
+ v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering),
+ v4l2_ctrl_ptr_create(NULL),
+ v4l2_ctrl_ptr_create(NULL));
+
v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE,
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
index 002ea6588edf..d022e1ed4835 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
@@ -94,6 +94,22 @@ 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)
+{
+ ctrl->type_ops->minimum(ctrl, 0, 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)
+{
+ ctrl->type_ops->maximum(ctrl, 0, ctrl->p_new);
+
+ return ptr_to_user(c, ctrl, ctrl->p_new);
+}
+
/* Helper function: copy the caller-provider value as the new control value */
static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
{
@@ -229,8 +245,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",
@@ -259,6 +275,15 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
return -EINVAL;
}
+ if (!(ctrl->flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX) &&
+ (cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
+ cs->which == V4L2_CTRL_WHICH_MAX_VAL)) {
+ dprintk(vdev,
+ "invalid which 0x%x or control id 0x%x\n",
+ cs->which, id);
+ return -EINVAL;
+ }
+
if (ctrl->cluster[0]->ncontrols > 1)
have_clusters = true;
if (ctrl->cluster[0] != ctrl)
@@ -368,8 +393,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;
}
@@ -389,10 +414,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);
@@ -432,13 +459,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++)
@@ -467,6 +495,10 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
ret = -ENOMEM;
else if (is_request && ref->p_req_valid)
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
@@ -564,9 +596,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 f1486ab032cf..f36d54810dbc 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -182,29 +182,69 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
}
}
-void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_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_min.p_const)
+ memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
+ else
+ memset(p, 0, ctrl->elem_size);
+}
+
+static int __v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
+ u32 which, union v4l2_ctrl_ptr ptr)
{
unsigned int i;
u32 tot_elems = ctrl->elems;
u32 elems = tot_elems - from_idx;
+ s64 value;
if (from_idx >= tot_elems)
- return;
+ return -EINVAL;
+
+ switch (which) {
+ case V4L2_CTRL_WHICH_DEF_VAL:
+ value = ctrl->default_value;
+ break;
+ case V4L2_CTRL_WHICH_MAX_VAL:
+ value = ctrl->maximum;
+ break;
+ case V4L2_CTRL_WHICH_MIN_VAL:
+ value = ctrl->minimum;
+ break;
+ default:
+ return -EINVAL;
+ }
switch (ctrl->type) {
case V4L2_CTRL_TYPE_STRING:
+ if (which == V4L2_CTRL_WHICH_DEF_VAL)
+ value = ctrl->minimum;
+
for (i = from_idx; i < tot_elems; i++) {
unsigned int offset = i * ctrl->elem_size;
- memset(ptr.p_char + offset, ' ', ctrl->minimum);
- ptr.p_char[offset + ctrl->minimum] = '\0';
+ memset(ptr.p_char + offset, ' ', value);
+ ptr.p_char[offset + value] = '\0';
}
break;
case V4L2_CTRL_TYPE_INTEGER64:
- if (ctrl->default_value) {
+ if (value) {
for (i = from_idx; i < tot_elems; i++)
- ptr.p_s64[i] = ctrl->default_value;
+ ptr.p_s64[i] = value;
} else {
memset(ptr.p_s64 + from_idx, 0, elems * sizeof(s64));
}
@@ -214,9 +254,9 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
case V4L2_CTRL_TYPE_MENU:
case V4L2_CTRL_TYPE_BITMASK:
case V4L2_CTRL_TYPE_BOOLEAN:
- if (ctrl->default_value) {
+ if (value) {
for (i = from_idx; i < tot_elems; i++)
- ptr.p_s32[i] = ctrl->default_value;
+ ptr.p_s32[i] = value;
} else {
memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
}
@@ -226,32 +266,63 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
break;
case V4L2_CTRL_TYPE_U8:
- memset(ptr.p_u8 + from_idx, ctrl->default_value, elems);
+ memset(ptr.p_u8 + from_idx, value, elems);
break;
case V4L2_CTRL_TYPE_U16:
- if (ctrl->default_value) {
+ if (value) {
for (i = from_idx; i < tot_elems; i++)
- ptr.p_u16[i] = ctrl->default_value;
+ ptr.p_u16[i] = value;
} else {
memset(ptr.p_u16 + from_idx, 0, elems * sizeof(u16));
}
break;
case V4L2_CTRL_TYPE_U32:
- if (ctrl->default_value) {
+ if (value) {
for (i = from_idx; i < tot_elems; i++)
- ptr.p_u32[i] = ctrl->default_value;
+ ptr.p_u32[i] = value;
} else {
memset(ptr.p_u32 + from_idx, 0, elems * sizeof(u32));
}
break;
default:
- for (i = from_idx; i < tot_elems; i++)
- std_init_compound(ctrl, i, ptr);
+ for (i = from_idx; i < tot_elems; i++) {
+ switch (which) {
+ case V4L2_CTRL_WHICH_DEF_VAL:
+ std_init_compound(ctrl, i, ptr);
+ break;
+ case V4L2_CTRL_WHICH_MAX_VAL:
+ std_max_compound(ctrl, i, ptr);
+ break;
+ case V4L2_CTRL_WHICH_MIN_VAL:
+ std_min_compound(ctrl, i, ptr);
+ break;
+ }
+ }
break;
}
+
+ return 0;
+}
+
+void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
+ union v4l2_ctrl_ptr ptr)
+{
+ __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_DEF_VAL, ptr);
}
EXPORT_SYMBOL(v4l2_ctrl_type_op_init);
+void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
+ union v4l2_ctrl_ptr ptr)
+{
+ __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MIN_VAL, ptr);
+}
+
+void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
+ union v4l2_ctrl_ptr ptr)
+{
+ __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MAX_VAL, ptr);
+}
+
void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
{
union v4l2_ctrl_ptr ptr = ctrl->p_cur;
@@ -1293,6 +1364,8 @@ EXPORT_SYMBOL(v4l2_ctrl_type_op_validate);
static const struct v4l2_ctrl_type_ops std_type_ops = {
.equal = v4l2_ctrl_type_op_equal,
.init = v4l2_ctrl_type_op_init,
+ .minimum = v4l2_ctrl_type_op_minimum,
+ .maximum = v4l2_ctrl_type_op_maximum,
.log = v4l2_ctrl_type_op_log,
.validate = v4l2_ctrl_type_op_validate,
};
@@ -1764,7 +1837,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;
@@ -1888,6 +1964,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
break;
}
+ if (type < V4L2_CTRL_COMPOUND_TYPES &&
+ type != V4L2_CTRL_TYPE_BUTTON &&
+ type != V4L2_CTRL_TYPE_CTRL_CLASS &&
+ type != V4L2_CTRL_TYPE_STRING)
+ flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
+
/* Sanity checks */
if (id == 0 || name == NULL || !elem_size ||
id >= V4L2_CID_PRIVATE_BASE ||
@@ -1896,6 +1978,21 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
handler_set_err(hdl, -ERANGE);
return NULL;
}
+
+ if ((!p_def.p_const && p_min.p_const) ||
+ (p_min.p_const && !p_max.p_const) ||
+ (!p_min.p_const && p_max.p_const)) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+
+ if (flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX &&
+ type >= V4L2_CTRL_COMPOUND_TYPES &&
+ (!p_min.p_const || !p_max.p_const)) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+
err = check_range(type, min, max, step, def);
if (err) {
handler_set_err(hdl, err);
@@ -1937,6 +2034,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const)
sz_extra += elem_size;
+ if (type >= V4L2_CTRL_COMPOUND_TYPES && p_min.p_const)
+ sz_extra += elem_size * 2;
ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
if (ctrl == NULL) {
@@ -2002,6 +2101,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
}
+ if (flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX &&
+ p_min.p_const && p_max.p_const) {
+ ctrl->p_min.p = ctrl->p_def.p + elem_size;
+ memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
+ ctrl->p_max.p = ctrl->p_min.p + elem_size;
+ memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
+ }
+
ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
cur_to_new(ctrl);
@@ -2052,7 +2159,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;
@@ -2077,7 +2185,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);
@@ -2110,7 +2219,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);
@@ -2142,7 +2252,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);
@@ -2150,7 +2261,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;
@@ -2164,7 +2277,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);
@@ -2188,7 +2301,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 01ba27f2ef87..aaf68bfaa601 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -884,7 +884,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 b0db167a3ac4..6e12493b30d3 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -133,6 +133,8 @@ struct v4l2_ctrl_ops {
*
* @equal: return true if all ctrl->elems array elements are equal.
* @init: initialize the value for array elements from from_idx to ctrl->elems.
+ * @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 for ctrl->new_elems array elements.
* Return 0 on success and a negative value otherwise.
@@ -142,6 +144,10 @@ struct v4l2_ctrl_type_ops {
union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2);
void (*init)(const struct v4l2_ctrl *ctrl, u32 from_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, union v4l2_ctrl_ptr ptr);
};
@@ -247,6 +253,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.
@@ -306,6 +318,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;
};
@@ -425,6 +439,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.
@@ -454,6 +470,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;
@@ -723,17 +741,23 @@ 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 fields. Use v4l2_ctrl_ptr_create() to create
+ * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
+ * default value of the compound control should be all zeroes. Use
+ * v4l2_ctrl_ptr_create(NULL) if the min/max value of the compound control
+ * is not defined, -ENODATA will be returned in this case.
*
*/
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 089d553cf736..d7670aaf3b5f 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -1863,6 +1863,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,
@@ -1970,6 +1972,7 @@ struct v4l2_querymenu {
#define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE 0x0200
#define V4L2_CTRL_FLAG_MODIFY_LAYOUT 0x0400
#define V4L2_CTRL_FLAG_DYNAMIC_ARRAY 0x0800
+#define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX 0x1000
/* Query flags, to be ORed with the control ID */
#define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000
--
2.41.0.694.ge786442a9b-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v12 07/11] media: vivid: Add an rectangle control
2023-08-17 7:17 [PATCH v12 00/11] Implement UVC v1.5 ROI Yunke Cao
` (5 preceding siblings ...)
2023-08-17 7:17 ` [PATCH v12 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
@ 2023-08-17 7:17 ` Yunke Cao
2023-08-17 7:17 ` [PATCH v12 08/11] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
` (4 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Yunke Cao @ 2023-08-17 7:17 UTC (permalink / raw)
To: Hans Verkuil, Laurent Pinchart, Daniel Scally
Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao
This control represents a generic read/write rectangle.
It supports V4L2_CTRL_WHICH_MIN/MAX_VAL.
---
.../media/test-drivers/vivid/vivid-ctrls.c | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c
index f2b20e25a7a4..27a1173c7734 100644
--- a/drivers/media/test-drivers/vivid/vivid-ctrls.c
+++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c
@@ -38,6 +38,7 @@
#define VIVID_CID_U8_PIXEL_ARRAY (VIVID_CID_CUSTOM_BASE + 14)
#define VIVID_CID_S32_ARRAY (VIVID_CID_CUSTOM_BASE + 15)
#define VIVID_CID_S64_ARRAY (VIVID_CID_CUSTOM_BASE + 16)
+#define VIVID_CID_RECT (VIVID_CID_CUSTOM_BASE + 17)
#define VIVID_CID_VIVID_BASE (0x00f00000 | 0xf000)
#define VIVID_CID_VIVID_CLASS (0x00f00000 | 1)
@@ -357,6 +358,38 @@ static const struct v4l2_ctrl_config vivid_ctrl_ro_int32 = {
.step = 1,
};
+static const struct v4l2_rect rect_def = {
+ .top = 100,
+ .left = 200,
+ .width = 300,
+ .height = 400,
+};
+
+static const struct v4l2_rect rect_min = {
+ .top = 0,
+ .left = 0,
+ .width = 1,
+ .height = 1,
+};
+
+static const struct v4l2_rect rect_max = {
+ .top = 0,
+ .left = 0,
+ .width = 1000,
+ .height = 2000,
+};
+
+static const struct v4l2_ctrl_config vivid_ctrl_rect = {
+ .ops = &vivid_user_gen_ctrl_ops,
+ .id = VIVID_CID_RECT,
+ .name = "Rect",
+ .type = V4L2_CTRL_TYPE_RECT,
+ .flags = V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX,
+ .p_def.p_const = &rect_def,
+ .p_min.p_const = &rect_min,
+ .p_max.p_const = &rect_max,
+};
+
/* Framebuffer Controls */
static int vivid_fb_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -1677,6 +1710,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
dev->int_menu = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_int_menu, NULL);
dev->ro_int32 = v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_ro_int32, NULL);
v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_area, NULL);
+ v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_rect, NULL);
v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u32_array, NULL);
v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u32_dyn_array, NULL);
v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_u16_matrix, NULL);
--
2.41.0.694.ge786442a9b-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v12 08/11] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL
2023-08-17 7:17 [PATCH v12 00/11] Implement UVC v1.5 ROI Yunke Cao
` (6 preceding siblings ...)
2023-08-17 7:17 ` [PATCH v12 07/11] media: vivid: Add an rectangle control Yunke Cao
@ 2023-08-17 7:17 ` Yunke Cao
2023-08-17 7:17 ` [PATCH v12 09/11] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
` (3 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Yunke Cao @ 2023-08-17 7:17 UTC (permalink / raw)
To: Hans Verkuil, Laurent Pinchart, Daniel Scally
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.
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Signed-off-by: Yunke Cao <yunkec@google.com>
---
Changelog since v11:
- No change.
Changelog since v10:
- Added Reviewed-by from Sergey.
Changelog since v9:
- Revert a change in v7 that causes v4l2-compliance failure:
- In uvc_ioctl_g_ext_ctrls(), when v4l2_which is not V4L2_CTRL_WHICH_*_VAL,
- treat it the same as cur instead of returning EINVAL. This is the existing
- behavior.
- The change in v7 of returning EINVAL fails the check in
- v4l2-compliance/v4l2-test-controls.cpp#L834.
Changelog since v8:
- No change.
Changelog since v7:
- Address some comments.
drivers/media/usb/uvc/uvc_ctrl.c | 64 +++++++++++++++++++++++++++-----
drivers/media/usb/uvc/uvc_v4l2.c | 7 +++-
drivers/media/usb/uvc/uvcvideo.h | 3 +-
3 files changed, 61 insertions(+), 13 deletions(-)
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 197e00e146b4..1d8df8551acd 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1247,11 +1247,18 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
if (!ctrl)
return -EINVAL;
- if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) && read)
- return -EACCES;
-
- if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) && !read)
- return -EACCES;
+ if (read) {
+ if (ctrls->which == V4L2_CTRL_WHICH_MIN_VAL ||
+ ctrls->which == V4L2_CTRL_WHICH_MAX_VAL) {
+ if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN) ||
+ !(ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX))
+ return -EINVAL;
+ } else if (!(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
+ return -EACCES;
+ } else {
+ if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))
+ return -EACCES;
+ }
if (ioctl != VIDIOC_S_EXT_CTRLS || !mapping->master_id)
return 0;
@@ -1332,6 +1339,9 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
v4l2_ctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))
v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_MAX) &&
+ (ctrl->info.flags & UVC_CTRL_FLAG_GET_MIN))
+ v4l2_ctrl->flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
if (mapping->master_id)
__uvc_find_control(ctrl->entity, mapping->master_id,
@@ -1978,6 +1988,7 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
static int __uvc_ctrl_get_boundary_std(struct uvc_video_chain *chain,
struct uvc_control *ctrl,
struct uvc_control_mapping *mapping,
+ u32 v4l2_which,
struct v4l2_ext_control *xctrl)
{
struct v4l2_queryctrl qc = { .id = xctrl->id };
@@ -1987,28 +1998,59 @@ static int __uvc_ctrl_get_boundary_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;
+ }
+
return 0;
}
static int __uvc_ctrl_get_boundary_compound(struct uvc_video_chain *chain,
struct uvc_control *ctrl,
struct uvc_control_mapping *mapping,
+ u32 v4l2_which,
struct v4l2_ext_control *xctrl)
{
+ u32 flag, id;
int ret;
+ 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;
+ }
+
+ if (!(ctrl->info.flags & flag) && flag != UVC_CTRL_FLAG_GET_DEF)
+ return -EINVAL;
+
if (!ctrl->cached) {
ret = uvc_ctrl_populate_cache(chain, ctrl);
if (ret < 0)
return ret;
}
- return __uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_DEF, xctrl);
+ return __uvc_ctrl_get_compound(mapping, ctrl, id, xctrl);
}
int uvc_ctrl_get_boundary(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;
@@ -2025,9 +2067,11 @@ int uvc_ctrl_get_boundary(struct uvc_video_chain *chain,
if (uvc_ctrl_mapping_is_compound(mapping))
ret = __uvc_ctrl_get_boundary_compound(chain, ctrl, mapping,
- xctrl);
+ v4l2_which, xctrl);
else
- ret = __uvc_ctrl_get_boundary_std(chain, ctrl, mapping, xctrl);
+ ret = __uvc_ctrl_get_boundary_std(chain, ctrl, mapping,
+ v4l2_which, xctrl);
+
done:
mutex_unlock(&chain->ctrl_mutex);
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index dee3151ff6c4..06aa819a6f75 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1094,9 +1094,12 @@ 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_boundary(chain, ctrl);
+ ret = uvc_ctrl_get_boundary(chain, ctrl, ctrls->which);
if (ret < 0) {
ctrls->error_idx = i;
return ret;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 11805b729c22..6fda40919e7f 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -790,7 +790,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_boundary(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,
const struct v4l2_ext_controls *ctrls,
--
2.41.0.694.ge786442a9b-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v12 09/11] media: uvcvideo: implement UVC v1.5 ROI
2023-08-17 7:17 [PATCH v12 00/11] Implement UVC v1.5 ROI Yunke Cao
` (7 preceding siblings ...)
2023-08-17 7:17 ` [PATCH v12 08/11] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
@ 2023-08-17 7:17 ` Yunke Cao
2023-08-17 7:17 ` [PATCH v12 10/11] media: uvcvideo: initilaize ROI control to default value Yunke Cao
` (2 subsequent siblings)
11 siblings, 0 replies; 17+ messages in thread
From: Yunke Cao @ 2023-08-17 7:17 UTC (permalink / raw)
To: Hans Verkuil, Laurent Pinchart, Daniel Scally
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.
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
Signed-off-by: Yunke Cao <yunkec@google.com>
---
Changelog since v11:
- No change.
Changelog since v10:
- Moved after the patches adding support for MIN/MAX.
- Clamp the set current value based on MIN/MAX.
- Thanks, Daniel!
Changelog since v9:
- No change.
Changelog since v8:
- No change.
Changelog since v7:
- Fix a few style issues.
- Only allow 4-byte aligned data.
- Add control names.
- Move initialization to 7/10.
drivers/media/usb/uvc/uvc_ctrl.c | 137 +++++++++++++++++++++++++++--
drivers/media/usb/uvc/uvc_v4l2.c | 5 +-
drivers/media/usb/uvc/uvcvideo.h | 12 ++-
include/uapi/linux/usb/video.h | 1 +
include/uapi/linux/uvcvideo.h | 13 +++
include/uapi/linux/v4l2-controls.h | 9 ++
6 files changed, 167 insertions(+), 10 deletions(-)
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 1d8df8551acd..6fe1331961d8 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -358,6 +358,24 @@ 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[] = {
@@ -459,6 +477,70 @@ static void uvc_ctrl_set_rel_speed(struct uvc_control_mapping *mapping,
data[first+1] = min_t(int, abs(value), 0xff);
}
+static int uvc_to_v4l2_rect(struct v4l2_rect *v4l2_rect,
+ const struct uvc_rect *uvc_rect)
+{
+ if (uvc_rect->bottom < uvc_rect->top ||
+ uvc_rect->right < uvc_rect->left)
+ return -EINVAL;
+
+ 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;
+ return 0;
+}
+
+static int v4l2_to_uvc_rect(struct uvc_rect *uvc_rect,
+ const struct v4l2_rect *min_rect,
+ const struct v4l2_rect *max_rect,
+ struct v4l2_rect *v4l2_rect)
+{
+ v4l2_rect->left = clamp_t(s32, v4l2_rect->left, 0, max_rect->width);
+ v4l2_rect->top = clamp_t(s32, v4l2_rect->top, 0, max_rect->height);
+ v4l2_rect->height = clamp_t(s32, v4l2_rect->height,
+ min_rect->height, max_rect->height);
+ v4l2_rect->width = clamp_t(s32, v4l2_rect->width,
+ min_rect->width, max_rect->width);
+
+ 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_compound_rect(struct uvc_control_mapping *mapping,
+ const u8 *data, u8 *data_out)
+{
+ struct uvc_rect *uvc_rect;
+
+ uvc_rect = (struct uvc_rect *)(data + mapping->offset / 8);
+ return uvc_to_v4l2_rect((struct v4l2_rect *)data_out, uvc_rect);
+}
+
+static int uvc_set_compound_rect(struct uvc_control_mapping *mapping,
+ const u8 *data_in, const u8 *data_min,
+ const u8 *data_max, u8 *data)
+{
+ struct uvc_rect *uvc_rect;
+ struct v4l2_rect min_rect, max_rect;
+ int ret;
+
+ uvc_rect = (struct uvc_rect *)(data + mapping->offset / 8);
+
+ ret = uvc_get_compound_rect(mapping, data_min, (u8 *)&min_rect);
+ if (ret)
+ return ret;
+
+ ret = uvc_get_compound_rect(mapping, data_max, (u8 *)&max_rect);
+ if (ret)
+ return ret;
+
+ return v4l2_to_uvc_rect(uvc_rect, &min_rect, &max_rect,
+ (struct v4l2_rect *)data_in);
+}
+
static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
{
.id = V4L2_CID_BRIGHTNESS,
@@ -748,6 +830,29 @@ 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,
+ .v4l2_size = sizeof(struct v4l2_rect) * 8,
+ .data_size = sizeof(struct uvc_rect) * 8,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_RECT,
+ .data_type = UVC_CTRL_DATA_TYPE_RECT,
+ .get_compound = uvc_get_compound_rect,
+ .set_compound = uvc_set_compound_rect,
+ .name = "Region Of Interest Rectangle",
+ },
+ {
+ .id = V4L2_CID_UVC_REGION_OF_INTEREST_AUTO,
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = UVC_CT_REGION_OF_INTEREST_CONTROL,
+ .data_size = 16,
+ .offset = 64,
+ .v4l2_type = V4L2_CTRL_TYPE_BITMASK,
+ .data_type = UVC_CTRL_DATA_TYPE_BITMASK,
+ .name = "Region Of Interest Auto Controls",
+ },
};
const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = {
@@ -900,12 +1005,12 @@ static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data,
* and mapping->data_size stored at 'data'.
*/
static int uvc_set_compound(struct uvc_control_mapping *mapping,
- const u8 *data_in, u8 *data)
+ const u8 *data_in, const u8 *data_min,
+ const u8 *data_max, u8 *data)
{
memcpy(data + mapping->offset / 8, data_in, mapping->data_size / 8);
return 0;
}
-
static bool
uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
{
@@ -2097,6 +2202,8 @@ static int __uvc_ctrl_set_compound(struct uvc_control_mapping *mapping,
goto out;
ret = mapping->set_compound(mapping, data,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN),
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX),
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
__uvc_ctrl_get_compound(mapping, ctrl, UVC_CTRL_DATA_CURRENT, xctrl);
@@ -2221,6 +2328,13 @@ int uvc_ctrl_set(struct uvc_fh *handle,
}
if (uvc_ctrl_mapping_is_compound(mapping)) {
+ /* Populates min/max value cache for clamping. */
+ if (!ctrl->cached) {
+ ret = uvc_ctrl_populate_cache(chain, ctrl);
+ if (ret < 0)
+ return ret;
+ }
+
ret = __uvc_ctrl_set_compound(mapping, xctrl, ctrl);
if (ret < 0)
return ret;
@@ -2638,12 +2752,21 @@ static int __uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
}
if (uvc_ctrl_mapping_is_compound(map)) {
- if (map->data_size != map->v4l2_size)
- return -EINVAL;
+ switch (map->v4l2_type) {
+ case V4L2_CTRL_TYPE_RECT:
+ /* Only supports 4 bytes-aligned data. */
+ if (WARN_ON(map->offset % 32))
+ return -EINVAL;
+ break;
+ default:
+ if (WARN_ON(map->data_size != map->v4l2_size))
+ return -EINVAL;
+
+ /* Only supports byte-aligned data. */
+ if (WARN_ON(map->offset % 8 || map->data_size % 8))
+ return -EINVAL;
+ }
- /* Only supports byte-aligned data. */
- if (WARN_ON(map->offset % 8 || map->data_size % 8))
- return -EINVAL;
}
if (!map->get && !uvc_ctrl_mapping_is_compound(map))
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 06aa819a6f75..47024a837e25 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -1053,7 +1053,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 6fda40919e7f..18da5e0b8cb2 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -133,8 +133,9 @@ struct uvc_control_mapping {
u8 *data_out);
void (*set)(struct uvc_control_mapping *mapping, s32 value,
u8 *data);
- int (*set_compound)(struct uvc_control_mapping *mapping, const u8 *data_in,
- u8 *data);
+ int (*set_compound)(struct uvc_control_mapping *mapping,
+ const u8 *data_in, const u8 *data_min,
+ const u8 *data_max, u8 *data);
};
struct uvc_control {
@@ -289,6 +290,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 2ff0e8a3a683..2afb4420e6c4 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 f86185456dc5..9d1e6085feba 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)
@@ -38,6 +39,18 @@
#define UVC_MENU_NAME_LEN 32
+/* V4L2 driver-specific 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[UVC_MENU_NAME_LEN];
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index c3604a0a3e30..01ab88bd89a7 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -1075,6 +1075,15 @@ enum v4l2_auto_focus_range {
#define V4L2_CID_HDR_SENSOR_MODE (V4L2_CID_CAMERA_CLASS_BASE+36)
+/* CAMERA-class private control IDs */
+
+/*
+ * The base for the uvc driver controls.
+ * See linux/uvcvideo.h for the list of controls.
+ * We reserve 64 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.41.0.694.ge786442a9b-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v12 10/11] media: uvcvideo: initilaize ROI control to default value
2023-08-17 7:17 [PATCH v12 00/11] Implement UVC v1.5 ROI Yunke Cao
` (8 preceding siblings ...)
2023-08-17 7:17 ` [PATCH v12 09/11] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
@ 2023-08-17 7:17 ` Yunke Cao
2023-08-17 7:17 ` [PATCH v12 11/11] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
2023-08-17 7:28 ` [PATCH v12 00/11] Implement " Yunke Cao
11 siblings, 0 replies; 17+ messages in thread
From: Yunke Cao @ 2023-08-17 7:17 UTC (permalink / raw)
To: Hans Verkuil, Laurent Pinchart, Daniel Scally
Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media, Yunke Cao
Add an init function to uvc_control_info. Use the function to
initialize ROI control to default value.
Also moves utility functions to the top of uvc_ctrl.c, above
the uvc_ctrls definition. uvc_ctrl_init_roi() calls uvc_ctrl_data()
and need to be declared before uvc_ctrls.
Signed-off-by: Yunke Cao <yunkec@google.com>
---
Changelog since v11:
- No change.
Changelog since v10:
- No change.
Changelog since v9:
- No change.
Changelog since v8:
- No change.
Changelog since v7:
- Newly added patch. Split initializing from the previous patch.
- Add an init operation to uvc_control_info and use it for ROI
initialization.
drivers/media/usb/uvc/uvc_ctrl.c | 273 ++++++++++++++++++-------------
drivers/media/usb/uvc/uvcvideo.h | 3 +
2 files changed, 160 insertions(+), 116 deletions(-)
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 6fe1331961d8..557a24107792 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -32,6 +32,158 @@
#define UVC_CTRL_DATA_DEF 5
#define UVC_CTRL_DATA_LAST 6
+/* ------------------------------------------------------------------------
+ * Utility functions
+ */
+
+static inline u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
+{
+ return ctrl->uvc_data + id * ctrl->info.size;
+}
+
+static inline int uvc_test_bit(const u8 *data, int bit)
+{
+ return (data[bit >> 3] >> (bit & 7)) & 1;
+}
+
+static inline void uvc_clear_bit(u8 *data, int bit)
+{
+ data[bit >> 3] &= ~(1 << (bit & 7));
+}
+
+/*
+ * Extract the bit string specified by mapping->offset and mapping->data_size
+ * from the little-endian data stored at 'data' and return the result as
+ * a signed 32bit integer. Sign extension will be performed if the mapping
+ * references a signed data type.
+ */
+static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
+ u8 query, const u8 *data)
+{
+ int bits = mapping->data_size;
+ int offset = mapping->offset;
+ s32 value = 0;
+ u8 mask;
+
+ data += offset / 8;
+ offset &= 7;
+ mask = ((1LL << bits) - 1) << offset;
+
+ while (1) {
+ u8 byte = *data & mask;
+
+ value |= offset > 0 ? (byte >> offset) : (byte << (-offset));
+ bits -= 8 - (offset > 0 ? offset : 0);
+ if (bits <= 0)
+ break;
+
+ offset -= 8;
+ mask = (1 << bits) - 1;
+ data++;
+ }
+
+ /* Sign-extend the value if needed. */
+ if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
+ value |= -(value & (1 << (mapping->data_size - 1)));
+
+ return value;
+}
+
+/*
+ * Set the bit string specified by mapping->offset and mapping->data_size
+ * in the little-endian data stored at 'data' to the value 'value'.
+ */
+static void uvc_set_le_value(struct uvc_control_mapping *mapping,
+ s32 value, u8 *data)
+{
+ int bits = mapping->data_size;
+ int offset = mapping->offset;
+ u8 mask;
+
+ /*
+ * According to the v4l2 spec, writing any value to a button control
+ * should result in the action belonging to the button control being
+ * triggered. UVC devices however want to see a 1 written -> override
+ * value.
+ */
+ if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON)
+ value = -1;
+
+ data += offset / 8;
+ offset &= 7;
+
+ for (; bits > 0; data++) {
+ mask = ((1LL << bits) - 1) << offset;
+ *data = (*data & ~mask) | ((value << offset) & mask);
+ value >>= offset ? offset : 8;
+ bits -= 8 - offset;
+ offset = 0;
+ }
+}
+
+/*
+ * Extract the byte array specified by mapping->offset and mapping->data_size
+ * stored at 'data' to the output array 'data_out'.
+ */
+static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data,
+ u8 *data_out)
+{
+ memcpy(data_out, data + mapping->offset / 8, mapping->data_size / 8);
+ return 0;
+}
+
+/*
+ * Copy the byte array 'data_in' to the destination specified by mapping->offset
+ * and mapping->data_size stored at 'data'.
+ */
+static int uvc_set_compound(struct uvc_control_mapping *mapping,
+ const u8 *data_in, const u8 *data_min,
+ const u8 *data_max, u8 *data)
+{
+ memcpy(data + mapping->offset / 8, data_in, mapping->data_size / 8);
+ return 0;
+}
+
+static bool
+uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
+{
+ return mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES;
+}
+
+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_CUR 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;
+}
+
/* ------------------------------------------------------------------------
* Controls
*/
@@ -375,6 +527,7 @@ static const struct uvc_control_info uvc_ctrls[] = {
| UVC_CTRL_FLAG_GET_MIN | UVC_CTRL_FLAG_GET_MAX
| UVC_CTRL_FLAG_GET_DEF
| UVC_CTRL_FLAG_AUTO_UPDATE,
+ .init = uvc_ctrl_init_roi,
},
};
@@ -901,122 +1054,6 @@ static const struct uvc_control_mapping *uvc_ctrl_mappings_uvc15[] = {
NULL, /* Sentinel */
};
-/* ------------------------------------------------------------------------
- * Utility functions
- */
-
-static inline u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
-{
- return ctrl->uvc_data + id * ctrl->info.size;
-}
-
-static inline int uvc_test_bit(const u8 *data, int bit)
-{
- return (data[bit >> 3] >> (bit & 7)) & 1;
-}
-
-static inline void uvc_clear_bit(u8 *data, int bit)
-{
- data[bit >> 3] &= ~(1 << (bit & 7));
-}
-
-/*
- * Extract the bit string specified by mapping->offset and mapping->data_size
- * from the little-endian data stored at 'data' and return the result as
- * a signed 32bit integer. Sign extension will be performed if the mapping
- * references a signed data type.
- */
-static s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
- u8 query, const u8 *data)
-{
- int bits = mapping->data_size;
- int offset = mapping->offset;
- s32 value = 0;
- u8 mask;
-
- data += offset / 8;
- offset &= 7;
- mask = ((1LL << bits) - 1) << offset;
-
- while (1) {
- u8 byte = *data & mask;
- value |= offset > 0 ? (byte >> offset) : (byte << (-offset));
- bits -= 8 - (offset > 0 ? offset : 0);
- if (bits <= 0)
- break;
-
- offset -= 8;
- mask = (1 << bits) - 1;
- data++;
- }
-
- /* Sign-extend the value if needed. */
- if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
- value |= -(value & (1 << (mapping->data_size - 1)));
-
- return value;
-}
-
-/*
- * Set the bit string specified by mapping->offset and mapping->data_size
- * in the little-endian data stored at 'data' to the value 'value'.
- */
-static void uvc_set_le_value(struct uvc_control_mapping *mapping,
- s32 value, u8 *data)
-{
- int bits = mapping->data_size;
- int offset = mapping->offset;
- u8 mask;
-
- /*
- * According to the v4l2 spec, writing any value to a button control
- * should result in the action belonging to the button control being
- * triggered. UVC devices however want to see a 1 written -> override
- * value.
- */
- if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON)
- value = -1;
-
- data += offset / 8;
- offset &= 7;
-
- for (; bits > 0; data++) {
- mask = ((1LL << bits) - 1) << offset;
- *data = (*data & ~mask) | ((value << offset) & mask);
- value >>= offset ? offset : 8;
- bits -= 8 - offset;
- offset = 0;
- }
-}
-
-/*
- * Extract the byte array specified by mapping->offset and mapping->data_size
- * stored at 'data' to the output array 'data_out'.
- */
-static int uvc_get_compound(struct uvc_control_mapping *mapping, const u8 *data,
- u8 *data_out)
-{
- memcpy(data_out, data + mapping->offset / 8, mapping->data_size / 8);
- return 0;
-}
-
-/*
- * Copy the byte array 'data_in' to the destination specified by mapping->offset
- * and mapping->data_size stored at 'data'.
- */
-static int uvc_set_compound(struct uvc_control_mapping *mapping,
- const u8 *data_in, const u8 *data_min,
- const u8 *data_max, u8 *data)
-{
- memcpy(data + mapping->offset / 8, data_in, mapping->data_size / 8);
- return 0;
-}
-static bool
-uvc_ctrl_mapping_is_compound(const struct uvc_control_mapping *mapping)
-{
- return mapping->v4l2_type >= V4L2_CTRL_COMPOUND_TYPES;
-}
-
/* ------------------------------------------------------------------------
* Terminal and unit management
*/
@@ -2981,6 +3018,10 @@ 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 (info->init)
+ info->init(chain->dev, ctrl);
+
break;
}
}
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 18da5e0b8cb2..335b565da6de 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -85,6 +85,7 @@
struct gpio_desc;
struct sg_table;
struct uvc_device;
+struct uvc_control;
/*
* TODO: Put the most frequently accessed fields at the beginning of
@@ -99,6 +100,8 @@ struct uvc_control_info {
u16 size;
u32 flags;
+
+ int (*init)(struct uvc_device *dev, struct uvc_control *ctrl);
};
struct uvc_control_mapping {
--
2.41.0.694.ge786442a9b-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v12 11/11] media: uvcvideo: document UVC v1.5 ROI
2023-08-17 7:17 [PATCH v12 00/11] Implement UVC v1.5 ROI Yunke Cao
` (9 preceding siblings ...)
2023-08-17 7:17 ` [PATCH v12 10/11] media: uvcvideo: initilaize ROI control to default value Yunke Cao
@ 2023-08-17 7:17 ` Yunke Cao
2023-08-17 7:28 ` [PATCH v12 00/11] Implement " Yunke Cao
11 siblings, 0 replies; 17+ messages in thread
From: Yunke Cao @ 2023-08-17 7:17 UTC (permalink / raw)
To: Hans Verkuil, Laurent Pinchart, Daniel Scally
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.
Reviewed-by: Ricardo Ribalda <ribalda@chromium.org>
Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Signed-off-by: Yunke Cao <yunkec@google.com>
---
Changelog since v11:
- No change.
Changelog since v10:
- Added Reviewed-by from Sergey.
Changelog since v9:
- No change.
Changelog since v8:
- No change.
Changelog since v7:
- Fix documentation for automatic exposure based on comment in v7.
.../userspace-api/media/drivers/uvcvideo.rst | 62 +++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
index aab4304e6bb5..3dc062221f8b 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,64 @@ 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
+
+
+Driver-specific V4L2 controls
+-----------------------------
+
+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 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. The left/top coordinates of a minimum or
+ maximum rectangle are always 0. 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_UVC_REGION_OF_INTEREST_AUTO_EXPOSURE``
+ - Setting this to true causes automatic exposure to track the region of
+ interest instead of the whole image.
+ * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_IRIS``
+ - Setting this to true causes automatic iris to track the region of
+ interest instead of the whole image.
+ * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_WHITE_BALANCE``
+ - Setting this to true causes automatic white balance to track the region
+ of interest instead of the whole image.
+ * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_FOCUS``
+ - Setting this to true causes automatic focus adjustment to track the
+ region of interest instead of the whole image.
+ * - ``V4L2_UVC_REGION_OF_INTEREST_AUTO_FACE_DETECT``
+ - Setting this to true causes automatic face detection to track the
+ region of interest instead of the whole image.
+ * - ``V4L2_UVC_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_UVC_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_UVC_REGION_OF_INTEREST_AUTO_HIGHER_QUALITY``
+ - Setting this to true enables automatically capture the specified region
+ with higher quality if possible.
--
2.41.0.694.ge786442a9b-goog
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v12 00/11] Implement UVC v1.5 ROI
2023-08-17 7:17 [PATCH v12 00/11] Implement UVC v1.5 ROI Yunke Cao
` (10 preceding siblings ...)
2023-08-17 7:17 ` [PATCH v12 11/11] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
@ 2023-08-17 7:28 ` Yunke Cao
11 siblings, 0 replies; 17+ messages in thread
From: Yunke Cao @ 2023-08-17 7:28 UTC (permalink / raw)
To: Hans Verkuil, Laurent Pinchart, Daniel Scally
Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media
The v4l-utils series can be found at
https://patchwork.linuxtv.org/project/linux-media/list/?series=11069 .
On Thu, Aug 17, 2023 at 4:17 PM Yunke Cao <yunkec@google.com> wrote:
>
> Hi,
>
> 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.
>
> A patchset in v4l-utils will be sent separately.
>
> Tested with v4l2-compliance, v4l2-ctl, calling ioctls on usb cameras and
> VIVID with a newly added V4L2_CTRL_TYPE_RECT control.
>
> 1/11 adds V4L2_CTRL_TYPE_RECT.
> 2/11, 3/11, 4/11 refactors uvc_ctrl.c.
> 5/11 adds support for compound controls.
> 6/11 is a cherry-pick for Hans' implementation of
> V4L2_CTRL_WHICH_MIN/MAX_VAL in v4l2-core.
> 7/11 addes the rectangle control in VIVID.
> 8/11 supports MIN/MAX in UVC.
> 9/11 implements ROI in UVC.
> 10/11 initializes ROI control to default value.
> 11/11 documents the ROI changes.
>
> Changes since v11
> - Git rid of media: v4l2: document the usage of min/max for V4L2_CTRL_TYPE_RECT.
> - Added a rectangle control in VIVID.
> - Addressed comments in 6/11, details in patch.
>
> Hans Verkuil (1):
> v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
>
> Yunke Cao (10):
> media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT
> media: uvcvideo: add uvc_ctrl_get_boundary for getting default value
> media: uvcvideo: introduce __uvc_ctrl_get_std()
> media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size
> media: uvcvideo: Add support for compound controls
> media: vivid: Add an rectangle control
> media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL
> media: uvcvideo: implement UVC v1.5 ROI
> media: uvcvideo: initilaize ROI control to default value
> media: uvcvideo: document UVC v1.5 ROI
>
> .../userspace-api/media/drivers/uvcvideo.rst | 64 +-
> .../media/v4l/vidioc-g-ext-ctrls.rst | 20 +-
> .../media/v4l/vidioc-queryctrl.rst | 14 +
> .../media/videodev2.h.rst.exceptions | 4 +
> drivers/media/i2c/imx214.c | 5 +-
> .../media/platform/qcom/venus/venc_ctrls.c | 9 +-
> .../media/test-drivers/vivid/vivid-ctrls.c | 34 +
> drivers/media/usb/uvc/uvc_ctrl.c | 721 ++++++++++++++----
> drivers/media/usb/uvc/uvc_v4l2.c | 18 +-
> drivers/media/usb/uvc/uvcvideo.h | 24 +-
> drivers/media/v4l2-core/v4l2-ctrls-api.c | 54 +-
> drivers/media/v4l2-core/v4l2-ctrls-core.c | 176 ++++-
> drivers/media/v4l2-core/v4l2-ioctl.c | 4 +-
> include/media/v4l2-ctrls.h | 36 +-
> include/uapi/linux/usb/video.h | 1 +
> include/uapi/linux/uvcvideo.h | 13 +
> include/uapi/linux/v4l2-controls.h | 9 +
> include/uapi/linux/videodev2.h | 5 +
> 18 files changed, 1001 insertions(+), 210 deletions(-)
>
> --
> 2.41.0.694.ge786442a9b-goog
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v12 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
2023-08-17 7:17 ` [PATCH v12 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
@ 2023-08-18 1:48 ` Yunke Cao
2023-09-13 7:43 ` Yunke Cao
2023-09-13 9:26 ` Hans Verkuil
1 sibling, 1 reply; 17+ messages in thread
From: Yunke Cao @ 2023-08-18 1:48 UTC (permalink / raw)
To: Hans Verkuil
Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media,
Laurent Pinchart, Dan Scally
Hi Hans,
I updated the patch based on your comments. Can you take another look?
Thanks!
Yunke
On Thu, Aug 17, 2023 at 4:18 PM Yunke Cao <yunkec@google.com> 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>
> ---
> Changelog since v11:
> - Added a flag V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX.
> - Modified std_min/max_compound() to be void function. Moved the check of
> whether WHICH_MIN/MAX_VAL into prepare_ext_ctrls(), and return EINVAL.
> - Modified documentations to reflect this change.
> Changelog since v10:
> - No change.
> Changelog since v9:
> - No change.
> Changelog since v8:
> - Return ENODATA when min/max is not implemented. Document this behavior.
> - Created a shared helper function __v4l2_ctrl_type_op_init that takes "which"
> as a parameter. Call it in def, min and max operations.
> Changelog since v7:
> - Document that the definition of the min/max are provided by compound controls
> are defined in control documentation.
> - Return error, instead of zeroed memory for v4l2_ctrl_ptr_create(NULL).
>
> 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 | 16 +-
> .../media/v4l/vidioc-queryctrl.rst | 9 +-
> .../media/videodev2.h.rst.exceptions | 3 +
> drivers/media/i2c/imx214.c | 5 +-
> .../media/platform/qcom/venus/venc_ctrls.c | 9 +-
> drivers/media/v4l2-core/v4l2-ctrls-api.c | 54 ++++--
> drivers/media/v4l2-core/v4l2-ctrls-core.c | 160 +++++++++++++++---
> drivers/media/v4l2-core/v4l2-ioctl.c | 4 +-
> include/media/v4l2-ctrls.h | 34 +++-
> include/uapi/linux/videodev2.h | 3 +
> 10 files changed, 252 insertions(+), 45 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 7b1001d11f9c..b4bf016411d3 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> @@ -330,14 +330,26 @@ 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.
> +
> + Whether a control supports querying the minimum and maximum values using
> + ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` is indicated
> + by the ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. Most non-compound
> + control types support this. For controls with compound types, the
> + definition of minimum/maximum values are provided by
> + the control documentation. If a compound control does not document the
> + meaning of minimum/maximum value, then querying the minimum or maximum
> + value will result in the error code -EINVAL.
>
> 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/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> index 56d5c8b0b88b..b39f7e27bbbe 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> @@ -447,7 +447,10 @@ See also the examples in :ref:`control`.
> - n/a
> - A struct :c:type:`v4l2_rect`, containing a rectangle described by
> the position of its top-left corner, the width and the height. Units
> - depend on the use case.
> + depend on the use case. Support for ``V4L2_CTRL_WHICH_MIN_VAL`` and
> + ``V4L2_CTRL_WHICH_MAX_VAL`` is optional and depends on the
> + ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. See the documentation of
> + the specific control on how to interpret the minimum and maximum values.
> * - ``V4L2_CTRL_TYPE_H264_SPS``
> - n/a
> - n/a
> @@ -664,6 +667,10 @@ See also the examples in :ref:`control`.
> ``dims[0]``. So setting the control with a differently sized
> array will change the ``elems`` field when the control is
> queried afterwards.
> + * - ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX``
> + - 0x1000
> + - This control supports getting minimum and maximum values using
> + vidioc_g_ext_ctrls with V4L2_CTRL_WHICH_MIN/MAX_VAL.
>
> Return Value
> ============
> diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> index c46082ef0e4d..a417af25e9a4 100644
> --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> @@ -393,6 +393,7 @@ replace define V4L2_CTRL_FLAG_HAS_PAYLOAD control-flags
> replace define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE control-flags
> replace define V4L2_CTRL_FLAG_MODIFY_LAYOUT control-flags
> replace define V4L2_CTRL_FLAG_DYNAMIC_ARRAY control-flags
> +replace define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX control-flags
>
> replace define V4L2_CTRL_FLAG_NEXT_CTRL control
> replace define V4L2_CTRL_FLAG_NEXT_COMPOUND control
> @@ -567,6 +568,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 2f9c8582f940..8db4a5eb1737 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 7468e43800a9..28eca8f9d148 100644
> --- a/drivers/media/platform/qcom/venus/venc_ctrls.c
> +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
> @@ -607,11 +607,16 @@ 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(&p_hdr10_cll));
> + v4l2_ctrl_ptr_create(&p_hdr10_cll),
> + 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((void *)&p_hdr10_mastering));
> + v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering),
> + v4l2_ctrl_ptr_create(NULL),
> + v4l2_ctrl_ptr_create(NULL));
> +
>
> v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
> V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE,
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> index 002ea6588edf..d022e1ed4835 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> @@ -94,6 +94,22 @@ 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)
> +{
> + ctrl->type_ops->minimum(ctrl, 0, 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)
> +{
> + ctrl->type_ops->maximum(ctrl, 0, ctrl->p_new);
> +
> + return ptr_to_user(c, ctrl, ctrl->p_new);
> +}
> +
> /* Helper function: copy the caller-provider value as the new control value */
> static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> {
> @@ -229,8 +245,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",
> @@ -259,6 +275,15 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
> return -EINVAL;
> }
>
> + if (!(ctrl->flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX) &&
> + (cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
> + cs->which == V4L2_CTRL_WHICH_MAX_VAL)) {
> + dprintk(vdev,
> + "invalid which 0x%x or control id 0x%x\n",
> + cs->which, id);
> + return -EINVAL;
> + }
> +
> if (ctrl->cluster[0]->ncontrols > 1)
> have_clusters = true;
> if (ctrl->cluster[0] != ctrl)
> @@ -368,8 +393,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;
> }
> @@ -389,10 +414,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);
> @@ -432,13 +459,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++)
> @@ -467,6 +495,10 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
> ret = -ENOMEM;
> else if (is_request && ref->p_req_valid)
> 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
> @@ -564,9 +596,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 f1486ab032cf..f36d54810dbc 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> @@ -182,29 +182,69 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> }
> }
>
> -void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_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_min.p_const)
> + memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
> + else
> + memset(p, 0, ctrl->elem_size);
> +}
> +
> +static int __v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> + u32 which, union v4l2_ctrl_ptr ptr)
> {
> unsigned int i;
> u32 tot_elems = ctrl->elems;
> u32 elems = tot_elems - from_idx;
> + s64 value;
>
> if (from_idx >= tot_elems)
> - return;
> + return -EINVAL;
> +
> + switch (which) {
> + case V4L2_CTRL_WHICH_DEF_VAL:
> + value = ctrl->default_value;
> + break;
> + case V4L2_CTRL_WHICH_MAX_VAL:
> + value = ctrl->maximum;
> + break;
> + case V4L2_CTRL_WHICH_MIN_VAL:
> + value = ctrl->minimum;
> + break;
> + default:
> + return -EINVAL;
> + }
>
> switch (ctrl->type) {
> case V4L2_CTRL_TYPE_STRING:
> + if (which == V4L2_CTRL_WHICH_DEF_VAL)
> + value = ctrl->minimum;
> +
> for (i = from_idx; i < tot_elems; i++) {
> unsigned int offset = i * ctrl->elem_size;
>
> - memset(ptr.p_char + offset, ' ', ctrl->minimum);
> - ptr.p_char[offset + ctrl->minimum] = '\0';
> + memset(ptr.p_char + offset, ' ', value);
> + ptr.p_char[offset + value] = '\0';
> }
> break;
> case V4L2_CTRL_TYPE_INTEGER64:
> - if (ctrl->default_value) {
> + if (value) {
> for (i = from_idx; i < tot_elems; i++)
> - ptr.p_s64[i] = ctrl->default_value;
> + ptr.p_s64[i] = value;
> } else {
> memset(ptr.p_s64 + from_idx, 0, elems * sizeof(s64));
> }
> @@ -214,9 +254,9 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> case V4L2_CTRL_TYPE_MENU:
> case V4L2_CTRL_TYPE_BITMASK:
> case V4L2_CTRL_TYPE_BOOLEAN:
> - if (ctrl->default_value) {
> + if (value) {
> for (i = from_idx; i < tot_elems; i++)
> - ptr.p_s32[i] = ctrl->default_value;
> + ptr.p_s32[i] = value;
> } else {
> memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
> }
> @@ -226,32 +266,63 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
> break;
> case V4L2_CTRL_TYPE_U8:
> - memset(ptr.p_u8 + from_idx, ctrl->default_value, elems);
> + memset(ptr.p_u8 + from_idx, value, elems);
> break;
> case V4L2_CTRL_TYPE_U16:
> - if (ctrl->default_value) {
> + if (value) {
> for (i = from_idx; i < tot_elems; i++)
> - ptr.p_u16[i] = ctrl->default_value;
> + ptr.p_u16[i] = value;
> } else {
> memset(ptr.p_u16 + from_idx, 0, elems * sizeof(u16));
> }
> break;
> case V4L2_CTRL_TYPE_U32:
> - if (ctrl->default_value) {
> + if (value) {
> for (i = from_idx; i < tot_elems; i++)
> - ptr.p_u32[i] = ctrl->default_value;
> + ptr.p_u32[i] = value;
> } else {
> memset(ptr.p_u32 + from_idx, 0, elems * sizeof(u32));
> }
> break;
> default:
> - for (i = from_idx; i < tot_elems; i++)
> - std_init_compound(ctrl, i, ptr);
> + for (i = from_idx; i < tot_elems; i++) {
> + switch (which) {
> + case V4L2_CTRL_WHICH_DEF_VAL:
> + std_init_compound(ctrl, i, ptr);
> + break;
> + case V4L2_CTRL_WHICH_MAX_VAL:
> + std_max_compound(ctrl, i, ptr);
> + break;
> + case V4L2_CTRL_WHICH_MIN_VAL:
> + std_min_compound(ctrl, i, ptr);
> + break;
> + }
> + }
> break;
> }
> +
> + return 0;
> +}
> +
> +void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> + union v4l2_ctrl_ptr ptr)
> +{
> + __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_DEF_VAL, ptr);
> }
> EXPORT_SYMBOL(v4l2_ctrl_type_op_init);
>
> +void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> + union v4l2_ctrl_ptr ptr)
> +{
> + __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MIN_VAL, ptr);
> +}
> +
> +void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> + union v4l2_ctrl_ptr ptr)
> +{
> + __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MAX_VAL, ptr);
> +}
> +
> void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
> {
> union v4l2_ctrl_ptr ptr = ctrl->p_cur;
> @@ -1293,6 +1364,8 @@ EXPORT_SYMBOL(v4l2_ctrl_type_op_validate);
> static const struct v4l2_ctrl_type_ops std_type_ops = {
> .equal = v4l2_ctrl_type_op_equal,
> .init = v4l2_ctrl_type_op_init,
> + .minimum = v4l2_ctrl_type_op_minimum,
> + .maximum = v4l2_ctrl_type_op_maximum,
> .log = v4l2_ctrl_type_op_log,
> .validate = v4l2_ctrl_type_op_validate,
> };
> @@ -1764,7 +1837,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;
> @@ -1888,6 +1964,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> break;
> }
>
> + if (type < V4L2_CTRL_COMPOUND_TYPES &&
> + type != V4L2_CTRL_TYPE_BUTTON &&
> + type != V4L2_CTRL_TYPE_CTRL_CLASS &&
> + type != V4L2_CTRL_TYPE_STRING)
> + flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
> +
> /* Sanity checks */
> if (id == 0 || name == NULL || !elem_size ||
> id >= V4L2_CID_PRIVATE_BASE ||
> @@ -1896,6 +1978,21 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> handler_set_err(hdl, -ERANGE);
> return NULL;
> }
> +
> + if ((!p_def.p_const && p_min.p_const) ||
> + (p_min.p_const && !p_max.p_const) ||
> + (!p_min.p_const && p_max.p_const)) {
> + handler_set_err(hdl, -EINVAL);
> + return NULL;
> + }
> +
> + if (flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX &&
> + type >= V4L2_CTRL_COMPOUND_TYPES &&
> + (!p_min.p_const || !p_max.p_const)) {
> + handler_set_err(hdl, -EINVAL);
> + return NULL;
> + }
> +
> err = check_range(type, min, max, step, def);
> if (err) {
> handler_set_err(hdl, err);
> @@ -1937,6 +2034,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>
> if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const)
> sz_extra += elem_size;
> + if (type >= V4L2_CTRL_COMPOUND_TYPES && p_min.p_const)
> + sz_extra += elem_size * 2;
>
> ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
> if (ctrl == NULL) {
> @@ -2002,6 +2101,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
> }
>
> + if (flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX &&
> + p_min.p_const && p_max.p_const) {
> + ctrl->p_min.p = ctrl->p_def.p + elem_size;
> + memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
> + ctrl->p_max.p = ctrl->p_min.p + elem_size;
> + memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
> + }
> +
> ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
> cur_to_new(ctrl);
>
> @@ -2052,7 +2159,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;
> @@ -2077,7 +2185,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);
>
> @@ -2110,7 +2219,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);
>
> @@ -2142,7 +2252,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);
> @@ -2150,7 +2261,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;
> @@ -2164,7 +2277,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);
>
> @@ -2188,7 +2301,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 01ba27f2ef87..aaf68bfaa601 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -884,7 +884,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 b0db167a3ac4..6e12493b30d3 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -133,6 +133,8 @@ struct v4l2_ctrl_ops {
> *
> * @equal: return true if all ctrl->elems array elements are equal.
> * @init: initialize the value for array elements from from_idx to ctrl->elems.
> + * @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 for ctrl->new_elems array elements.
> * Return 0 on success and a negative value otherwise.
> @@ -142,6 +144,10 @@ struct v4l2_ctrl_type_ops {
> union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2);
> void (*init)(const struct v4l2_ctrl *ctrl, u32 from_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, union v4l2_ctrl_ptr ptr);
> };
> @@ -247,6 +253,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.
> @@ -306,6 +318,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;
> };
> @@ -425,6 +439,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.
> @@ -454,6 +470,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;
> @@ -723,17 +741,23 @@ 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 fields. Use v4l2_ctrl_ptr_create() to create
> + * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
> + * default value of the compound control should be all zeroes. Use
> + * v4l2_ctrl_ptr_create(NULL) if the min/max value of the compound control
> + * is not defined, -ENODATA will be returned in this case.
> *
> */
> 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 089d553cf736..d7670aaf3b5f 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -1863,6 +1863,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,
> @@ -1970,6 +1972,7 @@ struct v4l2_querymenu {
> #define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE 0x0200
> #define V4L2_CTRL_FLAG_MODIFY_LAYOUT 0x0400
> #define V4L2_CTRL_FLAG_DYNAMIC_ARRAY 0x0800
> +#define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX 0x1000
>
> /* Query flags, to be ORed with the control ID */
> #define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000
> --
> 2.41.0.694.ge786442a9b-goog
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v12 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
2023-08-18 1:48 ` Yunke Cao
@ 2023-09-13 7:43 ` Yunke Cao
0 siblings, 0 replies; 17+ messages in thread
From: Yunke Cao @ 2023-09-13 7:43 UTC (permalink / raw)
To: Hans Verkuil
Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media,
Laurent Pinchart, Dan Scally
Hi Hans,
Did you have the chance to take a look at this?
Thanks,
Yunke
On Fri, Aug 18, 2023 at 10:48 AM Yunke Cao <yunkec@google.com> wrote:
>
> Hi Hans,
>
> I updated the patch based on your comments. Can you take another look?
>
> Thanks!
> Yunke
>
> On Thu, Aug 17, 2023 at 4:18 PM Yunke Cao <yunkec@google.com> 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>
> > ---
> > Changelog since v11:
> > - Added a flag V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX.
> > - Modified std_min/max_compound() to be void function. Moved the check of
> > whether WHICH_MIN/MAX_VAL into prepare_ext_ctrls(), and return EINVAL.
> > - Modified documentations to reflect this change.
> > Changelog since v10:
> > - No change.
> > Changelog since v9:
> > - No change.
> > Changelog since v8:
> > - Return ENODATA when min/max is not implemented. Document this behavior.
> > - Created a shared helper function __v4l2_ctrl_type_op_init that takes "which"
> > as a parameter. Call it in def, min and max operations.
> > Changelog since v7:
> > - Document that the definition of the min/max are provided by compound controls
> > are defined in control documentation.
> > - Return error, instead of zeroed memory for v4l2_ctrl_ptr_create(NULL).
> >
> > 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 | 16 +-
> > .../media/v4l/vidioc-queryctrl.rst | 9 +-
> > .../media/videodev2.h.rst.exceptions | 3 +
> > drivers/media/i2c/imx214.c | 5 +-
> > .../media/platform/qcom/venus/venc_ctrls.c | 9 +-
> > drivers/media/v4l2-core/v4l2-ctrls-api.c | 54 ++++--
> > drivers/media/v4l2-core/v4l2-ctrls-core.c | 160 +++++++++++++++---
> > drivers/media/v4l2-core/v4l2-ioctl.c | 4 +-
> > include/media/v4l2-ctrls.h | 34 +++-
> > include/uapi/linux/videodev2.h | 3 +
> > 10 files changed, 252 insertions(+), 45 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 7b1001d11f9c..b4bf016411d3 100644
> > --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > @@ -330,14 +330,26 @@ 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.
> > +
> > + Whether a control supports querying the minimum and maximum values using
> > + ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` is indicated
> > + by the ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. Most non-compound
> > + control types support this. For controls with compound types, the
> > + definition of minimum/maximum values are provided by
> > + the control documentation. If a compound control does not document the
> > + meaning of minimum/maximum value, then querying the minimum or maximum
> > + value will result in the error code -EINVAL.
> >
> > 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/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > index 56d5c8b0b88b..b39f7e27bbbe 100644
> > --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > @@ -447,7 +447,10 @@ See also the examples in :ref:`control`.
> > - n/a
> > - A struct :c:type:`v4l2_rect`, containing a rectangle described by
> > the position of its top-left corner, the width and the height. Units
> > - depend on the use case.
> > + depend on the use case. Support for ``V4L2_CTRL_WHICH_MIN_VAL`` and
> > + ``V4L2_CTRL_WHICH_MAX_VAL`` is optional and depends on the
> > + ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. See the documentation of
> > + the specific control on how to interpret the minimum and maximum values.
> > * - ``V4L2_CTRL_TYPE_H264_SPS``
> > - n/a
> > - n/a
> > @@ -664,6 +667,10 @@ See also the examples in :ref:`control`.
> > ``dims[0]``. So setting the control with a differently sized
> > array will change the ``elems`` field when the control is
> > queried afterwards.
> > + * - ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX``
> > + - 0x1000
> > + - This control supports getting minimum and maximum values using
> > + vidioc_g_ext_ctrls with V4L2_CTRL_WHICH_MIN/MAX_VAL.
> >
> > Return Value
> > ============
> > diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > index c46082ef0e4d..a417af25e9a4 100644
> > --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > @@ -393,6 +393,7 @@ replace define V4L2_CTRL_FLAG_HAS_PAYLOAD control-flags
> > replace define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE control-flags
> > replace define V4L2_CTRL_FLAG_MODIFY_LAYOUT control-flags
> > replace define V4L2_CTRL_FLAG_DYNAMIC_ARRAY control-flags
> > +replace define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX control-flags
> >
> > replace define V4L2_CTRL_FLAG_NEXT_CTRL control
> > replace define V4L2_CTRL_FLAG_NEXT_COMPOUND control
> > @@ -567,6 +568,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 2f9c8582f940..8db4a5eb1737 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 7468e43800a9..28eca8f9d148 100644
> > --- a/drivers/media/platform/qcom/venus/venc_ctrls.c
> > +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
> > @@ -607,11 +607,16 @@ 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(&p_hdr10_cll));
> > + v4l2_ctrl_ptr_create(&p_hdr10_cll),
> > + 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((void *)&p_hdr10_mastering));
> > + v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering),
> > + v4l2_ctrl_ptr_create(NULL),
> > + v4l2_ctrl_ptr_create(NULL));
> > +
> >
> > v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
> > V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE,
> > diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> > index 002ea6588edf..d022e1ed4835 100644
> > --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
> > +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> > @@ -94,6 +94,22 @@ 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)
> > +{
> > + ctrl->type_ops->minimum(ctrl, 0, 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)
> > +{
> > + ctrl->type_ops->maximum(ctrl, 0, ctrl->p_new);
> > +
> > + return ptr_to_user(c, ctrl, ctrl->p_new);
> > +}
> > +
> > /* Helper function: copy the caller-provider value as the new control value */
> > static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> > {
> > @@ -229,8 +245,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",
> > @@ -259,6 +275,15 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
> > return -EINVAL;
> > }
> >
> > + if (!(ctrl->flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX) &&
> > + (cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
> > + cs->which == V4L2_CTRL_WHICH_MAX_VAL)) {
> > + dprintk(vdev,
> > + "invalid which 0x%x or control id 0x%x\n",
> > + cs->which, id);
> > + return -EINVAL;
> > + }
> > +
> > if (ctrl->cluster[0]->ncontrols > 1)
> > have_clusters = true;
> > if (ctrl->cluster[0] != ctrl)
> > @@ -368,8 +393,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;
> > }
> > @@ -389,10 +414,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);
> > @@ -432,13 +459,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++)
> > @@ -467,6 +495,10 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
> > ret = -ENOMEM;
> > else if (is_request && ref->p_req_valid)
> > 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
> > @@ -564,9 +596,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 f1486ab032cf..f36d54810dbc 100644
> > --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > @@ -182,29 +182,69 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> > }
> > }
> >
> > -void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_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_min.p_const)
> > + memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
> > + else
> > + memset(p, 0, ctrl->elem_size);
> > +}
> > +
> > +static int __v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > + u32 which, union v4l2_ctrl_ptr ptr)
> > {
> > unsigned int i;
> > u32 tot_elems = ctrl->elems;
> > u32 elems = tot_elems - from_idx;
> > + s64 value;
> >
> > if (from_idx >= tot_elems)
> > - return;
> > + return -EINVAL;
> > +
> > + switch (which) {
> > + case V4L2_CTRL_WHICH_DEF_VAL:
> > + value = ctrl->default_value;
> > + break;
> > + case V4L2_CTRL_WHICH_MAX_VAL:
> > + value = ctrl->maximum;
> > + break;
> > + case V4L2_CTRL_WHICH_MIN_VAL:
> > + value = ctrl->minimum;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> >
> > switch (ctrl->type) {
> > case V4L2_CTRL_TYPE_STRING:
> > + if (which == V4L2_CTRL_WHICH_DEF_VAL)
> > + value = ctrl->minimum;
> > +
> > for (i = from_idx; i < tot_elems; i++) {
> > unsigned int offset = i * ctrl->elem_size;
> >
> > - memset(ptr.p_char + offset, ' ', ctrl->minimum);
> > - ptr.p_char[offset + ctrl->minimum] = '\0';
> > + memset(ptr.p_char + offset, ' ', value);
> > + ptr.p_char[offset + value] = '\0';
> > }
> > break;
> > case V4L2_CTRL_TYPE_INTEGER64:
> > - if (ctrl->default_value) {
> > + if (value) {
> > for (i = from_idx; i < tot_elems; i++)
> > - ptr.p_s64[i] = ctrl->default_value;
> > + ptr.p_s64[i] = value;
> > } else {
> > memset(ptr.p_s64 + from_idx, 0, elems * sizeof(s64));
> > }
> > @@ -214,9 +254,9 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > case V4L2_CTRL_TYPE_MENU:
> > case V4L2_CTRL_TYPE_BITMASK:
> > case V4L2_CTRL_TYPE_BOOLEAN:
> > - if (ctrl->default_value) {
> > + if (value) {
> > for (i = from_idx; i < tot_elems; i++)
> > - ptr.p_s32[i] = ctrl->default_value;
> > + ptr.p_s32[i] = value;
> > } else {
> > memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
> > }
> > @@ -226,32 +266,63 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
> > break;
> > case V4L2_CTRL_TYPE_U8:
> > - memset(ptr.p_u8 + from_idx, ctrl->default_value, elems);
> > + memset(ptr.p_u8 + from_idx, value, elems);
> > break;
> > case V4L2_CTRL_TYPE_U16:
> > - if (ctrl->default_value) {
> > + if (value) {
> > for (i = from_idx; i < tot_elems; i++)
> > - ptr.p_u16[i] = ctrl->default_value;
> > + ptr.p_u16[i] = value;
> > } else {
> > memset(ptr.p_u16 + from_idx, 0, elems * sizeof(u16));
> > }
> > break;
> > case V4L2_CTRL_TYPE_U32:
> > - if (ctrl->default_value) {
> > + if (value) {
> > for (i = from_idx; i < tot_elems; i++)
> > - ptr.p_u32[i] = ctrl->default_value;
> > + ptr.p_u32[i] = value;
> > } else {
> > memset(ptr.p_u32 + from_idx, 0, elems * sizeof(u32));
> > }
> > break;
> > default:
> > - for (i = from_idx; i < tot_elems; i++)
> > - std_init_compound(ctrl, i, ptr);
> > + for (i = from_idx; i < tot_elems; i++) {
> > + switch (which) {
> > + case V4L2_CTRL_WHICH_DEF_VAL:
> > + std_init_compound(ctrl, i, ptr);
> > + break;
> > + case V4L2_CTRL_WHICH_MAX_VAL:
> > + std_max_compound(ctrl, i, ptr);
> > + break;
> > + case V4L2_CTRL_WHICH_MIN_VAL:
> > + std_min_compound(ctrl, i, ptr);
> > + break;
> > + }
> > + }
> > break;
> > }
> > +
> > + return 0;
> > +}
> > +
> > +void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > + union v4l2_ctrl_ptr ptr)
> > +{
> > + __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_DEF_VAL, ptr);
> > }
> > EXPORT_SYMBOL(v4l2_ctrl_type_op_init);
> >
> > +void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > + union v4l2_ctrl_ptr ptr)
> > +{
> > + __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MIN_VAL, ptr);
> > +}
> > +
> > +void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > + union v4l2_ctrl_ptr ptr)
> > +{
> > + __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MAX_VAL, ptr);
> > +}
> > +
> > void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
> > {
> > union v4l2_ctrl_ptr ptr = ctrl->p_cur;
> > @@ -1293,6 +1364,8 @@ EXPORT_SYMBOL(v4l2_ctrl_type_op_validate);
> > static const struct v4l2_ctrl_type_ops std_type_ops = {
> > .equal = v4l2_ctrl_type_op_equal,
> > .init = v4l2_ctrl_type_op_init,
> > + .minimum = v4l2_ctrl_type_op_minimum,
> > + .maximum = v4l2_ctrl_type_op_maximum,
> > .log = v4l2_ctrl_type_op_log,
> > .validate = v4l2_ctrl_type_op_validate,
> > };
> > @@ -1764,7 +1837,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;
> > @@ -1888,6 +1964,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> > break;
> > }
> >
> > + if (type < V4L2_CTRL_COMPOUND_TYPES &&
> > + type != V4L2_CTRL_TYPE_BUTTON &&
> > + type != V4L2_CTRL_TYPE_CTRL_CLASS &&
> > + type != V4L2_CTRL_TYPE_STRING)
> > + flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
> > +
> > /* Sanity checks */
> > if (id == 0 || name == NULL || !elem_size ||
> > id >= V4L2_CID_PRIVATE_BASE ||
> > @@ -1896,6 +1978,21 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> > handler_set_err(hdl, -ERANGE);
> > return NULL;
> > }
> > +
> > + if ((!p_def.p_const && p_min.p_const) ||
> > + (p_min.p_const && !p_max.p_const) ||
> > + (!p_min.p_const && p_max.p_const)) {
> > + handler_set_err(hdl, -EINVAL);
> > + return NULL;
> > + }
> > +
> > + if (flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX &&
> > + type >= V4L2_CTRL_COMPOUND_TYPES &&
> > + (!p_min.p_const || !p_max.p_const)) {
> > + handler_set_err(hdl, -EINVAL);
> > + return NULL;
> > + }
> > +
> > err = check_range(type, min, max, step, def);
> > if (err) {
> > handler_set_err(hdl, err);
> > @@ -1937,6 +2034,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >
> > if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const)
> > sz_extra += elem_size;
> > + if (type >= V4L2_CTRL_COMPOUND_TYPES && p_min.p_const)
> > + sz_extra += elem_size * 2;
> >
> > ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
> > if (ctrl == NULL) {
> > @@ -2002,6 +2101,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> > memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
> > }
> >
> > + if (flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX &&
> > + p_min.p_const && p_max.p_const) {
> > + ctrl->p_min.p = ctrl->p_def.p + elem_size;
> > + memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
> > + ctrl->p_max.p = ctrl->p_min.p + elem_size;
> > + memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
> > + }
> > +
> > ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
> > cur_to_new(ctrl);
> >
> > @@ -2052,7 +2159,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;
> > @@ -2077,7 +2185,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);
> >
> > @@ -2110,7 +2219,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);
> >
> > @@ -2142,7 +2252,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);
> > @@ -2150,7 +2261,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;
> > @@ -2164,7 +2277,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);
> >
> > @@ -2188,7 +2301,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 01ba27f2ef87..aaf68bfaa601 100644
> > --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> > +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> > @@ -884,7 +884,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 b0db167a3ac4..6e12493b30d3 100644
> > --- a/include/media/v4l2-ctrls.h
> > +++ b/include/media/v4l2-ctrls.h
> > @@ -133,6 +133,8 @@ struct v4l2_ctrl_ops {
> > *
> > * @equal: return true if all ctrl->elems array elements are equal.
> > * @init: initialize the value for array elements from from_idx to ctrl->elems.
> > + * @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 for ctrl->new_elems array elements.
> > * Return 0 on success and a negative value otherwise.
> > @@ -142,6 +144,10 @@ struct v4l2_ctrl_type_ops {
> > union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2);
> > void (*init)(const struct v4l2_ctrl *ctrl, u32 from_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, union v4l2_ctrl_ptr ptr);
> > };
> > @@ -247,6 +253,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.
> > @@ -306,6 +318,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;
> > };
> > @@ -425,6 +439,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.
> > @@ -454,6 +470,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;
> > @@ -723,17 +741,23 @@ 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 fields. Use v4l2_ctrl_ptr_create() to create
> > + * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
> > + * default value of the compound control should be all zeroes. Use
> > + * v4l2_ctrl_ptr_create(NULL) if the min/max value of the compound control
> > + * is not defined, -ENODATA will be returned in this case.
> > *
> > */
> > 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 089d553cf736..d7670aaf3b5f 100644
> > --- a/include/uapi/linux/videodev2.h
> > +++ b/include/uapi/linux/videodev2.h
> > @@ -1863,6 +1863,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,
> > @@ -1970,6 +1972,7 @@ struct v4l2_querymenu {
> > #define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE 0x0200
> > #define V4L2_CTRL_FLAG_MODIFY_LAYOUT 0x0400
> > #define V4L2_CTRL_FLAG_DYNAMIC_ARRAY 0x0800
> > +#define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX 0x1000
> >
> > /* Query flags, to be ORed with the control ID */
> > #define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000
> > --
> > 2.41.0.694.ge786442a9b-goog
> >
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v12 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
2023-08-17 7:17 ` [PATCH v12 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
2023-08-18 1:48 ` Yunke Cao
@ 2023-09-13 9:26 ` Hans Verkuil
2023-09-15 7:28 ` Yunke Cao
1 sibling, 1 reply; 17+ messages in thread
From: Hans Verkuil @ 2023-09-13 9:26 UTC (permalink / raw)
To: Yunke Cao, Laurent Pinchart, Daniel Scally
Cc: Tomasz Figa, Sergey Senozhatsky, Ricardo Ribalda, linux-media
Hi Yunke,
Thank you for the reminder! Since this series is for uvc, I tend to forget that
it contains a single patch for the V4L2 core that I need to review. Just email
me a reminder for v13 if you don't see a review after a week.
On 17/08/2023 09:17, 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>
> ---
> Changelog since v11:
> - Added a flag V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX.
> - Modified std_min/max_compound() to be void function. Moved the check of
> whether WHICH_MIN/MAX_VAL into prepare_ext_ctrls(), and return EINVAL.
> - Modified documentations to reflect this change.
> Changelog since v10:
> - No change.
> Changelog since v9:
> - No change.
> Changelog since v8:
> - Return ENODATA when min/max is not implemented. Document this behavior.
> - Created a shared helper function __v4l2_ctrl_type_op_init that takes "which"
> as a parameter. Call it in def, min and max operations.
> Changelog since v7:
> - Document that the definition of the min/max are provided by compound controls
> are defined in control documentation.
> - Return error, instead of zeroed memory for v4l2_ctrl_ptr_create(NULL).
>
> 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 | 16 +-
> .../media/v4l/vidioc-queryctrl.rst | 9 +-
> .../media/videodev2.h.rst.exceptions | 3 +
> drivers/media/i2c/imx214.c | 5 +-
> .../media/platform/qcom/venus/venc_ctrls.c | 9 +-
> drivers/media/v4l2-core/v4l2-ctrls-api.c | 54 ++++--
> drivers/media/v4l2-core/v4l2-ctrls-core.c | 160 +++++++++++++++---
> drivers/media/v4l2-core/v4l2-ioctl.c | 4 +-
> include/media/v4l2-ctrls.h | 34 +++-
> include/uapi/linux/videodev2.h | 3 +
> 10 files changed, 252 insertions(+), 45 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 7b1001d11f9c..b4bf016411d3 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> @@ -330,14 +330,26 @@ 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
This text is getting a bit messy. I propose this instead:
* - :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, and ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
value of the control. ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
the control value has to be retrieved from a request or tried/set for
a request. In that case the ``request_fd`` field contains the
It's just a few minor changes, but it makes it easier to read.
> 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.
> +
> + Whether a control supports querying the minimum and maximum values using
> + ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` is indicated
> + by the ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. Most non-compound
> + control types support this. For controls with compound types, the
> + definition of minimum/maximum values are provided by
> + the control documentation. If a compound control does not document the
> + meaning of minimum/maximum value, then querying the minimum or maximum
> + value will result in the error code -EINVAL.
>
> 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/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> index 56d5c8b0b88b..b39f7e27bbbe 100644
> --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> @@ -447,7 +447,10 @@ See also the examples in :ref:`control`.
> - n/a
> - A struct :c:type:`v4l2_rect`, containing a rectangle described by
> the position of its top-left corner, the width and the height. Units
> - depend on the use case.
> + depend on the use case. Support for ``V4L2_CTRL_WHICH_MIN_VAL`` and
> + ``V4L2_CTRL_WHICH_MAX_VAL`` is optional and depends on the
> + ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. See the documentation of
> + the specific control on how to interpret the minimum and maximum values.
> * - ``V4L2_CTRL_TYPE_H264_SPS``
> - n/a
> - n/a
> @@ -664,6 +667,10 @@ See also the examples in :ref:`control`.
> ``dims[0]``. So setting the control with a differently sized
> array will change the ``elems`` field when the control is
> queried afterwards.
> + * - ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX``
> + - 0x1000
> + - This control supports getting minimum and maximum values using
> + vidioc_g_ext_ctrls with V4L2_CTRL_WHICH_MIN/MAX_VAL.
>
> Return Value
> ============
> diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> index c46082ef0e4d..a417af25e9a4 100644
> --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> @@ -393,6 +393,7 @@ replace define V4L2_CTRL_FLAG_HAS_PAYLOAD control-flags
> replace define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE control-flags
> replace define V4L2_CTRL_FLAG_MODIFY_LAYOUT control-flags
> replace define V4L2_CTRL_FLAG_DYNAMIC_ARRAY control-flags
> +replace define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX control-flags
>
> replace define V4L2_CTRL_FLAG_NEXT_CTRL control
> replace define V4L2_CTRL_FLAG_NEXT_COMPOUND control
> @@ -567,6 +568,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 2f9c8582f940..8db4a5eb1737 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 7468e43800a9..28eca8f9d148 100644
> --- a/drivers/media/platform/qcom/venus/venc_ctrls.c
> +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
> @@ -607,11 +607,16 @@ 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(&p_hdr10_cll));
> + v4l2_ctrl_ptr_create(&p_hdr10_cll),
> + 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((void *)&p_hdr10_mastering));
> + v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering),
> + v4l2_ctrl_ptr_create(NULL),
> + v4l2_ctrl_ptr_create(NULL));
> +
>
> v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
> V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE,
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> index 002ea6588edf..d022e1ed4835 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> @@ -94,6 +94,22 @@ 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)
> +{
> + ctrl->type_ops->minimum(ctrl, 0, 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)
> +{
> + ctrl->type_ops->maximum(ctrl, 0, ctrl->p_new);
> +
> + return ptr_to_user(c, ctrl, ctrl->p_new);
> +}
> +
> /* Helper function: copy the caller-provider value as the new control value */
> static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> {
> @@ -229,8 +245,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",
> @@ -259,6 +275,15 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
> return -EINVAL;
> }
>
> + if (!(ctrl->flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX) &&
> + (cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
> + cs->which == V4L2_CTRL_WHICH_MAX_VAL)) {
> + dprintk(vdev,
> + "invalid which 0x%x or control id 0x%x\n",
> + cs->which, id);
> + return -EINVAL;
> + }
> +
> if (ctrl->cluster[0]->ncontrols > 1)
> have_clusters = true;
> if (ctrl->cluster[0] != ctrl)
> @@ -368,8 +393,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;
> }
> @@ -389,10 +414,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);
> @@ -432,13 +459,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++)
> @@ -467,6 +495,10 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
> ret = -ENOMEM;
> else if (is_request && ref->p_req_valid)
> 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
> @@ -564,9 +596,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 f1486ab032cf..f36d54810dbc 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> @@ -182,29 +182,69 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> }
> }
>
> -void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_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_min.p_const)
Typo: p_min -> p_max
> + memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
> + else
> + memset(p, 0, ctrl->elem_size);
> +}
> +
> +static int __v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
Just keep this as a void function. The return value is never used anyway.
The assumption is that this function is only called with valid arguments.
> + u32 which, union v4l2_ctrl_ptr ptr)
> {
> unsigned int i;
> u32 tot_elems = ctrl->elems;
> u32 elems = tot_elems - from_idx;
> + s64 value;
>
> if (from_idx >= tot_elems)
> - return;
> + return -EINVAL;
> +
> + switch (which) {
> + case V4L2_CTRL_WHICH_DEF_VAL:
> + value = ctrl->default_value;
> + break;
> + case V4L2_CTRL_WHICH_MAX_VAL:
> + value = ctrl->maximum;
> + break;
> + case V4L2_CTRL_WHICH_MIN_VAL:
> + value = ctrl->minimum;
> + break;
> + default:
> + return -EINVAL;
> + }
>
> switch (ctrl->type) {
> case V4L2_CTRL_TYPE_STRING:
> + if (which == V4L2_CTRL_WHICH_DEF_VAL)
> + value = ctrl->minimum;
> +
> for (i = from_idx; i < tot_elems; i++) {
> unsigned int offset = i * ctrl->elem_size;
>
> - memset(ptr.p_char + offset, ' ', ctrl->minimum);
> - ptr.p_char[offset + ctrl->minimum] = '\0';
> + memset(ptr.p_char + offset, ' ', value);
> + ptr.p_char[offset + value] = '\0';
> }
> break;
> case V4L2_CTRL_TYPE_INTEGER64:
> - if (ctrl->default_value) {
> + if (value) {
> for (i = from_idx; i < tot_elems; i++)
> - ptr.p_s64[i] = ctrl->default_value;
> + ptr.p_s64[i] = value;
> } else {
> memset(ptr.p_s64 + from_idx, 0, elems * sizeof(s64));
> }
> @@ -214,9 +254,9 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> case V4L2_CTRL_TYPE_MENU:
> case V4L2_CTRL_TYPE_BITMASK:
> case V4L2_CTRL_TYPE_BOOLEAN:
> - if (ctrl->default_value) {
> + if (value) {
> for (i = from_idx; i < tot_elems; i++)
> - ptr.p_s32[i] = ctrl->default_value;
> + ptr.p_s32[i] = value;
> } else {
> memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
> }
> @@ -226,32 +266,63 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
> break;
> case V4L2_CTRL_TYPE_U8:
> - memset(ptr.p_u8 + from_idx, ctrl->default_value, elems);
> + memset(ptr.p_u8 + from_idx, value, elems);
> break;
> case V4L2_CTRL_TYPE_U16:
> - if (ctrl->default_value) {
> + if (value) {
> for (i = from_idx; i < tot_elems; i++)
> - ptr.p_u16[i] = ctrl->default_value;
> + ptr.p_u16[i] = value;
> } else {
> memset(ptr.p_u16 + from_idx, 0, elems * sizeof(u16));
> }
> break;
> case V4L2_CTRL_TYPE_U32:
> - if (ctrl->default_value) {
> + if (value) {
> for (i = from_idx; i < tot_elems; i++)
> - ptr.p_u32[i] = ctrl->default_value;
> + ptr.p_u32[i] = value;
> } else {
> memset(ptr.p_u32 + from_idx, 0, elems * sizeof(u32));
> }
> break;
> default:
> - for (i = from_idx; i < tot_elems; i++)
> - std_init_compound(ctrl, i, ptr);
> + for (i = from_idx; i < tot_elems; i++) {
> + switch (which) {
> + case V4L2_CTRL_WHICH_DEF_VAL:
> + std_init_compound(ctrl, i, ptr);
> + break;
> + case V4L2_CTRL_WHICH_MAX_VAL:
> + std_max_compound(ctrl, i, ptr);
> + break;
> + case V4L2_CTRL_WHICH_MIN_VAL:
> + std_min_compound(ctrl, i, ptr);
> + break;
> + }
> + }
> break;
> }
> +
> + return 0;
> +}
> +
> +void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> + union v4l2_ctrl_ptr ptr)
> +{
> + __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_DEF_VAL, ptr);
> }
> EXPORT_SYMBOL(v4l2_ctrl_type_op_init);
>
> +void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> + union v4l2_ctrl_ptr ptr)
> +{
> + __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MIN_VAL, ptr);
> +}
> +
> +void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> + union v4l2_ctrl_ptr ptr)
> +{
> + __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MAX_VAL, ptr);
> +}
> +
> void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
> {
> union v4l2_ctrl_ptr ptr = ctrl->p_cur;
> @@ -1293,6 +1364,8 @@ EXPORT_SYMBOL(v4l2_ctrl_type_op_validate);
> static const struct v4l2_ctrl_type_ops std_type_ops = {
> .equal = v4l2_ctrl_type_op_equal,
> .init = v4l2_ctrl_type_op_init,
> + .minimum = v4l2_ctrl_type_op_minimum,
> + .maximum = v4l2_ctrl_type_op_maximum,
> .log = v4l2_ctrl_type_op_log,
> .validate = v4l2_ctrl_type_op_validate,
> };
> @@ -1764,7 +1837,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;
> @@ -1888,6 +1964,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> break;
> }
>
> + if (type < V4L2_CTRL_COMPOUND_TYPES &&
> + type != V4L2_CTRL_TYPE_BUTTON &&
> + type != V4L2_CTRL_TYPE_CTRL_CLASS &&
> + type != V4L2_CTRL_TYPE_STRING)
> + flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
> +
> /* Sanity checks */
> if (id == 0 || name == NULL || !elem_size ||
> id >= V4L2_CID_PRIVATE_BASE ||
> @@ -1896,6 +1978,21 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> handler_set_err(hdl, -ERANGE);
> return NULL;
> }
> +
> + if ((!p_def.p_const && p_min.p_const) ||
> + (p_min.p_const && !p_max.p_const) ||
> + (!p_min.p_const && p_max.p_const)) {
> + handler_set_err(hdl, -EINVAL);
> + return NULL;
> + }
I would drop this. I think p_def, p_min and p_max can all be NULL (in which
case the corresponding value is a memset(0)).
> +
> + if (flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX &&
> + type >= V4L2_CTRL_COMPOUND_TYPES &&
> + (!p_min.p_const || !p_max.p_const)) {
> + handler_set_err(hdl, -EINVAL);
> + return NULL;
> + }
Same here. Suppose you have a compound control that for whatever reason has
just a single fixed value, that happens to be all zeroes. Then you can set
leave p_min/p_max to NULL, while still setting V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX.
I'd just drop this test.
> +
> err = check_range(type, min, max, step, def);
> if (err) {
> handler_set_err(hdl, err);
> @@ -1937,6 +2034,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>
> if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const)
> sz_extra += elem_size;
> + if (type >= V4L2_CTRL_COMPOUND_TYPES && p_min.p_const)
> + sz_extra += elem_size * 2;
Since one of p_min or p_max can be NULL, just do this:
if (type >= V4L2_CTRL_COMPOUND_TYPES && p_min.p_const)
sz_extra += elem_size;
if (type >= V4L2_CTRL_COMPOUND_TYPES && p_max.p_const)
sz_extra += elem_size;
>
> ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
> if (ctrl == NULL) {
> @@ -2002,6 +2101,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
> }
>
> + if (flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX &&
> + p_min.p_const && p_max.p_const) {
> + ctrl->p_min.p = ctrl->p_def.p + elem_size;
> + memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
> + ctrl->p_max.p = ctrl->p_min.p + elem_size;
> + memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
> + }
This needs a similar modification.
> +
> ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
> cur_to_new(ctrl);
>
> @@ -2052,7 +2159,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;
> @@ -2077,7 +2185,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);
>
> @@ -2110,7 +2219,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);
>
> @@ -2142,7 +2252,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);
> @@ -2150,7 +2261,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;
> @@ -2164,7 +2277,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);
>
> @@ -2188,7 +2301,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 01ba27f2ef87..aaf68bfaa601 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -884,7 +884,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 b0db167a3ac4..6e12493b30d3 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -133,6 +133,8 @@ struct v4l2_ctrl_ops {
> *
> * @equal: return true if all ctrl->elems array elements are equal.
> * @init: initialize the value for array elements from from_idx to ctrl->elems.
> + * @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 for ctrl->new_elems array elements.
> * Return 0 on success and a negative value otherwise.
> @@ -142,6 +144,10 @@ struct v4l2_ctrl_type_ops {
> union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2);
> void (*init)(const struct v4l2_ctrl *ctrl, u32 from_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, union v4l2_ctrl_ptr ptr);
> };
> @@ -247,6 +253,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.
> @@ -306,6 +318,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;
> };
> @@ -425,6 +439,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.
> @@ -454,6 +470,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;
> @@ -723,17 +741,23 @@ 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 fields. Use v4l2_ctrl_ptr_create() to create
> + * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
> + * default value of the compound control should be all zeroes. Use
> + * v4l2_ctrl_ptr_create(NULL) if the min/max value of the compound control
> + * is not defined, -ENODATA will be returned in this case.
This is outdated and should be:
* @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
* default, minimum or maximum 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 089d553cf736..d7670aaf3b5f 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -1863,6 +1863,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,
> @@ -1970,6 +1972,7 @@ struct v4l2_querymenu {
> #define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE 0x0200
> #define V4L2_CTRL_FLAG_MODIFY_LAYOUT 0x0400
> #define V4L2_CTRL_FLAG_DYNAMIC_ARRAY 0x0800
> +#define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX 0x1000
>
> /* Query flags, to be ORed with the control ID */
> #define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000
Regards,
Hans
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v12 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL
2023-09-13 9:26 ` Hans Verkuil
@ 2023-09-15 7:28 ` Yunke Cao
0 siblings, 0 replies; 17+ messages in thread
From: Yunke Cao @ 2023-09-15 7:28 UTC (permalink / raw)
To: Hans Verkuil
Cc: Laurent Pinchart, Daniel Scally, Tomasz Figa, Sergey Senozhatsky,
Ricardo Ribalda, linux-media
Thanks Hans!
These make sense. I will address them in the next version.
On Wed, Sep 13, 2023 at 6:26 PM Hans Verkuil <hverkuil-cisco@xs4all.nl> wrote:
>
> Hi Yunke,
>
> Thank you for the reminder! Since this series is for uvc, I tend to forget that
> it contains a single patch for the V4L2 core that I need to review. Just email
> me a reminder for v13 if you don't see a review after a week.
>
> On 17/08/2023 09:17, 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>
> > ---
> > Changelog since v11:
> > - Added a flag V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX.
> > - Modified std_min/max_compound() to be void function. Moved the check of
> > whether WHICH_MIN/MAX_VAL into prepare_ext_ctrls(), and return EINVAL.
> > - Modified documentations to reflect this change.
> > Changelog since v10:
> > - No change.
> > Changelog since v9:
> > - No change.
> > Changelog since v8:
> > - Return ENODATA when min/max is not implemented. Document this behavior.
> > - Created a shared helper function __v4l2_ctrl_type_op_init that takes "which"
> > as a parameter. Call it in def, min and max operations.
> > Changelog since v7:
> > - Document that the definition of the min/max are provided by compound controls
> > are defined in control documentation.
> > - Return error, instead of zeroed memory for v4l2_ctrl_ptr_create(NULL).
> >
> > 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 | 16 +-
> > .../media/v4l/vidioc-queryctrl.rst | 9 +-
> > .../media/videodev2.h.rst.exceptions | 3 +
> > drivers/media/i2c/imx214.c | 5 +-
> > .../media/platform/qcom/venus/venc_ctrls.c | 9 +-
> > drivers/media/v4l2-core/v4l2-ctrls-api.c | 54 ++++--
> > drivers/media/v4l2-core/v4l2-ctrls-core.c | 160 +++++++++++++++---
> > drivers/media/v4l2-core/v4l2-ioctl.c | 4 +-
> > include/media/v4l2-ctrls.h | 34 +++-
> > include/uapi/linux/videodev2.h | 3 +
> > 10 files changed, 252 insertions(+), 45 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 7b1001d11f9c..b4bf016411d3 100644
> > --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst
> > @@ -330,14 +330,26 @@ 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
>
> This text is getting a bit messy. I propose this instead:
>
> * - :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, and ``V4L2_CTRL_WHICH_MAX_VAL`` will return the maximum
> value of the control. ``V4L2_CTRL_WHICH_REQUEST_VAL`` indicates that
> the control value has to be retrieved from a request or tried/set for
> a request. In that case the ``request_fd`` field contains the
>
> It's just a few minor changes, but it makes it easier to read.
>
> > 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.
> > +
> > + Whether a control supports querying the minimum and maximum values using
> > + ``V4L2_CTRL_WHICH_MIN_VAL`` and ``V4L2_CTRL_WHICH_MAX_VAL`` is indicated
> > + by the ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. Most non-compound
> > + control types support this. For controls with compound types, the
> > + definition of minimum/maximum values are provided by
> > + the control documentation. If a compound control does not document the
> > + meaning of minimum/maximum value, then querying the minimum or maximum
> > + value will result in the error code -EINVAL.
> >
> > 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/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > index 56d5c8b0b88b..b39f7e27bbbe 100644
> > --- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > +++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
> > @@ -447,7 +447,10 @@ See also the examples in :ref:`control`.
> > - n/a
> > - A struct :c:type:`v4l2_rect`, containing a rectangle described by
> > the position of its top-left corner, the width and the height. Units
> > - depend on the use case.
> > + depend on the use case. Support for ``V4L2_CTRL_WHICH_MIN_VAL`` and
> > + ``V4L2_CTRL_WHICH_MAX_VAL`` is optional and depends on the
> > + ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX`` flag. See the documentation of
> > + the specific control on how to interpret the minimum and maximum values.
> > * - ``V4L2_CTRL_TYPE_H264_SPS``
> > - n/a
> > - n/a
> > @@ -664,6 +667,10 @@ See also the examples in :ref:`control`.
> > ``dims[0]``. So setting the control with a differently sized
> > array will change the ``elems`` field when the control is
> > queried afterwards.
> > + * - ``V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX``
> > + - 0x1000
> > + - This control supports getting minimum and maximum values using
> > + vidioc_g_ext_ctrls with V4L2_CTRL_WHICH_MIN/MAX_VAL.
> >
> > Return Value
> > ============
> > diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > index c46082ef0e4d..a417af25e9a4 100644
> > --- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > +++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
> > @@ -393,6 +393,7 @@ replace define V4L2_CTRL_FLAG_HAS_PAYLOAD control-flags
> > replace define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE control-flags
> > replace define V4L2_CTRL_FLAG_MODIFY_LAYOUT control-flags
> > replace define V4L2_CTRL_FLAG_DYNAMIC_ARRAY control-flags
> > +replace define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX control-flags
> >
> > replace define V4L2_CTRL_FLAG_NEXT_CTRL control
> > replace define V4L2_CTRL_FLAG_NEXT_COMPOUND control
> > @@ -567,6 +568,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 2f9c8582f940..8db4a5eb1737 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 7468e43800a9..28eca8f9d148 100644
> > --- a/drivers/media/platform/qcom/venus/venc_ctrls.c
> > +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
> > @@ -607,11 +607,16 @@ 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(&p_hdr10_cll));
> > + v4l2_ctrl_ptr_create(&p_hdr10_cll),
> > + 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((void *)&p_hdr10_mastering));
> > + v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering),
> > + v4l2_ctrl_ptr_create(NULL),
> > + v4l2_ctrl_ptr_create(NULL));
> > +
> >
> > v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
> > V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE,
> > diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> > index 002ea6588edf..d022e1ed4835 100644
> > --- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
> > +++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
> > @@ -94,6 +94,22 @@ 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)
> > +{
> > + ctrl->type_ops->minimum(ctrl, 0, 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)
> > +{
> > + ctrl->type_ops->maximum(ctrl, 0, ctrl->p_new);
> > +
> > + return ptr_to_user(c, ctrl, ctrl->p_new);
> > +}
> > +
> > /* Helper function: copy the caller-provider value as the new control value */
> > static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
> > {
> > @@ -229,8 +245,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",
> > @@ -259,6 +275,15 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
> > return -EINVAL;
> > }
> >
> > + if (!(ctrl->flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX) &&
> > + (cs->which == V4L2_CTRL_WHICH_MIN_VAL ||
> > + cs->which == V4L2_CTRL_WHICH_MAX_VAL)) {
> > + dprintk(vdev,
> > + "invalid which 0x%x or control id 0x%x\n",
> > + cs->which, id);
> > + return -EINVAL;
> > + }
> > +
> > if (ctrl->cluster[0]->ncontrols > 1)
> > have_clusters = true;
> > if (ctrl->cluster[0] != ctrl)
> > @@ -368,8 +393,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;
> > }
> > @@ -389,10 +414,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);
> > @@ -432,13 +459,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++)
> > @@ -467,6 +495,10 @@ int v4l2_g_ext_ctrls_common(struct v4l2_ctrl_handler *hdl,
> > ret = -ENOMEM;
> > else if (is_request && ref->p_req_valid)
> > 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
> > @@ -564,9 +596,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 f1486ab032cf..f36d54810dbc 100644
> > --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> > @@ -182,29 +182,69 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
> > }
> > }
> >
> > -void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_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_min.p_const)
>
> Typo: p_min -> p_max
>
> > + memcpy(p, ctrl->p_max.p_const, ctrl->elem_size);
> > + else
> > + memset(p, 0, ctrl->elem_size);
> > +}
> > +
> > +static int __v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
>
> Just keep this as a void function. The return value is never used anyway.
>
> The assumption is that this function is only called with valid arguments.
>
> > + u32 which, union v4l2_ctrl_ptr ptr)
> > {
> > unsigned int i;
> > u32 tot_elems = ctrl->elems;
> > u32 elems = tot_elems - from_idx;
> > + s64 value;
> >
> > if (from_idx >= tot_elems)
> > - return;
> > + return -EINVAL;
> > +
> > + switch (which) {
> > + case V4L2_CTRL_WHICH_DEF_VAL:
> > + value = ctrl->default_value;
> > + break;
> > + case V4L2_CTRL_WHICH_MAX_VAL:
> > + value = ctrl->maximum;
> > + break;
> > + case V4L2_CTRL_WHICH_MIN_VAL:
> > + value = ctrl->minimum;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> >
> > switch (ctrl->type) {
> > case V4L2_CTRL_TYPE_STRING:
> > + if (which == V4L2_CTRL_WHICH_DEF_VAL)
> > + value = ctrl->minimum;
> > +
> > for (i = from_idx; i < tot_elems; i++) {
> > unsigned int offset = i * ctrl->elem_size;
> >
> > - memset(ptr.p_char + offset, ' ', ctrl->minimum);
> > - ptr.p_char[offset + ctrl->minimum] = '\0';
> > + memset(ptr.p_char + offset, ' ', value);
> > + ptr.p_char[offset + value] = '\0';
> > }
> > break;
> > case V4L2_CTRL_TYPE_INTEGER64:
> > - if (ctrl->default_value) {
> > + if (value) {
> > for (i = from_idx; i < tot_elems; i++)
> > - ptr.p_s64[i] = ctrl->default_value;
> > + ptr.p_s64[i] = value;
> > } else {
> > memset(ptr.p_s64 + from_idx, 0, elems * sizeof(s64));
> > }
> > @@ -214,9 +254,9 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > case V4L2_CTRL_TYPE_MENU:
> > case V4L2_CTRL_TYPE_BITMASK:
> > case V4L2_CTRL_TYPE_BOOLEAN:
> > - if (ctrl->default_value) {
> > + if (value) {
> > for (i = from_idx; i < tot_elems; i++)
> > - ptr.p_s32[i] = ctrl->default_value;
> > + ptr.p_s32[i] = value;
> > } else {
> > memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
> > }
> > @@ -226,32 +266,63 @@ void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > memset(ptr.p_s32 + from_idx, 0, elems * sizeof(s32));
> > break;
> > case V4L2_CTRL_TYPE_U8:
> > - memset(ptr.p_u8 + from_idx, ctrl->default_value, elems);
> > + memset(ptr.p_u8 + from_idx, value, elems);
> > break;
> > case V4L2_CTRL_TYPE_U16:
> > - if (ctrl->default_value) {
> > + if (value) {
> > for (i = from_idx; i < tot_elems; i++)
> > - ptr.p_u16[i] = ctrl->default_value;
> > + ptr.p_u16[i] = value;
> > } else {
> > memset(ptr.p_u16 + from_idx, 0, elems * sizeof(u16));
> > }
> > break;
> > case V4L2_CTRL_TYPE_U32:
> > - if (ctrl->default_value) {
> > + if (value) {
> > for (i = from_idx; i < tot_elems; i++)
> > - ptr.p_u32[i] = ctrl->default_value;
> > + ptr.p_u32[i] = value;
> > } else {
> > memset(ptr.p_u32 + from_idx, 0, elems * sizeof(u32));
> > }
> > break;
> > default:
> > - for (i = from_idx; i < tot_elems; i++)
> > - std_init_compound(ctrl, i, ptr);
> > + for (i = from_idx; i < tot_elems; i++) {
> > + switch (which) {
> > + case V4L2_CTRL_WHICH_DEF_VAL:
> > + std_init_compound(ctrl, i, ptr);
> > + break;
> > + case V4L2_CTRL_WHICH_MAX_VAL:
> > + std_max_compound(ctrl, i, ptr);
> > + break;
> > + case V4L2_CTRL_WHICH_MIN_VAL:
> > + std_min_compound(ctrl, i, ptr);
> > + break;
> > + }
> > + }
> > break;
> > }
> > +
> > + return 0;
> > +}
> > +
> > +void v4l2_ctrl_type_op_init(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > + union v4l2_ctrl_ptr ptr)
> > +{
> > + __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_DEF_VAL, ptr);
> > }
> > EXPORT_SYMBOL(v4l2_ctrl_type_op_init);
> >
> > +void v4l2_ctrl_type_op_minimum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > + union v4l2_ctrl_ptr ptr)
> > +{
> > + __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MIN_VAL, ptr);
> > +}
> > +
> > +void v4l2_ctrl_type_op_maximum(const struct v4l2_ctrl *ctrl, u32 from_idx,
> > + union v4l2_ctrl_ptr ptr)
> > +{
> > + __v4l2_ctrl_type_op_init(ctrl, from_idx, V4L2_CTRL_WHICH_MAX_VAL, ptr);
> > +}
> > +
> > void v4l2_ctrl_type_op_log(const struct v4l2_ctrl *ctrl)
> > {
> > union v4l2_ctrl_ptr ptr = ctrl->p_cur;
> > @@ -1293,6 +1364,8 @@ EXPORT_SYMBOL(v4l2_ctrl_type_op_validate);
> > static const struct v4l2_ctrl_type_ops std_type_ops = {
> > .equal = v4l2_ctrl_type_op_equal,
> > .init = v4l2_ctrl_type_op_init,
> > + .minimum = v4l2_ctrl_type_op_minimum,
> > + .maximum = v4l2_ctrl_type_op_maximum,
> > .log = v4l2_ctrl_type_op_log,
> > .validate = v4l2_ctrl_type_op_validate,
> > };
> > @@ -1764,7 +1837,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;
> > @@ -1888,6 +1964,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> > break;
> > }
> >
> > + if (type < V4L2_CTRL_COMPOUND_TYPES &&
> > + type != V4L2_CTRL_TYPE_BUTTON &&
> > + type != V4L2_CTRL_TYPE_CTRL_CLASS &&
> > + type != V4L2_CTRL_TYPE_STRING)
> > + flags |= V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX;
> > +
> > /* Sanity checks */
> > if (id == 0 || name == NULL || !elem_size ||
> > id >= V4L2_CID_PRIVATE_BASE ||
> > @@ -1896,6 +1978,21 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> > handler_set_err(hdl, -ERANGE);
> > return NULL;
> > }
> > +
> > + if ((!p_def.p_const && p_min.p_const) ||
> > + (p_min.p_const && !p_max.p_const) ||
> > + (!p_min.p_const && p_max.p_const)) {
> > + handler_set_err(hdl, -EINVAL);
> > + return NULL;
> > + }
>
> I would drop this. I think p_def, p_min and p_max can all be NULL (in which
> case the corresponding value is a memset(0)).
>
> > +
> > + if (flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX &&
> > + type >= V4L2_CTRL_COMPOUND_TYPES &&
> > + (!p_min.p_const || !p_max.p_const)) {
> > + handler_set_err(hdl, -EINVAL);
> > + return NULL;
> > + }
>
> Same here. Suppose you have a compound control that for whatever reason has
> just a single fixed value, that happens to be all zeroes. Then you can set
> leave p_min/p_max to NULL, while still setting V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX.
>
> I'd just drop this test.
>
> > +
> > err = check_range(type, min, max, step, def);
> > if (err) {
> > handler_set_err(hdl, err);
> > @@ -1937,6 +2034,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> >
> > if (type >= V4L2_CTRL_COMPOUND_TYPES && p_def.p_const)
> > sz_extra += elem_size;
> > + if (type >= V4L2_CTRL_COMPOUND_TYPES && p_min.p_const)
> > + sz_extra += elem_size * 2;
>
> Since one of p_min or p_max can be NULL, just do this:
>
> if (type >= V4L2_CTRL_COMPOUND_TYPES && p_min.p_const)
> sz_extra += elem_size;
> if (type >= V4L2_CTRL_COMPOUND_TYPES && p_max.p_const)
> sz_extra += elem_size;
>
> >
> > ctrl = kvzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
> > if (ctrl == NULL) {
> > @@ -2002,6 +2101,14 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
> > memcpy(ctrl->p_def.p, p_def.p_const, elem_size);
> > }
> >
> > + if (flags & V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX &&
> > + p_min.p_const && p_max.p_const) {
> > + ctrl->p_min.p = ctrl->p_def.p + elem_size;
> > + memcpy(ctrl->p_min.p, p_min.p_const, elem_size);
> > + ctrl->p_max.p = ctrl->p_min.p + elem_size;
> > + memcpy(ctrl->p_max.p, p_max.p_const, elem_size);
> > + }
>
> This needs a similar modification.
>
> > +
> > ctrl->type_ops->init(ctrl, 0, ctrl->p_cur);
> > cur_to_new(ctrl);
> >
> > @@ -2052,7 +2159,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;
> > @@ -2077,7 +2185,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);
> >
> > @@ -2110,7 +2219,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);
> >
> > @@ -2142,7 +2252,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);
> > @@ -2150,7 +2261,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;
> > @@ -2164,7 +2277,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);
> >
> > @@ -2188,7 +2301,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 01ba27f2ef87..aaf68bfaa601 100644
> > --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> > +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> > @@ -884,7 +884,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 b0db167a3ac4..6e12493b30d3 100644
> > --- a/include/media/v4l2-ctrls.h
> > +++ b/include/media/v4l2-ctrls.h
> > @@ -133,6 +133,8 @@ struct v4l2_ctrl_ops {
> > *
> > * @equal: return true if all ctrl->elems array elements are equal.
> > * @init: initialize the value for array elements from from_idx to ctrl->elems.
> > + * @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 for ctrl->new_elems array elements.
> > * Return 0 on success and a negative value otherwise.
> > @@ -142,6 +144,10 @@ struct v4l2_ctrl_type_ops {
> > union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2);
> > void (*init)(const struct v4l2_ctrl *ctrl, u32 from_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, union v4l2_ctrl_ptr ptr);
> > };
> > @@ -247,6 +253,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.
> > @@ -306,6 +318,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;
> > };
> > @@ -425,6 +439,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.
> > @@ -454,6 +470,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;
> > @@ -723,17 +741,23 @@ 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 fields. Use v4l2_ctrl_ptr_create() to create
> > + * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
> > + * default value of the compound control should be all zeroes. Use
> > + * v4l2_ctrl_ptr_create(NULL) if the min/max value of the compound control
> > + * is not defined, -ENODATA will be returned in this case.
>
> This is outdated and should be:
>
> * @p_def/min/max from a pointer. Use v4l2_ctrl_ptr_create(NULL) if the
> * default, minimum or maximum 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 089d553cf736..d7670aaf3b5f 100644
> > --- a/include/uapi/linux/videodev2.h
> > +++ b/include/uapi/linux/videodev2.h
> > @@ -1863,6 +1863,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,
> > @@ -1970,6 +1972,7 @@ struct v4l2_querymenu {
> > #define V4L2_CTRL_FLAG_EXECUTE_ON_WRITE 0x0200
> > #define V4L2_CTRL_FLAG_MODIFY_LAYOUT 0x0400
> > #define V4L2_CTRL_FLAG_DYNAMIC_ARRAY 0x0800
> > +#define V4L2_CTRL_FLAG_HAS_WHICH_MIN_MAX 0x1000
> >
> > /* Query flags, to be ORed with the control ID */
> > #define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000
>
> Regards,
>
> Hans
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2023-09-15 7:29 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-17 7:17 [PATCH v12 00/11] Implement UVC v1.5 ROI Yunke Cao
2023-08-17 7:17 ` [PATCH v12 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT Yunke Cao
2023-08-17 7:17 ` [PATCH v12 02/11] media: uvcvideo: add uvc_ctrl_get_boundary for getting default value Yunke Cao
2023-08-17 7:17 ` [PATCH v12 03/11] media: uvcvideo: introduce __uvc_ctrl_get_std() Yunke Cao
2023-08-17 7:17 ` [PATCH v12 04/11] media: uvcvideo: Split uvc_control_mapping.size to v4l2 and data size Yunke Cao
2023-08-17 7:17 ` [PATCH v12 05/11] media: uvcvideo: Add support for compound controls Yunke Cao
2023-08-17 7:17 ` [PATCH v12 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
2023-08-18 1:48 ` Yunke Cao
2023-09-13 7:43 ` Yunke Cao
2023-09-13 9:26 ` Hans Verkuil
2023-09-15 7:28 ` Yunke Cao
2023-08-17 7:17 ` [PATCH v12 07/11] media: vivid: Add an rectangle control Yunke Cao
2023-08-17 7:17 ` [PATCH v12 08/11] media: uvcvideo: support V4L2_CTRL_WHICH_MIN/MAX_VAL Yunke Cao
2023-08-17 7:17 ` [PATCH v12 09/11] media: uvcvideo: implement UVC v1.5 ROI Yunke Cao
2023-08-17 7:17 ` [PATCH v12 10/11] media: uvcvideo: initilaize ROI control to default value Yunke Cao
2023-08-17 7:17 ` [PATCH v12 11/11] media: uvcvideo: document UVC v1.5 ROI Yunke Cao
2023-08-17 7:28 ` [PATCH v12 00/11] Implement " Yunke Cao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).