Patch Detail
Show a patch.
GET /api/patches/25991/?format=api
{ "id": 25991, "url": "https://patchwork.libcamera.org/api/patches/25991/?format=api", "web_url": "https://patchwork.libcamera.org/patch/25991/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20260128074956.760538-8-paul.elder@ideasonboard.com>", "date": "2026-01-28T07:49:55", "name": "[v5,7/8] layer: Add layer to inject AeEnable control", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "ca5ecc063477df1f44331fa5397932f0268ae20e", "submitter": { "id": 17, "url": "https://patchwork.libcamera.org/api/people/17/?format=api", "name": "Paul Elder", "email": "paul.elder@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/25991/mbox/", "series": [ { "id": 5747, "url": "https://patchwork.libcamera.org/api/series/5747/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=5747", "date": "2026-01-28T07:49:48", "name": "Add Layers support", "version": 5, "mbox": "https://patchwork.libcamera.org/series/5747/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/25991/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/25991/checks/", "tags": {}, "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 9EB24C3200\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 28 Jan 2026 07:50:26 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 381B161FDC;\n\tWed, 28 Jan 2026 08:50:26 +0100 (CET)", "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 9F59361FCE\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 28 Jan 2026 08:50:21 +0100 (CET)", "from neptunite.hamster-moth.ts.net (unknown\n\t[IPv6:2404:7a81:160:2100:508d:7983:72a6:2eeb])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id E2255C6F;\n\tWed, 28 Jan 2026 08:49:43 +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=\"BP72EKSv\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1769586584;\n\tbh=4+KMHYUZxNCf8iyiYnexPDdmOFQ/suJO74HfCD7TJaU=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=BP72EKSvcI3WcT0UxtqcJGPLQI3+5m5Y4NEnmV1N0hfVJmsdFW+ufUBEQeeXhm/NI\n\tqEKNkmhwmDIuKB6amQsSNt3LKjOztFIkbO122QLbwbxH307YZKJxk4PzYy4QgOvX/d\n\t0oH734LMSung0bES/1NMby8uh76gRKfIDRBxFir0=", "From": "Paul Elder <paul.elder@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Cc": "Paul Elder <paul.elder@ideasonboard.com>", "Subject": "[PATCH v5 7/8] layer: Add layer to inject AeEnable control", "Date": "Wed, 28 Jan 2026 16:49:55 +0900", "Message-ID": "<20260128074956.760538-8-paul.elder@ideasonboard.com>", "X-Mailer": "git-send-email 2.47.2", "In-Reply-To": "<20260128074956.760538-1-paul.elder@ideasonboard.com>", "References": "<20260128074956.760538-1-paul.elder@ideasonboard.com>", "MIME-Version": "1.0", "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>" }, "content": "Add a layer to implement the AeEnable control, so that we can remove it\nfrom all IPAs and the Camera class, so that it is transparent to all\nparties and it is available for applications to use.\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n\n---\nFor layers like this that we forsee to be \"always built-in\" we might\nwant a way to build it into libcamera instead of into separate shared\nobject files. In any case, this also serves as a demo of how I envision\na layer implementation to work.\n\nNo change in v4\n\nNo change in v3\n\nChanges in v2:\n- add init() and terminate()\n- use closures\n- remove layer namespacing\n- add gnu_symbol_visibility\n---\n src/layer/inject_controls/inject_controls.cpp | 176 ++++++++++++++++++\n src/layer/inject_controls/inject_controls.h | 24 +++\n src/layer/inject_controls/meson.build | 16 ++\n src/layer/meson.build | 2 +\n 4 files changed, 218 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", "diff": "diff --git a/src/layer/inject_controls/inject_controls.cpp b/src/layer/inject_controls/inject_controls.cpp\nnew file mode 100644\nindex 000000000000..f12faff8318c\n--- /dev/null\n+++ b/src/layer/inject_controls/inject_controls.cpp\n@@ -0,0 +1,176 @@\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/control_ids.h>\n+#include <libcamera/controls.h>\n+#include <libcamera/layer.h>\n+#include <libcamera/request.h>\n+\n+void *init([[maybe_unused]] const std::string &id)\n+{\n+\tInjectControls *closure = new InjectControls;\n+\t*closure = {};\n+\treturn static_cast<void *>(closure);\n+}\n+\n+void terminate(void *closure)\n+{\n+\tInjectControls *obj = static_cast<InjectControls *>(closure);\n+\tdelete obj;\n+}\n+\n+libcamera::ControlInfoMap::Map updateControls(void *closure, libcamera::ControlInfoMap &ctrls)\n+{\n+\tInjectControls *obj = static_cast<InjectControls *>(closure);\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\tobj->aeAvailable = true;\n+\t\t\tif (entry == libcamera::ControlValue(libcamera::controls::ExposureTimeModeManual))\n+\t\t\t\tobj->meAvailable = 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\tobj->agAvailable = true;\n+\t\t\tif (entry == libcamera::ControlValue(libcamera::controls::AnalogueGainModeManual))\n+\t\t\t\tobj->mgAvailable = true;\n+\t\t}\n+\t}\n+\n+\tstd::set<bool> values;\n+\tif (obj->aeAvailable || obj->agAvailable)\n+\t\tvalues.insert(true);\n+\tif (obj->meAvailable || obj->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(void *closure, libcamera::Request *request)\n+{\n+\tInjectControls *obj = static_cast<InjectControls *>(closure);\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 (obj->aeAvailable) {\n+\t\t\tctrls.set(libcamera::controls::ExposureTimeMode,\n+\t\t\t\t libcamera::controls::ExposureTimeModeAuto);\n+\t\t}\n+\n+\t\tif (obj->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 (obj->meAvailable) {\n+\t\t\tctrls.set(libcamera::controls::ExposureTimeMode,\n+\t\t\t\t libcamera::controls::ExposureTimeModeManual);\n+\t\t}\n+\n+\t\tif (obj->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([[maybe_unused]] void *closure, 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 libcamera {\n+\n+extern \"C\" {\n+\n+[[gnu::visibility(\"default\")]]\n+struct LayerInfo layerInfo{\n+\t.name = \"inject_controls\",\n+\t.layerAPIVersion = 1,\n+};\n+\n+[[gnu::visibility(\"default\")]]\n+struct LayerInterface layerInterface{\n+\t.init = init,\n+\t.terminate = terminate,\n+\t.bufferCompleted = nullptr,\n+\t.requestCompleted = requestCompleted,\n+\t.disconnected = nullptr,\n+\t.acquire = nullptr,\n+\t.release = nullptr,\n+\t.controls = updateControls,\n+\t.properties = nullptr,\n+\t.configure = nullptr,\n+\t.createRequest = nullptr,\n+\t.queueRequest = queueRequest,\n+\t.start = nullptr,\n+\t.stop = nullptr,\n+};\n+\n+}\n+\n+} /* namespace libcamera */\ndiff --git a/src/layer/inject_controls/inject_controls.h b/src/layer/inject_controls/inject_controls.h\nnew file mode 100644\nindex 000000000000..42c094d7f76a\n--- /dev/null\n+++ b/src/layer/inject_controls/inject_controls.h\n@@ -0,0 +1,24 @@\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+struct InjectControls {\n+\tbool aeAvailable;\n+\tbool meAvailable;\n+\tbool agAvailable;\n+\tbool mgAvailable;\n+};\n+\n+void *init(const std::string &id);\n+void terminate(void *closure);\n+libcamera::ControlInfoMap::Map updateControls(void *closure, libcamera::ControlInfoMap &ctrls);\n+void queueRequest(void *closure, libcamera::Request *request);\n+void requestCompleted([[maybe_unused]] void *closure, libcamera::Request *request);\ndiff --git a/src/layer/inject_controls/meson.build b/src/layer/inject_controls/meson.build\nnew file mode 100644\nindex 000000000000..d1e402746990\n--- /dev/null\n+++ b/src/layer/inject_controls/meson.build\n@@ -0,0 +1,16 @@\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+ gnu_symbol_visibility: 'hidden',\n+ install : true,\n+ install_dir : layer_install_dir)\ndiff --git a/src/layer/meson.build b/src/layer/meson.build\nindex 45dded512a13..3d8b70ad2cd2 100644\n--- a/src/layer/meson.build\n+++ b/src/layer/meson.build\n@@ -12,3 +12,5 @@ config_h.set('LAYER_DIR',\n layers_env = environment()\n layers_env.set('LIBCAMERA_LAYER_PATH', meson.current_build_dir())\n meson.add_devenv(layers_env)\n+\n+subdir('inject_controls')\n", "prefixes": [ "v5", "7/8" ] }