From patchwork Tue Mar 26 11:24:04 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Milan Zamazal X-Patchwork-Id: 19813 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 10E52C32CA for ; Tue, 26 Mar 2024 11:26:36 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7EE7E6336F; Tue, 26 Mar 2024 12:26:35 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="XM257og2"; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9845E6331B for ; Tue, 26 Mar 2024 12:26:29 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1711452379; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ZOpqgvP0/AzliHmux1yBNzHqutaIO8zDnOdJa0JHxP8=; b=XM257og2xve+NIOw68W0FZLVtUoXumadVI7XGMEG02muqvz550kjEg1nxrp00eXOmeDb92 lJvw6Nj4vKqRQ1exbedlECwBnlfYoveTT8p/87ASzPp5J+af6L744GMVJZ48RkxmCA5qvH v9twa3Kikl9CUj1bbaV74BVC8HqCEbE= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-438-9DbREjoKPSuP5Udupxeqbw-1; Tue, 26 Mar 2024 07:26:18 -0400 X-MC-Unique: 9DbREjoKPSuP5Udupxeqbw-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.rdu2.redhat.com [10.11.54.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 9DA01101A526 for ; Tue, 26 Mar 2024 11:26:15 +0000 (UTC) Received: from nuthatch.brq.redhat.com (unknown [10.43.17.39]) by smtp.corp.redhat.com (Postfix) with ESMTP id 110003C22; Tue, 26 Mar 2024 11:26:14 +0000 (UTC) From: Milan Zamazal To: libcamera-devel@lists.libcamera.org Cc: Milan Zamazal Subject: [RFC PATCH 01/11] config: Introduce global runtime configuration Date: Tue, 26 Mar 2024 12:24:04 +0100 Message-ID: <20240326112419.503286-2-mzamazal@redhat.com> In-Reply-To: <20240326112419.503286-1-mzamazal@redhat.com> References: <20240326112419.503286-1-mzamazal@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.1 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com 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" Currently, libcamera can be configured in runtime using several environment variables. With introducing more and more variables, this mechanism reaches its limits. It would be simpler and more flexible if it was possible to configure libcamera in a single file. For example, there have been a request for defining pipeline precedence in runtime. We want to have support for multiple pipelines compiled in order to have single packages in distributions. And then being able to select among them as needed based on the particular hardware or operating system environment. This can for example allow easy switching between hardware, CPU or GPU IPAs. Another possible use case is tuning image output, especially with software ISP, to user liking. For example, some users may prefer higher contrast without the need to use the corresponding knobs, if present at all, in every application. This patch introduces basic support for configuration files. GlobalConfiguration class reads, stores and adds access to the configuration. Its instance is stored as a singleton and be accessed using a static method of the class. libcamera configuration can be specified using a system-wide configuration file or a user configuration file. The user configuration file take precedence if present. There is currently no way to merge multiple configuration files, the one found is used as the only configuration file. If no configuration file is present, nothing changes to the current libcamera behavior (except for some log messages). The configuration file is a YAML file, we already have a mechanism for handling YAML configuration files in libcamera and the given infrastructure can be reused for the purpose. However, the configuration type is abstracted to make contingent future change of the underlying class easier, if retaining (most of) its API. The configuration is versioned. This has currently no particular meaning but is likely to have its purpose in future, especially once configuration validation is introduced. The configuration YAML file looks as follows: --- version: 1 configuration: WHATEVER CONFIGURATION NEEDED This patch introduces just the basic idea. Actually using the configuration in the corresponding places (everything what is currently configurable via environment variables should be configurable in the file configuration), refining the mechanism (e.g. configuration file validation to avoid mistakes caused by typos) and other improvements will be addressed in followup patches. GlobalConfiguration is meant to be used as a globally accessible singleton. There is currently no reason to have more than one instance. Signed-off-by: Milan Zamazal --- .../libcamera/internal/global_configuration.h | 44 ++++++ include/libcamera/internal/meson.build | 1 + src/libcamera/global_configuration.cpp | 132 ++++++++++++++++++ src/libcamera/meson.build | 1 + 4 files changed, 178 insertions(+) create mode 100644 include/libcamera/internal/global_configuration.h create mode 100644 src/libcamera/global_configuration.cpp diff --git a/include/libcamera/internal/global_configuration.h b/include/libcamera/internal/global_configuration.h new file mode 100644 index 00000000..0a4f3c02 --- /dev/null +++ b/include/libcamera/internal/global_configuration.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024 Red Hat, inc. + * + * global_configuration.h - Global configuration handling + */ + +#pragma once + +#include +#include + +#include "libcamera/internal/yaml_parser.h" + +namespace libcamera { + +class GlobalConfiguration +{ +public: + /** + * \brief Type representing global libcamera configuration. + * + * All code outside GlobalConfiguration must use this type and not the + * underlying type. + */ + using Configuration = const YamlObject &; + + static unsigned int version(); + static Configuration configuration(); + +private: + static const std::vector globalConfigurationFiles; + + bool initialized_; + std::unique_ptr configuration_; + + GlobalConfiguration(); + bool loadFile(const std::filesystem::path &fileName); + void load(); + static const GlobalConfiguration &instance(); + static Configuration get(); +}; + +} /* namespace libcamera */ diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build index 160fdc37..640a54aa 100644 --- a/include/libcamera/internal/meson.build +++ b/include/libcamera/internal/meson.build @@ -28,6 +28,7 @@ libcamera_internal_headers = files([ 'dma_heaps.h', 'formats.h', 'framebuffer.h', + 'global_configuration.h', 'ipa_manager.h', 'ipa_module.h', 'ipa_proxy.h', diff --git a/src/libcamera/global_configuration.cpp b/src/libcamera/global_configuration.cpp new file mode 100644 index 00000000..72c3ce26 --- /dev/null +++ b/src/libcamera/global_configuration.cpp @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024 Red Hat, inc. + * + * global_configuration.cpp - Global configuration handling + */ + +#include "libcamera/internal/global_configuration.h" + +#include +#include +#include +#include + +#include +#include + +#include "libcamera/internal/yaml_parser.h" + +#include "libcamera/base/utils.h" + +namespace libcamera { + +LOG_DEFINE_CATEGORY(Configuration) + +/** + * \class GlobalConfiguration + * \brief Support for global libcamera configuration + * + * ... + */ + +GlobalConfiguration::GlobalConfiguration() + : initialized_(false), configuration_(std::make_unique()) +{ +} + +const std::vector + GlobalConfiguration::globalConfigurationFiles = { + std::filesystem::path(LIBCAMERA_SYSCONF_DIR) / "configuration.yaml", + std::filesystem::path("/etc/libcamera/configuration.yaml"), + }; + +void GlobalConfiguration::load() +{ + std::filesystem::path userConfigurationDirectory; + char *xdgConfigHome = utils::secure_getenv("XDG_CONFIG_HOME"); + if (xdgConfigHome) { + userConfigurationDirectory = xdgConfigHome; + } else { + const char *home = utils::secure_getenv("HOME"); + if (home) + userConfigurationDirectory = + std::filesystem::path(home) / ".config"; + } + + if (!userConfigurationDirectory.empty()) { + std::filesystem::path user_configuration_file = + userConfigurationDirectory / "libcamera" / "configuration.yaml"; + if (loadFile(user_configuration_file)) + return; + } + + for (auto path : globalConfigurationFiles) + if (loadFile(path)) + return; + + initialized_ = true; + LOG(Configuration, Debug) << "No configuration file found"; +} + +bool GlobalConfiguration::loadFile(const std::filesystem::path &fileName) +{ + File file(fileName); + if (!file.exists()) { + return false; + } + + if (!file.open(File::OpenModeFlag::ReadOnly)) { + LOG(Configuration, Warning) + << "Failed to open configuration file '" << fileName << "'"; + return true; + } + + auto root = YamlParser::parse(file); + if (!root) { + LOG(Configuration, Warning) << "Failed to parse configuration file " + << fileName; + return true; + } + configuration_ = std::move(root); + initialized_ = true; + LOG(Configuration, Info) << "Configuration file " << fileName << "loaded"; + + return true; +} + +const GlobalConfiguration &GlobalConfiguration::instance() +{ + static GlobalConfiguration configuration; + if (!configuration.initialized_) { + configuration.load(); + } + return configuration; +} + +GlobalConfiguration::Configuration GlobalConfiguration::get() +{ + return (*instance().configuration_); +} + +/** + * \brief Return configuration version. + * + * ... + */ +unsigned int GlobalConfiguration::version() +{ + return get()["version"].get().value_or(0); +} + +/** + * \brief Return libcamera global configuration. + * + * ... + */ +GlobalConfiguration::Configuration GlobalConfiguration::configuration() +{ + return get()["configuration"]; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index a3b12bc1..b81c75b8 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -21,6 +21,7 @@ libcamera_sources = files([ 'framebuffer.cpp', 'framebuffer_allocator.cpp', 'geometry.cpp', + 'global_configuration.cpp', 'ipa_controls.cpp', 'ipa_data_serializer.cpp', 'ipa_interface.cpp',