[{"id":34654,"web_url":"https://patchwork.libcamera.org/comment/34654/","msgid":"<2d42ede2-5447-4110-8547-ba9a2046787c@ideasonboard.com>","date":"2025-06-26T10:39:00","subject":"Re: [RFC PATCH 6/7] layer: Add layer to inject AeEnable control","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/people/216/","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"content":"Hi\n\n2025. 06. 26. 11:59 keltezéssel, Paul Elder írta:\n> Add a layer to implement the AeEnable control, so that we can remove it\n> from all IPAs and the Camera class, so that it is transparent to all\n> parties and it is available for applications to use.\n> \n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> \n> ---\n> For layers like this that we forsee to be \"always built-in\" we might\n> want a way to build it into libcamera instead of into separate shared\n> object files. In any case, this also serves as a demo of how I envision\n> a layer implementation to work.\n> ---\n>   src/layer/inject_controls/inject_controls.cpp | 164 ++++++++++++++++++\n>   src/layer/inject_controls/inject_controls.h   |  29 ++++\n>   src/layer/inject_controls/meson.build         |  15 ++\n>   src/layer/meson.build                         |   2 +\n>   4 files changed, 210 insertions(+)\n>   create mode 100644 src/layer/inject_controls/inject_controls.cpp\n>   create mode 100644 src/layer/inject_controls/inject_controls.h\n>   create mode 100644 src/layer/inject_controls/meson.build\n> \n> diff --git a/src/layer/inject_controls/inject_controls.cpp b/src/layer/inject_controls/inject_controls.cpp\n> new file mode 100644\n> index 000000000000..133a8f51a1f6\n> --- /dev/null\n> +++ b/src/layer/inject_controls/inject_controls.cpp\n> @@ -0,0 +1,164 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2025, Ideas On Board Oy\n> + *\n> + * Layer implementation for injecting controls\n> + */\n> +\n> +#include \"inject_controls.h\"\n> +\n> +#include <algorithm>\n> +#include <set>\n> +\n> +#include <libcamera/layer.h>\n> +\n> +#include <libcamera/control_ids.h>\n> +#include <libcamera/controls.h>\n> +#include <libcamera/request.h>\n> +\n> +namespace layer {\n> +\n> +namespace inject_controls {\n\nAny reason for the namespaces? I think an anonymous namespace\nshould be sufficient.\n\n\n> +\n> +libcamera::ControlInfoMap::Map controls(libcamera::ControlInfoMap &ctrls)\n> +{\n> +\tauto it = ctrls.find(&libcamera::controls::ExposureTimeMode);\n> +\tif (it != ctrls.end()) {\n> +\t\tfor (auto entry : it->second.values()) {\n> +\t\t\tif (entry == libcamera::ControlValue(libcamera::controls::ExposureTimeModeAuto))\n> +\t\t\t\taeAvailable_ = true;\n> +\t\t\tif (entry == libcamera::ControlValue(libcamera::controls::ExposureTimeModeManual))\n> +\t\t\t\tmeAvailable_ = true;\n> +\t\t}\n> +\t}\n> +\n> +\tit = ctrls.find(&libcamera::controls::AnalogueGainMode);\n> +\tif (it != ctrls.end()) {\n> +\t\tfor (auto entry : it->second.values()) {\n> +\t\t\tif (entry == libcamera::ControlValue(libcamera::controls::AnalogueGainModeAuto))\n> +\t\t\t\tagAvailable_ = true;\n> +\t\t\tif (entry == libcamera::ControlValue(libcamera::controls::AnalogueGainModeManual))\n> +\t\t\t\tmgAvailable_ = true;\n> +\t\t}\n> +\t}\n> +\n> +\tstd::set<bool> values;\n> +\tif (aeAvailable_ || agAvailable_)\n> +\t\tvalues.insert(true);\n> +\tif (meAvailable_ || mgAvailable_)\n> +\t\tvalues.insert(false);\n> +\n> +\tif (values.empty())\n> +\t\treturn {};\n> +\n> +\tif (values.size() == 1) {\n> +\t\tbool value = *values.begin();\n> +\t\treturn { { &libcamera::controls::AeEnable,\n> +\t\t\t   libcamera::ControlInfo(value, value, value) } };\n> +\t}\n> +\n> +\treturn { { &libcamera::controls::AeEnable, libcamera::ControlInfo(false, true, true) } };\n> +}\n> +\n> +void queueRequest(libcamera::Request *request)\n> +{\n> +\tlibcamera::ControlList &ctrls = request->controls();\n> +\tauto aeEnable = ctrls.get<bool>(libcamera::controls::AeEnable);\n> +\tif (!aeEnable)\n> +\t\treturn;\n> +\n> +\tif (*aeEnable) {\n> +\t\tif (aeAvailable_) {\n> +\t\t\tctrls.set(libcamera::controls::ExposureTimeMode,\n> +\t\t\t\t  libcamera::controls::ExposureTimeModeAuto);\n> +\t\t}\n> +\n> +\t\tif (agAvailable_) {\n> +\t\t\tctrls.set(libcamera::controls::AnalogueGainMode,\n> +\t\t\t\t  libcamera::controls::AnalogueGainModeAuto);\n> +\t\t}\n> +\t} else {\n> +\t\tif (meAvailable_) {\n> +\t\t\tctrls.set(libcamera::controls::ExposureTimeMode,\n> +\t\t\t\t  libcamera::controls::ExposureTimeModeManual);\n> +\t\t}\n> +\n> +\t\tif (mgAvailable_) {\n> +\t\t\tctrls.set(libcamera::controls::AnalogueGainMode,\n> +\t\t\t\t  libcamera::controls::AnalogueGainModeManual);\n> +\t\t}\n> +\t}\n> +}\n> +\n> +void requestCompleted(libcamera::Request *request)\n> +{\n> +\tlibcamera::ControlList &metadata = request->metadata();\n> +\n> +\tauto eMode = metadata.get<int>(libcamera::controls::ExposureTimeMode);\n> +\tauto aMode = metadata.get<int>(libcamera::controls::AnalogueGainMode);\n> +\n> +\tif (!eMode && !aMode)\n> +\t\treturn;\n> +\n> +\tbool ae = eMode && eMode == libcamera::controls::ExposureTimeModeAuto;\n> +\tbool me = eMode && eMode == libcamera::controls::ExposureTimeModeManual;\n> +\tbool ag = aMode && aMode == libcamera::controls::AnalogueGainModeAuto;\n> +\tbool mg = aMode && aMode == libcamera::controls::AnalogueGainModeManual;\n> +\n> +\t/* Exposure time not reported at all; use gain only */\n> +\tif (!ae && !me) {\n> +\t\tmetadata.set(libcamera::controls::AeEnable, ag);\n> +\t\treturn;\n> +\t}\n> +\n> +\t/* Analogue gain not reported at all; use exposure time only */\n> +\tif (!ag && !mg) {\n> +\t\tmetadata.set(libcamera::controls::AeEnable, ae);\n> +\t\treturn;\n> +\t}\n> +\n> +\t/*\n> +\t * Gain mode and exposure mode are not equal; therefore at least one is\n> +\t * manual, so set AeEnable to false\n> +\t */\n> +\tif (ag != ae) {\n> +\t\tmetadata.set(libcamera::controls::AeEnable, false);\n> +\t\treturn;\n> +\t}\n> +\n> +\t/* ag and ae are equal, so just choose one */\n> +\tmetadata.set(libcamera::controls::AeEnable, ag);\n> +\treturn;\n> +}\n> +\n> +} /* namespace inject_controls */\n> +\n> +} /* namespace layer */\n> +\n> +namespace libcamera {\n\nAny reason for this namespace? It does not have any\neffect due to the `extern \"C\"`.\n\n\n> +\n> +extern \"C\" {\n> +\n> +struct Layer layerInfo {\n\nCould you modify this as follows:\n\n   [[gnu::visibility(\"default\")]]\n   extern const Layer layerInfo {\n\n? (See below for explanation.)\n\n\n> +\t.name = \"inject_controls\",\n> +\t.layerAPIVersion = 1,\n> +\t.init = nullptr,\n> +\t.bufferCompleted = nullptr,\n> +\t.requestCompleted = layer::inject_controls::requestCompleted,\n> +\t.disconnected = nullptr,\n> +\t.acquire = nullptr,\n> +\t.release = nullptr,\n> +\t.controls = layer::inject_controls::controls,\n> +\t.properties = nullptr,\n> +\t.streams = nullptr,\n> +\t.generateConfiguration = nullptr,\n> +\t.configure = nullptr,\n> +\t.createRequest = nullptr,\n> +\t.queueRequest = layer::inject_controls::queueRequest,\n> +\t.start = nullptr,\n> +\t.stop = nullptr,\n> +};\n> +\n> +}\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/layer/inject_controls/inject_controls.h b/src/layer/inject_controls/inject_controls.h\n> new file mode 100644\n> index 000000000000..a65cda897836\n> --- /dev/null\n> +++ b/src/layer/inject_controls/inject_controls.h\n> @@ -0,0 +1,29 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2025, Ideas On Board Oy\n> + *\n> + * Layer implementation for injecting controls\n> + */\n> +\n> +#pragma once\n> +\n> +#include <libcamera/controls.h>\n> +#include <libcamera/request.h>\n> +\n> +namespace layer {\n> +\n> +namespace inject_controls {\n> +\n> +libcamera::ControlInfoMap::Map controls(libcamera::ControlInfoMap &ctrls);\n> +void queueRequest(libcamera::Request *);\n> +void requestCompleted(libcamera::Request *);\n> +\n> +bool initialized_ = false;\n> +bool aeAvailable_ = false;\n> +bool meAvailable_ = false;\n> +bool agAvailable_ = false;\n> +bool mgAvailable_ = false;\n> +\n> +} /* namespace inject_controls */\n> +\n> +} /* namespace layer */\n> diff --git a/src/layer/inject_controls/meson.build b/src/layer/inject_controls/meson.build\n> new file mode 100644\n> index 000000000000..72f22e184923\n> --- /dev/null\n> +++ b/src/layer/inject_controls/meson.build\n> @@ -0,0 +1,15 @@\n> +# SPDX-License-Identifier: CC0-1.0\n> +\n> +layer_name = 'layer_inject_controls'\n> +\n> +layer_inject_sources = files([\n> +    'inject_controls.h',\n> +    'inject_controls.cpp',\n> +])\n> +\n> +mod = shared_module(layer_name, layer_inject_sources,\n> +                    name_prefix : '',\n> +                    include_directories : [layer_includes],\n> +                    dependencies : libcamera_public,\n> +                    install : true,\n> +                    install_dir : layer_install_dir)\n\nCould you add `gnu_symbol_visibility: 'hidden'`? I think it would be\nnice to start making every symbol hidden unless explicitly needed.\n\n\n> diff --git a/src/layer/meson.build b/src/layer/meson.build\n> index dee5e5ac5804..d5793f8c4525 100644\n> --- a/src/layer/meson.build\n> +++ b/src/layer/meson.build\n> @@ -8,3 +8,5 @@ layer_install_dir = libcamera_libdir / 'layers'\n>   \n>   config_h.set('LAYER_DIR',\n>                '\"' + get_option('prefix') / layer_install_dir + '\"')\n> +\n> +subdir('inject_controls')\n\n\nRegards,\nBarnabás Pőcze","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 DD285C3237\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 26 Jun 2025 10:39:06 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8E6EE68E00;\n\tThu, 26 Jun 2025 12:39:06 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 666DA68DFF\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 26 Jun 2025 12:39:04 +0200 (CEST)","from [192.168.33.8] (185.221.143.107.nat.pool.zt.hu\n\t[185.221.143.107])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 4F9E6743;\n\tThu, 26 Jun 2025 12:38:45 +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=\"l+HNtp0z\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1750934325;\n\tbh=9lOUDvUf58y0oeEZNV4DH0BHJa9VJtPnc92s1UHY/hM=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=l+HNtp0zxQSfnc3LcFoqxK7wVit/j3+VAnjNVxQLaFKRwAjaZorM8OHmWDGt4uWrZ\n\t2atWUl2KPTGGhNgagIhlnbH+bhPOGGcc2ib6arzOtp1+rrkxYlPwMAsqpuOto3v3Uf\n\tG2CU1t25SRba4T7Jii4nMp6DsgPyM9J5poYqK8Sc=","Message-ID":"<2d42ede2-5447-4110-8547-ba9a2046787c@ideasonboard.com>","Date":"Thu, 26 Jun 2025 12:39:00 +0200","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [RFC PATCH 6/7] layer: Add layer to inject AeEnable control","To":"Paul Elder <paul.elder@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Cc":"kieran.bingham@ideasonboard.com","References":"<20250626095944.1746345-1-paul.elder@ideasonboard.com>\n\t<20250626095944.1746345-7-paul.elder@ideasonboard.com>","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","Content-Language":"en-US, hu-HU","In-Reply-To":"<20250626095944.1746345-7-paul.elder@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":34686,"web_url":"https://patchwork.libcamera.org/comment/34686/","msgid":"<175101418128.2836253.12689497994919984470@neptunite.rasen.tech>","date":"2025-06-27T08:49:41","subject":"Re: [RFC PATCH 6/7] layer: Add layer to inject AeEnable control","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"Quoting Barnabás Pőcze (2025-06-26 19:39:00)\n> Hi\n> \n> 2025. 06. 26. 11:59 keltezéssel, Paul Elder írta:\n> > Add a layer to implement the AeEnable control, so that we can remove it\n> > from all IPAs and the Camera class, so that it is transparent to all\n> > parties and it is available for applications to use.\n> > \n> > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> > \n> > ---\n> > For layers like this that we forsee to be \"always built-in\" we might\n> > want a way to build it into libcamera instead of into separate shared\n> > object files. In any case, this also serves as a demo of how I envision\n> > a layer implementation to work.\n> > ---\n> >   src/layer/inject_controls/inject_controls.cpp | 164 ++++++++++++++++++\n> >   src/layer/inject_controls/inject_controls.h   |  29 ++++\n> >   src/layer/inject_controls/meson.build         |  15 ++\n> >   src/layer/meson.build                         |   2 +\n> >   4 files changed, 210 insertions(+)\n> >   create mode 100644 src/layer/inject_controls/inject_controls.cpp\n> >   create mode 100644 src/layer/inject_controls/inject_controls.h\n> >   create mode 100644 src/layer/inject_controls/meson.build\n> > \n> > diff --git a/src/layer/inject_controls/inject_controls.cpp b/src/layer/inject_controls/inject_controls.cpp\n> > new file mode 100644\n> > index 000000000000..133a8f51a1f6\n> > --- /dev/null\n> > +++ b/src/layer/inject_controls/inject_controls.cpp\n> > @@ -0,0 +1,164 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2025, Ideas On Board Oy\n> > + *\n> > + * Layer implementation for injecting controls\n> > + */\n> > +\n> > +#include \"inject_controls.h\"\n> > +\n> > +#include <algorithm>\n> > +#include <set>\n> > +\n> > +#include <libcamera/layer.h>\n> > +\n> > +#include <libcamera/control_ids.h>\n> > +#include <libcamera/controls.h>\n> > +#include <libcamera/request.h>\n> > +\n> > +namespace layer {\n> > +\n> > +namespace inject_controls {\n> \n> Any reason for the namespaces? I think an anonymous namespace\n> should be sufficient.\n\nNo particular reason.\n\n> \n> \n> > +\n> > +libcamera::ControlInfoMap::Map controls(libcamera::ControlInfoMap &ctrls)\n> > +{\n> > +     auto it = ctrls.find(&libcamera::controls::ExposureTimeMode);\n> > +     if (it != ctrls.end()) {\n> > +             for (auto entry : it->second.values()) {\n> > +                     if (entry == libcamera::ControlValue(libcamera::controls::ExposureTimeModeAuto))\n> > +                             aeAvailable_ = true;\n> > +                     if (entry == libcamera::ControlValue(libcamera::controls::ExposureTimeModeManual))\n> > +                             meAvailable_ = true;\n> > +             }\n> > +     }\n> > +\n> > +     it = ctrls.find(&libcamera::controls::AnalogueGainMode);\n> > +     if (it != ctrls.end()) {\n> > +             for (auto entry : it->second.values()) {\n> > +                     if (entry == libcamera::ControlValue(libcamera::controls::AnalogueGainModeAuto))\n> > +                             agAvailable_ = true;\n> > +                     if (entry == libcamera::ControlValue(libcamera::controls::AnalogueGainModeManual))\n> > +                             mgAvailable_ = true;\n> > +             }\n> > +     }\n> > +\n> > +     std::set<bool> values;\n> > +     if (aeAvailable_ || agAvailable_)\n> > +             values.insert(true);\n> > +     if (meAvailable_ || mgAvailable_)\n> > +             values.insert(false);\n> > +\n> > +     if (values.empty())\n> > +             return {};\n> > +\n> > +     if (values.size() == 1) {\n> > +             bool value = *values.begin();\n> > +             return { { &libcamera::controls::AeEnable,\n> > +                        libcamera::ControlInfo(value, value, value) } };\n> > +     }\n> > +\n> > +     return { { &libcamera::controls::AeEnable, libcamera::ControlInfo(false, true, true) } };\n> > +}\n> > +\n> > +void queueRequest(libcamera::Request *request)\n> > +{\n> > +     libcamera::ControlList &ctrls = request->controls();\n> > +     auto aeEnable = ctrls.get<bool>(libcamera::controls::AeEnable);\n> > +     if (!aeEnable)\n> > +             return;\n> > +\n> > +     if (*aeEnable) {\n> > +             if (aeAvailable_) {\n> > +                     ctrls.set(libcamera::controls::ExposureTimeMode,\n> > +                               libcamera::controls::ExposureTimeModeAuto);\n> > +             }\n> > +\n> > +             if (agAvailable_) {\n> > +                     ctrls.set(libcamera::controls::AnalogueGainMode,\n> > +                               libcamera::controls::AnalogueGainModeAuto);\n> > +             }\n> > +     } else {\n> > +             if (meAvailable_) {\n> > +                     ctrls.set(libcamera::controls::ExposureTimeMode,\n> > +                               libcamera::controls::ExposureTimeModeManual);\n> > +             }\n> > +\n> > +             if (mgAvailable_) {\n> > +                     ctrls.set(libcamera::controls::AnalogueGainMode,\n> > +                               libcamera::controls::AnalogueGainModeManual);\n> > +             }\n> > +     }\n> > +}\n> > +\n> > +void requestCompleted(libcamera::Request *request)\n> > +{\n> > +     libcamera::ControlList &metadata = request->metadata();\n> > +\n> > +     auto eMode = metadata.get<int>(libcamera::controls::ExposureTimeMode);\n> > +     auto aMode = metadata.get<int>(libcamera::controls::AnalogueGainMode);\n> > +\n> > +     if (!eMode && !aMode)\n> > +             return;\n> > +\n> > +     bool ae = eMode && eMode == libcamera::controls::ExposureTimeModeAuto;\n> > +     bool me = eMode && eMode == libcamera::controls::ExposureTimeModeManual;\n> > +     bool ag = aMode && aMode == libcamera::controls::AnalogueGainModeAuto;\n> > +     bool mg = aMode && aMode == libcamera::controls::AnalogueGainModeManual;\n> > +\n> > +     /* Exposure time not reported at all; use gain only */\n> > +     if (!ae && !me) {\n> > +             metadata.set(libcamera::controls::AeEnable, ag);\n> > +             return;\n> > +     }\n> > +\n> > +     /* Analogue gain not reported at all; use exposure time only */\n> > +     if (!ag && !mg) {\n> > +             metadata.set(libcamera::controls::AeEnable, ae);\n> > +             return;\n> > +     }\n> > +\n> > +     /*\n> > +      * Gain mode and exposure mode are not equal; therefore at least one is\n> > +      * manual, so set AeEnable to false\n> > +      */\n> > +     if (ag != ae) {\n> > +             metadata.set(libcamera::controls::AeEnable, false);\n> > +             return;\n> > +     }\n> > +\n> > +     /* ag and ae are equal, so just choose one */\n> > +     metadata.set(libcamera::controls::AeEnable, ag);\n> > +     return;\n> > +}\n> > +\n> > +} /* namespace inject_controls */\n> > +\n> > +} /* namespace layer */\n> > +\n> > +namespace libcamera {\n> \n> Any reason for this namespace? It does not have any\n> effect due to the `extern \"C\"`.\n\n(I copied it from IPAModule)\n\n> \n> \n> > +\n> > +extern \"C\" {\n> > +\n> > +struct Layer layerInfo {\n> \n> Could you modify this as follows:\n> \n>    [[gnu::visibility(\"default\")]]\n>    extern const Layer layerInfo {\n> \n> ? (See below for explanation.)\n> \n> \n> > +     .name = \"inject_controls\",\n> > +     .layerAPIVersion = 1,\n> > +     .init = nullptr,\n> > +     .bufferCompleted = nullptr,\n> > +     .requestCompleted = layer::inject_controls::requestCompleted,\n> > +     .disconnected = nullptr,\n> > +     .acquire = nullptr,\n> > +     .release = nullptr,\n> > +     .controls = layer::inject_controls::controls,\n> > +     .properties = nullptr,\n> > +     .streams = nullptr,\n> > +     .generateConfiguration = nullptr,\n> > +     .configure = nullptr,\n> > +     .createRequest = nullptr,\n> > +     .queueRequest = layer::inject_controls::queueRequest,\n> > +     .start = nullptr,\n> > +     .stop = nullptr,\n> > +};\n> > +\n> > +}\n> > +\n> > +} /* namespace libcamera */\n> > diff --git a/src/layer/inject_controls/inject_controls.h b/src/layer/inject_controls/inject_controls.h\n> > new file mode 100644\n> > index 000000000000..a65cda897836\n> > --- /dev/null\n> > +++ b/src/layer/inject_controls/inject_controls.h\n> > @@ -0,0 +1,29 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2025, Ideas On Board Oy\n> > + *\n> > + * Layer implementation for injecting controls\n> > + */\n> > +\n> > +#pragma once\n> > +\n> > +#include <libcamera/controls.h>\n> > +#include <libcamera/request.h>\n> > +\n> > +namespace layer {\n> > +\n> > +namespace inject_controls {\n> > +\n> > +libcamera::ControlInfoMap::Map controls(libcamera::ControlInfoMap &ctrls);\n> > +void queueRequest(libcamera::Request *);\n> > +void requestCompleted(libcamera::Request *);\n> > +\n> > +bool initialized_ = false;\n> > +bool aeAvailable_ = false;\n> > +bool meAvailable_ = false;\n> > +bool agAvailable_ = false;\n> > +bool mgAvailable_ = false;\n> > +\n> > +} /* namespace inject_controls */\n> > +\n> > +} /* namespace layer */\n> > diff --git a/src/layer/inject_controls/meson.build b/src/layer/inject_controls/meson.build\n> > new file mode 100644\n> > index 000000000000..72f22e184923\n> > --- /dev/null\n> > +++ b/src/layer/inject_controls/meson.build\n> > @@ -0,0 +1,15 @@\n> > +# SPDX-License-Identifier: CC0-1.0\n> > +\n> > +layer_name = 'layer_inject_controls'\n> > +\n> > +layer_inject_sources = files([\n> > +    'inject_controls.h',\n> > +    'inject_controls.cpp',\n> > +])\n> > +\n> > +mod = shared_module(layer_name, layer_inject_sources,\n> > +                    name_prefix : '',\n> > +                    include_directories : [layer_includes],\n> > +                    dependencies : libcamera_public,\n> > +                    install : true,\n> > +                    install_dir : layer_install_dir)\n> \n> Could you add `gnu_symbol_visibility: 'hidden'`? I think it would be\n> nice to start making every symbol hidden unless explicitly needed.\n\nI see, ok.\n\n\nThanks,\n\nPaul\n\n> \n> \n> > diff --git a/src/layer/meson.build b/src/layer/meson.build\n> > index dee5e5ac5804..d5793f8c4525 100644\n> > --- a/src/layer/meson.build\n> > +++ b/src/layer/meson.build\n> > @@ -8,3 +8,5 @@ layer_install_dir = libcamera_libdir / 'layers'\n> >   \n> >   config_h.set('LAYER_DIR',\n> >                '\"' + get_option('prefix') / layer_install_dir + '\"')\n> > +\n> > +subdir('inject_controls')\n> \n> \n> Regards,\n> Barnabás Pőcze\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 BA51DC3237\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 27 Jun 2025 08:49:49 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E0B9962C43;\n\tFri, 27 Jun 2025 10:49:48 +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 83F0162C43\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 27 Jun 2025 10:49:47 +0200 (CEST)","from neptunite.rasen.tech (unknown\n\t[IPv6:2404:7a81:160:2100:e17c:62b2:2597:dc1b])\n\tby perceval.ideasonboard.com (Postfix) with UTF8SMTPSA id AA1EE7E1;\n\tFri, 27 Jun 2025 10:49:27 +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=\"ikgOrylF\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1751014168;\n\tbh=gu+m6fVahONQ2rlXycXPyAClvimN4PPAPAChgmcX4So=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=ikgOrylFySeKRGLSuYYlgrmIiIr37a9FEx3Ujr9rngS48OxF5j4Zex91TdnDDJYdZ\n\t66UZDkcOuCqGFq3ELE2st+tE7RBiBK7pHkMA2xjVNupFIBKAsOe/6xF7KCZY9A/pO1\n\tg/yplMREujMTPVKnpz/+JUP1RM68tBS9KspkGNg4=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<2d42ede2-5447-4110-8547-ba9a2046787c@ideasonboard.com>","References":"<20250626095944.1746345-1-paul.elder@ideasonboard.com>\n\t<20250626095944.1746345-7-paul.elder@ideasonboard.com>\n\t<2d42ede2-5447-4110-8547-ba9a2046787c@ideasonboard.com>","Subject":"Re: [RFC PATCH 6/7] layer: Add layer to inject AeEnable control","From":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"kieran.bingham@ideasonboard.com","To":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Fri, 27 Jun 2025 17:49:41 +0900","Message-ID":"<175101418128.2836253.12689497994919984470@neptunite.rasen.tech>","User-Agent":"alot/0.0.0","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>"}}]