[{"id":31229,"web_url":"https://patchwork.libcamera.org/comment/31229/","msgid":"<20240914001732.GA3260@pendragon.ideasonboard.com>","date":"2024-09-14T00:17:32","subject":"Re: [PATCH] libcamera: pipeline: uvcvideo: Map focus controls","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Cláudio,\n\nThank you for the patch.\n\nOn Fri, Sep 13, 2024 at 05:22:03PM +0000, Cláudio Paulo wrote:\n> Added mapping of controls::LensPosition and controls::AfMode to\n> v4l2 controls V4L2_CID_FOCUS_ABSOLUTE and V4L2_CID_FOCUS_AUTO\n> respectively when the device supports them.\n> \n> If not supported, they are not registered.\n> \n> Signed-off-by: Cláudio Paulo <claudio.paulo@makewise.pt>\n> ---\n>  include/linux/v4l2-controls.h                |  4 +\n>  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 88 +++++++++++++++++---\n>  2 files changed, 82 insertions(+), 10 deletions(-)\n> \n> diff --git a/include/linux/v4l2-controls.h b/include/linux/v4l2-controls.h\n> index 882a8180..1ac85507 100644\n> --- a/include/linux/v4l2-controls.h\n> +++ b/include/linux/v4l2-controls.h\n> @@ -994,6 +994,10 @@ enum  v4l2_exposure_auto_type {\n>  #define V4L2_CID_FOCUS_ABSOLUTE\t\t\t(V4L2_CID_CAMERA_CLASS_BASE+10)\n>  #define V4L2_CID_FOCUS_RELATIVE\t\t\t(V4L2_CID_CAMERA_CLASS_BASE+11)\n>  #define V4L2_CID_FOCUS_AUTO\t\t\t(V4L2_CID_CAMERA_CLASS_BASE+12)\n> +enum v4l2_focus_auto_type {\n> +\tV4L2_FOCUS_MANUAL = 0,\n> +\tV4L2_FOCUS_AUTO = 1\n> +};\n\nFiles in the include/linux/ directory are imported from the Linux\nkernel. They shouldn't be modified manually, as indicated in\ninclude/linux/README. If you believe this change is important, it should\nbe submitted to the kernel first. As the V4L2_CID_FOCUS_AUTO control is\na boolean control, I don't think the enum is needed, you can simply\ncompare the value to == 0 and != 0.\n\n>  \n>  #define V4L2_CID_ZOOM_ABSOLUTE\t\t\t(V4L2_CID_CAMERA_CLASS_BASE+13)\n>  #define V4L2_CID_ZOOM_RELATIVE\t\t\t(V4L2_CID_CAMERA_CLASS_BASE+14)\n> diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> index 6b32fa18..fe3c71d0 100644\n> --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> @@ -304,13 +304,14 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,\n>  \t\tcid = V4L2_CID_EXPOSURE_ABSOLUTE;\n>  \telse if (id == controls::AnalogueGain)\n>  \t\tcid = V4L2_CID_GAIN;\n> +\telse if (id == controls::LensPosition && controls->idMap()->count(V4L2_CID_FOCUS_ABSOLUTE)) // Check if device supports this control\n\nlibcamera uses C-style comments. The checkstyle.py script, in utils/,\nshould have caught that. I recommend installing the\nutils/hooks/post-commit hook, as documented in\nDocumentation/coding-style.rst, to automate running the checkstyle\nscript.\n\nNote that checkstyle.py sometimes recommends changes that are false\npositives. The flagged issues should be reviewed, but the diff doesn't\nneed to be applied blindly.\n\n> +\t\tcid = V4L2_CID_FOCUS_ABSOLUTE;\n> +\telse if (id == controls::AfMode && controls->idMap()->count(V4L2_CID_FOCUS_AUTO)) // Check if device supports this control\n\nLet's move the idMap() checks later.\n\n> +\t\tcid = V4L2_CID_FOCUS_AUTO;\n>  \telse\n>  \t\treturn -EINVAL;\n\nSomething along the lines of\n\n\tif (controls->idMap()->find(cid) == controls->idMap()->end())\n\t\treturn -EINVAL;\n\nThis will avoid duplicating the check in multiple places above.\n\n>  \n>  \tconst ControlInfo &v4l2Info = controls->infoMap()->at(cid);\n> -\tint32_t min = v4l2Info.min().get<int32_t>();\n> -\tint32_t def = v4l2Info.def().get<int32_t>();\n> -\tint32_t max = v4l2Info.max().get<int32_t>();\n\nIt's not nice to duplicate this in most cases below. Is there a way to\ndo better ?\n\n>  \n>  \t/*\n>  \t * See UVCCameraData::addControl() for explanations of the different\n> @@ -318,6 +319,10 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,\n>  \t */\n>  \tswitch (cid) {\n>  \tcase V4L2_CID_BRIGHTNESS: {\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\n>  \t\tfloat scale = std::max(max - def, def - min);\n>  \t\tfloat fvalue = value.get<float>() * scale + def;\n>  \t\tcontrols->set(cid, static_cast<int32_t>(lroundf(fvalue)));\n> @@ -325,6 +330,9 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,\n>  \t}\n>  \n>  \tcase V4L2_CID_SATURATION: {\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n> +\n>  \t\tfloat scale = def - min;\n>  \t\tfloat fvalue = value.get<float>() * scale + min;\n>  \t\tcontrols->set(cid, static_cast<int32_t>(lroundf(fvalue)));\n> @@ -345,6 +353,10 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,\n>  \n>  \tcase V4L2_CID_CONTRAST:\n>  \tcase V4L2_CID_GAIN: {\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\n>  \t\tfloat m = (4.0f - 1.0f) / (max - def);\n>  \t\tfloat p = 1.0f - m * def;\n>  \n> @@ -358,6 +370,22 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,\n>  \t\tbreak;\n>  \t}\n>  \n> +\tcase V4L2_CID_FOCUS_ABSOLUTE: {\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\n> +\t\tfloat focusedAt50Cm = 0.15f * (max - min);\n> +\t\tfloat scale = focusedAt50Cm / 2.0f;\n> +\n> +\t\tcontrols->set(cid, static_cast<int>(min + value.get<float>() * scale));\n> +\t\tbreak;\n> +\t}\n> +\n> +\tcase V4L2_CID_FOCUS_AUTO: {\n> +\t\tcontrols->set(cid, static_cast<int>(value.get<int>() == 0 ? V4L2_FOCUS_MANUAL : V4L2_FOCUS_AUTO));\n\n'value' contains a value of the AfMode control, so you should compare it\nto AfModeManual, not 0.\n\nPlease use an explicit width for the integer, it should be\nget<int32_t>().\n\ncheckstyle should report that the line is too long. It should be\nwrapped.\n\n> +\t\tbreak;\n> +\t}\n> +\n>  \tdefault: {\n>  \t\tint32_t ivalue = value.get<int32_t>();\n>  \t\tcontrols->set(cid, ivalue);\n> @@ -655,14 +683,17 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,\n>  \tcase V4L2_CID_GAIN:\n>  \t\tid = &controls::AnalogueGain;\n>  \t\tbreak;\n> +\tcase V4L2_CID_FOCUS_ABSOLUTE:\n> +\t\tid = &controls::LensPosition;\n> +\t\tbreak;\n> +\tcase V4L2_CID_FOCUS_AUTO:\n> +\t\tid = &controls::AfMode;\n> +\t\tbreak;\n>  \tdefault:\n>  \t\treturn;\n>  \t}\n>  \n>  \t/* Map the control info. */\n> -\tint32_t min = v4l2Info.min().get<int32_t>();\n> -\tint32_t max = v4l2Info.max().get<int32_t>();\n> -\tint32_t def = v4l2Info.def().get<int32_t>();\n>  \n>  \tswitch (cid) {\n>  \tcase V4L2_CID_BRIGHTNESS: {\n> @@ -673,6 +704,9 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,\n>  \t\t * Accommodate this by restricting the range of the libcamera\n>  \t\t * control, but always within the maximum limits.\n>  \t\t */\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n>  \t\tfloat scale = std::max(max - def, def - min);\n>  \n>  \t\tinfo = ControlInfo{\n> @@ -683,36 +717,42 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,\n>  \t\tbreak;\n>  \t}\n>  \n> -\tcase V4L2_CID_SATURATION:\n> +\tcase V4L2_CID_SATURATION: {\n>  \t\t/*\n>  \t\t * The Saturation control is a float, with 0.0 mapped to the\n>  \t\t * minimum value (corresponding to a fully desaturated image)\n>  \t\t * and 1.0 mapped to the default value. Calculate the maximum\n>  \t\t * value accordingly.\n>  \t\t */\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n>  \t\tinfo = ControlInfo{\n>  \t\t\t{ 0.0f },\n>  \t\t\t{ static_cast<float>(max - min) / (def - min) },\n>  \t\t\t{ 1.0f }\n>  \t\t};\n>  \t\tbreak;\n> -\n> +\t}\n>  \tcase V4L2_CID_EXPOSURE_AUTO:\n>  \t\tinfo = ControlInfo{ false, true, true };\n>  \t\tbreak;\n>  \n> -\tcase V4L2_CID_EXPOSURE_ABSOLUTE:\n> +\tcase V4L2_CID_EXPOSURE_ABSOLUTE: {\n>  \t\t/*\n>  \t\t * ExposureTime is in units of 1 µs, and UVC expects\n>  \t\t * V4L2_CID_EXPOSURE_ABSOLUTE in units of 100 µs.\n>  \t\t */\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n>  \t\tinfo = ControlInfo{\n>  \t\t\t{ min * 100 },\n>  \t\t\t{ max * 100 },\n>  \t\t\t{ def * 100 }\n>  \t\t};\n>  \t\tbreak;\n> -\n> +\t}\n>  \tcase V4L2_CID_CONTRAST:\n>  \tcase V4L2_CID_GAIN: {\n>  \t\t/*\n> @@ -723,6 +763,9 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,\n>  \t\t * maximum values are respectively no lower than 0.5 and no\n>  \t\t * higher than 4.0.\n>  \t\t */\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n>  \t\tfloat m = (4.0f - 1.0f) / (max - def);\n>  \t\tfloat p = 1.0f - m * def;\n>  \n> @@ -738,6 +781,31 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,\n>  \t\t};\n>  \t\tbreak;\n>  \t}\n> +\tcase V4L2_CID_FOCUS_ABSOLUTE: {\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n> +\n> +\t\tfloat focusedAt50Cm = 0.15f * (max - min);\n\nWhere does 0.15 come from ?\n\n> +\t\tfloat scale = 2.0f / focusedAt50Cm;\n> +\n> +\t\tinfo = ControlInfo{\n> +\t\t\t{ 0.0f },\n> +\t\t\t{ scale * (max - min) },\n\nGiven the calculations just above, this is equivalent to\n\n\tscale * (max - min)\n=\t2.0f / focusedAt50Cm * (max - min)\n=\t2.0f / (0.15f * (max - min)) * (max - min)\n=\t2.0f / 0.15f\n\nAs this is the reciprocal of the focus distance, it corresponds to a\nfocus distance of 0.15/2 = 75mm. I'm curious to know why you picked this\nparticular value.\n\n> +\t\t\t{ scale * (def - min) }\n> +\t\t};\n> +\t\tbreak;\n> +\t}\n> +\tcase V4L2_CID_FOCUS_AUTO: {\n> +\t\tbool def = v4l2Info.def().get<bool>();\n> +\n> +\t\tinfo = ControlInfo{\n> +\t\t\t{ static_cast<int>(V4L2_FOCUS_MANUAL) },\n> +\t\t\t{ static_cast<int>(V4L2_FOCUS_AUTO) },\n> +\t\t\t{ static_cast<int>(def ? V4L2_FOCUS_AUTO : V4L2_FOCUS_MANUAL) },\n> +\t\t};\n\nThis isn't correct. ControlInfo describes the AfMode control, so the\nvalues should be AfMode* enumerators, not V4L2 control values. See the\ndefinition of the AfMode control in src/libcamera/control_ids_core.yaml\nfor the list of enumerators.\n\n> +\t\tbreak;\n> +\t}\n>  \n>  \tdefault:\n>  \t\tinfo = v4l2Info;","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 37483C324C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSat, 14 Sep 2024 00:18:12 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C6468634F8;\n\tSat, 14 Sep 2024 02:18:10 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 136C5634F2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 14 Sep 2024 02:18:08 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(213-229-8-243.static.upcbusiness.at [213.229.8.243])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 7D9F7219;\n\tSat, 14 Sep 2024 02:16:48 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"N68EzUFs\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1726273008;\n\tbh=AFAhEI2gc4X/tAexjbX/EpRX4x5gdtVgatfhQP4+5LQ=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=N68EzUFsV2xb8SyQHEn8o8cvSNXJmAUi9QzvQSsJGvSL3iXZK52fDXj7uhsTltXcF\n\t2jn46fkunycmludTQ94v/KmL5D6mlghzFDMpUvW5VKYoHpgdOuMEuQJc46kfya5pbM\n\t/kCHCsmf71HMldIpR1je5AlKmURYvNanB4m6rYPI=","Date":"Sat, 14 Sep 2024 03:17:32 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"=?utf-8?q?Cl=C3=A1udio?= Paulo <claudio.paulo@makewise.pt>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH] libcamera: pipeline: uvcvideo: Map focus controls","Message-ID":"<20240914001732.GA3260@pendragon.ideasonboard.com>","References":"<01020191ec67fb81-a9b3be00-461b-4814-a6bb-892404f7420f-000000@eu-west-1.amazonses.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<01020191ec67fb81-a9b3be00-461b-4814-a6bb-892404f7420f-000000@eu-west-1.amazonses.com>","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":31230,"web_url":"https://patchwork.libcamera.org/comment/31230/","msgid":"<0902aff7-75ef-4dee-9cb6-3a1806703fbb@redhat.com>","date":"2024-09-14T10:05:41","subject":"Re: [PATCH] libcamera: pipeline: uvcvideo: Map focus controls","submitter":{"id":102,"url":"https://patchwork.libcamera.org/api/people/102/","name":"Hans de Goede","email":"hdegoede@redhat.com"},"content":"Hi Cláudio,\n\nOn 9/13/24 7:22 PM, Cláudio Paulo wrote:\n> Added mapping of controls::LensPosition and controls::AfMode to\n> v4l2 controls V4L2_CID_FOCUS_ABSOLUTE and V4L2_CID_FOCUS_AUTO\n> respectively when the device supports them.\n> \n> If not supported, they are not registered.\n> \n> Signed-off-by: Cláudio Paulo <claudio.paulo@makewise.pt>\n\nThank you for your patch.\n\nLike last year I have a group of students from my local university\nworking on libcamera for ~1 day/week during the first semester\nof the academic year.\n\nThey have just started so they have not introduced themselves to\nthe list yet ...\n\nThe reason I mention this is that I have given them the assignment\nto add auto-focus support to the software ISP. One of their \"could have\"\nrequirements is to integrate support for software auto-focus into\nthe UVC driver, because some cameras like e.g. the Logitech quickcam\n9000 pro have a non fixed lens / a V4L2_CID_FOCUS_ABSOLUTE control,\nbut these cameras do not support autofocus inside the camera instead\nrelying on autofocus in the driver / OS.\n\nI know there are also UVC camera with builtin auto-focus so I guess\nyou are mostly adding these mappings for those?\n\nI just want to make sure you are not also looking into software\nauto-focus support ?  (to avoid double work getting done)\n\nRegards,\n\nHans\n\n\n\n\n\n\n> ---\n>  include/linux/v4l2-controls.h                |  4 +\n>  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 88 +++++++++++++++++---\n>  2 files changed, 82 insertions(+), 10 deletions(-)\n> \n> diff --git a/include/linux/v4l2-controls.h b/include/linux/v4l2-controls.h\n> index 882a8180..1ac85507 100644\n> --- a/include/linux/v4l2-controls.h\n> +++ b/include/linux/v4l2-controls.h\n> @@ -994,6 +994,10 @@ enum  v4l2_exposure_auto_type {\n>  #define V4L2_CID_FOCUS_ABSOLUTE\t\t\t(V4L2_CID_CAMERA_CLASS_BASE+10)\n>  #define V4L2_CID_FOCUS_RELATIVE\t\t\t(V4L2_CID_CAMERA_CLASS_BASE+11)\n>  #define V4L2_CID_FOCUS_AUTO\t\t\t(V4L2_CID_CAMERA_CLASS_BASE+12)\n> +enum v4l2_focus_auto_type {\n> +\tV4L2_FOCUS_MANUAL = 0,\n> +\tV4L2_FOCUS_AUTO = 1\n> +};\n>  \n>  #define V4L2_CID_ZOOM_ABSOLUTE\t\t\t(V4L2_CID_CAMERA_CLASS_BASE+13)\n>  #define V4L2_CID_ZOOM_RELATIVE\t\t\t(V4L2_CID_CAMERA_CLASS_BASE+14)\n> diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> index 6b32fa18..fe3c71d0 100644\n> --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> @@ -304,13 +304,14 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,\n>  \t\tcid = V4L2_CID_EXPOSURE_ABSOLUTE;\n>  \telse if (id == controls::AnalogueGain)\n>  \t\tcid = V4L2_CID_GAIN;\n> +\telse if (id == controls::LensPosition && controls->idMap()->count(V4L2_CID_FOCUS_ABSOLUTE)) // Check if device supports this control\n> +\t\tcid = V4L2_CID_FOCUS_ABSOLUTE;\n> +\telse if (id == controls::AfMode && controls->idMap()->count(V4L2_CID_FOCUS_AUTO)) // Check if device supports this control\n> +\t\tcid = V4L2_CID_FOCUS_AUTO;\n>  \telse\n>  \t\treturn -EINVAL;\n>  \n>  \tconst ControlInfo &v4l2Info = controls->infoMap()->at(cid);\n> -\tint32_t min = v4l2Info.min().get<int32_t>();\n> -\tint32_t def = v4l2Info.def().get<int32_t>();\n> -\tint32_t max = v4l2Info.max().get<int32_t>();\n>  \n>  \t/*\n>  \t * See UVCCameraData::addControl() for explanations of the different\n> @@ -318,6 +319,10 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,\n>  \t */\n>  \tswitch (cid) {\n>  \tcase V4L2_CID_BRIGHTNESS: {\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\n>  \t\tfloat scale = std::max(max - def, def - min);\n>  \t\tfloat fvalue = value.get<float>() * scale + def;\n>  \t\tcontrols->set(cid, static_cast<int32_t>(lroundf(fvalue)));\n> @@ -325,6 +330,9 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,\n>  \t}\n>  \n>  \tcase V4L2_CID_SATURATION: {\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n> +\n>  \t\tfloat scale = def - min;\n>  \t\tfloat fvalue = value.get<float>() * scale + min;\n>  \t\tcontrols->set(cid, static_cast<int32_t>(lroundf(fvalue)));\n> @@ -345,6 +353,10 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,\n>  \n>  \tcase V4L2_CID_CONTRAST:\n>  \tcase V4L2_CID_GAIN: {\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\n>  \t\tfloat m = (4.0f - 1.0f) / (max - def);\n>  \t\tfloat p = 1.0f - m * def;\n>  \n> @@ -358,6 +370,22 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,\n>  \t\tbreak;\n>  \t}\n>  \n> +\tcase V4L2_CID_FOCUS_ABSOLUTE: {\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\n> +\t\tfloat focusedAt50Cm = 0.15f * (max - min);\n> +\t\tfloat scale = focusedAt50Cm / 2.0f;\n> +\n> +\t\tcontrols->set(cid, static_cast<int>(min + value.get<float>() * scale));\n> +\t\tbreak;\n> +\t}\n> +\n> +\tcase V4L2_CID_FOCUS_AUTO: {\n> +\t\tcontrols->set(cid, static_cast<int>(value.get<int>() == 0 ? V4L2_FOCUS_MANUAL : V4L2_FOCUS_AUTO));\n> +\t\tbreak;\n> +\t}\n> +\n>  \tdefault: {\n>  \t\tint32_t ivalue = value.get<int32_t>();\n>  \t\tcontrols->set(cid, ivalue);\n> @@ -655,14 +683,17 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,\n>  \tcase V4L2_CID_GAIN:\n>  \t\tid = &controls::AnalogueGain;\n>  \t\tbreak;\n> +\tcase V4L2_CID_FOCUS_ABSOLUTE:\n> +\t\tid = &controls::LensPosition;\n> +\t\tbreak;\n> +\tcase V4L2_CID_FOCUS_AUTO:\n> +\t\tid = &controls::AfMode;\n> +\t\tbreak;\n>  \tdefault:\n>  \t\treturn;\n>  \t}\n>  \n>  \t/* Map the control info. */\n> -\tint32_t min = v4l2Info.min().get<int32_t>();\n> -\tint32_t max = v4l2Info.max().get<int32_t>();\n> -\tint32_t def = v4l2Info.def().get<int32_t>();\n>  \n>  \tswitch (cid) {\n>  \tcase V4L2_CID_BRIGHTNESS: {\n> @@ -673,6 +704,9 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,\n>  \t\t * Accommodate this by restricting the range of the libcamera\n>  \t\t * control, but always within the maximum limits.\n>  \t\t */\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n>  \t\tfloat scale = std::max(max - def, def - min);\n>  \n>  \t\tinfo = ControlInfo{\n> @@ -683,36 +717,42 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,\n>  \t\tbreak;\n>  \t}\n>  \n> -\tcase V4L2_CID_SATURATION:\n> +\tcase V4L2_CID_SATURATION: {\n>  \t\t/*\n>  \t\t * The Saturation control is a float, with 0.0 mapped to the\n>  \t\t * minimum value (corresponding to a fully desaturated image)\n>  \t\t * and 1.0 mapped to the default value. Calculate the maximum\n>  \t\t * value accordingly.\n>  \t\t */\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n>  \t\tinfo = ControlInfo{\n>  \t\t\t{ 0.0f },\n>  \t\t\t{ static_cast<float>(max - min) / (def - min) },\n>  \t\t\t{ 1.0f }\n>  \t\t};\n>  \t\tbreak;\n> -\n> +\t}\n>  \tcase V4L2_CID_EXPOSURE_AUTO:\n>  \t\tinfo = ControlInfo{ false, true, true };\n>  \t\tbreak;\n>  \n> -\tcase V4L2_CID_EXPOSURE_ABSOLUTE:\n> +\tcase V4L2_CID_EXPOSURE_ABSOLUTE: {\n>  \t\t/*\n>  \t\t * ExposureTime is in units of 1 µs, and UVC expects\n>  \t\t * V4L2_CID_EXPOSURE_ABSOLUTE in units of 100 µs.\n>  \t\t */\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n>  \t\tinfo = ControlInfo{\n>  \t\t\t{ min * 100 },\n>  \t\t\t{ max * 100 },\n>  \t\t\t{ def * 100 }\n>  \t\t};\n>  \t\tbreak;\n> -\n> +\t}\n>  \tcase V4L2_CID_CONTRAST:\n>  \tcase V4L2_CID_GAIN: {\n>  \t\t/*\n> @@ -723,6 +763,9 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,\n>  \t\t * maximum values are respectively no lower than 0.5 and no\n>  \t\t * higher than 4.0.\n>  \t\t */\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n>  \t\tfloat m = (4.0f - 1.0f) / (max - def);\n>  \t\tfloat p = 1.0f - m * def;\n>  \n> @@ -738,6 +781,31 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,\n>  \t\t};\n>  \t\tbreak;\n>  \t}\n> +\tcase V4L2_CID_FOCUS_ABSOLUTE: {\n> +\t\tint32_t min = v4l2Info.min().get<int32_t>();\n> +\t\tint32_t max = v4l2Info.max().get<int32_t>();\n> +\t\tint32_t def = v4l2Info.def().get<int32_t>();\n> +\n> +\t\tfloat focusedAt50Cm = 0.15f * (max - min);\n> +\t\tfloat scale = 2.0f / focusedAt50Cm;\n> +\n> +\t\tinfo = ControlInfo{\n> +\t\t\t{ 0.0f },\n> +\t\t\t{ scale * (max - min) },\n> +\t\t\t{ scale * (def - min) }\n> +\t\t};\n> +\t\tbreak;\n> +\t}\n> +\tcase V4L2_CID_FOCUS_AUTO: {\n> +\t\tbool def = v4l2Info.def().get<bool>();\n> +\n> +\t\tinfo = ControlInfo{\n> +\t\t\t{ static_cast<int>(V4L2_FOCUS_MANUAL) },\n> +\t\t\t{ static_cast<int>(V4L2_FOCUS_AUTO) },\n> +\t\t\t{ static_cast<int>(def ? V4L2_FOCUS_AUTO : V4L2_FOCUS_MANUAL) },\n> +\t\t};\n> +\t\tbreak;\n> +\t}\n>  \n>  \tdefault:\n>  \t\tinfo = v4l2Info;","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id D28C4C3257\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSat, 14 Sep 2024 10:05:50 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 50242618F6;\n\tSat, 14 Sep 2024 12:05:49 +0200 (CEST)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.133.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B16F0618F6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 14 Sep 2024 12:05:46 +0200 (CEST)","from mail-ej1-f70.google.com (mail-ej1-f70.google.com\n\t[209.85.218.70]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n\tus-mta-648-IQly633eN06wz0aqACzN3w-1; Sat, 14 Sep 2024 06:05:44 -0400","by mail-ej1-f70.google.com with SMTP id\n\ta640c23a62f3a-a8d2e6a6989so127809466b.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 14 Sep 2024 03:05:43 -0700 (PDT)","from ?IPV6:2001:1c00:c32:7800:5bfa:a036:83f0:f9ec?\n\t(2001-1c00-0c32-7800-5bfa-a036-83f0-f9ec.cable.dynamic.v6.ziggo.nl.\n\t[2001:1c00:c32:7800:5bfa:a036:83f0:f9ec])\n\tby smtp.gmail.com with ESMTPSA id\n\ta640c23a62f3a-a90612b3901sm62091066b.99.2024.09.14.03.05.41\n\t(version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n\tSat, 14 Sep 2024 03:05:41 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"Saz1c4IB\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1726308345;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:mime-version:mime-version:content-type:content-type:\n\tcontent-transfer-encoding:content-transfer-encoding:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=1zrMjkq7pmJF81mNT2eyal7pRHvcrnz+De/g+JZN5FE=;\n\tb=Saz1c4IBJhFOmW/yy2k+xJxcfrOJsBE7/aP4FiJX4aD/SaoKkOLwdkejoWXxSEAV/UZuFU\n\tv2xQ1edirudSTAqMlFYwDOt9ONnQ6wJ79m0x3JW6ZZ9jXNmk2tKtCXGv04O01mWiYDEwYO\n\tbX8+QfOsKqRqV+qjiJLqH60kW7LKink=","X-MC-Unique":"IQly633eN06wz0aqACzN3w-1","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1726308342; x=1726913142;\n\th=content-transfer-encoding:in-reply-to:from:content-language\n\t:references:to:subject:user-agent:mime-version:date:message-id\n\t:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to;\n\tbh=1zrMjkq7pmJF81mNT2eyal7pRHvcrnz+De/g+JZN5FE=;\n\tb=NnvNArwNFLzGQrnntbttOP/2ddwtT3SgSl4kbR0PZ/9UCTIJEu3IDs6Z8ShvIzBpSB\n\tckEq9ppd3xYzWUXsXyUctDy7m8P8MBZdiLyPXiHDwrCsJHWBV7ZqX3YsWhQ7EzRAAahf\n\tR/ybJ64bODAthpcecO7QkLaqCRlqc6fCPcV22O91yW536YOqH9luOUgFTDQPdKVugJ2e\n\tkRYeO/EwF1AQmUht8w+7qZceVn+nE648dfqguRPe7ooQVGesBV5asP3OS140vKbAHwLt\n\t2cWzjstmeMIpYBu/1sSSjBhHvEBeNwG2pxmkISRrhnCB3qfUizmj6jyLRSfWJJEPuztd\n\tEu+Q==","X-Forwarded-Encrypted":"i=1;\n\tAJvYcCVQCz29tl7nmqaDMJG7SZEY3G6c34MFG2xn+R31GIMYAihvJ6nYjGB3L198Mq0kkoS//LtHgPW/WFLXcRwTW48=@lists.libcamera.org","X-Gm-Message-State":"AOJu0YxyZFbIeXim004fi0Y4xZ+K7s+R6jvWVyJgCvCRKacTDA5/HxRF\n\tjtQsqN42Jb7NIlmX71B8x8aAH88gRaurHb5C17k9bgY6ZrkSYv6rQUOEzDXjr93TFM3OtuLXOEj\n\tmhr5Qvk8VU3Js8vhHuinVUVfeJJK27BumEu/HCrf0AyW9p2LXe6UMrmCiHi8M9tpHhf6HQdm9+c\n\t+MTLs=","X-Received":["by 2002:a17:906:fe0a:b0:a7d:a680:23b5 with SMTP id\n\ta640c23a62f3a-a9047d35b7dmr586063566b.33.1726308342485; \n\tSat, 14 Sep 2024 03:05:42 -0700 (PDT)","by 2002:a17:906:fe0a:b0:a7d:a680:23b5 with SMTP id\n\ta640c23a62f3a-a9047d35b7dmr586059866b.33.1726308341855; \n\tSat, 14 Sep 2024 03:05:41 -0700 (PDT)"],"X-Google-Smtp-Source":"AGHT+IF13D/dss4610HMMdwlJ8iEmFYwY4uqEK9oUHLqww0m35+EOxhbBiAwoPRgvWqutFia6qCOcg==","Message-ID":"<0902aff7-75ef-4dee-9cb6-3a1806703fbb@redhat.com>","Date":"Sat, 14 Sep 2024 12:05:41 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH] libcamera: pipeline: uvcvideo: Map focus controls","To":"=?utf-8?q?Cl=C3=A1udio_Paulo?= <claudio.paulo@makewise.pt>,\n\tlibcamera-devel@lists.libcamera.org","References":"<01020191ec67fb81-a9b3be00-461b-4814-a6bb-892404f7420f-000000@eu-west-1.amazonses.com>","From":"Hans de Goede <hdegoede@redhat.com>","In-Reply-To":"<01020191ec67fb81-a9b3be00-461b-4814-a6bb-892404f7420f-000000@eu-west-1.amazonses.com>","X-Mimecast-Spam-Score":"0","X-Mimecast-Originator":"redhat.com","Content-Language":"en-US, nl","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":31231,"web_url":"https://patchwork.libcamera.org/comment/31231/","msgid":"<CAAVu9WyO29UdCWqMSc3UOnq=k3ukgpNr2mRwkuWeo74-7KFD+A@mail.gmail.com>","date":"2024-09-14T22:22:31","subject":"Re: [PATCH] libcamera: pipeline: uvcvideo: Map focus controls","submitter":{"id":207,"url":"https://patchwork.libcamera.org/api/people/207/","name":"Cláudio Paulo","email":"claudio.paulo@makewise.pt"},"content":"Hi Hans,\n\nOur use case is a bit different.\n\nWe are working with UVC cameras that have built-in auto-focus and a\nV4L2_CID_FOCUS_ABSOLUTE control.\n\nWe just need to lock the lens position once they are focused on the\ncorrect point.\n\nAfter the cameras are installed they should remain about the same\ndistance from the objects they are recording, with only occasional\nrefocusing needed. Sometimes, certain objects may appear closer\nto the camera, but we do not want the focus to shift when that\nhappens, in order to avoid losing time.\n\nBest regards,\n\nCláudio\n\n\nHans de Goede <hdegoede@redhat.com> escreveu (sábado, 14/09/2024 à(s)\n11:05):\n\n> Hi Cláudio,\n>\n> On 9/13/24 7:22 PM, Cláudio Paulo wrote:\n> > Added mapping of controls::LensPosition and controls::AfMode to\n> > v4l2 controls V4L2_CID_FOCUS_ABSOLUTE and V4L2_CID_FOCUS_AUTO\n> > respectively when the device supports them.\n> >\n> > If not supported, they are not registered.\n> >\n> > Signed-off-by: Cláudio Paulo <claudio.paulo@makewise.pt>\n>\n> Thank you for your patch.\n>\n> Like last year I have a group of students from my local university\n> working on libcamera for ~1 day/week during the first semester\n> of the academic year.\n>\n> They have just started so they have not introduced themselves to\n> the list yet ...\n>\n> The reason I mention this is that I have given them the assignment\n> to add auto-focus support to the software ISP. One of their \"could have\"\n> requirements is to integrate support for software auto-focus into\n> the UVC driver, because some cameras like e.g. the Logitech quickcam\n> 9000 pro have a non fixed lens / a V4L2_CID_FOCUS_ABSOLUTE control,\n> but these cameras do not support autofocus inside the camera instead\n> relying on autofocus in the driver / OS.\n>\n> I know there are also UVC camera with builtin auto-focus so I guess\n> you are mostly adding these mappings for those?\n>\n> I just want to make sure you are not also looking into software\n> auto-focus support ?  (to avoid double work getting done)\n>\n> Regards,\n>\n> Hans\n>\n>\n>\n>\n>\n>\n> > ---\n> >  include/linux/v4l2-controls.h                |  4 +\n> >  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 88 +++++++++++++++++---\n> >  2 files changed, 82 insertions(+), 10 deletions(-)\n> >\n> > diff --git a/include/linux/v4l2-controls.h\n> b/include/linux/v4l2-controls.h\n> > index 882a8180..1ac85507 100644\n> > --- a/include/linux/v4l2-controls.h\n> > +++ b/include/linux/v4l2-controls.h\n> > @@ -994,6 +994,10 @@ enum  v4l2_exposure_auto_type {\n> >  #define V4L2_CID_FOCUS_ABSOLUTE\n> (V4L2_CID_CAMERA_CLASS_BASE+10)\n> >  #define V4L2_CID_FOCUS_RELATIVE\n> (V4L2_CID_CAMERA_CLASS_BASE+11)\n> >  #define V4L2_CID_FOCUS_AUTO\n> (V4L2_CID_CAMERA_CLASS_BASE+12)\n> > +enum v4l2_focus_auto_type {\n> > +     V4L2_FOCUS_MANUAL = 0,\n> > +     V4L2_FOCUS_AUTO = 1\n> > +};\n> >\n> >  #define V4L2_CID_ZOOM_ABSOLUTE\n>  (V4L2_CID_CAMERA_CLASS_BASE+13)\n> >  #define V4L2_CID_ZOOM_RELATIVE\n>  (V4L2_CID_CAMERA_CLASS_BASE+14)\n> > diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> > index 6b32fa18..fe3c71d0 100644\n> > --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> > +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n> > @@ -304,13 +304,14 @@ int PipelineHandlerUVC::processControl(ControlList\n> *controls, unsigned int id,\n> >               cid = V4L2_CID_EXPOSURE_ABSOLUTE;\n> >       else if (id == controls::AnalogueGain)\n> >               cid = V4L2_CID_GAIN;\n> > +     else if (id == controls::LensPosition &&\n> controls->idMap()->count(V4L2_CID_FOCUS_ABSOLUTE)) // Check if device\n> supports this control\n> > +             cid = V4L2_CID_FOCUS_ABSOLUTE;\n> > +     else if (id == controls::AfMode &&\n> controls->idMap()->count(V4L2_CID_FOCUS_AUTO)) // Check if device supports\n> this control\n> > +             cid = V4L2_CID_FOCUS_AUTO;\n> >       else\n> >               return -EINVAL;\n> >\n> >       const ControlInfo &v4l2Info = controls->infoMap()->at(cid);\n> > -     int32_t min = v4l2Info.min().get<int32_t>();\n> > -     int32_t def = v4l2Info.def().get<int32_t>();\n> > -     int32_t max = v4l2Info.max().get<int32_t>();\n> >\n> >       /*\n> >        * See UVCCameraData::addControl() for explanations of the\n> different\n> > @@ -318,6 +319,10 @@ int PipelineHandlerUVC::processControl(ControlList\n> *controls, unsigned int id,\n> >        */\n> >       switch (cid) {\n> >       case V4L2_CID_BRIGHTNESS: {\n> > +             int32_t min = v4l2Info.min().get<int32_t>();\n> > +             int32_t def = v4l2Info.def().get<int32_t>();\n> > +             int32_t max = v4l2Info.max().get<int32_t>();\n> > +\n> >               float scale = std::max(max - def, def - min);\n> >               float fvalue = value.get<float>() * scale + def;\n> >               controls->set(cid, static_cast<int32_t>(lroundf(fvalue)));\n> > @@ -325,6 +330,9 @@ int PipelineHandlerUVC::processControl(ControlList\n> *controls, unsigned int id,\n> >       }\n> >\n> >       case V4L2_CID_SATURATION: {\n> > +             int32_t min = v4l2Info.min().get<int32_t>();\n> > +             int32_t def = v4l2Info.def().get<int32_t>();\n> > +\n> >               float scale = def - min;\n> >               float fvalue = value.get<float>() * scale + min;\n> >               controls->set(cid, static_cast<int32_t>(lroundf(fvalue)));\n> > @@ -345,6 +353,10 @@ int PipelineHandlerUVC::processControl(ControlList\n> *controls, unsigned int id,\n> >\n> >       case V4L2_CID_CONTRAST:\n> >       case V4L2_CID_GAIN: {\n> > +             int32_t min = v4l2Info.min().get<int32_t>();\n> > +             int32_t def = v4l2Info.def().get<int32_t>();\n> > +             int32_t max = v4l2Info.max().get<int32_t>();\n> > +\n> >               float m = (4.0f - 1.0f) / (max - def);\n> >               float p = 1.0f - m * def;\n> >\n> > @@ -358,6 +370,22 @@ int PipelineHandlerUVC::processControl(ControlList\n> *controls, unsigned int id,\n> >               break;\n> >       }\n> >\n> > +     case V4L2_CID_FOCUS_ABSOLUTE: {\n> > +             int32_t min = v4l2Info.min().get<int32_t>();\n> > +             int32_t max = v4l2Info.max().get<int32_t>();\n> > +\n> > +             float focusedAt50Cm = 0.15f * (max - min);\n> > +             float scale = focusedAt50Cm / 2.0f;\n> > +\n> > +             controls->set(cid, static_cast<int>(min +\n> value.get<float>() * scale));\n> > +             break;\n> > +     }\n> > +\n> > +     case V4L2_CID_FOCUS_AUTO: {\n> > +             controls->set(cid, static_cast<int>(value.get<int>() == 0\n> ? V4L2_FOCUS_MANUAL : V4L2_FOCUS_AUTO));\n> > +             break;\n> > +     }\n> > +\n> >       default: {\n> >               int32_t ivalue = value.get<int32_t>();\n> >               controls->set(cid, ivalue);\n> > @@ -655,14 +683,17 @@ void UVCCameraData::addControl(uint32_t cid, const\n> ControlInfo &v4l2Info,\n> >       case V4L2_CID_GAIN:\n> >               id = &controls::AnalogueGain;\n> >               break;\n> > +     case V4L2_CID_FOCUS_ABSOLUTE:\n> > +             id = &controls::LensPosition;\n> > +             break;\n> > +     case V4L2_CID_FOCUS_AUTO:\n> > +             id = &controls::AfMode;\n> > +             break;\n> >       default:\n> >               return;\n> >       }\n> >\n> >       /* Map the control info. */\n> > -     int32_t min = v4l2Info.min().get<int32_t>();\n> > -     int32_t max = v4l2Info.max().get<int32_t>();\n> > -     int32_t def = v4l2Info.def().get<int32_t>();\n> >\n> >       switch (cid) {\n> >       case V4L2_CID_BRIGHTNESS: {\n> > @@ -673,6 +704,9 @@ void UVCCameraData::addControl(uint32_t cid, const\n> ControlInfo &v4l2Info,\n> >                * Accommodate this by restricting the range of the\n> libcamera\n> >                * control, but always within the maximum limits.\n> >                */\n> > +             int32_t min = v4l2Info.min().get<int32_t>();\n> > +             int32_t max = v4l2Info.max().get<int32_t>();\n> > +             int32_t def = v4l2Info.def().get<int32_t>();\n> >               float scale = std::max(max - def, def - min);\n> >\n> >               info = ControlInfo{\n> > @@ -683,36 +717,42 @@ void UVCCameraData::addControl(uint32_t cid, const\n> ControlInfo &v4l2Info,\n> >               break;\n> >       }\n> >\n> > -     case V4L2_CID_SATURATION:\n> > +     case V4L2_CID_SATURATION: {\n> >               /*\n> >                * The Saturation control is a float, with 0.0 mapped to\n> the\n> >                * minimum value (corresponding to a fully desaturated\n> image)\n> >                * and 1.0 mapped to the default value. Calculate the\n> maximum\n> >                * value accordingly.\n> >                */\n> > +             int32_t min = v4l2Info.min().get<int32_t>();\n> > +             int32_t max = v4l2Info.max().get<int32_t>();\n> > +             int32_t def = v4l2Info.def().get<int32_t>();\n> >               info = ControlInfo{\n> >                       { 0.0f },\n> >                       { static_cast<float>(max - min) / (def - min) },\n> >                       { 1.0f }\n> >               };\n> >               break;\n> > -\n> > +     }\n> >       case V4L2_CID_EXPOSURE_AUTO:\n> >               info = ControlInfo{ false, true, true };\n> >               break;\n> >\n> > -     case V4L2_CID_EXPOSURE_ABSOLUTE:\n> > +     case V4L2_CID_EXPOSURE_ABSOLUTE: {\n> >               /*\n> >                * ExposureTime is in units of 1 µs, and UVC expects\n> >                * V4L2_CID_EXPOSURE_ABSOLUTE in units of 100 µs.\n> >                */\n> > +             int32_t min = v4l2Info.min().get<int32_t>();\n> > +             int32_t max = v4l2Info.max().get<int32_t>();\n> > +             int32_t def = v4l2Info.def().get<int32_t>();\n> >               info = ControlInfo{\n> >                       { min * 100 },\n> >                       { max * 100 },\n> >                       { def * 100 }\n> >               };\n> >               break;\n> > -\n> > +     }\n> >       case V4L2_CID_CONTRAST:\n> >       case V4L2_CID_GAIN: {\n> >               /*\n> > @@ -723,6 +763,9 @@ void UVCCameraData::addControl(uint32_t cid, const\n> ControlInfo &v4l2Info,\n> >                * maximum values are respectively no lower than 0.5 and no\n> >                * higher than 4.0.\n> >                */\n> > +             int32_t min = v4l2Info.min().get<int32_t>();\n> > +             int32_t max = v4l2Info.max().get<int32_t>();\n> > +             int32_t def = v4l2Info.def().get<int32_t>();\n> >               float m = (4.0f - 1.0f) / (max - def);\n> >               float p = 1.0f - m * def;\n> >\n> > @@ -738,6 +781,31 @@ void UVCCameraData::addControl(uint32_t cid, const\n> ControlInfo &v4l2Info,\n> >               };\n> >               break;\n> >       }\n> > +     case V4L2_CID_FOCUS_ABSOLUTE: {\n> > +             int32_t min = v4l2Info.min().get<int32_t>();\n> > +             int32_t max = v4l2Info.max().get<int32_t>();\n> > +             int32_t def = v4l2Info.def().get<int32_t>();\n> > +\n> > +             float focusedAt50Cm = 0.15f * (max - min);\n> > +             float scale = 2.0f / focusedAt50Cm;\n> > +\n> > +             info = ControlInfo{\n> > +                     { 0.0f },\n> > +                     { scale * (max - min) },\n> > +                     { scale * (def - min) }\n> > +             };\n> > +             break;\n> > +     }\n> > +     case V4L2_CID_FOCUS_AUTO: {\n> > +             bool def = v4l2Info.def().get<bool>();\n> > +\n> > +             info = ControlInfo{\n> > +                     { static_cast<int>(V4L2_FOCUS_MANUAL) },\n> > +                     { static_cast<int>(V4L2_FOCUS_AUTO) },\n> > +                     { static_cast<int>(def ? V4L2_FOCUS_AUTO :\n> V4L2_FOCUS_MANUAL) },\n> > +             };\n> > +             break;\n> > +     }\n> >\n> >       default:\n> >               info = v4l2Info;\n>\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id EE1C6C3257\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSat, 14 Sep 2024 22:22:47 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 90DB5634FC;\n\tSun, 15 Sep 2024 00:22:46 +0200 (CEST)","from mail-ed1-x531.google.com (mail-ed1-x531.google.com\n\t[IPv6:2a00:1450:4864:20::531])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id E3F07634E3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 15 Sep 2024 00:22:43 +0200 (CEST)","by mail-ed1-x531.google.com with SMTP id\n\t4fb4d7f45d1cf-5c3d2f9f896so2699768a12.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 14 Sep 2024 15:22:43 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=makewise.pt header.i=@makewise.pt\n\theader.b=\"A9P6YekZ\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=makewise.pt; s=gapps; t=1726352563; x=1726957363;\n\tdarn=lists.libcamera.org; \n\th=cc:to:subject:message-id:date:from:in-reply-to:references\n\t:mime-version:from:to:cc:subject:date:message-id:reply-to;\n\tbh=/yjFupKHYEqSVn1pF9vtbRuUoi0mu93n7mhJ4L5TNGs=;\n\tb=A9P6YekZVzYGDjyr1INn/EAoCpopnOW1lV083FnaxuoND3YyCsMHgHrvfAys3/IF9h\n\t6Cd92qEYveZeM2TOwfwVnhK0+1GWU0piY4i5GgTSQkwRK5TO3UnV1BA2qUVw02dnyW2J\n\twlIDn6+Nlt5u7LUEjBXoULq37eNXhhdrbc2zo=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1726352563; x=1726957363;\n\th=cc:to:subject:message-id:date:from:in-reply-to:references\n\t:mime-version:x-gm-message-state:from:to:cc:subject:date:message-id\n\t:reply-to;\n\tbh=/yjFupKHYEqSVn1pF9vtbRuUoi0mu93n7mhJ4L5TNGs=;\n\tb=BKmqNNmJ8wbLtx0h/DEClkU1bJPKSgRJ1HgvnhS/3J9V1scdsWOoLKCPX2uEmjxdki\n\t4k8y++i5mbdPgtRjAeytnNDy7cztFXIggOh3JHZ5EduL5eVrzprM1fQ+8Vp3fsQnU8hA\n\tqTwkyhZ08eHYUZfh6S7Sv9K406J5q/EH4UsVEShx9AGyhCRxSlq6YiBZK+NFeLn2giPc\n\tY/iYil6Vpmj+QpZFwoWAWNpkfQQRV6UleVREPGG/w3C922K6bKc5EMtqWzFhFjRroz7B\n\tmK/bWQ7F47xI9j5g8PJ+DXz7UpjX44jXCSbxPTBRSL1NiMmmophOQztpL6nhZG1UeVHm\n\t80Fg==","X-Gm-Message-State":"AOJu0YzBLbaMghwV7Tfc7L8Uj3CqJoYR9BYmoy/W8Y3W5ThnngIXK4BI\n\t8JApH1UOrjthEsQ4yhQhdhYMV7WNfQTODDWzSpUMLOQReByuQbUll+LLpI1N2wOWfN/z4K5PxR4\n\tHzUkJg1tH9OnMaX+87/ZWZcmumztaBdKmKFbAmg==","X-Google-Smtp-Source":"AGHT+IGuFt9BMC149dYn6MuxXj7fza0SOH8tIs36JRMB9su0cpkFfq9eOrj/4p8quiRZ4zjfj76TD/A9LCS4/7mTUkw=","X-Received":"by 2002:a05:6402:1d53:b0:5c2:6e51:9d11 with SMTP id\n\t4fb4d7f45d1cf-5c41e1ace3bmr6050668a12.27.1726352562129;\n\tSat, 14 Sep 2024 15:22:42 -0700 (PDT)","MIME-Version":"1.0","References":"<01020191ec67fb81-a9b3be00-461b-4814-a6bb-892404f7420f-000000@eu-west-1.amazonses.com>\n\t<0902aff7-75ef-4dee-9cb6-3a1806703fbb@redhat.com>","In-Reply-To":"<0902aff7-75ef-4dee-9cb6-3a1806703fbb@redhat.com>","From":"=?utf-8?q?Cl=C3=A1udio_Paulo?= <claudio.paulo@makewise.pt>","Date":"Sat, 14 Sep 2024 23:22:31 +0100","Message-ID":"<CAAVu9WyO29UdCWqMSc3UOnq=k3ukgpNr2mRwkuWeo74-7KFD+A@mail.gmail.com>","Subject":"Re: [PATCH] libcamera: pipeline: uvcvideo: Map focus controls","To":"Hans de Goede <hdegoede@redhat.com>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"multipart/alternative; boundary=\"00000000000039290a06221bc754\"","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]