[{"id":35936,"web_url":"https://patchwork.libcamera.org/comment/35936/","msgid":"<20250921042702.GD30137@pendragon.ideasonboard.com>","date":"2025-09-21T04:27:02","subject":"Re: [PATCH v18 05/12] ipa: Look up IPA configurables in\n\tconfiguration file","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Milan,\n\nOn Fri, Sep 12, 2025 at 04:29:06PM +0200, Milan Zamazal wrote:\n> This patch adds configuration options for environment variables used in\n> the IPA proxy.\n> \n> The configuration snippet:\n> \n>   configuration:\n>     ipa:\n>       config_paths:\n>       - config path 1\n>       - config path 2\n>       - ...\n\nThe list should be indented by one more level:\n\n       config_paths:\n         - config path 1\n         - config path 2\n         - ...\n\nand now that I wrote this, I started wondering why both options work,\nand which one is preferred. The YAML spec makes the indentation option,\nor rather indicates that \"both the “-” indicator and the following\nspaces are considered to be part of the indentation\". As we use explicit\nindentation in the existing yaml files (with the exception of the\nvirtual pipeline handler configuration files it seems), I'd like to\nstandardize on that.\n\nI'll update the commit message.\n\n>       module_paths:\n>       - module path 1\n>       - module path 2\n>       - ...\n>       proxy_paths:\n>       - proxy path 1\n>       - proxy path 2\n>       - ...\n>       force_isolation: BOOL\n> \n> LIBCAMERA_<IPA_NAME>_TUNING_FILE remains configurable only via the\n> environment variable; this is supposed to be used only for testing and\n> debugging and it's not clear what to do about IPA names like \"rpi/vc4\"\n> and \"rpi/pisp\" exactly.\n> \n> There are two ways to pass the configuration to the places where it is\n> needed: Either to pass it as an argument to the method calls that need\n> it, or to pass it to the class constructors and extract the needed\n> configuration from there.  This patch uses the second method as it is\n> less polluting the code.\n> \n> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n> ---\n>  include/libcamera/internal/ipa_manager.h      |  7 ++-\n>  include/libcamera/internal/ipa_proxy.h        |  8 ++-\n>  src/libcamera/camera_manager.cpp              |  2 +-\n>  src/libcamera/ipa_manager.cpp                 | 39 ++++++++-----\n>  src/libcamera/ipa_proxy.cpp                   | 58 +++++++++----------\n>  .../module_ipa_proxy.cpp.tmpl                 |  4 +-\n>  .../module_ipa_proxy.h.tmpl                   |  2 +-\n>  7 files changed, 68 insertions(+), 52 deletions(-)\n> \n> diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h\n> index a0d448cf9..b0b44c74a 100644\n> --- a/include/libcamera/internal/ipa_manager.h\n> +++ b/include/libcamera/internal/ipa_manager.h\n> @@ -17,6 +17,7 @@\n>  #include <libcamera/ipa/ipa_module_info.h>\n>  \n>  #include \"libcamera/internal/camera_manager.h\"\n> +#include \"libcamera/internal/global_configuration.h\"\n>  #include \"libcamera/internal/ipa_module.h\"\n>  #include \"libcamera/internal/pipeline_handler.h\"\n>  #include \"libcamera/internal/pub_key.h\"\n> @@ -28,7 +29,7 @@ LOG_DECLARE_CATEGORY(IPAManager)\n>  class IPAManager\n>  {\n>  public:\n> -\tIPAManager();\n> +\tIPAManager(const GlobalConfiguration &configuration);\n\nI wonder if we shouldn't pass the camera manager instance instead. The\nrationale is that the camera manager is considered to be the top-level\ncontext in libcamera, so it could be useful to access it in the\nIPAManager (and elsewhere) for other purposes too. I may experiment with\nthat on top.\n\n>  \t~IPAManager();\n>  \n>  \ttemplate<typename T>\n> @@ -42,7 +43,8 @@ public:\n>  \t\tif (!m)\n>  \t\t\treturn nullptr;\n>  \n> -\t\tstd::unique_ptr<T> proxy = std::make_unique<T>(m, !self->isSignatureValid(m));\n> +\t\tconst GlobalConfiguration &configuration = cm->_d()->configuration();\n> +\t\tstd::unique_ptr<T> proxy = std::make_unique<T>(m, !self->isSignatureValid(m), configuration);\n>  \t\tif (!proxy->isValid()) {\n>  \t\t\tLOG(IPAManager, Error) << \"Failed to load proxy\";\n>  \t\t\treturn nullptr;\n> @@ -73,6 +75,7 @@ private:\n>  #if HAVE_IPA_PUBKEY\n>  \tstatic const uint8_t publicKeyData_[];\n>  \tstatic const PubKey pubKey_;\n> +\tbool forceIsolation_;\n>  #endif\n>  };\n>  \n> diff --git a/include/libcamera/internal/ipa_proxy.h b/include/libcamera/internal/ipa_proxy.h\n> index 983bcc5fa..f1865d67e 100644\n> --- a/include/libcamera/internal/ipa_proxy.h\n> +++ b/include/libcamera/internal/ipa_proxy.h\n> @@ -7,10 +7,14 @@\n>  \n>  #pragma once\n>  \n> +#include <optional>\n>  #include <string>\n> +#include <vector>\n>  \n>  #include <libcamera/ipa/ipa_interface.h>\n>  \n> +#include \"libcamera/internal/global_configuration.h\"\n> +\n>  namespace libcamera {\n>  \n>  class IPAModule;\n> @@ -24,7 +28,7 @@ public:\n>  \t\tProxyRunning,\n>  \t};\n>  \n> -\tIPAProxy(IPAModule *ipam);\n> +\tIPAProxy(IPAModule *ipam, const GlobalConfiguration &configuration);\n>  \t~IPAProxy();\n>  \n>  \tbool isValid() const { return valid_; }\n> @@ -40,6 +44,8 @@ protected:\n>  \n>  private:\n>  \tIPAModule *ipam_;\n> +\tstd::vector<std::string> configPaths_;\n> +\tstd::vector<std::string> execPaths_;\n>  };\n>  \n>  } /* namespace libcamera */\n> diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp\n> index dca3d9a83..64df62444 100644\n> --- a/src/libcamera/camera_manager.cpp\n> +++ b/src/libcamera/camera_manager.cpp\n> @@ -41,7 +41,7 @@ LOG_DEFINE_CATEGORY(Camera)\n>  CameraManager::Private::Private()\n>  \t: initialized_(false)\n>  {\n> -\tipaManager_ = std::make_unique<IPAManager>();\n> +\tipaManager_ = std::make_unique<IPAManager>(this->configuration());\n>  }\n>  \n>  int CameraManager::Private::start()\n> diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp\n> index 830750dcc..e5172f6ce 100644\n> --- a/src/libcamera/ipa_manager.cpp\n> +++ b/src/libcamera/ipa_manager.cpp\n> @@ -10,12 +10,15 @@\n>  #include <algorithm>\n>  #include <dirent.h>\n>  #include <string.h>\n> +#include <string>\n>  #include <sys/types.h>\n> +#include <vector>\n>  \n>  #include <libcamera/base/file.h>\n>  #include <libcamera/base/log.h>\n>  #include <libcamera/base/utils.h>\n>  \n> +#include \"libcamera/internal/global_configuration.h\"\n>  #include \"libcamera/internal/ipa_module.h\"\n>  #include \"libcamera/internal/ipa_proxy.h\"\n>  #include \"libcamera/internal/pipeline_handler.h\"\n> @@ -101,30 +104,37 @@ LOG_DEFINE_CATEGORY(IPAManager)\n>   * The IPAManager class is meant to only be instantiated once, by the\n>   * CameraManager.\n>   */\n> -IPAManager::IPAManager()\n> +IPAManager::IPAManager(const GlobalConfiguration &configuration)\n>  {\n>  #if HAVE_IPA_PUBKEY\n>  \tif (!pubKey_.isValid())\n>  \t\tLOG(IPAManager, Warning) << \"Public key not valid\";\n> +\n> +\tchar *force = utils::secure_getenv(\"LIBCAMERA_IPA_FORCE_ISOLATION\");\n> +\tforceIsolation_ = (force && force[0] != '\\0') ||\n> +\t\t\t  (!force && configuration.option<bool>({ \"ipa\", \"force_isolation\" })\n> +\t\t\t\t\t     .value_or(false));\n>  #endif\n>  \n>  \tunsigned int ipaCount = 0;\n>  \n>  \t/* User-specified paths take precedence. */\n> -\tconst char *modulePaths = utils::secure_getenv(\"LIBCAMERA_IPA_MODULE_PATH\");\n> -\tif (modulePaths) {\n> -\t\tfor (const auto &dir : utils::split(modulePaths, \":\")) {\n> -\t\t\tif (dir.empty())\n> -\t\t\t\tcontinue;\n> -\n> -\t\t\tipaCount += addDir(dir.c_str());\n> -\t\t}\n> +\tconst auto modulePaths =\n> +\t\tconfiguration.envListOption(\n> +\t\t\t\t     \"LIBCAMERA_IPA_MODULE_PATH\", { \"ipa\", \"module_paths\" })\n> +\t\t\t.value_or(std::vector<std::string>());\n> +\tfor (const auto &dir : modulePaths) {\n> +\t\tif (dir.empty())\n> +\t\t\tcontinue;\n>  \n> -\t\tif (!ipaCount)\n> -\t\t\tLOG(IPAManager, Warning)\n> -\t\t\t\t<< \"No IPA found in '\" << modulePaths << \"'\";\n> +\t\tipaCount += addDir(dir.c_str());\n>  \t}\n>  \n> +\tif (!ipaCount)\n> +\t\tLOG(IPAManager, Warning) << \"No IPA found in '\"\n> +\t\t\t\t\t << utils::join(modulePaths, \":\")\n> +\t\t\t\t\t << \"'\";\n> +\n\nThis should only be printed if modulesPaths is not empty. I'll fix it.\n\n>  \t/*\n>  \t * When libcamera is used before it is installed, load IPAs from the\n>  \t * same build directory as the libcamera library itself.\n> @@ -279,11 +289,10 @@ IPAModule *IPAManager::module(PipelineHandler *pipe, uint32_t minVersion,\n>  bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa) const\n>  {\n>  #if HAVE_IPA_PUBKEY\n> -\tchar *force = utils::secure_getenv(\"LIBCAMERA_IPA_FORCE_ISOLATION\");\n> -\tif (force && force[0] != '\\0') {\n> +\tif (forceIsolation_) {\n>  \t\tLOG(IPAManager, Debug)\n>  \t\t\t<< \"Isolation of IPA module \" << ipa->path()\n> -\t\t\t<< \" forced through environment variable\";\n> +\t\t\t<< \" forced through configuration\";\n>  \t\treturn false;\n>  \t}\n>  \n> diff --git a/src/libcamera/ipa_proxy.cpp b/src/libcamera/ipa_proxy.cpp\n> index b5c13a30f..9d2acd716 100644\n> --- a/src/libcamera/ipa_proxy.cpp\n> +++ b/src/libcamera/ipa_proxy.cpp\n> @@ -7,13 +7,16 @@\n>  \n>  #include \"libcamera/internal/ipa_proxy.h\"\n>  \n> +#include <string>\n>  #include <sys/stat.h>\n>  #include <sys/types.h>\n>  #include <unistd.h>\n> +#include <vector>\n>  \n>  #include <libcamera/base/log.h>\n>  #include <libcamera/base/utils.h>\n>  \n> +#include \"libcamera/internal/global_configuration.h\"\n>  #include \"libcamera/internal/ipa_module.h\"\n>  \n>  /**\n> @@ -27,7 +30,8 @@ LOG_DEFINE_CATEGORY(IPAProxy)\n>  \n>  namespace {\n>  \n> -std::string ipaConfigurationFile(const std::string &ipaName, const std::string &name)\n> +std::string ipaConfigurationFile(const std::string &ipaName, const std::string &name,\n> +\t\t\t\t const std::vector<std::string> &configPaths)\n>  {\n>  \t/*\n>  \t * Start with any user override through the module-specific environment\n> @@ -47,20 +51,15 @@ std::string ipaConfigurationFile(const std::string &ipaName, const std::string &\n>  \tint ret;\n>  \n>  \t/*\n> -\t * Check the directory pointed to by the IPA config path environment\n> -\t * variable next.\n> +\t * Check the directory pointed to by the IPA config path next.\n\n\t/* Check the directory pointed to by the IPA config path next. */\n\n>  \t */\n> -\tconst char *confPaths = utils::secure_getenv(\"LIBCAMERA_IPA_CONFIG_PATH\");\n> -\tif (confPaths) {\n> -\t\tfor (const auto &dir : utils::split(confPaths, \":\")) {\n> -\t\t\tif (dir.empty())\n> -\t\t\t\tcontinue;\n> -\n> -\t\t\tstd::string confPath = dir + \"/\" + ipaName + \"/\" + name;\n> -\t\t\tret = stat(confPath.c_str(), &statbuf);\n> -\t\t\tif (ret == 0 && (statbuf.st_mode & S_IFMT) == S_IFREG)\n> -\t\t\t\treturn confPath;\n> -\t\t}\n> +\tfor (const auto &dir : configPaths) {\n> +\t\tif (dir.empty())\n> +\t\t\tcontinue;\n> +\t\tstd::string confPath = dir + \"/\" + ipaName + \"/\" + name;\n> +\t\tret = stat(confPath.c_str(), &statbuf);\n> +\t\tif (ret == 0 && (statbuf.st_mode & S_IFMT) == S_IFREG)\n> +\t\t\treturn confPath;\n>  \t}\n>  \n>  \tstd::string root = utils::libcameraSourcePath();\n> @@ -120,9 +119,12 @@ std::string ipaConfigurationFile(const std::string &ipaName, const std::string &\n>  /**\n>   * \\brief Construct an IPAProxy instance\n>   * \\param[in] ipam The IPA module\n> + * \\param[in] configuration The global configuration\n>   */\n> -IPAProxy::IPAProxy(IPAModule *ipam)\n> -\t: valid_(false), state_(ProxyStopped), ipam_(ipam)\n> +IPAProxy::IPAProxy(IPAModule *ipam, const GlobalConfiguration &configuration)\n> +\t: valid_(false), state_(ProxyStopped), ipam_(ipam),\n> +\t  configPaths_(configuration.envListOption(\"LIBCAMERA_IPA_CONFIG_PATH\", { \"ipa\", \"config_paths\" }).value_or(std::vector<std::string>())),\n> +\t  execPaths_(configuration.envListOption(\"LIBCAMERA_IPA_PROXY_PATH\", { \"ipa\", \"proxy_paths\" }).value_or(std::vector<std::string>()))\n\nGiven that the IPAProxy::configurationFile() and IPAProxy::resolvePath()\nare called once only, the config and exec paths don't need to be cached.\nI suppose it doesn't hurt though. What do you think ? It can be\naddressed on top in any case.\n\n>  {\n>  }\n>  \n> @@ -175,7 +177,7 @@ std::string IPAProxy::configurationFile(const std::string &name,\n>  \t * has been validated when loading the module.\n>  \t */\n>  \tconst std::string ipaName = ipam_->info().name;\n> -\tstd::string confPath = ipaConfigurationFile(ipaName, name);\n> +\tstd::string confPath = ipaConfigurationFile(ipaName, name, configPaths_);\n>  \tif (!confPath.empty()) {\n>  \t\tLOG(IPAProxy, Info) << \"Using tuning file \" << confPath;\n>  \t\treturn confPath;\n> @@ -188,7 +190,7 @@ std::string IPAProxy::configurationFile(const std::string &name,\n>  \t\treturn std::string();\n>  \t}\n>  \n> -\tconfPath = ipaConfigurationFile(ipaName, fallbackName);\n> +\tconfPath = ipaConfigurationFile(ipaName, fallbackName, configPaths_);\n>  \tLOG(IPAProxy, Warning)\n>  \t\t<< \"Configuration file '\" << name\n>  \t\t<< \"' not found for IPA module '\" << ipaName\n> @@ -214,18 +216,14 @@ std::string IPAProxy::resolvePath(const std::string &file) const\n>  {\n>  \tstd::string proxyFile = \"/\" + file;\n>  \n> -\t/* Check env variable first. */\n> -\tconst char *execPaths = utils::secure_getenv(\"LIBCAMERA_IPA_PROXY_PATH\");\n> -\tif (execPaths) {\n> -\t\tfor (const auto &dir : utils::split(execPaths, \":\")) {\n> -\t\t\tif (dir.empty())\n> -\t\t\t\tcontinue;\n> -\n> -\t\t\tstd::string proxyPath = dir;\n> -\t\t\tproxyPath += proxyFile;\n> -\t\t\tif (!access(proxyPath.c_str(), X_OK))\n> -\t\t\t\treturn proxyPath;\n> -\t\t}\n> +\t/* Check the configuration first. */\n\n\t/* Try paths from the configuration first. */\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n> +\tfor (const auto &dir : execPaths_) {\n> +\t\tif (dir.empty())\n> +\t\t\tcontinue;\n> +\n> +\t\tstd::string proxyPath = dir + proxyFile;\n> +\t\tif (!access(proxyPath.c_str(), X_OK))\n> +\t\t\treturn proxyPath;\n>  \t}\n>  \n>  \t/*\n> diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> index beb646e2d..18b4ab5e5 100644\n> --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> @@ -45,8 +45,8 @@ namespace {{ns}} {\n>  {% endfor %}\n>  {%- endif %}\n>  \n> -{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate)\n> -\t: IPAProxy(ipam), isolate_(isolate),\n> +{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration)\n> +\t: IPAProxy(ipam, configuration), isolate_(isolate),\n>  \t  controlSerializer_(ControlSerializer::Role::Proxy), seq_(0)\n>  {\n>  \tLOG(IPAProxy, Debug)\n> diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> index a0312a7c1..057c3ab03 100644\n> --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> @@ -37,7 +37,7 @@ namespace {{ns}} {\n>  class {{proxy_name}} : public IPAProxy, public {{interface_name}}, public Object\n>  {\n>  public:\n> -\t{{proxy_name}}(IPAModule *ipam, bool isolate);\n> +\t{{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration);\n>  \t~{{proxy_name}}();\n>  \n>  {% for method in interface_main.methods %}","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 20A5DBE173\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 21 Sep 2025 04:27:37 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C55336B59C;\n\tSun, 21 Sep 2025 06:27:35 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2704062C35\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 21 Sep 2025 06:27:33 +0200 (CEST)","from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi\n\t[81.175.209.231])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id 62F327CE;\n\tSun, 21 Sep 2025 06:26:11 +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=\"pFWyOKDe\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1758428771;\n\tbh=I8X/2SPN2l4P5E7IuT7s1RFe4d3cN7mMN0XXrxkXBs4=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=pFWyOKDeNROSiKAxtFcqxLpW6Gv9RD6KMQ83QRpExFNGCpPxzPZUR6XY6yjfMgvoS\n\tcB8UGnmdv4wO90kUn/Cbk+c5rEeW0AOAkn/1Lh57w0dqZc9OAO+0yO77TsM9FNqg47\n\ty7eq4iyzQ5PSekvKO3nGqYzt0/c8gX/gu7JUvOTQ=","Date":"Sun, 21 Sep 2025 07:27:02 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Milan Zamazal <mzamazal@redhat.com>","Cc":"libcamera-devel@lists.libcamera.org, Kieran Bingham\n\t<kieran.bingham@ideasonboard.com>, =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?=\n\t<barnabas.pocze@ideasonboard.com>, Paul Elder\n\t<paul.elder@ideasonboard.com>","Subject":"Re: [PATCH v18 05/12] ipa: Look up IPA configurables in\n\tconfiguration file","Message-ID":"<20250921042702.GD30137@pendragon.ideasonboard.com>","References":"<20250912142915.53949-1-mzamazal@redhat.com>\n\t<20250912142915.53949-6-mzamazal@redhat.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20250912142915.53949-6-mzamazal@redhat.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":35953,"web_url":"https://patchwork.libcamera.org/comment/35953/","msgid":"<85plbiiq18.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","date":"2025-09-22T13:18:43","subject":"Re: [PATCH v18 05/12] ipa: Look up IPA configurables in\n\tconfiguration file","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/people/177/","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"content":"Hi Laurent,\n\nthank you for all the reviews and corrections.  I think my response is\nneeded only here, the rest is either clear or to be answered by somebody\nelse.\n\nLaurent Pinchart <laurent.pinchart@ideasonboard.com> writes:\n\n> Hi Milan,\n>\n> On Fri, Sep 12, 2025 at 04:29:06PM +0200, Milan Zamazal wrote:\n>> This patch adds configuration options for environment variables used in\n>> the IPA proxy.\n>> \n>> The configuration snippet:\n>> \n>>   configuration:\n>>     ipa:\n>>       config_paths:\n>>       - config path 1\n>>       - config path 2\n>>       - ...\n>\n> The list should be indented by one more level:\n>\n>        config_paths:\n>          - config path 1\n>          - config path 2\n>          - ...\n>\n> and now that I wrote this, I started wondering why both options work,\n> and which one is preferred. The YAML spec makes the indentation option,\n> or rather indicates that \"both the “-” indicator and the following\n> spaces are considered to be part of the indentation\". As we use explicit\n> indentation in the existing yaml files (with the exception of the\n> virtual pipeline handler configuration files it seems), I'd like to\n> standardize on that.\n\nIIRC I followed the indentation as presented on the yaml.org front page.\nBut given I actually use spaced indentation everywhere else, I agree to\nhave it indented here.\n\n> I'll update the commit message.\n>\n>>       module_paths:\n>>       - module path 1\n>>       - module path 2\n>>       - ...\n>>       proxy_paths:\n>>       - proxy path 1\n>>       - proxy path 2\n>>       - ...\n>>       force_isolation: BOOL\n>> \n>> LIBCAMERA_<IPA_NAME>_TUNING_FILE remains configurable only via the\n>> environment variable; this is supposed to be used only for testing and\n>> debugging and it's not clear what to do about IPA names like \"rpi/vc4\"\n>> and \"rpi/pisp\" exactly.\n>> \n>> There are two ways to pass the configuration to the places where it is\n>> needed: Either to pass it as an argument to the method calls that need\n>> it, or to pass it to the class constructors and extract the needed\n>> configuration from there.  This patch uses the second method as it is\n>> less polluting the code.\n>> \n>> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n>> Signed-off-by: Milan Zamazal <mzamazal@redhat.com>\n>> ---\n>>  include/libcamera/internal/ipa_manager.h      |  7 ++-\n>>  include/libcamera/internal/ipa_proxy.h        |  8 ++-\n>>  src/libcamera/camera_manager.cpp              |  2 +-\n>>  src/libcamera/ipa_manager.cpp                 | 39 ++++++++-----\n>>  src/libcamera/ipa_proxy.cpp                   | 58 +++++++++----------\n>>  .../module_ipa_proxy.cpp.tmpl                 |  4 +-\n>>  .../module_ipa_proxy.h.tmpl                   |  2 +-\n>>  7 files changed, 68 insertions(+), 52 deletions(-)\n>> \n>> diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h\n>> index a0d448cf9..b0b44c74a 100644\n>> --- a/include/libcamera/internal/ipa_manager.h\n>> +++ b/include/libcamera/internal/ipa_manager.h\n>> @@ -17,6 +17,7 @@\n>>  #include <libcamera/ipa/ipa_module_info.h>\n>>  \n>>  #include \"libcamera/internal/camera_manager.h\"\n>> +#include \"libcamera/internal/global_configuration.h\"\n>>  #include \"libcamera/internal/ipa_module.h\"\n>>  #include \"libcamera/internal/pipeline_handler.h\"\n>>  #include \"libcamera/internal/pub_key.h\"\n>> @@ -28,7 +29,7 @@ LOG_DECLARE_CATEGORY(IPAManager)\n>>  class IPAManager\n>>  {\n>>  public:\n>> -\tIPAManager();\n>> +\tIPAManager(const GlobalConfiguration &configuration);\n>\n> I wonder if we shouldn't pass the camera manager instance instead. The\n> rationale is that the camera manager is considered to be the top-level\n> context in libcamera, so it could be useful to access it in the\n> IPAManager (and elsewhere) for other purposes too. I may experiment with\n> that on top.\n\nOK, might make sense.\n\n>>  \t~IPAManager();\n>>  \n>>  \ttemplate<typename T>\n>> @@ -42,7 +43,8 @@ public:\n>>  \t\tif (!m)\n>>  \t\t\treturn nullptr;\n>>  \n>> -\t\tstd::unique_ptr<T> proxy = std::make_unique<T>(m, !self->isSignatureValid(m));\n>> +\t\tconst GlobalConfiguration &configuration = cm->_d()->configuration();\n>> +\t\tstd::unique_ptr<T> proxy = std::make_unique<T>(m, !self->isSignatureValid(m), configuration);\n>>  \t\tif (!proxy->isValid()) {\n>>  \t\t\tLOG(IPAManager, Error) << \"Failed to load proxy\";\n>>  \t\t\treturn nullptr;\n>> @@ -73,6 +75,7 @@ private:\n>>  #if HAVE_IPA_PUBKEY\n>>  \tstatic const uint8_t publicKeyData_[];\n>>  \tstatic const PubKey pubKey_;\n>> +\tbool forceIsolation_;\n>>  #endif\n>>  };\n>>  \n>> diff --git a/include/libcamera/internal/ipa_proxy.h b/include/libcamera/internal/ipa_proxy.h\n>> index 983bcc5fa..f1865d67e 100644\n>> --- a/include/libcamera/internal/ipa_proxy.h\n>> +++ b/include/libcamera/internal/ipa_proxy.h\n>> @@ -7,10 +7,14 @@\n>>  \n>>  #pragma once\n>>  \n>> +#include <optional>\n>>  #include <string>\n>> +#include <vector>\n>>  \n>>  #include <libcamera/ipa/ipa_interface.h>\n>>  \n>> +#include \"libcamera/internal/global_configuration.h\"\n>> +\n>>  namespace libcamera {\n>>  \n>>  class IPAModule;\n>> @@ -24,7 +28,7 @@ public:\n>>  \t\tProxyRunning,\n>>  \t};\n>>  \n>> -\tIPAProxy(IPAModule *ipam);\n>> +\tIPAProxy(IPAModule *ipam, const GlobalConfiguration &configuration);\n>>  \t~IPAProxy();\n>>  \n>>  \tbool isValid() const { return valid_; }\n>> @@ -40,6 +44,8 @@ protected:\n>>  \n>>  private:\n>>  \tIPAModule *ipam_;\n>> +\tstd::vector<std::string> configPaths_;\n>> +\tstd::vector<std::string> execPaths_;\n>>  };\n>>  \n>>  } /* namespace libcamera */\n>> diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp\n>> index dca3d9a83..64df62444 100644\n>> --- a/src/libcamera/camera_manager.cpp\n>> +++ b/src/libcamera/camera_manager.cpp\n>> @@ -41,7 +41,7 @@ LOG_DEFINE_CATEGORY(Camera)\n>>  CameraManager::Private::Private()\n>>  \t: initialized_(false)\n>>  {\n>> -\tipaManager_ = std::make_unique<IPAManager>();\n>> +\tipaManager_ = std::make_unique<IPAManager>(this->configuration());\n>>  }\n>>  \n>>  int CameraManager::Private::start()\n>> diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp\n>> index 830750dcc..e5172f6ce 100644\n>> --- a/src/libcamera/ipa_manager.cpp\n>> +++ b/src/libcamera/ipa_manager.cpp\n>> @@ -10,12 +10,15 @@\n>>  #include <algorithm>\n>>  #include <dirent.h>\n>>  #include <string.h>\n>> +#include <string>\n>>  #include <sys/types.h>\n>> +#include <vector>\n>>  \n>>  #include <libcamera/base/file.h>\n>>  #include <libcamera/base/log.h>\n>>  #include <libcamera/base/utils.h>\n>>  \n>> +#include \"libcamera/internal/global_configuration.h\"\n>>  #include \"libcamera/internal/ipa_module.h\"\n>>  #include \"libcamera/internal/ipa_proxy.h\"\n>>  #include \"libcamera/internal/pipeline_handler.h\"\n>> @@ -101,30 +104,37 @@ LOG_DEFINE_CATEGORY(IPAManager)\n>>   * The IPAManager class is meant to only be instantiated once, by the\n>>   * CameraManager.\n>>   */\n>> -IPAManager::IPAManager()\n>> +IPAManager::IPAManager(const GlobalConfiguration &configuration)\n>>  {\n>>  #if HAVE_IPA_PUBKEY\n>>  \tif (!pubKey_.isValid())\n>>  \t\tLOG(IPAManager, Warning) << \"Public key not valid\";\n>> +\n>> +\tchar *force = utils::secure_getenv(\"LIBCAMERA_IPA_FORCE_ISOLATION\");\n>> +\tforceIsolation_ = (force && force[0] != '\\0') ||\n>> +\t\t\t  (!force && configuration.option<bool>({ \"ipa\", \"force_isolation\" })\n>> +\t\t\t\t\t     .value_or(false));\n>>  #endif\n>>  \n>>  \tunsigned int ipaCount = 0;\n>>  \n>>  \t/* User-specified paths take precedence. */\n>> -\tconst char *modulePaths = utils::secure_getenv(\"LIBCAMERA_IPA_MODULE_PATH\");\n>> -\tif (modulePaths) {\n>> -\t\tfor (const auto &dir : utils::split(modulePaths, \":\")) {\n>> -\t\t\tif (dir.empty())\n>> -\t\t\t\tcontinue;\n>> -\n>> -\t\t\tipaCount += addDir(dir.c_str());\n>> -\t\t}\n>> +\tconst auto modulePaths =\n>> +\t\tconfiguration.envListOption(\n>> +\t\t\t\t     \"LIBCAMERA_IPA_MODULE_PATH\", { \"ipa\", \"module_paths\" })\n>> +\t\t\t.value_or(std::vector<std::string>());\n>> +\tfor (const auto &dir : modulePaths) {\n>> +\t\tif (dir.empty())\n>> +\t\t\tcontinue;\n>>  \n>> -\t\tif (!ipaCount)\n>> -\t\t\tLOG(IPAManager, Warning)\n>> -\t\t\t\t<< \"No IPA found in '\" << modulePaths << \"'\";\n>> +\t\tipaCount += addDir(dir.c_str());\n>>  \t}\n>>  \n>> +\tif (!ipaCount)\n>> +\t\tLOG(IPAManager, Warning) << \"No IPA found in '\"\n>> +\t\t\t\t\t << utils::join(modulePaths, \":\")\n>> +\t\t\t\t\t << \"'\";\n>> +\n>\n> This should only be printed if modulesPaths is not empty. I'll fix it.\n>\n>>  \t/*\n>>  \t * When libcamera is used before it is installed, load IPAs from the\n>>  \t * same build directory as the libcamera library itself.\n>> @@ -279,11 +289,10 @@ IPAModule *IPAManager::module(PipelineHandler *pipe, uint32_t minVersion,\n>>  bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa) const\n>>  {\n>>  #if HAVE_IPA_PUBKEY\n>> -\tchar *force = utils::secure_getenv(\"LIBCAMERA_IPA_FORCE_ISOLATION\");\n>> -\tif (force && force[0] != '\\0') {\n>> +\tif (forceIsolation_) {\n>>  \t\tLOG(IPAManager, Debug)\n>>  \t\t\t<< \"Isolation of IPA module \" << ipa->path()\n>> -\t\t\t<< \" forced through environment variable\";\n>> +\t\t\t<< \" forced through configuration\";\n>>  \t\treturn false;\n>>  \t}\n>>  \n>> diff --git a/src/libcamera/ipa_proxy.cpp b/src/libcamera/ipa_proxy.cpp\n>> index b5c13a30f..9d2acd716 100644\n>> --- a/src/libcamera/ipa_proxy.cpp\n>> +++ b/src/libcamera/ipa_proxy.cpp\n>> @@ -7,13 +7,16 @@\n>>  \n>>  #include \"libcamera/internal/ipa_proxy.h\"\n>>  \n>> +#include <string>\n>>  #include <sys/stat.h>\n>>  #include <sys/types.h>\n>>  #include <unistd.h>\n>> +#include <vector>\n>>  \n>>  #include <libcamera/base/log.h>\n>>  #include <libcamera/base/utils.h>\n>>  \n>> +#include \"libcamera/internal/global_configuration.h\"\n>>  #include \"libcamera/internal/ipa_module.h\"\n>>  \n>>  /**\n>> @@ -27,7 +30,8 @@ LOG_DEFINE_CATEGORY(IPAProxy)\n>>  \n>>  namespace {\n>>  \n>> -std::string ipaConfigurationFile(const std::string &ipaName, const std::string &name)\n>> +std::string ipaConfigurationFile(const std::string &ipaName, const std::string &name,\n>> +\t\t\t\t const std::vector<std::string> &configPaths)\n>>  {\n>>  \t/*\n>>  \t * Start with any user override through the module-specific environment\n>> @@ -47,20 +51,15 @@ std::string ipaConfigurationFile(const std::string &ipaName, const std::string &\n>>  \tint ret;\n>>  \n>>  \t/*\n>> -\t * Check the directory pointed to by the IPA config path environment\n>> -\t * variable next.\n>> +\t * Check the directory pointed to by the IPA config path next.\n>\n> \t/* Check the directory pointed to by the IPA config path next. */\n>\n>>  \t */\n>> -\tconst char *confPaths = utils::secure_getenv(\"LIBCAMERA_IPA_CONFIG_PATH\");\n>> -\tif (confPaths) {\n>> -\t\tfor (const auto &dir : utils::split(confPaths, \":\")) {\n>> -\t\t\tif (dir.empty())\n>> -\t\t\t\tcontinue;\n>> -\n>> -\t\t\tstd::string confPath = dir + \"/\" + ipaName + \"/\" + name;\n>> -\t\t\tret = stat(confPath.c_str(), &statbuf);\n>> -\t\t\tif (ret == 0 && (statbuf.st_mode & S_IFMT) == S_IFREG)\n>> -\t\t\t\treturn confPath;\n>> -\t\t}\n>> +\tfor (const auto &dir : configPaths) {\n>> +\t\tif (dir.empty())\n>> +\t\t\tcontinue;\n>> +\t\tstd::string confPath = dir + \"/\" + ipaName + \"/\" + name;\n>> +\t\tret = stat(confPath.c_str(), &statbuf);\n>> +\t\tif (ret == 0 && (statbuf.st_mode & S_IFMT) == S_IFREG)\n>> +\t\t\treturn confPath;\n>>  \t}\n>>  \n>>  \tstd::string root = utils::libcameraSourcePath();\n>> @@ -120,9 +119,12 @@ std::string ipaConfigurationFile(const std::string &ipaName, const std::string &\n>>  /**\n>>   * \\brief Construct an IPAProxy instance\n>>   * \\param[in] ipam The IPA module\n>> + * \\param[in] configuration The global configuration\n>>   */\n>> -IPAProxy::IPAProxy(IPAModule *ipam)\n>> -\t: valid_(false), state_(ProxyStopped), ipam_(ipam)\n>> +IPAProxy::IPAProxy(IPAModule *ipam, const GlobalConfiguration &configuration)\n>> +\t: valid_(false), state_(ProxyStopped), ipam_(ipam),\n>> +\t  configPaths_(configuration.envListOption(\"LIBCAMERA_IPA_CONFIG_PATH\", { \"ipa\", \"config_paths\" }).value_or(std::vector<std::string>())),\n>> +\t  execPaths_(configuration.envListOption(\"LIBCAMERA_IPA_PROXY_PATH\", { \"ipa\", \"proxy_paths\" }).value_or(std::vector<std::string>()))\n>\n> Given that the IPAProxy::configurationFile() and IPAProxy::resolvePath()\n> are called once only, the config and exec paths don't need to be cached.\n> I suppose it doesn't hurt though. What do you think ? \n\nPerhaps more important is not having to pass the configuration in all\nthe pipelines, where IPAProxy::configurationFile() is called.  And I\ndon't like implicit assumptions, like this one about once-only calls.\nBut possibly multiple calls to envListOption would be no disaster so I\ndon't have a strong opinion on this.\n\n> It can be addressed on top in any case.\n>\n>>  {\n>>  }\n>>  \n>> @@ -175,7 +177,7 @@ std::string IPAProxy::configurationFile(const std::string &name,\n>>  \t * has been validated when loading the module.\n>>  \t */\n>>  \tconst std::string ipaName = ipam_->info().name;\n>> -\tstd::string confPath = ipaConfigurationFile(ipaName, name);\n>> +\tstd::string confPath = ipaConfigurationFile(ipaName, name, configPaths_);\n>>  \tif (!confPath.empty()) {\n>>  \t\tLOG(IPAProxy, Info) << \"Using tuning file \" << confPath;\n>>  \t\treturn confPath;\n>> @@ -188,7 +190,7 @@ std::string IPAProxy::configurationFile(const std::string &name,\n>>  \t\treturn std::string();\n>>  \t}\n>>  \n>> -\tconfPath = ipaConfigurationFile(ipaName, fallbackName);\n>> +\tconfPath = ipaConfigurationFile(ipaName, fallbackName, configPaths_);\n>>  \tLOG(IPAProxy, Warning)\n>>  \t\t<< \"Configuration file '\" << name\n>>  \t\t<< \"' not found for IPA module '\" << ipaName\n>> @@ -214,18 +216,14 @@ std::string IPAProxy::resolvePath(const std::string &file) const\n>>  {\n>>  \tstd::string proxyFile = \"/\" + file;\n>>  \n>> -\t/* Check env variable first. */\n>> -\tconst char *execPaths = utils::secure_getenv(\"LIBCAMERA_IPA_PROXY_PATH\");\n>> -\tif (execPaths) {\n>> -\t\tfor (const auto &dir : utils::split(execPaths, \":\")) {\n>> -\t\t\tif (dir.empty())\n>> -\t\t\t\tcontinue;\n>> -\n>> -\t\t\tstd::string proxyPath = dir;\n>> -\t\t\tproxyPath += proxyFile;\n>> -\t\t\tif (!access(proxyPath.c_str(), X_OK))\n>> -\t\t\t\treturn proxyPath;\n>> -\t\t}\n>> +\t/* Check the configuration first. */\n>\n> \t/* Try paths from the configuration first. */\n>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n>\n>> +\tfor (const auto &dir : execPaths_) {\n>> +\t\tif (dir.empty())\n>> +\t\t\tcontinue;\n>> +\n>> +\t\tstd::string proxyPath = dir + proxyFile;\n>> +\t\tif (!access(proxyPath.c_str(), X_OK))\n>> +\t\t\treturn proxyPath;\n>>  \t}\n>>  \n>>  \t/*\n>> diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n>> index beb646e2d..18b4ab5e5 100644\n>> --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n>> +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n>> @@ -45,8 +45,8 @@ namespace {{ns}} {\n>>  {% endfor %}\n>>  {%- endif %}\n>>  \n>> -{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate)\n>> -\t: IPAProxy(ipam), isolate_(isolate),\n>> +{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration)\n>> +\t: IPAProxy(ipam, configuration), isolate_(isolate),\n>>  \t  controlSerializer_(ControlSerializer::Role::Proxy), seq_(0)\n>>  {\n>>  \tLOG(IPAProxy, Debug)\n>> diff --git a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n>> index a0312a7c1..057c3ab03 100644\n>> --- a/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n>> +++ b/utils/codegen/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n>> @@ -37,7 +37,7 @@ namespace {{ns}} {\n>>  class {{proxy_name}} : public IPAProxy, public {{interface_name}}, public Object\n>>  {\n>>  public:\n>> -\t{{proxy_name}}(IPAModule *ipam, bool isolate);\n>> +\t{{proxy_name}}(IPAModule *ipam, bool isolate, const GlobalConfiguration &configuration);\n>>  \t~{{proxy_name}}();\n>>  \n>>  {% for method in interface_main.methods %}","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 50442BDB1C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 22 Sep 2025 13:36:26 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 491036B5F3;\n\tMon, 22 Sep 2025 15:36:22 +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 100E96B599\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 22 Sep 2025 15:18:49 +0200 (CEST)","from mail-wm1-f69.google.com (mail-wm1-f69.google.com\n\t[209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n\tus-mta-136-krLS0_1GNZe2FZKhWv-kYg-1; Mon, 22 Sep 2025 09:18:47 -0400","by mail-wm1-f69.google.com with SMTP id\n\t5b1f17b1804b1-45e037fd142so40348865e9.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 22 Sep 2025 06:18:47 -0700 (PDT)","from mzamazal-thinkpadp1gen7.tpbc.csb\n\t(ip-77-48-47-2.net.vodafone.cz. [77.48.47.2])\n\tby smtp.gmail.com with ESMTPSA id\n\tffacd0b85a97d-3ee073f4f3csm19198156f8f.2.2025.09.22.06.18.44\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tMon, 22 Sep 2025 06:18:44 -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=\"ZZvukXS5\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1758547129;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc: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=cTtnx2mDXOG0fYEQaJS3kU1pmmuJRohIMVzubt4Dbj8=;\n\tb=ZZvukXS5vbYUYbKUVlBzxX+tGOWV64YfTo77b7yM0eAtWMQHpWtGm/gjhigFt2JhqJW2+w\n\tvd0Ca08Pxfa2Qe5htlNPRT/GfG1HMT/0jNVd2hGxywg9cjvkJ+PVgyQVVOYJaNjUozwH/f\n\t8/fjVMk/N4WegPoW2+IiojuADB7Eaw4=","X-MC-Unique":"krLS0_1GNZe2FZKhWv-kYg-1","X-Mimecast-MFC-AGG-ID":"krLS0_1GNZe2FZKhWv-kYg_1758547126","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1758547126; x=1759151926;\n\th=content-transfer-encoding:mime-version:user-agent:message-id:date\n\t:references:in-reply-to:subject:cc:to:from:x-gm-message-state:from\n\t:to:cc:subject:date:message-id:reply-to;\n\tbh=ouldVeShyUw/RXRIVZOHQq+dkycq6vYZy/gOpN8y5Og=;\n\tb=HW2CzyIvhvAQg68Y0RIRiNqiSuxeZJ4jKojjcE5tiwcJxT+BlMxiRsncYakhVDJntE\n\tBys5Hl41DwXlfoUZSpr1u7cPRlvX69ba9d5Txf85dCEsoFb53HioX3Vo7+PizVlKYemZ\n\tF5AM6FLy0tqcK3cDSkaqx88nkJby21+0TUATvkk+vp4Fn5AywcBIsr3DXVooXxihFV9v\n\tkqogPbu0rc783RYSowA2lXDuyLEfg5ON4Fs/O/uv8uXxZKM9+P/GKfcUZoqf+BIgtrDK\n\tt3aI40reyMtRyI+YzRFDGGqqKrsNSokdKu5GUSqfLl7WS+48BRNubzcvmZVMqFk5ToYZ\n\tJ7AA==","X-Gm-Message-State":"AOJu0YxNiz8hB5AFw7trnfFFXd9BxYvKhfiEmobGeKrGH4otnLgCAIL/\n\tkVtgU9MvSMsPZ0edtZPrNRMQU0+sV3yArqcyTicWFYaO/85cQtJxL0UGystCwijiYPJ5VBWcaWb\n\t4ympHPMZ6p0e9HvEAxcpvKFSzXh3WkcfG1Tb7GffBsG7nD/L//JQFuHNUowLuUPSGUmQyuxU8wu\n\tg=","X-Gm-Gg":"ASbGncsFujK2kYlLJ21Apiguacm71xZl0q+2hwHQDxD2j7pvIAoJo5mjG2/FFsFJnuf\n\t4AWqQ8eW2tRdOP8n33sHUDMsNT504ySa2qlHj6lCFTxY/nGDVL23rsWrL49slzo4jy12OB+7EUa\n\tE2ysiUwTiZ9pF5pEm0t08HalBd/OghqAiIBBFeNxj6OwpG/ha4xUguobbVIJZldl5NX8pTgupee\n\tb4YtREUtRbBs2H9shjgMKYS9YACz1vlfY1RClYaUXwu+02Lo3XxCjnroI/NB6zU3dfXtFQavzUN\n\tLAxcA2K1RrPqhssCn7UXYBZKQDdl+jGAVqx7o1oxMdPsb553dt6dUJhGSWxjzdzNE8lwN5pixZ9\n\tV9Qx42dqWQBhiPD6FBQ==","X-Received":["by 2002:a05:600c:3553:b0:45d:d2d2:f081 with SMTP id\n\t5b1f17b1804b1-467ea00456emr110545595e9.20.1758547126032; \n\tMon, 22 Sep 2025 06:18:46 -0700 (PDT)","by 2002:a05:600c:3553:b0:45d:d2d2:f081 with SMTP id\n\t5b1f17b1804b1-467ea00456emr110545265e9.20.1758547125409; \n\tMon, 22 Sep 2025 06:18:45 -0700 (PDT)"],"X-Google-Smtp-Source":"AGHT+IGhF8YoAHdXYv7lNekD+C4TpxxL7yDn899Kmrh2OS/0fOWM75ON5/NjXl34xruRGcXNqsi0wA==","From":"Milan Zamazal <mzamazal@redhat.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org,\n\tKieran Bingham <kieran.bingham@ideasonboard.com>, =?utf-8?q?Barnab?=\n\t=?utf-8?b?w6FzIFDFkWN6ZQ==?=\n\t<barnabas.pocze@ideasonboard.com>, Paul Elder\n\t<paul.elder@ideasonboard.com>","Subject":"Re: [PATCH v18 05/12] ipa: Look up IPA configurables in\n\tconfiguration file","In-Reply-To":"<20250921042702.GD30137@pendragon.ideasonboard.com> (Laurent\n\tPinchart's message of \"Sun, 21 Sep 2025 07:27:02 +0300\")","References":"<20250912142915.53949-1-mzamazal@redhat.com>\n\t<20250912142915.53949-6-mzamazal@redhat.com>\n\t<20250921042702.GD30137@pendragon.ideasonboard.com>","Date":"Mon, 22 Sep 2025 15:18:43 +0200","Message-ID":"<85plbiiq18.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","User-Agent":"Gnus/5.13 (Gnus v5.13)","MIME-Version":"1.0","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"9_-Xthbungum0mHzq5q8hWb60l48nC3hxhVqqt2onZE_1758547126","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain; charset=utf-8","Content-Transfer-Encoding":"quoted-printable","X-Mailman-Approved-At":"Mon, 22 Sep 2025 15:36:17 +0200","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>"}}]