From patchwork Tue Oct 10 02:21:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yunke Cao X-Patchwork-Id: 733130 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B2444CD68E3 for ; Tue, 10 Oct 2023 02:21:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1379404AbjJJCVv (ORCPT ); Mon, 9 Oct 2023 22:21:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55800 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1379400AbjJJCVu (ORCPT ); Mon, 9 Oct 2023 22:21:50 -0400 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2DF3993 for ; Mon, 9 Oct 2023 19:21:49 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-5a7a6fd18abso15174827b3.1 for ; Mon, 09 Oct 2023 19:21:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1696904508; x=1697509308; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=/j/k0wnr5OTIj2PFl+c3jEMpzoGNzeZvbWu6+Id8COM=; b=FVuWvEg18HIhb9uBdvuNcZYjsnvLoVKRlaIEfaj0pkvUD4YANh2NiIjx7d8I1yVffH 8hYc46I7PUM9GBp+nwy97E4aI9c/JjAInDM+N+TJerO7z7ByVxEwsgyEGpcsrCaczz3y kVrg4gypDo8JVAQQNtBANTySeJsTT/jQIJ/dj5LufaRVWEts1EK85lmWPw0zbBh6BgSg EFxSoA+KGYtAqJiwRKIp2FHOTZnS4eC1SNVS9rlDmGIX3QHInZpT7AeR5Pg1ALO4ZJkR BfqTP1QgEk+IWhDb3sG8uaHFXlh3oySW+3XS3gMHGe0nVP3TnaZr1apD0u5QjivQEcUE XFfg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1696904508; x=1697509308; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=/j/k0wnr5OTIj2PFl+c3jEMpzoGNzeZvbWu6+Id8COM=; b=RO8qP/+f5/iEJCcei3/J0h6olyp8nzxQPKATY7SY53Wr+IlvYzcGBtxW7W0iqy6Nqp TiUZ+/kws+sU86Pl4yz6BpzWQjIYL8hf0m7zfABnStfRB7J4U4Gql3g2uw3XNM9z4I2H YfoJ39p4kz/3+Fzvt9IF8JS2Z5yzUdUx/0fJhix8dfb0MUOHGyfr7rDrmy9Fkat2kTLP RxkLQNvypO3bHQz3Uv4tmrqUOnP+TpTggYCm9GZQMmua0HIVYxuDc//VqfSIus7tQZFb 8zkeZH7Wc6HkizmqTVLvTxqJtPbTUd0U5dTQvxJkfIDTPKOvBeNyxksACASImU6hxoDP qfFw== X-Gm-Message-State: AOJu0YzjXXeyhy7bR1TkjUoylnYxZ7daQHvc3bWhG3Bm8S4wjmCUuKRe Wej55uX5qfSRMasgH4V/t6Gi70/XiDQ= X-Google-Smtp-Source: AGHT+IEqORpx2ns7oyZr2jZAcnCfnk7JkXj610EjbknHXnpYnArcttaVT5FuQHMOtuRhJpbxxF5PGGwk8YU= X-Received: from yunkec1.tok.corp.google.com ([2401:fa00:8f:203:7303:2d54:5b83:e60b]) (user=yunkec job=sendgmr) by 2002:a81:b3c3:0:b0:59b:eb63:4beb with SMTP id r186-20020a81b3c3000000b0059beb634bebmr319348ywh.7.1696904508453; Mon, 09 Oct 2023 19:21:48 -0700 (PDT) Date: Tue, 10 Oct 2023 11:21:24 +0900 In-Reply-To: <20231010022136.1504015-1-yunkec@google.com> Mime-Version: 1.0 References: <20231010022136.1504015-1-yunkec@google.com> X-Mailer: git-send-email 2.42.0.609.gbb76f46606-goog Message-ID: <20231010022136.1504015-2-yunkec@google.com> Subject: [PATCH v13 01/11] media: v4l2_ctrl: Add V4L2_CTRL_TYPE_RECT From: Yunke Cao To: Hans Verkuil , Laurent Pinchart , Daniel Scally Cc: Tomasz Figa , Sergey Senozhatsky , Ricardo Ribalda , linux-media@vger.kernel.org, Yunke Cao Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Add p_rect to struct v4l2_ext_control with basic support in v4l2-ctrls. Reviewed-by: Laurent Pinchart Reviewed-by: Ricardo Ribalda Reviewed-by: Sergey Senozhatsky Reviewed-by: Daniel Scally Signed-off-by: Yunke Cao Reviewed-by: Hans Verkuil --- Changelog since v12: - No Change. 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 78260e5d9985..9a8836ee8da9 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1814,6 +1814,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; @@ -1882,6 +1883,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, From patchwork Tue Oct 10 02:21:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yunke Cao X-Patchwork-Id: 733129 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DD803CD68E4 for ; Tue, 10 Oct 2023 02:22:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1441853AbjJJCWB (ORCPT ); Mon, 9 Oct 2023 22:22:01 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51586 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1441833AbjJJCWA (ORCPT ); Mon, 9 Oct 2023 22:22:00 -0400 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4BC80A4 for ; Mon, 9 Oct 2023 19:21:59 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-5a1f12cf1ddso50883657b3.0 for ; Mon, 09 Oct 2023 19:21:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1696904518; x=1697509318; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=eGg+Bt9jkw0iGa7ol+nB6Xgm3s3q27aJePZnVTI8Jv4=; b=R/tkPOCQzSx7be2EidBvFlRwV6gQZBoUbsn62lcLPXSn3zl+nBWEZ260Qn6ZFQ+NLg iOlNOOWPtaWM9NoNOlC205DlLt0+RrEIjWY96elodpLfcAayzEZ4Z6p/aLThgSbzii1G OtiC1GedGUGDmXIXFMF4MeieoDKS04EuREtrMfkLmbMXTsbUffNswlX9EgXPZrlltx+L OF0hybwRi3I05W8qTUvaM+aKAYR0LDFCEiwNCta4NapSehJi46wjw5WbgZN6uxDVQAeL AB3TCmOLzr4gFM/Iavn84wxg+bJqOQjVi51kJQ/dMBszXckRQT4cmAuWUZIaYe0S/8FF UynQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1696904518; x=1697509318; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=eGg+Bt9jkw0iGa7ol+nB6Xgm3s3q27aJePZnVTI8Jv4=; b=JD0wRmioV8D5WRPyCoEu7+HN8/xnKKyeyPSFeikXyM1KVaB9vF36GFHcCpOYCjNy1l CP0IHY0kXuOkAgIFH3pESLo1fA9B6iwf8WiJTsdyUSgjt218PNV0EdF1oG0Kq2Vx0MtW mkUQdPHSYWJ3/Rcruw2Cxo4WNg21xK9V8fYf93T72yS9tO2wPwHuUouhPMhKU6PIfYmu /Nv/x1ouTZHKQWPl0A2hGePKgMYUhkHUqrzC6qPNHsKYQQlkKgVldEidgwGjf7KkaDxk hqzhF4fbY1N8MxX0JqyZiRaRewxV/GzEvuyFNd3+RWvDbccOYjq9fqkliYhW3P0rpZAQ FLJg== X-Gm-Message-State: AOJu0Yx6bmypA3XqVSWi+gdmRZyzomQBnaUegr/K9I8x20h45kgMQuxj qYLAXjKQwuQ+cMKk3iDsEUDyMoMAPWo= X-Google-Smtp-Source: AGHT+IGqGDigNevLvpTkcHhBgkud7KrBq9QG5Y4M1YtHBdcK4havHqi4tLXvWaUxeoAsZpgEVoMJ1WGKQ8c= X-Received: from yunkec1.tok.corp.google.com ([2401:fa00:8f:203:7303:2d54:5b83:e60b]) (user=yunkec job=sendgmr) by 2002:a25:824f:0:b0:d9a:66eb:b516 with SMTP id d15-20020a25824f000000b00d9a66ebb516mr206ybn.6.1696904518378; Mon, 09 Oct 2023 19:21:58 -0700 (PDT) Date: Tue, 10 Oct 2023 11:21:26 +0900 In-Reply-To: <20231010022136.1504015-1-yunkec@google.com> Mime-Version: 1.0 References: <20231010022136.1504015-1-yunkec@google.com> X-Mailer: git-send-email 2.42.0.609.gbb76f46606-goog Message-ID: <20231010022136.1504015-4-yunkec@google.com> Subject: [PATCH v13 03/11] media: uvcvideo: introduce __uvc_ctrl_get_std() From: Yunke Cao To: Hans Verkuil , Laurent Pinchart , Daniel Scally Cc: Tomasz Figa , Sergey Senozhatsky , Ricardo Ribalda , linux-media@vger.kernel.org, Yunke Cao Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org 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 --- Changelog since v12: - No change. 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 8d8333786333..4a685532f7eb 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; @@ -1532,7 +1537,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); @@ -1703,7 +1709,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, @@ -1883,7 +1890,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, @@ -2042,8 +2052,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; From patchwork Tue Oct 10 02:21:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yunke Cao X-Patchwork-Id: 733128 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 65DECCD68E3 for ; Tue, 10 Oct 2023 02:22:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1441867AbjJJCWN (ORCPT ); Mon, 9 Oct 2023 22:22:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:55852 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1441858AbjJJCWM (ORCPT ); Mon, 9 Oct 2023 22:22:12 -0400 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5B6E293 for ; Mon, 9 Oct 2023 19:22:09 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id 3f1490d57ef6-d86dac81f8fso7221791276.1 for ; Mon, 09 Oct 2023 19:22:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1696904528; x=1697509328; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=SZCydqZRsosFofjHKVRHHb4gDgTLmwFEZUuzdOn38mE=; b=2ccgMqGt9Z285IdISivHTFETizIMl/o/Y7lm2aMAiQq+XSxY78GygRHPn0RJwnuhSq IkWXFS/ff3BHlwuuoKImNNc9jsnXgLDAk+UZaeHCmplXt8ce43QeHskcxqeBYI+Avh61 r/TCQlhOhpC/bCD82knoJeEBEsC4yr3JMC/XC+uP1EVtdYeSR/9q3rVolVwox3UiX/IV cvKDWAVCgZhCAJnZzj26vtW2n6EF8UQUY3ImLm1crG7FVIIy2CwIADFpWxf4T9othDCu oqVF0/jy4f+5pYGB4vr2x4Mo4sdPe4BC9zGUBtJsHoWE/TpQaCnVDByv+Ey3K4jmapZh H5eg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1696904528; x=1697509328; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=SZCydqZRsosFofjHKVRHHb4gDgTLmwFEZUuzdOn38mE=; b=TdFvDnBj4bSorpacB2GxWMnuV0M4y+qGTJnAL4/ARMuhQp8LMV5JPTiF/piumD9sfC nLpw+5zLj8bD94KxVNaV6Bo0Tpvr5pfagYaj/n3MV+Uj51i7ZOJcbkxVn3bBR2LWZDky Dw3NUiOXdrm/mYTVn56yOvKWzbE4k5B7TgEpBKh5tkkjyMcKmrCVpdiE9qKYpOd/5HJr rou6z0965KgEzQk/M6Cu45XPkQiihXneWqrRrb0DlElPi4yJM7VAG4Fd928bNPZ+q74z CD6MKvo1i0C88n+tld7oSNeoERXhOCGczC6IBpm+ghDLdaNzX5UxZzrRscdOYq6stIQ0 9S5A== X-Gm-Message-State: AOJu0YwjgYckuN+F6+e6hsmpcwgnEKcCKHb1zqjVQ2vUlXHJt+Eio2k+ MAj+kr5FiJd7UkaJMOKHFgntkHU+J+c= X-Google-Smtp-Source: AGHT+IHHe/u32lmywrc68MYztHzRD3TK/a64enWZTH8dpjnBRmDx8pnmg8z90+XwrwYdqNcO1523wvoMHdM= X-Received: from yunkec1.tok.corp.google.com ([2401:fa00:8f:203:7303:2d54:5b83:e60b]) (user=yunkec job=sendgmr) by 2002:a25:d42:0:b0:d7f:8e0a:4b3f with SMTP id 63-20020a250d42000000b00d7f8e0a4b3fmr268105ybn.3.1696904528570; Mon, 09 Oct 2023 19:22:08 -0700 (PDT) Date: Tue, 10 Oct 2023 11:21:28 +0900 In-Reply-To: <20231010022136.1504015-1-yunkec@google.com> Mime-Version: 1.0 References: <20231010022136.1504015-1-yunkec@google.com> X-Mailer: git-send-email 2.42.0.609.gbb76f46606-goog Message-ID: <20231010022136.1504015-6-yunkec@google.com> Subject: [PATCH v13 05/11] media: uvcvideo: Add support for compound controls From: Yunke Cao To: Hans Verkuil , Laurent Pinchart , Daniel Scally Cc: Tomasz Figa , Sergey Senozhatsky , Ricardo Ribalda , linux-media@vger.kernel.org, Yunke Cao Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Supports getting/setting current value. Supports getting default value. Handles V4L2_CTRL_FLAG_NEXT_COMPOUND. Reviewed-by: Ricardo Ribalda Signed-off-by: Yunke Cao --- 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 98254b93eb46..aae2480496b7 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) @@ -1533,7 +1615,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; @@ -1843,7 +1925,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; } @@ -1891,7 +1973,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); } @@ -1912,6 +1994,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) { @@ -1929,7 +2027,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); @@ -1938,6 +2037,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) { @@ -2052,13 +2179,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; @@ -2468,10 +2596,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 { From patchwork Tue Oct 10 02:21:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yunke Cao X-Patchwork-Id: 733127 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 35C0BCD68E3 for ; Tue, 10 Oct 2023 02:22:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1441872AbjJJCWU (ORCPT ); Mon, 9 Oct 2023 22:22:20 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53848 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1441877AbjJJCWT (ORCPT ); Mon, 9 Oct 2023 22:22:19 -0400 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 84935AF for ; Mon, 9 Oct 2023 19:22:14 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-5a507986ed6so89792787b3.1 for ; Mon, 09 Oct 2023 19:22:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1696904534; x=1697509334; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=lXXkyDEWldldz+kjS9KZpQvfinHCPJ1Jvv/qEJH+zGQ=; b=L07W8MmwLp0ifv0W9Qp6xGz2dy4/h8Uod9SSw2KzZ1eaLsTW+rrjOUREOhgEDBWN/p h3C2Urhe52cBc3u6qoGlNHlNMKMoyE9SYLXAnnufHc1yJ+UDmPv+ctS/81BGyrDwNopY v+AqPayaKokiFMp52Ea1OC5ufs3RmxwH6+xBIcPR8eM37XVmcy7PYLo2ID28HsOFBreU 1NnpIiN1jxneT1fVu29brgKr9xarBFG+BGS9he7I7P4Cz0ZLeS17pvlstFyRvaOcvfi5 RLf/3jJZ7lY3Kypei0cCs04Xr9mgEU/1C8TSKkBF14AvS6+VEfKQ8bRe4SIVGexe0fp1 wnlA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1696904534; x=1697509334; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=lXXkyDEWldldz+kjS9KZpQvfinHCPJ1Jvv/qEJH+zGQ=; b=l8NpRHQ+krzUD2hSsKfM4swhpGSzcnFhLueDZC8PyehrwZ37BZKZX7lrzbXgeKDFvk jyQrMB/cbX2Zw5jJ7qMaYAV9JxxHr9Ph8esd2JS9QL2oL2XFJ7+WQecxQpbu+viGpqoa dvz3SlcEhPsw32N89lU6muvRi1dfPmNrNS56kHiUskbXNwP8+ycw3s/yu4xFxhWHCqe2 gUbc+krkm+8q05WWFpOW6ojWrPpt6SF4uiuwsxFQs0w/yoYG482tbvCChfHFhDsBrLAs WovyafEORUUEd3xhvKs+x6/O5W+01dAruGO6IODwxCTY53yGszgSNVLlDs1DSQSxegxg XlDg== X-Gm-Message-State: AOJu0YzzreuF/vB0OjSEDqGA141f4Py5CDhn3z+XSOJ1LJr1h34xh6et Cwwqhn3y3x2qW20u0JJryZ+YmLWME2c= X-Google-Smtp-Source: AGHT+IEuZ0J6ckBIX5qauw8hVNmi9bubswiu15dXfC2OZpq1GjLkI++VQ4WfeaFV2oqrTIFi+E2PkyGCQIk= X-Received: from yunkec1.tok.corp.google.com ([2401:fa00:8f:203:7303:2d54:5b83:e60b]) (user=yunkec job=sendgmr) by 2002:a0d:e643:0:b0:5a7:acc1:5142 with SMTP id p64-20020a0de643000000b005a7acc15142mr42560ywe.8.1696904533734; Mon, 09 Oct 2023 19:22:13 -0700 (PDT) Date: Tue, 10 Oct 2023 11:21:29 +0900 In-Reply-To: <20231010022136.1504015-1-yunkec@google.com> Mime-Version: 1.0 References: <20231010022136.1504015-1-yunkec@google.com> X-Mailer: git-send-email 2.42.0.609.gbb76f46606-goog Message-ID: <20231010022136.1504015-7-yunkec@google.com> Subject: [PATCH v13 06/11] v4l2-ctrls: add support for V4L2_CTRL_WHICH_MIN/MAX_VAL From: Yunke Cao To: Hans Verkuil , Laurent Pinchart , Daniel Scally Cc: Tomasz Figa , Sergey Senozhatsky , Ricardo Ribalda , linux-media@vger.kernel.org, Yunke Cao Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Hans Verkuil Add the capability of retrieving the min and max values of a compound control. Signed-off-by: Hans Verkuil Signed-off-by: Yunke Cao --- Changelog since v12: - Addressed comments from Hans. 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). .../media/v4l/vidioc-g-ext-ctrls.rst | 22 ++- .../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 | 151 +++++++++++++++--- drivers/media/v4l2-core/v4l2-ioctl.c | 4 +- include/media/v4l2-ctrls.h | 34 +++- include/uapi/linux/videodev2.h | 3 + 10 files changed, 246 insertions(+), 48 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..0b87c23e66ff 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 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 + 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 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 d9d2a293f3ef..7f370438d655 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..ef418165e88d 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -182,29 +182,66 @@ 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_max.p_const) + memcpy(p, ctrl->p_max.p_const, ctrl->elem_size); + else + memset(p, 0, ctrl->elem_size); +} + +static void __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) + 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; + } 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 +251,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 +263,61 @@ 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; } } + +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 +1359,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 +1832,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 +1959,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 +1973,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -ERANGE); return NULL; } + err = check_range(type, min, max, step, def); if (err) { handler_set_err(hdl, err); @@ -1937,6 +2015,10 @@ 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; + 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 +2084,22 @@ 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) { + void *ptr = ctrl->p_def.p; + + if (p_min.p_const) { + ptr += elem_size; + ctrl->p_min.p = ptr; + memcpy(ctrl->p_min.p, p_min.p_const, elem_size); + } + + if (p_max.p_const) { + ptr += elem_size; + ctrl->p_max.p = ptr; + 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 +2150,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 +2176,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 +2210,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 +2243,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 +2252,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 +2268,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 +2292,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 f4d9d6279094..89edd662e0f1 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..940bf1da2be0 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, minimum and maximum 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 9a8836ee8da9..b723f39d43dc 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1865,6 +1865,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, @@ -1972,6 +1974,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 From patchwork Tue Oct 10 02:21:32 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Yunke Cao X-Patchwork-Id: 733126 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5F35BCD68E3 for ; Tue, 10 Oct 2023 02:22:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1441871AbjJJCWe (ORCPT ); Mon, 9 Oct 2023 22:22:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44200 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1441877AbjJJCWc (ORCPT ); Mon, 9 Oct 2023 22:22:32 -0400 Received: from mail-yb1-xb4a.google.com (mail-yb1-xb4a.google.com [IPv6:2607:f8b0:4864:20::b4a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F408DAF for ; Mon, 9 Oct 2023 19:22:29 -0700 (PDT) Received: by mail-yb1-xb4a.google.com with SMTP id 3f1490d57ef6-d9a3a98b34dso1181072276.3 for ; Mon, 09 Oct 2023 19:22:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1696904549; x=1697509349; darn=vger.kernel.org; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:from:to:cc:subject:date:message-id :reply-to; bh=ADa9vJnwOE1d8IiN1GZrKGiha0yuHsQ8zR0tF4h9Dls=; b=DHRHlaMEGfF7Z6lX9n9uRZTSjIhSGg0MxUtVvnNgQr1XAaiHHCxAXryrlxx7va7ovX 3rhiGD00jA+geO/GssOKoDOhsl5RWeSSJs4IlspPivuih2+KsZ4vfk/lfrsZh0Yiv+jZ VQGD/HNb03q+KkbEkyNOHGAd6TV+caEMZRyYhR0rtHJ5tsgGuNLOKZHMnj/nGvuYQUw+ YQfe7NVjRXA3JbN5WljFV2NZWEjGdcTRePDiKeWQAbLysHg4Svl0ZRLwHyuv0zLij5jI oE30FVW6hpA+3pf1TFZh7416UgLVn5m4XsPEfYjsF3NaFxArsrr5lwcpkRpOUtwxHXzA 3G2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1696904549; x=1697509349; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to; bh=ADa9vJnwOE1d8IiN1GZrKGiha0yuHsQ8zR0tF4h9Dls=; b=imVeN8nKEksAQPOpu8KPpmL915ysOG91Z8YmVtoUORi7fudtN9dKjORHfuMWHKRGU9 CGvFTXGKFBhOI/eoryOz/lsx711lwRd0obpZMQeYgsUUPfScA9fs7Iqc5bOxKbgpofvv bfm4S0UESzp/ajn2ivtOlrUG2NnQWU1H7vbPWepB996X7LzVcjSUI6LZ1XBBQKv9uxdz Q9WCrN/+di+Q6bGx8C+8t1ICDkcf4tPsgUsiWcLg5nFyh5jC8hwmYFALc9QOuQ76B2vz 0xnEI2+hAzTFjf8Vypx+bdK4oKJODQs6KvD69YIqByLx0N2cAIRrjySK7euuf/qW/PZj HdTQ== X-Gm-Message-State: AOJu0YzPj48gvEj019HsZx2MGPIP7RGAYdHqnHne4j80HG6p/TmJmBPb Vt7S9CGwpBby7IJYswDeRf6FpfcynKM= X-Google-Smtp-Source: AGHT+IFTvODmtiQa/PQ+5twWrqOoh15oO4SjvS22HKzONoyRKV4ywtsQ3FRXAXoRrolZiwPSVCG87aAao4s= X-Received: from yunkec1.tok.corp.google.com ([2401:fa00:8f:203:7303:2d54:5b83:e60b]) (user=yunkec job=sendgmr) by 2002:a25:ad16:0:b0:d9a:521f:c442 with SMTP id y22-20020a25ad16000000b00d9a521fc442mr16373ybi.5.1696904549174; Mon, 09 Oct 2023 19:22:29 -0700 (PDT) Date: Tue, 10 Oct 2023 11:21:32 +0900 In-Reply-To: <20231010022136.1504015-1-yunkec@google.com> Mime-Version: 1.0 References: <20231010022136.1504015-1-yunkec@google.com> X-Mailer: git-send-email 2.42.0.609.gbb76f46606-goog Message-ID: <20231010022136.1504015-10-yunkec@google.com> Subject: [PATCH v13 09/11] media: uvcvideo: implement UVC v1.5 ROI From: Yunke Cao To: Hans Verkuil , Laurent Pinchart , Daniel Scally Cc: Tomasz Figa , Sergey Senozhatsky , Ricardo Ribalda , linux-media@vger.kernel.org, Yunke Cao Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org 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 Signed-off-by: Yunke Cao --- 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 c073c2a02102..d405b2a9d477 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) { @@ -2100,6 +2205,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); @@ -2224,6 +2331,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; @@ -2641,12 +2755,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 352f62ef02f2..c0a7c0091099 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) From patchwork Tue Oct 10 02:21:34 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yunke Cao X-Patchwork-Id: 733125 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 534D1CD68E3 for ; Tue, 10 Oct 2023 02:22:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1441876AbjJJCWl (ORCPT ); Mon, 9 Oct 2023 22:22:41 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58146 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1441843AbjJJCWk (ORCPT ); Mon, 9 Oct 2023 22:22:40 -0400 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6B8F39C for ; Mon, 9 Oct 2023 19:22:39 -0700 (PDT) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-59b5a586da6so42034857b3.1 for ; Mon, 09 Oct 2023 19:22:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1696904558; x=1697509358; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=U8Kme82A4K2KxhYlD870r5GvvSSeq1S7+B98al6SOxk=; b=Jk6txltOSMO/1PphRIu4IxNWkcuexakuhKYlQ5Owy5I06/93KQPzEjWuX6VkQnAtqy sch5PqUJCG3ItLE2XKYeGgFdLKP4o4iTGLNbX4MJ5IOyOAEC9qf+W4DiB0dE3lBNs6pC fZ0AQm72uZej1l8UMB8OeUC7Mt5XMdixiPjcwdjSsXDUe1oG74hElyJkJ2ySTNSvR6DM jU2qXBNCQMmmEQG5M0Kc23f9WS/GYVwPIyOUSAh+gnjl5RqPTqEaepDrCfXywH9JqIrd qF0P5mLBLSpCJQvjm0X1JNC9MRood618WhjyjMvF4xW1LChr3hslGk7GKN3aoboR5P3l ZErQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1696904558; x=1697509358; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=U8Kme82A4K2KxhYlD870r5GvvSSeq1S7+B98al6SOxk=; b=gkFzcshvz14PaiHTFWdvwA5PnY1Iqxt+Bw59wahbVW+KrqkLCnPrzpdmqAmK9EFKqI BcOIM1w9YnilYy9/CPtaFV7K9bepJNLnh3JmMuZXMkthNVdbQseP9ojIHq67FA7AYxtZ Mbqxpmvn4iThwctt5WoZiScVr8Ii7u+agsIFU7rRcmxMLs//2DvQJZiJqL6MQh0nSZ7v zV9qCtk9bMqvLl+G3GGkJkySp06fTrRbJy7PDbvZGQBT78B3fJSO8LQ80LtHFdQQDuYp BB/3525OHq3pHhjwe09Kvjvbjui8TqADWtZXvbJ4Ivc1bHyF+73GoZW+Tebjakxi2Hd+ IPnQ== X-Gm-Message-State: AOJu0YwpPnwtYaa8L46G1akAvOvLtQ3J5lvNAkCEzZ2FECgrVBYwuWgX cTS/RoBBPiq3AoDmDjL9jAWiQ1Drn0Q= X-Google-Smtp-Source: AGHT+IHiX+UQGGXlYWcYsWYFMBDC2WCDLG13plA6Vsqo4449JegYNsFTO99g3Bjl2ppBKsXxfQ6DxkGAcwU= X-Received: from yunkec1.tok.corp.google.com ([2401:fa00:8f:203:7303:2d54:5b83:e60b]) (user=yunkec job=sendgmr) by 2002:a05:690c:d95:b0:589:a533:405b with SMTP id da21-20020a05690c0d9500b00589a533405bmr394042ywb.3.1696904558717; Mon, 09 Oct 2023 19:22:38 -0700 (PDT) Date: Tue, 10 Oct 2023 11:21:34 +0900 In-Reply-To: <20231010022136.1504015-1-yunkec@google.com> Mime-Version: 1.0 References: <20231010022136.1504015-1-yunkec@google.com> X-Mailer: git-send-email 2.42.0.609.gbb76f46606-goog Message-ID: <20231010022136.1504015-12-yunkec@google.com> Subject: [PATCH v13 11/11] media: uvcvideo: document UVC v1.5 ROI From: Yunke Cao To: Hans Verkuil , Laurent Pinchart , Daniel Scally Cc: Tomasz Figa , Sergey Senozhatsky , Ricardo Ribalda , linux-media@vger.kernel.org, Yunke Cao Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Added documentation of V4L2_CID_UVC_REGION_OF_INTEREST_RECT and V4L2_CID_UVC_REGION_OF_INTEREST_AUTO. Reviewed-by: Ricardo Ribalda Reviewed-by: Sergey Senozhatsky Signed-off-by: Yunke Cao --- 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.