[{"id":29015,"web_url":"https://patchwork.libcamera.org/comment/29015/","msgid":"<97a4f26c-c3d2-4312-863b-298d07e14690@ideasonboard.com>","date":"2024-03-20T15:30:08","subject":"Re: [PATCH 1/4] libcamera: Add 'required' property to controls","submitter":{"id":86,"url":"https://patchwork.libcamera.org/api/people/86/","name":"Umang Jain","email":"umang.jain@ideasonboard.com"},"content":"Hi Jacopo,\n\nThank  you for the patch.\n\nOn 20/03/24 3:46 pm, Jacopo Mondi wrote:\n> Add to the ControlId class a 'required' boolean flag that determine\n> if the control (or property) is mandatory to be supported by a Camera\n> in order to comply with the libcamera API specification.\n>\n> Add support for a 'required' field to the controls and properties yaml\n> file definition and control generation scripts.\n>\n> Also plumb support for the flag in the control serializer component\n> to allow pass the information across IPC borders.\n>\n> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n\nLGTM,\n\nReviewed-by: Umang Jain <umang.jain@ideasonboard.com>\n\n> ---\n>   Documentation/guides/pipeline-handler.rst | 13 ++++++++----\n>   include/libcamera/controls.h              | 10 ++++++----\n>   include/libcamera/ipa/ipa_controls.h      |  3 ++-\n>   src/libcamera/control_serializer.cpp      |  4 +++-\n>   src/libcamera/controls.cpp                | 24 +++++++++++++++++------\n>   src/libcamera/ipa_controls.cpp            |  2 ++\n>   src/libcamera/v4l2_device.cpp             |  2 +-\n>   utils/gen-controls.py                     |  8 +++++++-\n>   8 files changed, 48 insertions(+), 18 deletions(-)\n>\n> diff --git a/Documentation/guides/pipeline-handler.rst b/Documentation/guides/pipeline-handler.rst\n> index 728e9676b4a6..b0d77d795211 100644\n> --- a/Documentation/guides/pipeline-handler.rst\n> +++ b/Documentation/guides/pipeline-handler.rst\n> @@ -610,10 +610,15 @@ information can be found in the `ControlInfoMap`_ class documentation.\n>   .. _ControlInfoMap: https://libcamera.org/api-html/classlibcamera_1_1ControlInfoMap.html\n>   \n>   Pipeline handlers register controls to expose the tunable device and IPA\n> -parameters to applications. Our example pipeline handler only exposes trivial\n> -controls of the video device, by registering a ``ControlId`` instance with\n> -associated values for each supported V4L2 control but demonstrates the mapping\n> -of V4L2 Controls to libcamera ControlIDs.\n> +parameters to applications and register properties to expose the Camera\n> +immutable characteristics. Controls and properties which have the ``required``\n> +field specified in the YAML definition are mandatory to be supported by a Camera\n> +in order for it to comply with the libcamera API specification.\n> +\n> +Our example pipeline handler only exposes trivial controls of the video device,\n> +by registering a ``ControlId`` instance with associated values for each\n> +supported V4L2 control but demonstrates the mapping of V4L2 Controls to\n> +libcamera ControlIDs.\n>   \n>   Complete the initialization of the ``VividCameraData`` class by adding the\n>   following code to the ``VividCameraData::init()`` function to initialise the\n> diff --git a/include/libcamera/controls.h b/include/libcamera/controls.h\n> index 82b955995380..0382a99de7fa 100644\n> --- a/include/libcamera/controls.h\n> +++ b/include/libcamera/controls.h\n> @@ -213,14 +213,15 @@ private:\n>   class ControlId\n>   {\n>   public:\n> -\tControlId(unsigned int id, const std::string &name, ControlType type)\n> -\t\t: id_(id), name_(name), type_(type)\n> +\tControlId(unsigned int id, const std::string &name, ControlType type, bool required)\n> +\t\t: id_(id), name_(name), type_(type), required_(required)\n>   \t{\n>   \t}\n>   \n>   \tunsigned int id() const { return id_; }\n>   \tconst std::string &name() const { return name_; }\n>   \tControlType type() const { return type_; }\n> +\tbool required() const { return required_; }\n>   \n>   private:\n>   \tLIBCAMERA_DISABLE_COPY_AND_MOVE(ControlId)\n> @@ -228,6 +229,7 @@ private:\n>   \tunsigned int id_;\n>   \tstd::string name_;\n>   \tControlType type_;\n> +\tbool required_;\n>   };\n>   \n>   static inline bool operator==(unsigned int lhs, const ControlId &rhs)\n> @@ -256,8 +258,8 @@ class Control : public ControlId\n>   public:\n>   \tusing type = T;\n>   \n> -\tControl(unsigned int id, const char *name)\n> -\t\t: ControlId(id, name, details::control_type<std::remove_cv_t<T>>::value)\n> +\tControl(unsigned int id, const char *name, bool required)\n> +\t\t: ControlId(id, name, details::control_type<std::remove_cv_t<T>>::value, required)\n>   \t{\n>   \t}\n>   \n> diff --git a/include/libcamera/ipa/ipa_controls.h b/include/libcamera/ipa/ipa_controls.h\n> index e5da1946ce1d..5268b0a8f659 100644\n> --- a/include/libcamera/ipa/ipa_controls.h\n> +++ b/include/libcamera/ipa/ipa_controls.h\n> @@ -46,7 +46,8 @@ struct ipa_control_info_entry {\n>   \tuint32_t id;\n>   \tuint32_t type;\n>   \tuint32_t offset;\n> -\tuint32_t padding[1];\n> +\tuint8_t required;\n> +\tuint8_t padding[3];\n>   };\n>   \n>   #ifdef __cplusplus\n> diff --git a/src/libcamera/control_serializer.cpp b/src/libcamera/control_serializer.cpp\n> index 0cf719bde798..b9ed1eea6552 100644\n> --- a/src/libcamera/control_serializer.cpp\n> +++ b/src/libcamera/control_serializer.cpp\n> @@ -281,6 +281,7 @@ int ControlSerializer::serialize(const ControlInfoMap &infoMap,\n>   \t\tentry.id = id->id();\n>   \t\tentry.type = id->type();\n>   \t\tentry.offset = values.offset();\n> +\t\tentry.required = id->required();\n>   \t\tentries.write(&entry);\n>   \n>   \t\tstore(info, values);\n> @@ -498,7 +499,8 @@ ControlInfoMap ControlSerializer::deserialize<ControlInfoMap>(ByteStreamBuffer &\n>   \t\t\t * debugging purpose.\n>   \t\t\t */\n>   \t\t\tcontrolIds_.emplace_back(std::make_unique<ControlId>(entry->id,\n> -\t\t\t\t\t\t\t\t\t     \"\", type));\n> +\t\t\t\t\t\t\t\t\t     \"\", type,\n> +\t\t\t\t\t\t\t\t\t     entry->required));\n>   \t\t\t(*localIdMap)[entry->id] = controlIds_.back().get();\n>   \t\t}\n>   \n> diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp\n> index 16d3547c8c07..2799d89869d7 100644\n> --- a/src/libcamera/controls.cpp\n> +++ b/src/libcamera/controls.cpp\n> @@ -378,18 +378,19 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen\n>    * \\class ControlId\n>    * \\brief Control static metadata\n>    *\n> - * The ControlId class stores a control ID, name and data type. It provides\n> - * unique identification of a control, but without support for compile-time\n> - * type deduction that the derived template Control class supports. See the\n> - * Control class for more information.\n> + * The ControlId class stores a control ID, name, data type and a boolean\n> + * 'required' flag. It provides unique identification of a control, but without\n> + * support for compile-time type deduction that the derived template Control\n> + * class supports. See the Control class for more information.\n>    */\n>   \n>   /**\n> - * \\fn ControlId::ControlId(unsigned int id, const std::string &name, ControlType type)\n> + * \\fn ControlId::ControlId(unsigned int id, const std::string &name, ControlType type, bool required)\n>    * \\brief Construct a ControlId instance\n>    * \\param[in] id The control numerical ID\n>    * \\param[in] name The control name\n>    * \\param[in] type The control data type\n> + * \\param[in] required Boolean flag that determine if the control is required\n>    */\n>   \n>   /**\n> @@ -410,6 +411,16 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen\n>    * \\return The control data type\n>    */\n>   \n> +/**\n> + * \\fn bool ControlId::required() const\n> + * \\brief Determine if the control is required or not\n> + *\n> + * A control is 'required' if it is mandatory for a Camera to support it to\n> + * comply with the libcamera API specification.\n> + *\n> + * \\return True if the control is required, false otherwise\n> + */\n> +\n>   /**\n>    * \\fn bool operator==(unsigned int lhs, const ControlId &rhs)\n>    * \\brief Compare a ControlId with a control numerical ID\n> @@ -456,10 +467,11 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen\n>    */\n>   \n>   /**\n> - * \\fn Control::Control(unsigned int id, const char *name)\n> + * \\fn Control::Control(unsigned int id, const char *name, bool required)\n>    * \\brief Construct a Control instance\n>    * \\param[in] id The control numerical ID\n>    * \\param[in] name The control name\n> + * \\param[in] required Boolean flag that determine if a control is required\n>    *\n>    * The control data type is automatically deduced from the template type T.\n>    */\n> diff --git a/src/libcamera/ipa_controls.cpp b/src/libcamera/ipa_controls.cpp\n> index 870a443b0f38..2376cbd9df60 100644\n> --- a/src/libcamera/ipa_controls.cpp\n> +++ b/src/libcamera/ipa_controls.cpp\n> @@ -220,6 +220,8 @@ static_assert(sizeof(ipa_control_value_entry) == 16,\n>    * \\var ipa_control_info_entry::offset\n>    * The offset in bytes from the beginning of the data section to the control\n>    * info data (shall be a multiple of 8 bytes)\n> + * \\var ipa_control_info_entry::required\n> + * Boolean flag that determines if the controls is required or not\n>    * \\var ipa_control_info_entry::padding\n>    * Padding bytes (shall be set to 0)\n>    */\n> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> index 24d208ef77dc..b055d14c2e51 100644\n> --- a/src/libcamera/v4l2_device.cpp\n> +++ b/src/libcamera/v4l2_device.cpp\n> @@ -522,7 +522,7 @@ std::unique_ptr<ControlId> V4L2Device::v4l2ControlId(const v4l2_query_ext_ctrl &\n>   \tconst std::string name(static_cast<const char *>(ctrl.name), len);\n>   \tconst ControlType type = v4l2CtrlType(ctrl.type);\n>   \n> -\treturn std::make_unique<ControlId>(ctrl.id, name, type);\n> +\treturn std::make_unique<ControlId>(ctrl.id, name, type, false);\n>   }\n>   \n>   /**\n> diff --git a/utils/gen-controls.py b/utils/gen-controls.py\n> index 6cd5e362c66f..1e997708b10d 100755\n> --- a/utils/gen-controls.py\n> +++ b/utils/gen-controls.py\n> @@ -112,6 +112,11 @@ class Control(object):\n>           else:\n>               return f\"Span<const {typ}>\"\n>   \n> +    @property\n> +    def required(self):\n> +        \"\"\"Is the control required\"\"\"\n> +        return self.__data.get('required')\n> +\n>   \n>   def snake_case(s):\n>       return ''.join([c.isupper() and ('_' + c) or c for c in s]).strip('_')\n> @@ -133,7 +138,7 @@ ${description}''')\n>    * \\\\var ${name}\n>   ${description}\n>    */''')\n> -    def_template = string.Template('extern const Control<${type}> ${name}(${id_name}, \"${name}\");')\n> +    def_template = string.Template('extern const Control<${type}> ${name}(${id_name}, \"${name}\", ${required});')\n>       enum_values_doc = string.Template('''/**\n>    * \\\\var ${name}Values\n>    * \\\\brief List of all $name supported values\n> @@ -158,6 +163,7 @@ ${description}\n>               'type': ctrl.type,\n>               'description': format_description(ctrl.description),\n>               'id_name': id_name,\n> +            'required': \"true\" if ctrl.required else \"false\"\n>           }\n>   \n>           target_doc = ctrls_doc[vendor]","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 8C2A3C3272\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 20 Mar 2024 15:30:17 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id BC69362834;\n\tWed, 20 Mar 2024 16:30:16 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 3722A62820\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 20 Mar 2024 16:30:15 +0100 (CET)","from [192.168.1.105] (unknown [103.86.18.138])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id C0A513F1;\n\tWed, 20 Mar 2024 16:29:46 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"ovxnSIqn\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1710948587;\n\tbh=VwG86v7x/dIB8fYUa5EwnDX5GTToYHmeKikHV534jrU=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=ovxnSIqngyyWWkgUhG57Nvu0pn1p1REFFwK4mjX1A5z+a6Wm982C1Er51kjb9W6a3\n\tW/6Q1wZDbOwC2ZtgsN0sk84C37cetJ7s2uPvzxkzwkMPE9Ymg7kHdu2GnRLn0cumT6\n\twlyCrjWb7FXnYvmoUCHVojlrmRCkcyaKcISapU8s=","Message-ID":"<97a4f26c-c3d2-4312-863b-298d07e14690@ideasonboard.com>","Date":"Wed, 20 Mar 2024 21:00:08 +0530","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 1/4] libcamera: Add 'required' property to controls","To":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20240320101652.12873-1-jacopo.mondi@ideasonboard.com>\n\t<20240320101652.12873-2-jacopo.mondi@ideasonboard.com>","Content-Language":"en-US","From":"Umang Jain <umang.jain@ideasonboard.com>","In-Reply-To":"<20240320101652.12873-2-jacopo.mondi@ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","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":34135,"web_url":"https://patchwork.libcamera.org/comment/34135/","msgid":"<aBty5vIak8D7Rb3j@pyrite.rasen.tech>","date":"2025-05-07T14:49:10","subject":"Re: [PATCH 1/4] libcamera: Add 'required' property to controls","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"On Wed, Mar 20, 2024 at 11:16:48AM +0100, Jacopo Mondi wrote:\n> Add to the ControlId class a 'required' boolean flag that determine\n> if the control (or property) is mandatory to be supported by a Camera\n> in order to comply with the libcamera API specification.\n> \n> Add support for a 'required' field to the controls and properties yaml\n> file definition and control generation scripts.\n> \n> Also plumb support for the flag in the control serializer component\n> to allow pass the information across IPC borders.\n> \n> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n\nPersonally I'm not feeling this patch. I don't really feel like\napplications care whether or not a control is required or not.\n\nHowever, it might be a good idea for applications to be able to query\nthe library for what controls are required, but I think it's a\nlibrary-wide thing, as opposed to a flag that belongs to each control.\nAt the moment we get it from documentation (is it even documented?) but\nperhaps in the future the list might be extended, in which case being\nable to query the library is useful.\n\nThe list could also be used for the test in the next patch.\n\n\nPaul\n\n> ---\n>  Documentation/guides/pipeline-handler.rst | 13 ++++++++----\n>  include/libcamera/controls.h              | 10 ++++++----\n>  include/libcamera/ipa/ipa_controls.h      |  3 ++-\n>  src/libcamera/control_serializer.cpp      |  4 +++-\n>  src/libcamera/controls.cpp                | 24 +++++++++++++++++------\n>  src/libcamera/ipa_controls.cpp            |  2 ++\n>  src/libcamera/v4l2_device.cpp             |  2 +-\n>  utils/gen-controls.py                     |  8 +++++++-\n>  8 files changed, 48 insertions(+), 18 deletions(-)\n> \n> diff --git a/Documentation/guides/pipeline-handler.rst b/Documentation/guides/pipeline-handler.rst\n> index 728e9676b4a6..b0d77d795211 100644\n> --- a/Documentation/guides/pipeline-handler.rst\n> +++ b/Documentation/guides/pipeline-handler.rst\n> @@ -610,10 +610,15 @@ information can be found in the `ControlInfoMap`_ class documentation.\n>  .. _ControlInfoMap: https://libcamera.org/api-html/classlibcamera_1_1ControlInfoMap.html\n>  \n>  Pipeline handlers register controls to expose the tunable device and IPA\n> -parameters to applications. Our example pipeline handler only exposes trivial\n> -controls of the video device, by registering a ``ControlId`` instance with\n> -associated values for each supported V4L2 control but demonstrates the mapping\n> -of V4L2 Controls to libcamera ControlIDs.\n> +parameters to applications and register properties to expose the Camera\n> +immutable characteristics. Controls and properties which have the ``required``\n> +field specified in the YAML definition are mandatory to be supported by a Camera\n> +in order for it to comply with the libcamera API specification.\n> +\n> +Our example pipeline handler only exposes trivial controls of the video device,\n> +by registering a ``ControlId`` instance with associated values for each\n> +supported V4L2 control but demonstrates the mapping of V4L2 Controls to\n> +libcamera ControlIDs.\n>  \n>  Complete the initialization of the ``VividCameraData`` class by adding the\n>  following code to the ``VividCameraData::init()`` function to initialise the\n> diff --git a/include/libcamera/controls.h b/include/libcamera/controls.h\n> index 82b955995380..0382a99de7fa 100644\n> --- a/include/libcamera/controls.h\n> +++ b/include/libcamera/controls.h\n> @@ -213,14 +213,15 @@ private:\n>  class ControlId\n>  {\n>  public:\n> -\tControlId(unsigned int id, const std::string &name, ControlType type)\n> -\t\t: id_(id), name_(name), type_(type)\n> +\tControlId(unsigned int id, const std::string &name, ControlType type, bool required)\n> +\t\t: id_(id), name_(name), type_(type), required_(required)\n>  \t{\n>  \t}\n>  \n>  \tunsigned int id() const { return id_; }\n>  \tconst std::string &name() const { return name_; }\n>  \tControlType type() const { return type_; }\n> +\tbool required() const { return required_; }\n>  \n>  private:\n>  \tLIBCAMERA_DISABLE_COPY_AND_MOVE(ControlId)\n> @@ -228,6 +229,7 @@ private:\n>  \tunsigned int id_;\n>  \tstd::string name_;\n>  \tControlType type_;\n> +\tbool required_;\n>  };\n>  \n>  static inline bool operator==(unsigned int lhs, const ControlId &rhs)\n> @@ -256,8 +258,8 @@ class Control : public ControlId\n>  public:\n>  \tusing type = T;\n>  \n> -\tControl(unsigned int id, const char *name)\n> -\t\t: ControlId(id, name, details::control_type<std::remove_cv_t<T>>::value)\n> +\tControl(unsigned int id, const char *name, bool required)\n> +\t\t: ControlId(id, name, details::control_type<std::remove_cv_t<T>>::value, required)\n>  \t{\n>  \t}\n>  \n> diff --git a/include/libcamera/ipa/ipa_controls.h b/include/libcamera/ipa/ipa_controls.h\n> index e5da1946ce1d..5268b0a8f659 100644\n> --- a/include/libcamera/ipa/ipa_controls.h\n> +++ b/include/libcamera/ipa/ipa_controls.h\n> @@ -46,7 +46,8 @@ struct ipa_control_info_entry {\n>  \tuint32_t id;\n>  \tuint32_t type;\n>  \tuint32_t offset;\n> -\tuint32_t padding[1];\n> +\tuint8_t required;\n> +\tuint8_t padding[3];\n>  };\n>  \n>  #ifdef __cplusplus\n> diff --git a/src/libcamera/control_serializer.cpp b/src/libcamera/control_serializer.cpp\n> index 0cf719bde798..b9ed1eea6552 100644\n> --- a/src/libcamera/control_serializer.cpp\n> +++ b/src/libcamera/control_serializer.cpp\n> @@ -281,6 +281,7 @@ int ControlSerializer::serialize(const ControlInfoMap &infoMap,\n>  \t\tentry.id = id->id();\n>  \t\tentry.type = id->type();\n>  \t\tentry.offset = values.offset();\n> +\t\tentry.required = id->required();\n>  \t\tentries.write(&entry);\n>  \n>  \t\tstore(info, values);\n> @@ -498,7 +499,8 @@ ControlInfoMap ControlSerializer::deserialize<ControlInfoMap>(ByteStreamBuffer &\n>  \t\t\t * debugging purpose.\n>  \t\t\t */\n>  \t\t\tcontrolIds_.emplace_back(std::make_unique<ControlId>(entry->id,\n> -\t\t\t\t\t\t\t\t\t     \"\", type));\n> +\t\t\t\t\t\t\t\t\t     \"\", type,\n> +\t\t\t\t\t\t\t\t\t     entry->required));\n>  \t\t\t(*localIdMap)[entry->id] = controlIds_.back().get();\n>  \t\t}\n>  \n> diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp\n> index 16d3547c8c07..2799d89869d7 100644\n> --- a/src/libcamera/controls.cpp\n> +++ b/src/libcamera/controls.cpp\n> @@ -378,18 +378,19 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen\n>   * \\class ControlId\n>   * \\brief Control static metadata\n>   *\n> - * The ControlId class stores a control ID, name and data type. It provides\n> - * unique identification of a control, but without support for compile-time\n> - * type deduction that the derived template Control class supports. See the\n> - * Control class for more information.\n> + * The ControlId class stores a control ID, name, data type and a boolean\n> + * 'required' flag. It provides unique identification of a control, but without\n> + * support for compile-time type deduction that the derived template Control\n> + * class supports. See the Control class for more information.\n>   */\n>  \n>  /**\n> - * \\fn ControlId::ControlId(unsigned int id, const std::string &name, ControlType type)\n> + * \\fn ControlId::ControlId(unsigned int id, const std::string &name, ControlType type, bool required)\n>   * \\brief Construct a ControlId instance\n>   * \\param[in] id The control numerical ID\n>   * \\param[in] name The control name\n>   * \\param[in] type The control data type\n> + * \\param[in] required Boolean flag that determine if the control is required\n>   */\n>  \n>  /**\n> @@ -410,6 +411,16 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen\n>   * \\return The control data type\n>   */\n>  \n> +/**\n> + * \\fn bool ControlId::required() const\n> + * \\brief Determine if the control is required or not\n> + *\n> + * A control is 'required' if it is mandatory for a Camera to support it to\n> + * comply with the libcamera API specification.\n> + *\n> + * \\return True if the control is required, false otherwise\n> + */\n> +\n>  /**\n>   * \\fn bool operator==(unsigned int lhs, const ControlId &rhs)\n>   * \\brief Compare a ControlId with a control numerical ID\n> @@ -456,10 +467,11 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen\n>   */\n>  \n>  /**\n> - * \\fn Control::Control(unsigned int id, const char *name)\n> + * \\fn Control::Control(unsigned int id, const char *name, bool required)\n>   * \\brief Construct a Control instance\n>   * \\param[in] id The control numerical ID\n>   * \\param[in] name The control name\n> + * \\param[in] required Boolean flag that determine if a control is required\n>   *\n>   * The control data type is automatically deduced from the template type T.\n>   */\n> diff --git a/src/libcamera/ipa_controls.cpp b/src/libcamera/ipa_controls.cpp\n> index 870a443b0f38..2376cbd9df60 100644\n> --- a/src/libcamera/ipa_controls.cpp\n> +++ b/src/libcamera/ipa_controls.cpp\n> @@ -220,6 +220,8 @@ static_assert(sizeof(ipa_control_value_entry) == 16,\n>   * \\var ipa_control_info_entry::offset\n>   * The offset in bytes from the beginning of the data section to the control\n>   * info data (shall be a multiple of 8 bytes)\n> + * \\var ipa_control_info_entry::required\n> + * Boolean flag that determines if the controls is required or not\n>   * \\var ipa_control_info_entry::padding\n>   * Padding bytes (shall be set to 0)\n>   */\n> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> index 24d208ef77dc..b055d14c2e51 100644\n> --- a/src/libcamera/v4l2_device.cpp\n> +++ b/src/libcamera/v4l2_device.cpp\n> @@ -522,7 +522,7 @@ std::unique_ptr<ControlId> V4L2Device::v4l2ControlId(const v4l2_query_ext_ctrl &\n>  \tconst std::string name(static_cast<const char *>(ctrl.name), len);\n>  \tconst ControlType type = v4l2CtrlType(ctrl.type);\n>  \n> -\treturn std::make_unique<ControlId>(ctrl.id, name, type);\n> +\treturn std::make_unique<ControlId>(ctrl.id, name, type, false);\n>  }\n>  \n>  /**\n> diff --git a/utils/gen-controls.py b/utils/gen-controls.py\n> index 6cd5e362c66f..1e997708b10d 100755\n> --- a/utils/gen-controls.py\n> +++ b/utils/gen-controls.py\n> @@ -112,6 +112,11 @@ class Control(object):\n>          else:\n>              return f\"Span<const {typ}>\"\n>  \n> +    @property\n> +    def required(self):\n> +        \"\"\"Is the control required\"\"\"\n> +        return self.__data.get('required')\n> +\n>  \n>  def snake_case(s):\n>      return ''.join([c.isupper() and ('_' + c) or c for c in s]).strip('_')\n> @@ -133,7 +138,7 @@ ${description}''')\n>   * \\\\var ${name}\n>  ${description}\n>   */''')\n> -    def_template = string.Template('extern const Control<${type}> ${name}(${id_name}, \"${name}\");')\n> +    def_template = string.Template('extern const Control<${type}> ${name}(${id_name}, \"${name}\", ${required});')\n>      enum_values_doc = string.Template('''/**\n>   * \\\\var ${name}Values\n>   * \\\\brief List of all $name supported values\n> @@ -158,6 +163,7 @@ ${description}\n>              'type': ctrl.type,\n>              'description': format_description(ctrl.description),\n>              'id_name': id_name,\n> +            'required': \"true\" if ctrl.required else \"false\"\n>          }\n>  \n>          target_doc = ctrls_doc[vendor]\n> -- \n> 2.44.0\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 89F6AC3226\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  7 May 2025 14:49:16 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 95F5368ADE;\n\tWed,  7 May 2025 16:49:15 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4289968AD8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  7 May 2025 16:49:14 +0200 (CEST)","from pyrite.rasen.tech (unknown\n\t[IPv6:2001:861:3a80:3300:4f2f:8c2c:b3ef:17d4])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 94C73E92;\n\tWed,  7 May 2025 16:49:02 +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=\"c3rLXnlP\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1746629342;\n\tbh=NVv8de2cnz8bERsdDelZVN2K8hqWq2rCHagLta681YA=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=c3rLXnlP1m66MGIbAwe/Qrxqc0tbNWyijfeXO039vnTwmbLPIe+5IwcJaDyTQowAJ\n\tCoFZQLnn9F0M8+KT8vevvyhSDfcQW3eeJ9ICV49M4hmsPX5WTu0N+2djcttnl/YRF3\n\t+mJEqenihspVeECQ5JfP4hz0JvWy/vOHNRwovIQQ=","Date":"Wed, 7 May 2025 16:49:10 +0200","From":"Paul Elder <paul.elder@ideasonboard.com>","To":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH 1/4] libcamera: Add 'required' property to controls","Message-ID":"<aBty5vIak8D7Rb3j@pyrite.rasen.tech>","References":"<20240320101652.12873-1-jacopo.mondi@ideasonboard.com>\n\t<20240320101652.12873-2-jacopo.mondi@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=us-ascii","Content-Disposition":"inline","In-Reply-To":"<20240320101652.12873-2-jacopo.mondi@ideasonboard.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":34136,"web_url":"https://patchwork.libcamera.org/comment/34136/","msgid":"<aBt01awE0A7rOkde@pyrite.rasen.tech>","date":"2025-05-07T14:57:25","subject":"Re: [PATCH 1/4] libcamera: Add 'required' property to controls","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"On Wed, May 07, 2025 at 04:49:10PM +0200, Paul Elder wrote:\n> On Wed, Mar 20, 2024 at 11:16:48AM +0100, Jacopo Mondi wrote:\n> > Add to the ControlId class a 'required' boolean flag that determine\n> > if the control (or property) is mandatory to be supported by a Camera\n> > in order to comply with the libcamera API specification.\n> > \n> > Add support for a 'required' field to the controls and properties yaml\n> > file definition and control generation scripts.\n> > \n> > Also plumb support for the flag in the control serializer component\n> > to allow pass the information across IPC borders.\n> > \n> > Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n> \n> Personally I'm not feeling this patch. I don't really feel like\n> applications care whether or not a control is required or not.\n> \n> However, it might be a good idea for applications to be able to query\n> the library for what controls are required, but I think it's a\n> library-wide thing, as opposed to a flag that belongs to each control.\n> At the moment we get it from documentation (is it even documented?) but\n> perhaps in the future the list might be extended, in which case being\n> able to query the library is useful.\n\nOn second thought, we don't really have a better other place to put this\nso ig it's fine.\n\nAlso I just realized this is libcamera controls and not v4l2 controls. I\nthink this needs to be supplemented in documentation, though (unless\nit's already around and I missed it).\n\nReviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n\n> \n> The list could also be used for the test in the next patch.\n> \n> \n> Paul\n> \n> > ---\n> >  Documentation/guides/pipeline-handler.rst | 13 ++++++++----\n> >  include/libcamera/controls.h              | 10 ++++++----\n> >  include/libcamera/ipa/ipa_controls.h      |  3 ++-\n> >  src/libcamera/control_serializer.cpp      |  4 +++-\n> >  src/libcamera/controls.cpp                | 24 +++++++++++++++++------\n> >  src/libcamera/ipa_controls.cpp            |  2 ++\n> >  src/libcamera/v4l2_device.cpp             |  2 +-\n> >  utils/gen-controls.py                     |  8 +++++++-\n> >  8 files changed, 48 insertions(+), 18 deletions(-)\n> > \n> > diff --git a/Documentation/guides/pipeline-handler.rst b/Documentation/guides/pipeline-handler.rst\n> > index 728e9676b4a6..b0d77d795211 100644\n> > --- a/Documentation/guides/pipeline-handler.rst\n> > +++ b/Documentation/guides/pipeline-handler.rst\n> > @@ -610,10 +610,15 @@ information can be found in the `ControlInfoMap`_ class documentation.\n> >  .. _ControlInfoMap: https://libcamera.org/api-html/classlibcamera_1_1ControlInfoMap.html\n> >  \n> >  Pipeline handlers register controls to expose the tunable device and IPA\n> > -parameters to applications. Our example pipeline handler only exposes trivial\n> > -controls of the video device, by registering a ``ControlId`` instance with\n> > -associated values for each supported V4L2 control but demonstrates the mapping\n> > -of V4L2 Controls to libcamera ControlIDs.\n> > +parameters to applications and register properties to expose the Camera\n> > +immutable characteristics. Controls and properties which have the ``required``\n> > +field specified in the YAML definition are mandatory to be supported by a Camera\n> > +in order for it to comply with the libcamera API specification.\n> > +\n> > +Our example pipeline handler only exposes trivial controls of the video device,\n> > +by registering a ``ControlId`` instance with associated values for each\n> > +supported V4L2 control but demonstrates the mapping of V4L2 Controls to\n> > +libcamera ControlIDs.\n> >  \n> >  Complete the initialization of the ``VividCameraData`` class by adding the\n> >  following code to the ``VividCameraData::init()`` function to initialise the\n> > diff --git a/include/libcamera/controls.h b/include/libcamera/controls.h\n> > index 82b955995380..0382a99de7fa 100644\n> > --- a/include/libcamera/controls.h\n> > +++ b/include/libcamera/controls.h\n> > @@ -213,14 +213,15 @@ private:\n> >  class ControlId\n> >  {\n> >  public:\n> > -\tControlId(unsigned int id, const std::string &name, ControlType type)\n> > -\t\t: id_(id), name_(name), type_(type)\n> > +\tControlId(unsigned int id, const std::string &name, ControlType type, bool required)\n> > +\t\t: id_(id), name_(name), type_(type), required_(required)\n> >  \t{\n> >  \t}\n> >  \n> >  \tunsigned int id() const { return id_; }\n> >  \tconst std::string &name() const { return name_; }\n> >  \tControlType type() const { return type_; }\n> > +\tbool required() const { return required_; }\n> >  \n> >  private:\n> >  \tLIBCAMERA_DISABLE_COPY_AND_MOVE(ControlId)\n> > @@ -228,6 +229,7 @@ private:\n> >  \tunsigned int id_;\n> >  \tstd::string name_;\n> >  \tControlType type_;\n> > +\tbool required_;\n> >  };\n> >  \n> >  static inline bool operator==(unsigned int lhs, const ControlId &rhs)\n> > @@ -256,8 +258,8 @@ class Control : public ControlId\n> >  public:\n> >  \tusing type = T;\n> >  \n> > -\tControl(unsigned int id, const char *name)\n> > -\t\t: ControlId(id, name, details::control_type<std::remove_cv_t<T>>::value)\n> > +\tControl(unsigned int id, const char *name, bool required)\n> > +\t\t: ControlId(id, name, details::control_type<std::remove_cv_t<T>>::value, required)\n> >  \t{\n> >  \t}\n> >  \n> > diff --git a/include/libcamera/ipa/ipa_controls.h b/include/libcamera/ipa/ipa_controls.h\n> > index e5da1946ce1d..5268b0a8f659 100644\n> > --- a/include/libcamera/ipa/ipa_controls.h\n> > +++ b/include/libcamera/ipa/ipa_controls.h\n> > @@ -46,7 +46,8 @@ struct ipa_control_info_entry {\n> >  \tuint32_t id;\n> >  \tuint32_t type;\n> >  \tuint32_t offset;\n> > -\tuint32_t padding[1];\n> > +\tuint8_t required;\n> > +\tuint8_t padding[3];\n> >  };\n> >  \n> >  #ifdef __cplusplus\n> > diff --git a/src/libcamera/control_serializer.cpp b/src/libcamera/control_serializer.cpp\n> > index 0cf719bde798..b9ed1eea6552 100644\n> > --- a/src/libcamera/control_serializer.cpp\n> > +++ b/src/libcamera/control_serializer.cpp\n> > @@ -281,6 +281,7 @@ int ControlSerializer::serialize(const ControlInfoMap &infoMap,\n> >  \t\tentry.id = id->id();\n> >  \t\tentry.type = id->type();\n> >  \t\tentry.offset = values.offset();\n> > +\t\tentry.required = id->required();\n> >  \t\tentries.write(&entry);\n> >  \n> >  \t\tstore(info, values);\n> > @@ -498,7 +499,8 @@ ControlInfoMap ControlSerializer::deserialize<ControlInfoMap>(ByteStreamBuffer &\n> >  \t\t\t * debugging purpose.\n> >  \t\t\t */\n> >  \t\t\tcontrolIds_.emplace_back(std::make_unique<ControlId>(entry->id,\n> > -\t\t\t\t\t\t\t\t\t     \"\", type));\n> > +\t\t\t\t\t\t\t\t\t     \"\", type,\n> > +\t\t\t\t\t\t\t\t\t     entry->required));\n> >  \t\t\t(*localIdMap)[entry->id] = controlIds_.back().get();\n> >  \t\t}\n> >  \n> > diff --git a/src/libcamera/controls.cpp b/src/libcamera/controls.cpp\n> > index 16d3547c8c07..2799d89869d7 100644\n> > --- a/src/libcamera/controls.cpp\n> > +++ b/src/libcamera/controls.cpp\n> > @@ -378,18 +378,19 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen\n> >   * \\class ControlId\n> >   * \\brief Control static metadata\n> >   *\n> > - * The ControlId class stores a control ID, name and data type. It provides\n> > - * unique identification of a control, but without support for compile-time\n> > - * type deduction that the derived template Control class supports. See the\n> > - * Control class for more information.\n> > + * The ControlId class stores a control ID, name, data type and a boolean\n> > + * 'required' flag. It provides unique identification of a control, but without\n> > + * support for compile-time type deduction that the derived template Control\n> > + * class supports. See the Control class for more information.\n> >   */\n> >  \n> >  /**\n> > - * \\fn ControlId::ControlId(unsigned int id, const std::string &name, ControlType type)\n> > + * \\fn ControlId::ControlId(unsigned int id, const std::string &name, ControlType type, bool required)\n> >   * \\brief Construct a ControlId instance\n> >   * \\param[in] id The control numerical ID\n> >   * \\param[in] name The control name\n> >   * \\param[in] type The control data type\n> > + * \\param[in] required Boolean flag that determine if the control is required\n> >   */\n> >  \n> >  /**\n> > @@ -410,6 +411,16 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen\n> >   * \\return The control data type\n> >   */\n> >  \n> > +/**\n> > + * \\fn bool ControlId::required() const\n> > + * \\brief Determine if the control is required or not\n> > + *\n> > + * A control is 'required' if it is mandatory for a Camera to support it to\n> > + * comply with the libcamera API specification.\n> > + *\n> > + * \\return True if the control is required, false otherwise\n> > + */\n> > +\n> >  /**\n> >   * \\fn bool operator==(unsigned int lhs, const ControlId &rhs)\n> >   * \\brief Compare a ControlId with a control numerical ID\n> > @@ -456,10 +467,11 @@ void ControlValue::reserve(ControlType type, bool isArray, std::size_t numElemen\n> >   */\n> >  \n> >  /**\n> > - * \\fn Control::Control(unsigned int id, const char *name)\n> > + * \\fn Control::Control(unsigned int id, const char *name, bool required)\n> >   * \\brief Construct a Control instance\n> >   * \\param[in] id The control numerical ID\n> >   * \\param[in] name The control name\n> > + * \\param[in] required Boolean flag that determine if a control is required\n> >   *\n> >   * The control data type is automatically deduced from the template type T.\n> >   */\n> > diff --git a/src/libcamera/ipa_controls.cpp b/src/libcamera/ipa_controls.cpp\n> > index 870a443b0f38..2376cbd9df60 100644\n> > --- a/src/libcamera/ipa_controls.cpp\n> > +++ b/src/libcamera/ipa_controls.cpp\n> > @@ -220,6 +220,8 @@ static_assert(sizeof(ipa_control_value_entry) == 16,\n> >   * \\var ipa_control_info_entry::offset\n> >   * The offset in bytes from the beginning of the data section to the control\n> >   * info data (shall be a multiple of 8 bytes)\n> > + * \\var ipa_control_info_entry::required\n> > + * Boolean flag that determines if the controls is required or not\n> >   * \\var ipa_control_info_entry::padding\n> >   * Padding bytes (shall be set to 0)\n> >   */\n> > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp\n> > index 24d208ef77dc..b055d14c2e51 100644\n> > --- a/src/libcamera/v4l2_device.cpp\n> > +++ b/src/libcamera/v4l2_device.cpp\n> > @@ -522,7 +522,7 @@ std::unique_ptr<ControlId> V4L2Device::v4l2ControlId(const v4l2_query_ext_ctrl &\n> >  \tconst std::string name(static_cast<const char *>(ctrl.name), len);\n> >  \tconst ControlType type = v4l2CtrlType(ctrl.type);\n> >  \n> > -\treturn std::make_unique<ControlId>(ctrl.id, name, type);\n> > +\treturn std::make_unique<ControlId>(ctrl.id, name, type, false);\n> >  }\n> >  \n> >  /**\n> > diff --git a/utils/gen-controls.py b/utils/gen-controls.py\n> > index 6cd5e362c66f..1e997708b10d 100755\n> > --- a/utils/gen-controls.py\n> > +++ b/utils/gen-controls.py\n> > @@ -112,6 +112,11 @@ class Control(object):\n> >          else:\n> >              return f\"Span<const {typ}>\"\n> >  \n> > +    @property\n> > +    def required(self):\n> > +        \"\"\"Is the control required\"\"\"\n> > +        return self.__data.get('required')\n> > +\n> >  \n> >  def snake_case(s):\n> >      return ''.join([c.isupper() and ('_' + c) or c for c in s]).strip('_')\n> > @@ -133,7 +138,7 @@ ${description}''')\n> >   * \\\\var ${name}\n> >  ${description}\n> >   */''')\n> > -    def_template = string.Template('extern const Control<${type}> ${name}(${id_name}, \"${name}\");')\n> > +    def_template = string.Template('extern const Control<${type}> ${name}(${id_name}, \"${name}\", ${required});')\n> >      enum_values_doc = string.Template('''/**\n> >   * \\\\var ${name}Values\n> >   * \\\\brief List of all $name supported values\n> > @@ -158,6 +163,7 @@ ${description}\n> >              'type': ctrl.type,\n> >              'description': format_description(ctrl.description),\n> >              'id_name': id_name,\n> > +            'required': \"true\" if ctrl.required else \"false\"\n> >          }\n> >  \n> >          target_doc = ctrls_doc[vendor]\n> > -- \n> > 2.44.0\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 93CF0C3200\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  7 May 2025 14:57:31 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DAF0468AD8;\n\tWed,  7 May 2025 16:57:30 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9996368AD8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  7 May 2025 16:57:29 +0200 (CEST)","from pyrite.rasen.tech (unknown\n\t[IPv6:2001:861:3a80:3300:4f2f:8c2c:b3ef:17d4])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 032A5E92;\n\tWed,  7 May 2025 16:57:17 +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=\"ZSl3WXyw\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1746629838;\n\tbh=S68blA64opWo78nzJinEEAvH4wFlsYMl7zXDWB6x6hA=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=ZSl3WXywNkTsdX++XvQlhV1f33eN1WUEW51vJ8WzIgmhMVszKnCz55U+en1kMzb1a\n\teAlRSLxv6VBnJzHV7d121M8eTJs4c0v9XolsdsanrzU6pCJyCKpD+NBwxhB70vF8IF\n\tcDtbgyJcUncm1WikzDH8Un5G01kfxrIen4LXPW5w=","Date":"Wed, 7 May 2025 16:57:25 +0200","From":"Paul Elder <paul.elder@ideasonboard.com>","To":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH 1/4] libcamera: Add 'required' property to controls","Message-ID":"<aBt01awE0A7rOkde@pyrite.rasen.tech>","References":"<20240320101652.12873-1-jacopo.mondi@ideasonboard.com>\n\t<20240320101652.12873-2-jacopo.mondi@ideasonboard.com>\n\t<aBty5vIak8D7Rb3j@pyrite.rasen.tech>","MIME-Version":"1.0","Content-Type":"text/plain; charset=us-ascii","Content-Disposition":"inline","In-Reply-To":"<aBty5vIak8D7Rb3j@pyrite.rasen.tech>","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>"}}]