From patchwork Thu Jun 26 09:59:36 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 23659 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 409F3BDCBF for ; Thu, 26 Jun 2025 10:00:20 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id B5E1A68DFC; Thu, 26 Jun 2025 12:00:19 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="SexQ9c3c"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 83D2168DFA for ; Thu, 26 Jun 2025 12:00:16 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (unknown [IPv6:2404:7a81:160:2100:258b:9e43:6dff:c39d]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1C868743; Thu, 26 Jun 2025 11:59:55 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1750931997; bh=wdTd5agQtsooTqocV7IfsdTtA7ZRA3j7uAHsNRLtngs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SexQ9c3cZ32d14VjcBVyPKv8yubeyLEjRv9e+01taaiwyXITcyfZeZbtThP19z3Bf rwnfW+/ChuGbJIB5lXmkDs93ApXLq00jyDGhnFnFbBu4x6quD0nK+4FJLpNHmiSwQo dEe8Exjx0V5sizvdteEg6WC67RuiUD2vAHe1n+Vs= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , kieran.bingham@ideasonboard.com Subject: [RFC PATCH 1/7] libcamera: ipa_manager: Factor out .so file searching Date: Thu, 26 Jun 2025 18:59:36 +0900 Message-ID: <20250626095944.1746345-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250626095944.1746345-1-paul.elder@ideasonboard.com> References: <20250626095944.1746345-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" In the near future we will add support for layers/shoes on top of libcamera, which will need to search for shared object files similar to how IPAManager does. To avoid code duplication, move this code out of IPAManager into internal utils. Signed-off-by: Paul Elder --- include/libcamera/internal/ipa_manager.h | 4 - include/libcamera/internal/meson.build | 1 + include/libcamera/internal/utils.h | 26 ++++++ src/libcamera/ipa_manager.cpp | 108 ++++------------------- src/libcamera/meson.build | 1 + src/libcamera/utils.cpp | 107 ++++++++++++++++++++++ 6 files changed, 154 insertions(+), 93 deletions(-) create mode 100644 include/libcamera/internal/utils.h create mode 100644 src/libcamera/utils.cpp diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h index a0d448cf9862..6b0ba4b5477f 100644 --- a/include/libcamera/internal/ipa_manager.h +++ b/include/libcamera/internal/ipa_manager.h @@ -59,10 +59,6 @@ public: #endif private: - void parseDir(const char *libDir, unsigned int maxDepth, - std::vector &files); - unsigned int addDir(const char *libDir, unsigned int maxDepth = 0); - IPAModule *module(PipelineHandler *pipe, uint32_t minVersion, uint32_t maxVersion); diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build index 5c80a28c4cbe..690f5c5ec9f6 100644 --- a/include/libcamera/internal/meson.build +++ b/include/libcamera/internal/meson.build @@ -41,6 +41,7 @@ libcamera_internal_headers = files([ 'shared_mem_object.h', 'source_paths.h', 'sysfs.h', + 'utils.h', 'v4l2_device.h', 'v4l2_pixelformat.h', 'v4l2_subdevice.h', diff --git a/include/libcamera/internal/utils.h b/include/libcamera/internal/utils.h new file mode 100644 index 000000000000..742657bebb28 --- /dev/null +++ b/include/libcamera/internal/utils.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018, Google Inc. + * + * Miscellaneous utility functions + */ + +#pragma once + +#include +#include +#include + +namespace libcamera { + +namespace utils { + +void parseDir(const char *libDir, unsigned int maxDepth, + std::vector &files); + +unsigned int addDir(const char *libDir, unsigned int maxDepth, + std::function func); + +} /* namespace utils */ + +} /* namespace libcamera */ diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp index 830750dcc0fb..ecad4845a077 100644 --- a/src/libcamera/ipa_manager.cpp +++ b/src/libcamera/ipa_manager.cpp @@ -8,9 +8,8 @@ #include "libcamera/internal/ipa_manager.h" #include -#include +#include #include -#include #include #include @@ -19,6 +18,7 @@ #include "libcamera/internal/ipa_module.h" #include "libcamera/internal/ipa_proxy.h" #include "libcamera/internal/pipeline_handler.h" +#include "libcamera/internal/utils.h" /** * \file ipa_manager.h @@ -110,6 +110,20 @@ IPAManager::IPAManager() unsigned int ipaCount = 0; + auto &modules = modules_; + std::function addDirHandler = + [&modules](const std::string &file) { + auto ipaModule = std::make_unique(file); + if (!ipaModule->isValid()) + return 0; + + LOG(IPAManager, Debug) << "Loaded IPA module '" << file << "'"; + + modules.push_back(std::move(ipaModule)); + + return 1; + }; + /* User-specified paths take precedence. */ const char *modulePaths = utils::secure_getenv("LIBCAMERA_IPA_MODULE_PATH"); if (modulePaths) { @@ -117,7 +131,7 @@ IPAManager::IPAManager() if (dir.empty()) continue; - ipaCount += addDir(dir.c_str()); + ipaCount += utils::addDir(dir.c_str(), 0, addDirHandler); } if (!ipaCount) @@ -138,11 +152,11 @@ IPAManager::IPAManager() << "libcamera is not installed. Adding '" << ipaBuildPath << "' to the IPA search path"; - ipaCount += addDir(ipaBuildPath.c_str(), maxDepth); + ipaCount += utils::addDir(ipaBuildPath.c_str(), maxDepth, addDirHandler); } /* Finally try to load IPAs from the installed system path. */ - ipaCount += addDir(IPA_MODULE_DIR); + ipaCount += utils::addDir(IPA_MODULE_DIR, 0, addDirHandler); if (!ipaCount) LOG(IPAManager, Warning) @@ -151,90 +165,6 @@ IPAManager::IPAManager() IPAManager::~IPAManager() = default; -/** - * \brief Identify shared library objects within a directory - * \param[in] libDir The directory to search for shared objects - * \param[in] maxDepth The maximum depth of sub-directories to parse - * \param[out] files A vector of paths to shared object library files - * - * Search a directory for .so files, allowing recursion down to sub-directories - * no further than the depth specified by \a maxDepth. - * - * Discovered shared objects are added to the \a files vector. - */ -void IPAManager::parseDir(const char *libDir, unsigned int maxDepth, - std::vector &files) -{ - struct dirent *ent; - DIR *dir; - - dir = opendir(libDir); - if (!dir) - return; - - while ((ent = readdir(dir)) != nullptr) { - if (ent->d_type == DT_DIR && maxDepth) { - if (strcmp(ent->d_name, ".") == 0 || - strcmp(ent->d_name, "..") == 0) - continue; - - std::string subdir = std::string(libDir) + "/" + ent->d_name; - - /* Recursion is limited to maxDepth. */ - parseDir(subdir.c_str(), maxDepth - 1, files); - - continue; - } - - int offset = strlen(ent->d_name) - 3; - if (offset < 0) - continue; - if (strcmp(&ent->d_name[offset], ".so")) - continue; - - files.push_back(std::string(libDir) + "/" + ent->d_name); - } - - closedir(dir); -} - -/** - * \brief Load IPA modules from a directory - * \param[in] libDir The directory to search for IPA modules - * \param[in] maxDepth The maximum depth of sub-directories to search - * - * This function tries to create an IPAModule instance for every shared object - * found in \a libDir, and skips invalid IPA modules. - * - * Sub-directories are searched up to a depth of \a maxDepth. A \a maxDepth - * value of 0 only searches the directory specified in \a libDir. - * - * \return Number of modules loaded by this call - */ -unsigned int IPAManager::addDir(const char *libDir, unsigned int maxDepth) -{ - std::vector files; - - parseDir(libDir, maxDepth, files); - - /* Ensure a stable ordering of modules. */ - std::sort(files.begin(), files.end()); - - unsigned int count = 0; - for (const std::string &file : files) { - auto ipaModule = std::make_unique(file); - if (!ipaModule->isValid()) - continue; - - LOG(IPAManager, Debug) << "Loaded IPA module '" << file << "'"; - - modules_.push_back(std::move(ipaModule)); - count++; - } - - return count; -} - /** * \brief Retrieve an IPA module that matches a given pipeline handler * \param[in] pipe The pipeline handler diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 28a3b0f36d53..8e2aa921a620 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -51,6 +51,7 @@ libcamera_internal_sources = files([ 'shared_mem_object.cpp', 'source_paths.cpp', 'sysfs.cpp', + 'utils.cpp', 'v4l2_device.cpp', 'v4l2_pixelformat.cpp', 'v4l2_subdevice.cpp', diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp new file mode 100644 index 000000000000..ef046ac3134e --- /dev/null +++ b/src/libcamera/utils.cpp @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019, Google Inc. + * + * Miscellaneous utility functions (internal) + */ + +#include "libcamera/internal/utils.h" + +#include +#include +#include +#include +#include +#include +#include + +/** + * \file internal/utils.h + * \brief Miscellaneous utility functions (internal) + */ + +namespace libcamera { + +namespace utils { + +/** + * \brief Identify shared library objects within a directory + * \param[in] libDir The directory to search for shared objects + * \param[in] maxDepth The maximum depth of sub-directories to parse + * \param[out] files A vector of paths to shared object library files + * + * Search a directory for .so files, allowing recursion down to sub-directories + * no further than the depth specified by \a maxDepth. + * + * Discovered shared objects are added to the \a files vector. + */ +void parseDir(const char *libDir, unsigned int maxDepth, + std::vector &files) +{ + struct dirent *ent; + DIR *dir; + + dir = opendir(libDir); + if (!dir) + return; + + while ((ent = readdir(dir)) != nullptr) { + if (ent->d_type == DT_DIR && maxDepth) { + if (strcmp(ent->d_name, ".") == 0 || + strcmp(ent->d_name, "..") == 0) + continue; + + std::string subdir = std::string(libDir) + "/" + ent->d_name; + + /* Recursion is limited to maxDepth. */ + parseDir(subdir.c_str(), maxDepth - 1, files); + + continue; + } + + int offset = strlen(ent->d_name) - 3; + if (offset < 0) + continue; + if (strcmp(&ent->d_name[offset], ".so")) + continue; + + files.push_back(std::string(libDir) + "/" + ent->d_name); + } + + closedir(dir); +} + +/** + * \brief Execute some function on shared object files from a directory + * \param[in] libDir The directory to search for shared objects + * \param[in] maxDepth The maximum depth of sub-directories to search + * \param[in] func The function to execute on every shared object + * + * This function tries to execute the given function \a func for every shared + * object found in \a libDir. + * + * Sub-directories are searched up to a depth of \a maxDepth. A \a maxDepth + * value of 0 only searches the directory specified in \a libDir. + * + * \return Number of shared objects loaded by this call + */ +unsigned int addDir(const char *libDir, unsigned int maxDepth, + std::function func) +{ + std::vector files; + + parseDir(libDir, maxDepth, files); + + /* Ensure a stable ordering of modules. */ + std::sort(files.begin(), files.end()); + + unsigned int count = 0; + for (const std::string &file : files) + count += func(file); + + return count; +} + +} /* namespace utils */ + +} /* namespace libcamera */ From patchwork Thu Jun 26 09:59:37 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 23660 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 7B7C5BDCBF for ; Thu, 26 Jun 2025 10:00:23 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E6E0E68DFE; Thu, 26 Jun 2025 12:00:21 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="RlL9M6LA"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id BD4A668DE8 for ; Thu, 26 Jun 2025 12:00:17 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (unknown [IPv6:2404:7a81:160:2100:258b:9e43:6dff:c39d]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E2F3D6AE; Thu, 26 Jun 2025 11:59:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1750931999; bh=nFD+Nu2biO+fu8+9A5jz6v9l+O9cqRIbFr+408kMze4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=RlL9M6LA6bHt9RkOhVp4JaIxWtU+XaI99eYRUc6LoPv5fUfDkozWB/DOkZ9sVxac+ 1w26Z99ibI/7o/PKcCa+7s6cKTYo/4izKwadw1+IiZzuJFn3H3gFiZsz1XdY89ExFh ujCows4KGqc66VyppxryBQ0MV/OllwtobyAQSpuw= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , kieran.bingham@ideasonboard.com Subject: [RFC PATCH 2/7] libcamera: ipa_module: Factor out ELF file handling Date: Thu, 26 Jun 2025 18:59:37 +0900 Message-ID: <20250626095944.1746345-3-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250626095944.1746345-1-paul.elder@ideasonboard.com> References: <20250626095944.1746345-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" In the near future we will add support for layers/shoes on top of libcamera, which will need to validate and load symbols from ELF files similar to how IPAModule does. To avoid code duplication, move this code out of IPAModule into internal utils. Signed-off-by: Paul Elder --- include/libcamera/internal/utils.h | 6 ++ src/libcamera/ipa_module.cpp | 152 +--------------------------- src/libcamera/utils.cpp | 157 +++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 149 deletions(-) diff --git a/include/libcamera/internal/utils.h b/include/libcamera/internal/utils.h index 742657bebb28..98d5094eb88d 100644 --- a/include/libcamera/internal/utils.h +++ b/include/libcamera/internal/utils.h @@ -8,9 +8,12 @@ #pragma once #include +#include #include #include +#include + namespace libcamera { namespace utils { @@ -21,6 +24,9 @@ void parseDir(const char *libDir, unsigned int maxDepth, unsigned int addDir(const char *libDir, unsigned int maxDepth, std::function func); +int elfVerifyIdent(Span elf); +Span elfLoadSymbol(Span elf, const char *symbol); + } /* namespace utils */ } /* namespace libcamera */ diff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp index e6ea61e44829..c8559ec8b74c 100644 --- a/src/libcamera/ipa_module.cpp +++ b/src/libcamera/ipa_module.cpp @@ -25,6 +25,7 @@ #include #include "libcamera/internal/pipeline_handler.h" +#include "libcamera/internal/utils.h" /** * \file ipa_module.h @@ -40,153 +41,6 @@ namespace libcamera { LOG_DEFINE_CATEGORY(IPAModule) -namespace { - -template -typename std::remove_extent_t *elfPointer(Span elf, - off_t offset, size_t objSize) -{ - size_t size = offset + objSize; - if (size > elf.size() || size < objSize) - return nullptr; - - return reinterpret_cast *>( - reinterpret_cast(elf.data()) + offset); -} - -template -typename std::remove_extent_t *elfPointer(Span elf, - off_t offset) -{ - return elfPointer(elf, offset, sizeof(T)); -} - -int elfVerifyIdent(Span elf) -{ - const char *e_ident = elfPointer(elf, 0); - if (!e_ident) - return -ENOEXEC; - - if (e_ident[EI_MAG0] != ELFMAG0 || - e_ident[EI_MAG1] != ELFMAG1 || - e_ident[EI_MAG2] != ELFMAG2 || - e_ident[EI_MAG3] != ELFMAG3 || - e_ident[EI_VERSION] != EV_CURRENT) - return -ENOEXEC; - - int bitClass = sizeof(unsigned long) == 4 ? ELFCLASS32 : ELFCLASS64; - if (e_ident[EI_CLASS] != bitClass) - return -ENOEXEC; - - int a = 1; - unsigned char endianness = *reinterpret_cast(&a) == 1 - ? ELFDATA2LSB : ELFDATA2MSB; - if (e_ident[EI_DATA] != endianness) - return -ENOEXEC; - - return 0; -} - -const ElfW(Shdr) *elfSection(Span elf, const ElfW(Ehdr) *eHdr, - ElfW(Half) idx) -{ - if (idx >= eHdr->e_shnum) - return nullptr; - - off_t offset = eHdr->e_shoff + idx * - static_cast(eHdr->e_shentsize); - return elfPointer(elf, offset); -} - -/** - * \brief Retrieve address and size of a symbol from an mmap'ed ELF file - * \param[in] elf Address and size of mmap'ed ELF file - * \param[in] symbol Symbol name - * - * \return The memory region storing the symbol on success, or an empty span - * otherwise - */ -Span elfLoadSymbol(Span elf, const char *symbol) -{ - const ElfW(Ehdr) *eHdr = elfPointer(elf, 0); - if (!eHdr) - return {}; - - const ElfW(Shdr) *sHdr = elfSection(elf, eHdr, eHdr->e_shstrndx); - if (!sHdr) - return {}; - off_t shnameoff = sHdr->sh_offset; - - /* Locate .dynsym section header. */ - const ElfW(Shdr) *dynsym = nullptr; - for (unsigned int i = 0; i < eHdr->e_shnum; i++) { - sHdr = elfSection(elf, eHdr, i); - if (!sHdr) - return {}; - - off_t offset = shnameoff + sHdr->sh_name; - const char *name = elfPointer(elf, offset); - if (!name) - return {}; - - if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, ".dynsym")) { - dynsym = sHdr; - break; - } - } - - if (dynsym == nullptr) { - LOG(IPAModule, Error) << "ELF has no .dynsym section"; - return {}; - } - - sHdr = elfSection(elf, eHdr, dynsym->sh_link); - if (!sHdr) - return {}; - off_t dynsym_nameoff = sHdr->sh_offset; - - /* Locate symbol in the .dynsym section. */ - const ElfW(Sym) *targetSymbol = nullptr; - unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize; - for (unsigned int i = 0; i < dynsym_num; i++) { - off_t offset = dynsym->sh_offset + dynsym->sh_entsize * i; - const ElfW(Sym) *sym = elfPointer(elf, offset); - if (!sym) - return {}; - - offset = dynsym_nameoff + sym->st_name; - const char *name = elfPointer(elf, offset, - strlen(symbol) + 1); - if (!name) - return {}; - - if (!strcmp(name, symbol) && - sym->st_info & STB_GLOBAL) { - targetSymbol = sym; - break; - } - } - - if (targetSymbol == nullptr) { - LOG(IPAModule, Error) << "Symbol " << symbol << " not found"; - return {}; - } - - /* Locate and return data of symbol. */ - sHdr = elfSection(elf, eHdr, targetSymbol->st_shndx); - if (!sHdr) - return {}; - off_t offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr); - const uint8_t *data = elfPointer(elf, offset, - targetSymbol->st_size); - if (!data) - return {}; - - return { data, targetSymbol->st_size }; -} - -} /* namespace */ - /** * \def IPA_MODULE_API_VERSION * \brief The IPA module API version @@ -280,13 +134,13 @@ int IPAModule::loadIPAModuleInfo() } Span data = file.map(); - int ret = elfVerifyIdent(data); + int ret = utils::elfVerifyIdent(data); if (ret) { LOG(IPAModule, Error) << "IPA module is not an ELF file"; return ret; } - Span info = elfLoadSymbol(data, "ipaModuleInfo"); + Span info = utils::elfLoadSymbol(data, "ipaModuleInfo"); if (info.size() < sizeof(info_)) { LOG(IPAModule, Error) << "IPA module has no valid info"; return -EINVAL; diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp index ef046ac3134e..f7bc5c54ff57 100644 --- a/src/libcamera/utils.cpp +++ b/src/libcamera/utils.cpp @@ -10,20 +10,63 @@ #include #include #include +#include +#include #include #include #include #include +#include + /** * \file internal/utils.h * \brief Miscellaneous utility functions (internal) */ +/* musl doesn't declare _DYNAMIC in link.h, declare it manually. */ +extern ElfW(Dyn) _DYNAMIC[]; + namespace libcamera { +namespace { + +template +typename std::remove_extent_t *elfPointer(Span elf, + off_t offset, size_t objSize) +{ + size_t size = offset + objSize; + if (size > elf.size() || size < objSize) + return nullptr; + + return reinterpret_cast *>( + reinterpret_cast(elf.data()) + offset); +} + +template +typename std::remove_extent_t *elfPointer(Span elf, + off_t offset) +{ + return elfPointer(elf, offset, sizeof(T)); +} + +const ElfW(Shdr) *elfSection(Span elf, const ElfW(Ehdr) *eHdr, + ElfW(Half) idx) +{ + if (idx >= eHdr->e_shnum) + return nullptr; + + off_t offset = eHdr->e_shoff + idx * + static_cast(eHdr->e_shentsize); + return elfPointer(elf, offset); +} + +} /* namespace */ + namespace utils { +LOG_DEFINE_CATEGORY(Utils) + /** * \brief Identify shared library objects within a directory * \param[in] libDir The directory to search for shared objects @@ -102,6 +145,120 @@ unsigned int addDir(const char *libDir, unsigned int maxDepth, return count; } +int elfVerifyIdent(Span elf) +{ + const char *e_ident = elfPointer(elf, 0); + if (!e_ident) + return -ENOEXEC; + + if (e_ident[EI_MAG0] != ELFMAG0 || + e_ident[EI_MAG1] != ELFMAG1 || + e_ident[EI_MAG2] != ELFMAG2 || + e_ident[EI_MAG3] != ELFMAG3 || + e_ident[EI_VERSION] != EV_CURRENT) + return -ENOEXEC; + + int bitClass = sizeof(unsigned long) == 4 ? ELFCLASS32 : ELFCLASS64; + if (e_ident[EI_CLASS] != bitClass) + return -ENOEXEC; + + int a = 1; + unsigned char endianness = *reinterpret_cast(&a) == 1 + ? ELFDATA2LSB : ELFDATA2MSB; + if (e_ident[EI_DATA] != endianness) + return -ENOEXEC; + + return 0; +} + +/** + * \brief Retrieve address and size of a symbol from an mmap'ed ELF file + * \param[in] elf Address and size of mmap'ed ELF file + * \param[in] symbol Symbol name + * + * \return The memory region storing the symbol on success, or an empty span + * otherwise + */ +Span elfLoadSymbol(Span elf, const char *symbol) +{ + const ElfW(Ehdr) *eHdr = elfPointer(elf, 0); + if (!eHdr) + return {}; + + const ElfW(Shdr) *sHdr = elfSection(elf, eHdr, eHdr->e_shstrndx); + if (!sHdr) + return {}; + off_t shnameoff = sHdr->sh_offset; + + /* Locate .dynsym section header. */ + const ElfW(Shdr) *dynsym = nullptr; + for (unsigned int i = 0; i < eHdr->e_shnum; i++) { + sHdr = elfSection(elf, eHdr, i); + if (!sHdr) + return {}; + + off_t offset = shnameoff + sHdr->sh_name; + const char *name = elfPointer(elf, offset); + if (!name) + return {}; + + if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, ".dynsym")) { + dynsym = sHdr; + break; + } + } + + if (dynsym == nullptr) { + LOG(Utils, Error) << "ELF has no .dynsym section"; + return {}; + } + + sHdr = elfSection(elf, eHdr, dynsym->sh_link); + if (!sHdr) + return {}; + off_t dynsym_nameoff = sHdr->sh_offset; + + /* Locate symbol in the .dynsym section. */ + const ElfW(Sym) *targetSymbol = nullptr; + unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize; + for (unsigned int i = 0; i < dynsym_num; i++) { + off_t offset = dynsym->sh_offset + dynsym->sh_entsize * i; + const ElfW(Sym) *sym = elfPointer(elf, offset); + if (!sym) + return {}; + + offset = dynsym_nameoff + sym->st_name; + const char *name = elfPointer(elf, offset, + strlen(symbol) + 1); + if (!name) + return {}; + + if (!strcmp(name, symbol) && + sym->st_info & STB_GLOBAL) { + targetSymbol = sym; + break; + } + } + + if (targetSymbol == nullptr) { + LOG(Utils, Error) << "Symbol " << symbol << " not found"; + return {}; + } + + /* Locate and return data of symbol. */ + sHdr = elfSection(elf, eHdr, targetSymbol->st_shndx); + if (!sHdr) + return {}; + off_t offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr); + const uint8_t *data = elfPointer(elf, offset, + targetSymbol->st_size); + if (!data) + return {}; + + return { data, targetSymbol->st_size }; +} + + } /* namespace utils */ } /* namespace libcamera */ From patchwork Thu Jun 26 09:59:38 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 23661 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id B3315BDCBF for ; Thu, 26 Jun 2025 10:00:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 038C668E01; Thu, 26 Jun 2025 12:00:24 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="HiiZoet1"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8717568DF4 for ; Thu, 26 Jun 2025 12:00:19 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (unknown [IPv6:2404:7a81:160:2100:258b:9e43:6dff:c39d]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id B264E743; Thu, 26 Jun 2025 11:59:59 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1750932000; bh=jpm2IqAsPQETaFvOFA18lMB/Zly2mSavp3k6Usah3xI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HiiZoet1aGQ7ssoRHEI1RH3bMe0DvOycJrdi+B2ADzl91MzH0nXKxmIXpR3f+LuB/ epExQLzTythSg0drH3fsFS3VMiko4GGH6JvOOqMh6OQiBuykpCFPE2OXkIqZIo/Wuw wE2YAxVi5fU4Ol5MwxwObRdXKj31cjQOeSK0aVT8= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , kieran.bingham@ideasonboard.com Subject: [RFC PATCH 3/7] libcamera: camera: Add indirection to Camera signal emissions Date: Thu, 26 Jun 2025 18:59:38 +0900 Message-ID: <20250626095944.1746345-4-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250626095944.1746345-1-paul.elder@ideasonboard.com> References: <20250626095944.1746345-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add an extra level of indirection when emitting signals from the Camera. This is to facilitate the implementation of the layer system in the near future, which will need to hook into Camera signal emissions. Signed-off-by: Paul Elder --- include/libcamera/internal/camera.h | 4 ++++ src/libcamera/camera.cpp | 23 +++++++++++++++++++++-- src/libcamera/pipeline_handler.cpp | 2 +- src/libcamera/request.cpp | 2 +- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/include/libcamera/internal/camera.h b/include/libcamera/internal/camera.h index 18f5c32a18e4..967d4e1693ec 100644 --- a/include/libcamera/internal/camera.h +++ b/include/libcamera/internal/camera.h @@ -35,6 +35,10 @@ public: PipelineHandler *pipe() { return pipe_.get(); } const PipelineHandler *pipe() const { return pipe_.get(); } + void emitBufferCompleted(Request *request, FrameBuffer *buffer); + void emitRequestCompleted(Request *request); + void emitDisconnected(); + std::list queuedRequests_; ControlInfoMap controlInfo_; ControlList properties_; diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index c180a5fdde93..c3e656cab671 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -737,6 +737,25 @@ void Camera::Private::setState(State state) { state_.store(state, std::memory_order_release); } + +void Camera::Private::emitBufferCompleted(Request *request, FrameBuffer *buffer) +{ + Camera *camera = _o(); + camera->bufferCompleted.emit(request, buffer); +} + +void Camera::Private::emitRequestCompleted(Request *request) +{ + Camera *camera = _o(); + camera->requestCompleted.emit(request); +} + +void Camera::Private::emitDisconnected() +{ + Camera *camera = _o(); + camera->disconnected.emit(); +} + #endif /* __DOXYGEN_PUBLIC__ */ /** @@ -947,7 +966,7 @@ void Camera::disconnect() LOG(Camera, Debug) << "Disconnecting camera " << id(); _d()->disconnect(); - disconnected.emit(); + _d()->emitDisconnected(); } int Camera::exportFrameBuffers(Stream *stream, @@ -1451,7 +1470,7 @@ void Camera::requestComplete(Request *request) true)) LOG(Camera, Fatal) << "Trying to complete a request when stopped"; - requestCompleted.emit(request); + _d()->emitRequestCompleted(request); } } /* namespace libcamera */ diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index d84dff3c9f19..b5faaae08d4c 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -527,7 +527,7 @@ void PipelineHandler::doQueueRequests() bool PipelineHandler::completeBuffer(Request *request, FrameBuffer *buffer) { Camera *camera = request->_d()->camera(); - camera->bufferCompleted.emit(request, buffer); + camera->_d()->emitBufferCompleted(request, buffer); return request->_d()->completeBuffer(buffer); } diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp index 7f1e11e8f913..b4ae0f41a34c 100644 --- a/src/libcamera/request.cpp +++ b/src/libcamera/request.cpp @@ -137,7 +137,7 @@ void Request::Private::doCancelRequest() for (FrameBuffer *buffer : pending_) { buffer->_d()->cancel(); - camera_->bufferCompleted.emit(request, buffer); + camera_->_d()->emitBufferCompleted(request, buffer); } cancelled_ = true; From patchwork Thu Jun 26 09:59:39 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 23662 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 6E2C8BDCBF for ; Thu, 26 Jun 2025 10:00:29 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1DE3A68E01; Thu, 26 Jun 2025 12:00:29 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="EBbc2ZsJ"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 617BD68DF4 for ; Thu, 26 Jun 2025 12:00:21 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (unknown [IPv6:2404:7a81:160:2100:258b:9e43:6dff:c39d]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7E35118B1; Thu, 26 Jun 2025 12:00:01 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1750932002; bh=IdCHyFP5H/VnkfK2asiXy7JZ5uG17lDxZbRHeHlRYG0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EBbc2ZsJhCCPSlUdfmJvxmwrJl8na/EtPhhIXGSltoVANemcyN4mPNGTPVE94Srgq TC4xdFJsLsTVCVp+iIJRd02S54tmBDN5X9eXNFon7i4Hy0v5dSizmH9OQsh5OkMPva G+6AmACS9FyTOw/LDF4sFGRijtIuYIx8ZlP/E2Rs= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , kieran.bingham@ideasonboard.com Subject: [RFC PATCH 4/7] libcamera: layer_manager: Add LayerManager implementation Date: Thu, 26 Jun 2025 18:59:39 +0900 Message-ID: <20250626095944.1746345-5-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250626095944.1746345-1-paul.elder@ideasonboard.com> References: <20250626095944.1746345-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" We want to be able to implement layers in libcamera, which conceptually sit in between the Camera class and the application. This can be useful for implementing things that don't belong inside the Camera/IPA nor inside the application, such as intercepting and translation the AeEnable control, or implementing the Sync algorithm. To achieve this, first add a LayerManager implementation, which searches for and loads layers from shared object files, and orchestrates executing them. Actually calling into these functions from the Camera class will be added in the following patch. Signed-off-by: Paul Elder --- include/libcamera/internal/layer_manager.h | 74 +++++ include/libcamera/internal/meson.build | 1 + include/libcamera/layer.h | 56 ++++ include/libcamera/meson.build | 1 + src/layer/meson.build | 10 + src/libcamera/layer_manager.cpp | 314 +++++++++++++++++++++ src/libcamera/meson.build | 1 + src/meson.build | 1 + 8 files changed, 458 insertions(+) create mode 100644 include/libcamera/internal/layer_manager.h create mode 100644 include/libcamera/layer.h create mode 100644 src/layer/meson.build create mode 100644 src/libcamera/layer_manager.cpp diff --git a/include/libcamera/internal/layer_manager.h b/include/libcamera/internal/layer_manager.h new file mode 100644 index 000000000000..73ccad01bca0 --- /dev/null +++ b/include/libcamera/internal/layer_manager.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas On Board Oy + * + * Layer manager interface + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace libcamera { + +LOG_DECLARE_CATEGORY(LayerManager) + +class LayerManager +{ +public: + LayerManager(); + ~LayerManager(); + + void bufferCompleted(Request *request, FrameBuffer *buffer); + void requestCompleted(Request *request); + void disconnected(); + + void acquire(); + void release(); + + const ControlInfoMap &controls(const ControlInfoMap &controlInfoMap); + const ControlList &properties(const ControlList &properties); + const std::set &streams(const std::set &streams); + + void generateConfiguration(Span &roles, + CameraConfiguration *config); + + void configure(CameraConfiguration *config); + + void createRequest(uint64_t cookie, Request *request); + + void queueRequest(Request *request); + + void start(const ControlList *controls); + void stop(); + +private: + /* Extend the layer with information specific to load-handling */ + struct LayerLoaded + { + Layer layer; + void *dlHandle; + }; + + std::unique_ptr createLayer(const std::string &file); + std::deque> executionQueue_; + + ControlInfoMap controlInfoMap_; + ControlList properties_; + std::set streams_; +}; + +} /* namespace libcamera */ diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build index 690f5c5ec9f6..20e6c295601f 100644 --- a/include/libcamera/internal/meson.build +++ b/include/libcamera/internal/meson.build @@ -29,6 +29,7 @@ libcamera_internal_headers = files([ 'ipa_proxy.h', 'ipc_pipe.h', 'ipc_unixsocket.h', + 'layer_manager.h', 'mapped_framebuffer.h', 'matrix.h', 'media_device.h', diff --git a/include/libcamera/layer.h b/include/libcamera/layer.h new file mode 100644 index 000000000000..8fa0ee7d24e6 --- /dev/null +++ b/include/libcamera/layer.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas On Board Oy + * + * Layer interface + */ + +#pragma once + +#include +#include + +#include + +namespace libcamera { + +class CameraConfiguration; +class ControlInfoMap; +class ControlList; +class FrameBuffer; +class Request; +class Stream; +enum class StreamRole; + +struct Layer +{ + const char *name; + int layerAPIVersion; + + void (*init)(const std::string &id); + + void (*bufferCompleted)(Request *, FrameBuffer *); + void (*requestCompleted)(Request *); + void (*disconnected)(); + + void (*acquire)(); + void (*release)(); + + ControlInfoMap::Map (*controls)(ControlInfoMap &); + ControlList (*properties)(ControlList &); + std::set (*streams)(std::set &); + + void (*generateConfiguration)(Span &, + CameraConfiguration *); + + void (*configure)(CameraConfiguration *); + + void (*createRequest)(uint64_t, Request *); + + void (*queueRequest)(Request *); + + void (*start)(const ControlList *); + void (*stop)(); +} __attribute__((packed)); + +} /* namespace libcamera */ diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index 30ea76f9470a..552af112abb5 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -11,6 +11,7 @@ libcamera_public_headers = files([ 'framebuffer.h', 'framebuffer_allocator.h', 'geometry.h', + 'layer.h', 'logging.h', 'orientation.h', 'pixel_format.h', diff --git a/src/layer/meson.build b/src/layer/meson.build new file mode 100644 index 000000000000..dee5e5ac5804 --- /dev/null +++ b/src/layer/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: CC0-1.0 + +layer_includes = [ + libcamera_includes, +] + +layer_install_dir = libcamera_libdir / 'layers' + +config_h.set('LAYER_DIR', + '"' + get_option('prefix') / layer_install_dir + '"') diff --git a/src/libcamera/layer_manager.cpp b/src/libcamera/layer_manager.cpp new file mode 100644 index 000000000000..96d53d4fc75d --- /dev/null +++ b/src/libcamera/layer_manager.cpp @@ -0,0 +1,314 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas On Board Oy + * + * Layer manager + */ + +#include "libcamera/internal/layer_manager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "libcamera/internal/utils.h" + +/** + * \file layer_manager.h + * \brief Layer manager + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(LayerManager) + +/** + * \class LayerManager + * \brief Layer manager + * + * The Layer manager discovers layer implementations from disk, and creates + * execution queues for every function that is implemented by each layer and + * executes them. A layer is a layer that sits between libcamera and the + * application, and hooks into the public Camera interface. + */ + +/** + * \brief Construct a LayerManager instance + * + * The LayerManager class is meant be instantiated by the Camera. + */ +LayerManager::LayerManager() +{ + std::map> layers; + + /* This returns the number of "modules" successfully loaded */ + std::function addDirHandler = + [this, &layers](const std::string &file) { + std::unique_ptr layer = createLayer(file); + if (!layer) + return 0; + + LOG(LayerManager, Debug) << "Loaded layer '" << file << "'"; + + layers.insert({std::string(layer->layer.name), std::move(layer)}); + + return 1; + }; + + /* User-specified paths take precedence. */ + const char *layerPaths = utils::secure_getenv("LIBCAMERA_LAYER_PATH"); + if (layerPaths) { + for (const auto &dir : utils::split(layerPaths, ":")) { + if (dir.empty()) + continue; + + utils::addDir(dir.c_str(), 0, addDirHandler); + } + } + + /* + * When libcamera is used before it is installed, load layers from the + * same build directory as the libcamera library itself. + */ + std::string root = utils::libcameraBuildPath(); + if (!root.empty()) { + std::string layerBuildPath = root + "src/layer"; + constexpr int maxDepth = 2; + + LOG(LayerManager, Info) + << "libcamera is not installed. Adding '" + << layerBuildPath << "' to the layer search path"; + + utils::addDir(layerBuildPath.c_str(), maxDepth, addDirHandler); + } + + /* Finally try to load layers from the installed system path. */ + utils::addDir(LAYER_DIR, 0, addDirHandler); + + /* Order the layers */ + /* \todo Document this. First is closer to application, last is closer to libcamera */ + const char *layerList = utils::secure_getenv("LIBCAMERA_LAYERS_ENABLE"); + if (layerList) { + for (const auto &layerName : utils::split(layerList, ":")) { + if (layerName.empty()) + continue; + + const auto &it = layers.find(layerName); + if (it == layers.end()) + continue; + + executionQueue_.push_back(std::move(it->second)); + } + } +} + +LayerManager::~LayerManager() +{ + for (auto &layer : executionQueue_) + dlclose(layer->dlHandle); +} + +std::unique_ptr LayerManager::createLayer(const std::string &filename) +{ + File file{ filename }; + if (!file.open(File::OpenModeFlag::ReadOnly)) { + LOG(LayerManager, Error) << "Failed to open layer: " + << strerror(-file.error()); + return nullptr; + } + + Span data = file.map(); + int ret = utils::elfVerifyIdent(data); + if (ret) { + LOG(LayerManager, Error) << "Layer is not an ELF file"; + return nullptr; + } + + Span info = utils::elfLoadSymbol(data, "layerInfo"); + if (info.size() < sizeof(Layer)) { + LOG(LayerManager, Error) << "Layer has no valid info"; + return nullptr; + } + + void *dlHandle = dlopen(file.fileName().c_str(), RTLD_LAZY); + if (!dlHandle) { + LOG(LayerManager, Error) + << "Failed to open layer shared object: " + << dlerror(); + return nullptr; + } + + void *symbol = dlsym(dlHandle, "layerInfo"); + if (!symbol) { + LOG(LayerManager, Error) + << "Failed to load layerInfo from layer shared object: " + << dlerror(); + dlclose(dlHandle); + dlHandle = nullptr; + return nullptr; + } + + std::unique_ptr layer = + std::make_unique(); + layer->layer = *reinterpret_cast(symbol); + + /* \todo Implement this. It should come from the libcamera version */ + if (layer->layer.layerAPIVersion != 1) { + LOG(LayerManager, Error) << "Layer API version mismatch"; + return nullptr; + } + + /* \todo Validate the layer name. */ + + layer->dlHandle = dlHandle; + + return layer; +} + +void LayerManager::bufferCompleted(Request *request, FrameBuffer *buffer) +{ + /* Reverse order because this comes from a Signal emission */ + for (auto it = executionQueue_.rbegin(); + it != executionQueue_.rend(); it++) { + if ((*it)->layer.bufferCompleted) + (*it)->layer.bufferCompleted(request, buffer); + } +} + +void LayerManager::requestCompleted(Request *request) +{ + /* Reverse order because this comes from a Signal emission */ + for (auto it = executionQueue_.rbegin(); + it != executionQueue_.rend(); it++) { + if ((*it)->layer.requestCompleted) + (*it)->layer.requestCompleted(request); + } +} + +void LayerManager::disconnected() +{ + /* Reverse order because this comes from a Signal emission */ + for (auto it = executionQueue_.rbegin(); + it != executionQueue_.rend(); it++) { + if ((*it)->layer.disconnected) + (*it)->layer.disconnected(); + } +} + +void LayerManager::acquire() +{ + for (std::unique_ptr &layer : executionQueue_) + if (layer->layer.acquire) + layer->layer.acquire(); +} + +void LayerManager::release() +{ + for (std::unique_ptr &layer : executionQueue_) + if (layer->layer.release) + layer->layer.release(); +} + +const ControlInfoMap &LayerManager::controls(const ControlInfoMap &controlInfoMap) +{ + controlInfoMap_ = controlInfoMap; + + /* \todo Simplify this once ControlInfoMaps become easier to modify */ + for (std::unique_ptr &layer : executionQueue_) { + if (layer->layer.controls) { + ControlInfoMap::Map ret = layer->layer.controls(controlInfoMap_); + ControlInfoMap::Map map; + /* Merge the layer's ret later so that layers can overwrite */ + for (auto &pair : controlInfoMap_) + map.insert(pair); + for (auto &pair : ret) + map.insert(pair); + controlInfoMap_ = ControlInfoMap(std::move(map), + libcamera::controls::controls); + } + } + return controlInfoMap_; +} + +const ControlList &LayerManager::properties(const ControlList &properties) +{ + properties_ = properties; + for (std::unique_ptr &layer : executionQueue_) { + if (layer->layer.properties) { + ControlList ret = layer->layer.properties(properties_); + properties_.merge(ret, ControlList::MergePolicy::OverwriteExisting); + } + } + return properties_; +} + +const std::set &LayerManager::streams(const std::set &streams) +{ + streams_ = streams; + for (std::unique_ptr &layer : executionQueue_) { + if (layer->layer.streams) { + std::set ret = layer->layer.streams(streams_); + streams_.insert(ret.begin(), ret.end()); + } + } + return streams_; +} + +void LayerManager::generateConfiguration(Span &roles, + CameraConfiguration *config) +{ + for (std::unique_ptr &layer : executionQueue_) + if (layer->layer.generateConfiguration) + layer->layer.generateConfiguration(roles, config); +} + +void LayerManager::configure(CameraConfiguration *config) +{ + for (std::unique_ptr &layer : executionQueue_) + if (layer->layer.configure) + layer->layer.configure(config); +} + +void LayerManager::createRequest(uint64_t cookie, Request *request) +{ + for (std::unique_ptr &layer : executionQueue_) + if (layer->layer.createRequest) + layer->layer.createRequest(cookie, request); +} + +void LayerManager::queueRequest(Request *request) +{ + for (std::unique_ptr &layer : executionQueue_) + if (layer->layer.queueRequest) + layer->layer.queueRequest(request); +} + +void LayerManager::start(const ControlList *controls) +{ + for (std::unique_ptr &layer : executionQueue_) + if (layer->layer.start) + layer->layer.start(controls); +} + +void LayerManager::stop() +{ + for (std::unique_ptr &layer : executionQueue_) + if (layer->layer.stop) + layer->layer.stop(); +} + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 8e2aa921a620..226a94768514 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -40,6 +40,7 @@ libcamera_internal_sources = files([ 'ipc_pipe.cpp', 'ipc_pipe_unixsocket.cpp', 'ipc_unixsocket.cpp', + 'layer_manager.cpp', 'mapped_framebuffer.cpp', 'matrix.cpp', 'media_device.cpp', diff --git a/src/meson.build b/src/meson.build index 8eb8f05b362f..37368b01cbf2 100644 --- a/src/meson.build +++ b/src/meson.build @@ -63,6 +63,7 @@ subdir('libcamera') subdir('android') subdir('ipa') +subdir('layer') subdir('apps') From patchwork Thu Jun 26 09:59:40 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 23663 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id DA594BDCBF for ; Thu, 26 Jun 2025 10:00:30 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3335868E09; Thu, 26 Jun 2025 12:00:30 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ESY7bwnt"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 267B468DFA for ; Thu, 26 Jun 2025 12:00:23 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (unknown [IPv6:2404:7a81:160:2100:258b:9e43:6dff:c39d]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 4F0C01D54; Thu, 26 Jun 2025 12:00:03 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1750932004; bh=V/cY7vn7mRHYdKtgvbI8eScUhW68cgybMgTOSZc8C2k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ESY7bwntxH6oUjxB5j0czSR4USpS+9GxXJBmnQl4Iih8NqK1JsCNyMlTVQtyXnb2b xSdQnOXOQjvnxXxoPcMjFaXJke2ClMWpOHPE9LcbCGFVPnBSDyq4H4wUr5VJIcntkL ra3QsgvnTC1/cLukcnbjT02ojSC6EZMGJz5pHtrI= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , kieran.bingham@ideasonboard.com Subject: [RFC PATCH 5/7] libcamera: camera: Hook into the LayerManager Date: Thu, 26 Jun 2025 18:59:40 +0900 Message-ID: <20250626095944.1746345-6-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250626095944.1746345-1-paul.elder@ideasonboard.com> References: <20250626095944.1746345-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add hooks into the CameraManager to call into the LayerManager on all relevant public-facing interface of Camera. The entry point for each function into the LayerManager is has been chosen based on the capabilities that we want to allow to Layer implementations. Signed-off-by: Paul Elder --- include/libcamera/internal/camera.h | 4 ++++ src/libcamera/camera.cpp | 26 +++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/include/libcamera/internal/camera.h b/include/libcamera/internal/camera.h index 967d4e1693ec..1b9c3da695b6 100644 --- a/include/libcamera/internal/camera.h +++ b/include/libcamera/internal/camera.h @@ -18,6 +18,8 @@ #include +#include "libcamera/internal/layer_manager.h" + namespace libcamera { class CameraControlValidator; @@ -76,6 +78,8 @@ private: std::atomic state_; std::unique_ptr validator_; + + std::unique_ptr layerManager_; }; } /* namespace libcamera */ diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index c3e656cab671..895180452edb 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -741,18 +741,21 @@ void Camera::Private::setState(State state) void Camera::Private::emitBufferCompleted(Request *request, FrameBuffer *buffer) { Camera *camera = _o(); + layerManager_->bufferCompleted(request, buffer); camera->bufferCompleted.emit(request, buffer); } void Camera::Private::emitRequestCompleted(Request *request) { Camera *camera = _o(); + layerManager_->requestCompleted(request); camera->requestCompleted.emit(request); } void Camera::Private::emitDisconnected() { Camera *camera = _o(); + layerManager_->disconnected(); camera->disconnected.emit(); } @@ -943,6 +946,7 @@ Camera::Camera(std::unique_ptr d, const std::string &id, _d()->id_ = id; _d()->streams_ = streams; _d()->validator_ = std::make_unique(this); + _d()->layerManager_ = std::make_unique(); } Camera::~Camera() @@ -1032,6 +1036,8 @@ int Camera::acquire() return -EBUSY; } + d->layerManager_->acquire(); + d->setState(Private::CameraAcquired); return 0; @@ -1064,6 +1070,8 @@ int Camera::release() d->pipe_->invokeMethod(&PipelineHandler::release, ConnectionTypeBlocking, this); + d->layerManager_->release(); + d->setState(Private::CameraAvailable); return 0; @@ -1081,7 +1089,7 @@ int Camera::release() */ const ControlInfoMap &Camera::controls() const { - return _d()->controlInfo_; + return _d()->layerManager_->controls(_d()->controlInfo_); } /** @@ -1094,7 +1102,7 @@ const ControlInfoMap &Camera::controls() const */ const ControlList &Camera::properties() const { - return _d()->properties_; + return _d()->layerManager_->properties(_d()->properties_); } /** @@ -1110,7 +1118,7 @@ const ControlList &Camera::properties() const */ const std::set &Camera::streams() const { - return _d()->streams_; + return _d()->layerManager_->streams(_d()->streams_); } /** @@ -1158,6 +1166,8 @@ std::unique_ptr Camera::generateConfiguration(SpanlayerManager_->generateConfiguration(roles, config.get()); + return config; } @@ -1242,6 +1252,8 @@ int Camera::configure(CameraConfiguration *config) d->activeStreams_.insert(stream); } + d->layerManager_->configure(config); + d->setState(Private::CameraConfigured); return 0; @@ -1282,6 +1294,8 @@ std::unique_ptr Camera::createRequest(uint64_t cookie) /* Associate the request with the pipeline handler. */ d->pipe_->registerRequest(request.get()); + d->layerManager_->createRequest(cookie, request.get()); + return request; } @@ -1366,6 +1380,8 @@ int Camera::queueRequest(Request *request) } } + d->layerManager_->queueRequest(request); + d->pipe_->invokeMethod(&PipelineHandler::queueRequest, ConnectionTypeQueued, request); @@ -1402,6 +1418,8 @@ int Camera::start(const ControlList *controls) ASSERT(d->requestSequence_ == 0); + d->layerManager_->start(controls); + ret = d->pipe_->invokeMethod(&PipelineHandler::start, ConnectionTypeBlocking, this, controls); if (ret) @@ -1446,6 +1464,8 @@ int Camera::stop() d->setState(Private::CameraStopping); + d->layerManager_->stop(); + d->pipe_->invokeMethod(&PipelineHandler::stop, ConnectionTypeBlocking, this); From patchwork Thu Jun 26 09:59:41 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 23664 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id AD752BDCBF for ; Thu, 26 Jun 2025 10:00:32 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0F9BE68DFE; Thu, 26 Jun 2025 12:00:32 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="roR3kXJi"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E4E1F68DFC for ; Thu, 26 Jun 2025 12:00:24 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (unknown [IPv6:2404:7a81:160:2100:258b:9e43:6dff:c39d]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1B1CA743; Thu, 26 Jun 2025 12:00:04 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1750932006; bh=a0vtHLYTYKS0mx8qsHaCE3ZbBQ8aSnBmLOL29vEioM0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=roR3kXJir96qGSYESEdR41c+eQvFKFeh5YCLuaYtuGDBNNm6cQdIAM23qasAn/whf kNdWTxpd46ARks0ZwYzOoemrFAJMhw9m8BtNBqiKZ1ItmaE9NM2srTqwp0l+VDcHIe 3l18b+WY/AxxDSJkGkZmoJq5yvYqcI95amFpCmuo= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , kieran.bingham@ideasonboard.com Subject: [RFC PATCH 6/7] layer: Add layer to inject AeEnable control Date: Thu, 26 Jun 2025 18:59:41 +0900 Message-ID: <20250626095944.1746345-7-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250626095944.1746345-1-paul.elder@ideasonboard.com> References: <20250626095944.1746345-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a layer to implement the AeEnable control, so that we can remove it from all IPAs and the Camera class, so that it is transparent to all parties and it is available for applications to use. Signed-off-by: Paul Elder --- For layers like this that we forsee to be "always built-in" we might want a way to build it into libcamera instead of into separate shared object files. In any case, this also serves as a demo of how I envision a layer implementation to work. --- src/layer/inject_controls/inject_controls.cpp | 164 ++++++++++++++++++ src/layer/inject_controls/inject_controls.h | 29 ++++ src/layer/inject_controls/meson.build | 15 ++ src/layer/meson.build | 2 + 4 files changed, 210 insertions(+) create mode 100644 src/layer/inject_controls/inject_controls.cpp create mode 100644 src/layer/inject_controls/inject_controls.h create mode 100644 src/layer/inject_controls/meson.build diff --git a/src/layer/inject_controls/inject_controls.cpp b/src/layer/inject_controls/inject_controls.cpp new file mode 100644 index 000000000000..133a8f51a1f6 --- /dev/null +++ b/src/layer/inject_controls/inject_controls.cpp @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas On Board Oy + * + * Layer implementation for injecting controls + */ + +#include "inject_controls.h" + +#include +#include + +#include + +#include +#include +#include + +namespace layer { + +namespace inject_controls { + +libcamera::ControlInfoMap::Map controls(libcamera::ControlInfoMap &ctrls) +{ + auto it = ctrls.find(&libcamera::controls::ExposureTimeMode); + if (it != ctrls.end()) { + for (auto entry : it->second.values()) { + if (entry == libcamera::ControlValue(libcamera::controls::ExposureTimeModeAuto)) + aeAvailable_ = true; + if (entry == libcamera::ControlValue(libcamera::controls::ExposureTimeModeManual)) + meAvailable_ = true; + } + } + + it = ctrls.find(&libcamera::controls::AnalogueGainMode); + if (it != ctrls.end()) { + for (auto entry : it->second.values()) { + if (entry == libcamera::ControlValue(libcamera::controls::AnalogueGainModeAuto)) + agAvailable_ = true; + if (entry == libcamera::ControlValue(libcamera::controls::AnalogueGainModeManual)) + mgAvailable_ = true; + } + } + + std::set values; + if (aeAvailable_ || agAvailable_) + values.insert(true); + if (meAvailable_ || mgAvailable_) + values.insert(false); + + if (values.empty()) + return {}; + + if (values.size() == 1) { + bool value = *values.begin(); + return { { &libcamera::controls::AeEnable, + libcamera::ControlInfo(value, value, value) } }; + } + + return { { &libcamera::controls::AeEnable, libcamera::ControlInfo(false, true, true) } }; +} + +void queueRequest(libcamera::Request *request) +{ + libcamera::ControlList &ctrls = request->controls(); + auto aeEnable = ctrls.get(libcamera::controls::AeEnable); + if (!aeEnable) + return; + + if (*aeEnable) { + if (aeAvailable_) { + ctrls.set(libcamera::controls::ExposureTimeMode, + libcamera::controls::ExposureTimeModeAuto); + } + + if (agAvailable_) { + ctrls.set(libcamera::controls::AnalogueGainMode, + libcamera::controls::AnalogueGainModeAuto); + } + } else { + if (meAvailable_) { + ctrls.set(libcamera::controls::ExposureTimeMode, + libcamera::controls::ExposureTimeModeManual); + } + + if (mgAvailable_) { + ctrls.set(libcamera::controls::AnalogueGainMode, + libcamera::controls::AnalogueGainModeManual); + } + } +} + +void requestCompleted(libcamera::Request *request) +{ + libcamera::ControlList &metadata = request->metadata(); + + auto eMode = metadata.get(libcamera::controls::ExposureTimeMode); + auto aMode = metadata.get(libcamera::controls::AnalogueGainMode); + + if (!eMode && !aMode) + return; + + bool ae = eMode && eMode == libcamera::controls::ExposureTimeModeAuto; + bool me = eMode && eMode == libcamera::controls::ExposureTimeModeManual; + bool ag = aMode && aMode == libcamera::controls::AnalogueGainModeAuto; + bool mg = aMode && aMode == libcamera::controls::AnalogueGainModeManual; + + /* Exposure time not reported at all; use gain only */ + if (!ae && !me) { + metadata.set(libcamera::controls::AeEnable, ag); + return; + } + + /* Analogue gain not reported at all; use exposure time only */ + if (!ag && !mg) { + metadata.set(libcamera::controls::AeEnable, ae); + return; + } + + /* + * Gain mode and exposure mode are not equal; therefore at least one is + * manual, so set AeEnable to false + */ + if (ag != ae) { + metadata.set(libcamera::controls::AeEnable, false); + return; + } + + /* ag and ae are equal, so just choose one */ + metadata.set(libcamera::controls::AeEnable, ag); + return; +} + +} /* namespace inject_controls */ + +} /* namespace layer */ + +namespace libcamera { + +extern "C" { + +struct Layer layerInfo { + .name = "inject_controls", + .layerAPIVersion = 1, + .init = nullptr, + .bufferCompleted = nullptr, + .requestCompleted = layer::inject_controls::requestCompleted, + .disconnected = nullptr, + .acquire = nullptr, + .release = nullptr, + .controls = layer::inject_controls::controls, + .properties = nullptr, + .streams = nullptr, + .generateConfiguration = nullptr, + .configure = nullptr, + .createRequest = nullptr, + .queueRequest = layer::inject_controls::queueRequest, + .start = nullptr, + .stop = nullptr, +}; + +} + +} /* namespace libcamera */ diff --git a/src/layer/inject_controls/inject_controls.h b/src/layer/inject_controls/inject_controls.h new file mode 100644 index 000000000000..a65cda897836 --- /dev/null +++ b/src/layer/inject_controls/inject_controls.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2025, Ideas On Board Oy + * + * Layer implementation for injecting controls + */ + +#pragma once + +#include +#include + +namespace layer { + +namespace inject_controls { + +libcamera::ControlInfoMap::Map controls(libcamera::ControlInfoMap &ctrls); +void queueRequest(libcamera::Request *); +void requestCompleted(libcamera::Request *); + +bool initialized_ = false; +bool aeAvailable_ = false; +bool meAvailable_ = false; +bool agAvailable_ = false; +bool mgAvailable_ = false; + +} /* namespace inject_controls */ + +} /* namespace layer */ diff --git a/src/layer/inject_controls/meson.build b/src/layer/inject_controls/meson.build new file mode 100644 index 000000000000..72f22e184923 --- /dev/null +++ b/src/layer/inject_controls/meson.build @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: CC0-1.0 + +layer_name = 'layer_inject_controls' + +layer_inject_sources = files([ + 'inject_controls.h', + 'inject_controls.cpp', +]) + +mod = shared_module(layer_name, layer_inject_sources, + name_prefix : '', + include_directories : [layer_includes], + dependencies : libcamera_public, + install : true, + install_dir : layer_install_dir) diff --git a/src/layer/meson.build b/src/layer/meson.build index dee5e5ac5804..d5793f8c4525 100644 --- a/src/layer/meson.build +++ b/src/layer/meson.build @@ -8,3 +8,5 @@ layer_install_dir = libcamera_libdir / 'layers' config_h.set('LAYER_DIR', '"' + get_option('prefix') / layer_install_dir + '"') + +subdir('inject_controls') From patchwork Thu Jun 26 09:59:42 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 23665 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id A1BB5BDCBF for ; Thu, 26 Jun 2025 10:00:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id EF6CE68E01; Thu, 26 Jun 2025 12:00:33 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="wKU/RlnU"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B36DB68DF6 for ; Thu, 26 Jun 2025 12:00:26 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (unknown [IPv6:2404:7a81:160:2100:258b:9e43:6dff:c39d]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id DB01752B4; Thu, 26 Jun 2025 12:00:06 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1750932008; bh=Y/NQC5yCR8L/c+6lMIweKqgHtyeobNQeatfrnBDQqvw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=wKU/RlnUoi+bIbRHweDdRlG/0A1uIUmpnzGQVNTpcNhXx4y3XZwNhKj9Gjmi5re+z WfEUzFi7Wchmyhg31k8EEH5d2kEbvSaXnhYsjB73foZhRJISoiJJAkmzdIZB6VBuGp wy48s24b+jsFtp8VHHy8h9K2HaI9xM3TJNnL88JM= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , kieran.bingham@ideasonboard.com Subject: [RFC PATCH 7/7] camera, ipa: all: Remove AeEnable handling Date: Thu, 26 Jun 2025 18:59:42 +0900 Message-ID: <20250626095944.1746345-8-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250626095944.1746345-1-paul.elder@ideasonboard.com> References: <20250626095944.1746345-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Remove AeEnable handling from the Camera and all IPAs, as it is now handled by the inject_controls layer. Signed-off-by: Paul Elder --- The mali-c55 IPA needs to support AnalogueGainMode and ExposureTimeMode, but that is out of scope for this series. --- src/ipa/mali-c55/algorithms/agc.cpp | 1 + src/ipa/rkisp1/algorithms/agc.cpp | 2 -- src/ipa/rpi/common/ipa_base.cpp | 2 -- src/libcamera/camera.cpp | 19 ------------------- src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 5 ----- 5 files changed, 1 insertion(+), 28 deletions(-) diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp index 70667db34f1e..818cea35afa9 100644 --- a/src/ipa/mali-c55/algorithms/agc.cpp +++ b/src/ipa/mali-c55/algorithms/agc.cpp @@ -137,6 +137,7 @@ int Agc::init(IPAContext &context, const YamlObject &tuningData) if (ret) return ret; + /* \todo Support AnalogueGainMode and ExposureTimeMode */ context.ctrlMap[&controls::AeEnable] = ControlInfo(false, true); context.ctrlMap[&controls::DigitalGain] = ControlInfo( static_cast(kMinDigitalGain), diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 137a0750017b..8a890d387efb 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -155,8 +155,6 @@ int Agc::init(IPAContext &context, const YamlObject &tuningData) ControlInfo({ { ControlValue(controls::AnalogueGainModeAuto), ControlValue(controls::AnalogueGainModeManual) } }, ControlValue(controls::AnalogueGainModeAuto)); - /* \todo Move this to the Camera class */ - context.ctrlMap[&controls::AeEnable] = ControlInfo(false, true, true); context.ctrlMap.merge(controls()); return 0; diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp index 6565f5366312..059b1bd5942e 100644 --- a/src/ipa/rpi/common/ipa_base.cpp +++ b/src/ipa/rpi/common/ipa_base.cpp @@ -55,8 +55,6 @@ constexpr Duration controllerMinFrameDuration = 1.0s / 30.0; /* List of controls handled by the Raspberry Pi IPA */ const ControlInfoMap::Map ipaControls{ - /* \todo Move this to the Camera class */ - { &controls::AeEnable, ControlInfo(false, true, true) }, { &controls::ExposureTimeMode, ControlInfo({ { ControlValue(controls::ExposureTimeModeAuto), ControlValue(controls::ExposureTimeModeManual) } }, diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp index 895180452edb..2fb82a7ae1d5 100644 --- a/src/libcamera/camera.cpp +++ b/src/libcamera/camera.cpp @@ -1361,25 +1361,6 @@ int Camera::queueRequest(Request *request) } } - /* Pre-process AeEnable. */ - ControlList &controls = request->controls(); - const auto &aeEnable = controls.get(controls::AeEnable); - if (aeEnable) { - if (_d()->controlInfo_.count(controls::AnalogueGainMode.id()) && - !controls.contains(controls::AnalogueGainMode.id())) { - controls.set(controls::AnalogueGainMode, - *aeEnable ? controls::AnalogueGainModeAuto - : controls::AnalogueGainModeManual); - } - - if (_d()->controlInfo_.count(controls::ExposureTimeMode.id()) && - !controls.contains(controls::ExposureTimeMode.id())) { - controls.set(controls::ExposureTimeMode, - *aeEnable ? controls::ExposureTimeModeAuto - : controls::ExposureTimeModeManual); - } - } - d->layerManager_->queueRequest(request); d->pipe_->invokeMethod(&PipelineHandler::queueRequest, diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp index 58aa0eb4cb52..586e932d2dac 100644 --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp @@ -596,11 +596,6 @@ int UVCCameraData::init(MediaDevice *media) addControl(cid, info, &ctrls); } - if (autoExposureMode_ && manualExposureMode_) { - /* \todo Move this to the Camera class */ - ctrls[&controls::AeEnable] = ControlInfo(false, true, true); - } - controlInfo_ = ControlInfoMap(std::move(ctrls), controls::controls); /*