From patchwork Fri Oct 2 14:31:17 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9910 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 CA811C3B5C for ; Fri, 2 Oct 2020 14:32:09 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 864E663B6A; Fri, 2 Oct 2020 16:32:09 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="OklxCzZ6"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id D954763B59 for ; Fri, 2 Oct 2020 16:32:07 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id ABF36528; Fri, 2 Oct 2020 16:32:05 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649127; bh=u2gyxacPihn9ZLzANFT9dxHtFdESh1oXWYz2HI8cvTg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OklxCzZ6Z2k6+yDqawnnKD+UHf6iF1Si0gZXwUx2bxVOscY5yqQHlPKj81lVIiz7F 2Fa6qI19h9t9vN6he7qgfPnSFl5vaXNzMptlsGG6qwL7PmrEsZciGwbzSZi8r6ZG1v BBtXLQOSvObSjA1mj/FgI/KcvAPivcE/vKr6P5/I= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:17 +0900 Message-Id: <20201002143154.468162-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 01/38] Documentation: coding-style: Document global variable guidelines 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" Document the issue related to global variable dependencies and how to avoid them. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- Changes in v3: - add more detail New in v2 --- Documentation/coding-style.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Documentation/coding-style.rst b/Documentation/coding-style.rst index 8af06d6a..71d5c0b2 100644 --- a/Documentation/coding-style.rst +++ b/Documentation/coding-style.rst @@ -187,6 +187,25 @@ These rules match the `object ownership rules from the Chromium C++ Style Guide` long term borrowing isn't marked through language constructs, it shall be documented explicitly in details in the API. +Global Variables +~~~~~~~~~~~~~~~~ + +The order of initializations and destructions of global variables cannot be +reasonably controlled. This can cause problems (including segfaults) when global +variables depend on each other, directly or indirectly. For example, if the +declaration of a global variable calls a constructor which uses another global +variable that hasn't been initialized yet, incorrect behavior is likely. +Similar issues may occur when the library is unloaded and global variables are +destroyed. + +Global variables that are statically initialized and have trivial destructors +(such as an integer constant) do not cause any issue. Other global variables +shall be avoided when possible, but are allowed when required (for instance to +implement factories with auto-registration). They shall not depend on any other +global variable, should run a minimal amount of code in the constructor and +destructor, and code that contains dependencies should be moved to a later +point in time. + C Compatibility Headers ~~~~~~~~~~~~~~~~~~~~~~~ From patchwork Fri Oct 2 14:31:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9911 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 1598DC3B5C for ; Fri, 2 Oct 2020 14:32:12 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id DABDD63B29; Fri, 2 Oct 2020 16:32:11 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="hGPZEHJN"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DE22463B29 for ; Fri, 2 Oct 2020 16:32:10 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 270C31227; Fri, 2 Oct 2020 16:32:07 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649130; bh=XrSFf2tnw9MzSNlbvuyTuFlIVcjBkgThcOJeD3SNrDI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hGPZEHJNEWFlKRtugeqRVB8LqMS9s6boTLHrTPHHHfHLAMsDGFJ7ql2pmqubwyb4a j20FlGquhkaDBFFDf0VtGYK9FKMQH1LgRwWj/P5jlprYalyZGh8hPeIcZhfCCAhEMq poBSAlMFgSW9SHG5kvBiX/pFGEzmSgYdhGDdN2dI= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:18 +0900 Message-Id: <20201002143154.468162-3-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 02/38] libcamera: ProcessManager: make ProcessManager lifetime explicitly managed 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" If any Process instances are destroyed after the ProcessManager is destroyed, then a segfault will occur. Fix this by making the lifetime of the ProcessManager explicit, and make the CameraManager construct and deconstruct (automatically, via a member variable) the ProcessManager. Update the tests accordingly. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart Reviewed-by: Niklas Söderlund Reviewed-by: Jacopo Mondi --- Changes in v3: - squashed "process: forward-declare EventNotifier" Changes in v2: - update changelog (member variable, instead of unique pointer) - update tests --- include/libcamera/internal/process.h | 29 ++++++++++++++++++ src/libcamera/camera_manager.cpp | 2 ++ src/libcamera/process.cpp | 46 ++++++++++++---------------- test/log/log_process.cpp | 2 ++ test/process/process_test.cpp | 2 ++ 5 files changed, 54 insertions(+), 27 deletions(-) diff --git a/include/libcamera/internal/process.h b/include/libcamera/internal/process.h index 2688557c..254cda85 100644 --- a/include/libcamera/internal/process.h +++ b/include/libcamera/internal/process.h @@ -7,6 +7,7 @@ #ifndef __LIBCAMERA_INTERNAL_PROCESS_H__ #define __LIBCAMERA_INTERNAL_PROCESS_H__ +#include #include #include @@ -14,6 +15,8 @@ namespace libcamera { +class EventNotifier; + class Process final { public: @@ -50,6 +53,32 @@ private: friend class ProcessManager; }; +class ProcessManager +{ +public: + ProcessManager(); + ~ProcessManager(); + + void registerProcess(Process *proc); + + static ProcessManager *instance(); + + int writePipe() const; + + const struct sigaction &oldsa() const; + +private: + static ProcessManager *self_; + + void sighandler(EventNotifier *notifier); + + std::list processes_; + + struct sigaction oldsa_; + EventNotifier *sigEvent_; + int pipe_[2]; +}; + } /* namespace libcamera */ #endif /* __LIBCAMERA_INTERNAL_PROCESS_H__ */ diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index 47d56256..b8d3ccc8 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -18,6 +18,7 @@ #include "libcamera/internal/ipa_manager.h" #include "libcamera/internal/log.h" #include "libcamera/internal/pipeline_handler.h" +#include "libcamera/internal/process.h" #include "libcamera/internal/thread.h" #include "libcamera/internal/utils.h" @@ -67,6 +68,7 @@ private: std::unique_ptr enumerator_; IPAManager ipaManager_; + ProcessManager processManager_; }; CameraManager::Private::Private(CameraManager *cm) diff --git a/src/libcamera/process.cpp b/src/libcamera/process.cpp index 994190dc..72b5afe2 100644 --- a/src/libcamera/process.cpp +++ b/src/libcamera/process.cpp @@ -41,28 +41,6 @@ LOG_DEFINE_CATEGORY(Process) * The ProcessManager singleton keeps track of all created Process instances, * and manages the signal handling involved in terminating processes. */ -class ProcessManager -{ -public: - void registerProcess(Process *proc); - - static ProcessManager *instance(); - - int writePipe() const; - - const struct sigaction &oldsa() const; - -private: - void sighandler(EventNotifier *notifier); - ProcessManager(); - ~ProcessManager(); - - std::list processes_; - - struct sigaction oldsa_; - EventNotifier *sigEvent_; - int pipe_[2]; -}; namespace { @@ -127,8 +105,20 @@ void ProcessManager::registerProcess(Process *proc) processes_.push_back(proc); } +ProcessManager *ProcessManager::self_ = nullptr; + +/** + * \brief Construct a ProcessManager instance + * + * The ProcessManager class is meant to only be instantiated once, by the + * CameraManager. + */ ProcessManager::ProcessManager() { + if (self_) + LOG(Process, Fatal) + << "Multiple ProcessManager objects are not allowed"; + sigaction(SIGCHLD, NULL, &oldsa_); struct sigaction sa; @@ -145,6 +135,8 @@ ProcessManager::ProcessManager() << "Failed to initialize pipe for signal handling"; sigEvent_ = new EventNotifier(pipe_[0], EventNotifier::Read); sigEvent_->activated.connect(this, &ProcessManager::sighandler); + + self_ = this; } ProcessManager::~ProcessManager() @@ -153,21 +145,21 @@ ProcessManager::~ProcessManager() delete sigEvent_; close(pipe_[0]); close(pipe_[1]); + + self_ = nullptr; } /** * \brief Retrieve the Process manager instance * - * The ProcessManager is a singleton and can't be constructed manually. This - * method shall instead be used to retrieve the single global instance of the - * manager. + * The ProcessManager is constructed by the CameraManager. This function shall + * be used to retrieve the single instance of the manager. * * \return The Process manager instance */ ProcessManager *ProcessManager::instance() { - static ProcessManager processManager; - return &processManager; + return self_; } /** diff --git a/test/log/log_process.cpp b/test/log/log_process.cpp index b024f002..2a826222 100644 --- a/test/log/log_process.cpp +++ b/test/log/log_process.cpp @@ -132,6 +132,8 @@ private: exitCode_ = exitCode; } + ProcessManager processManager_; + Process proc_; Process::ExitStatus exitStatus_; string logPath_; diff --git a/test/process/process_test.cpp b/test/process/process_test.cpp index 42a26749..a3eaef80 100644 --- a/test/process/process_test.cpp +++ b/test/process/process_test.cpp @@ -87,6 +87,8 @@ private: exitCode_ = exitCode; } + ProcessManager processManager_; + Process proc_; enum Process::ExitStatus exitStatus_; int exitCode_; From patchwork Fri Oct 2 14:31:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9912 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 6B29DC3B5C for ; Fri, 2 Oct 2020 14:32:15 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3A3B363B72; Fri, 2 Oct 2020 16:32:15 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="UU5SFXn8"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9AB2763B6A for ; Fri, 2 Oct 2020 16:32:13 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 38F402A2; Fri, 2 Oct 2020 16:32:10 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649133; bh=C5e/tYKbcIgAGB36WLEAVYnxBQ8EfsCOOgtMfV9F9kQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UU5SFXn8iOEohy21RaRn8Lk3O3emOkv+d5KM4g738o0HpajW3WX7h26sBGdjGmMcz LNHr94XEoca/uXV+lnngupwymfVXa2j2C1hnzJ2og7++0r8oXUP35osZdNVgXQLnjA pTYri9ICBsHdU2EebEh6/qK00WG5edGQua2eKyW0= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:19 +0900 Message-Id: <20201002143154.468162-4-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 03/38] libcamera: PipelineHandler: Move printing pipeline names to CameraManager 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" Since pipeline registration is done with declaring static factory objects, there is a risk that pipeline factories will be constructed before libcamera facilities are ready. For example, logging in the constructor of a pipeline handler factory may cause a segfault if threading isn't ready yet. Avoid this issue by moving printing the registration of the pipeline handler to the camera manager. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- No change in v3 Cosmetic changes in v2 --- src/libcamera/camera_manager.cpp | 3 +++ src/libcamera/pipeline_handler.cpp | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp index b8d3ccc8..756f5b2b 100644 --- a/src/libcamera/camera_manager.cpp +++ b/src/libcamera/camera_manager.cpp @@ -142,6 +142,9 @@ void CameraManager::Private::createPipelineHandlers() PipelineHandlerFactory::factories(); for (PipelineHandlerFactory *factory : factories) { + LOG(Camera, Debug) + << "Found registered pipeline handler '" + << factory->name() << "'"; /* * Try each pipeline handler until it exhaust * all pipelines it can provide. diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 918aea1e..894200ee 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -689,9 +689,6 @@ void PipelineHandlerFactory::registerType(PipelineHandlerFactory *factory) std::vector &factories = PipelineHandlerFactory::factories(); factories.push_back(factory); - - LOG(Pipeline, Debug) - << "Registered pipeline handler \"" << factory->name() << "\""; } /** From patchwork Fri Oct 2 14:31:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9913 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 3F721C3B5C for ; Fri, 2 Oct 2020 14:32:20 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 00AC663BA1; Fri, 2 Oct 2020 16:32:20 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="oZdwDAjq"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id E295E63B29 for ; Fri, 2 Oct 2020 16:32:18 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7002F2A2; Fri, 2 Oct 2020 16:32:16 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649138; bh=iroTZMQHKG6AvKyEBW9MuSwg3YEDgCWLQtHkfiQT4OU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=oZdwDAjql4zZKzsKR6iAPU4kzSbUds9vSzJdAjrR1vsNQczhhQK/DlkJvjQqGv8kv ul9RUdR9cdGvaUA3YWy77AdOT/F/e5ZbtkX7ixI9rQNWKyyFUGQf5obJ0X/GyoHa5l Q811O1Nh2jpEiBXFpO6ERYDVcAa8n3IaEekC3ykA= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:21 +0900 Message-Id: <20201002143154.468162-6-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 05/38] libcamera: Update dep5 to specify license for mojo 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 licensing information for mojo in dep5. Signed-off-by: Paul Elder Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- Changes in v3: - fix copyright years New in v2 --- .reuse/dep5 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.reuse/dep5 b/.reuse/dep5 index a66f27e7..1fd90dd7 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -13,3 +13,9 @@ Files: src/qcam/assets/feathericons/*.svg Copyright: 2019 Cole Bemis (and other Feather icons contributors) License: MIT Comment: https://feathericons.com/ + +Files: utils/ipc/mojo + utils/ipc/tools +Copyright: Copyright 2013-2020 The Chromium Authors. All rights reserved. +License: BSD-3-Clause +Source: https://chromium.googlesource.com/chromium/src.git/ From patchwork Fri Oct 2 14:31:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9914 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 9A71EC3B5C for ; Fri, 2 Oct 2020 14:32:23 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 636B863BA1; Fri, 2 Oct 2020 16:32:23 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="d+1hgNOP"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C156C63B6A for ; Fri, 2 Oct 2020 16:32:21 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 67DBD528; Fri, 2 Oct 2020 16:32:19 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649141; bh=rE3BHKDsXxEOn2oJ2j4jGIPSJsSETywG5xNgBxEvKrs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=d+1hgNOPV39kd/8ZR4Hft3+JDsb8zTyvva2wiwriWLAit4mlfAFrPQoGTIV6NPUZf 0usI36zy6sLL70i0H0J72D30wiUMwS0/R2EMeU3VuLqd+fDZsXmyKfEydfuOhzbLiz b1fWX1lREGGht34Q80whlqhlCryBhblqZt6mHmEU= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:22 +0900 Message-Id: <20201002143154.468162-7-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 06/38] utils: ipc: add templates for code generation for IPC mechanism 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 templates to mojo to generate code for the IPC mechanism. These templates generate: - module header - module serializer - IPA proxy cpp, header, and worker Given an input data definition mojom file for a pipeline. Signed-off-by: Paul Elder --- Changes in v3: - add support for namespaces - fix enum assignment (used to have +1 for CMD applied to all enums) - use readHeader, writeHeader, and eraseHeader as static class functions of IPAIPCUnixSocket (in the proxy worker) - add requirement that base controls *must* be defined in libcamera::{pipeline_name}::Controls Changes in v2: - mandate the main and callback interfaces, and init(), start(), stop() and their parameters - fix returning single pod value from IPC-called function - add licenses - improve auto-generated message - other fixes related to serdes --- .../module_generated.h.tmpl | 106 ++++ .../module_ipa_proxy.cpp.tmpl | 233 +++++++++ .../module_ipa_proxy.h.tmpl | 117 +++++ .../module_ipa_proxy_worker.cpp.tmpl | 184 +++++++ .../module_serializer.h.tmpl | 44 ++ .../libcamera_templates/proxy_functions.tmpl | 185 +++++++ .../libcamera_templates/serializer.tmpl | 276 +++++++++++ .../generators/mojom_libcamera_generator.py | 461 ++++++++++++++++++ 8 files changed, 1606 insertions(+) create mode 100644 utils/ipc/generators/libcamera_templates/module_generated.h.tmpl create mode 100644 utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl create mode 100644 utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl create mode 100644 utils/ipc/generators/libcamera_templates/module_ipa_proxy_worker.cpp.tmpl create mode 100644 utils/ipc/generators/libcamera_templates/module_serializer.h.tmpl create mode 100644 utils/ipc/generators/libcamera_templates/proxy_functions.tmpl create mode 100644 utils/ipc/generators/libcamera_templates/serializer.tmpl create mode 100644 utils/ipc/generators/mojom_libcamera_generator.py diff --git a/utils/ipc/generators/libcamera_templates/module_generated.h.tmpl b/utils/ipc/generators/libcamera_templates/module_generated.h.tmpl new file mode 100644 index 00000000..7dd48b0b --- /dev/null +++ b/utils/ipc/generators/libcamera_templates/module_generated.h.tmpl @@ -0,0 +1,106 @@ +{#- SPDX-License-Identifier: LGPL-2.1-or-later -#} +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) {{ year }}, Google Inc. + * + * {{ module_name }}_generated.h - Image Processing Algorithm interface for {{ module_name }} + * + * This file is auto-generated. Do not edit. + */ + +#ifndef __LIBCAMERA_IPA_INTERFACE_{{ module_name|upper }}_GENERATED_H__ +#define __LIBCAMERA_IPA_INTERFACE_{{ module_name|upper }}_GENERATED_H__ + +#include + +#include + +{% if has_map %}#include {% endif %} +{% if has_array %}#include {% endif %} + +namespace libcamera { +{%- if has_namespace %} +{% for ns in namespace %} +namespace {{ns}} { +{% endfor %} +{%- endif %} + +enum {{ cmd_enum_name }} { + CMD_EXIT = 0, +{%- for method in interface_main.methods %} + CMD_{{ method.mojom_name|upper }} = {{loop.index}}, +{%- endfor %} +{%- set count = interface_main.methods|length -%} +{%- for method in interface_cb.methods %} + CMD_{{ method.mojom_name|upper }} = {{loop.index + count}}, +{%- endfor %} +}; + +{% for enum in enums %} +enum {{ enum.mojom_name }} { +{%- for field in enum.fields %} + {{ field.mojom_name }} = {{ field.numeric_value }}, +{%- endfor %} +}; +{% endfor %} + +{%- for struct in structs_nonempty %} +struct {{ struct.mojom_name }} +{ +public: + {{ struct.mojom_name }}() {%- if struct|has_default_fields %} :{% endif %} +{%- for field in struct.fields|with_default_values -%} +{{" " if loop.first}}{{ field.mojom_name}}_({{ field|default_value }}){{ ", " if not loop.last }} +{%- endfor %} {} + ~{{ struct.mojom_name }}() {} + + {{ struct.mojom_name }}( +{%- for field in struct.fields -%} +{{ field|name }} {{ field.mojom_name }}{{ ", " if not loop.last }} +{%- endfor -%} +) : +{%- for field in struct.fields -%} +{{" " if loop.first}}{{ field.mojom_name}}_({{ field.mojom_name }}){{ ", " if not loop.last }} +{%- endfor %} {} +{% for field in struct.fields %} + {{ field|name }} {{ field.mojom_name }}_; +{%- endfor %} +}; +{% endfor %} + +{#- +What to do about the #includes? Should we forward-declare, or +require {{module_name}}.h to include the required headers? +For now I'm going for the latter, coz this header essentially +extends {{module_name}}.h. +#} +class {{ interface_name }} : public IPAInterface +{ +public: + virtual ~{{interface_name}}() {}; +{% for method in interface_main.methods %} + virtual {{method|method_return_value}} {{method.mojom_name}}( +{%- for param in method|method_parameters %} + {{param}}{{- "," if not loop.last}} +{%- endfor -%} +) = 0; +{% endfor %} + +{%- for method in interface_cb.methods %} + Signal< +{%- for param in method.parameters -%} + const {{param|name}}{{" &" if not param|is_pod}} + {{- ", " if not loop.last }} +{%- endfor -%} +> {{method.mojom_name}}; +{% endfor -%} +}; + +{%- if has_namespace %} +{% for ns in namespace|reverse %} +} /* {{ns}} */ +{% endfor %} +{%- endif %} +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_IPA_INTERFACE_{{ module_name|upper }}_GENERATED_H__ */ diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl new file mode 100644 index 00000000..f3eeeaaa --- /dev/null +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl @@ -0,0 +1,233 @@ +{#- SPDX-License-Identifier: LGPL-2.1-or-later -#} +{%- import "proxy_functions.tmpl" as proxy_funcs -%} + +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) {{ year }}, Google Inc. + * + * ipa_proxy_{{ module_name }}.cpp - Image Processing Algorithm proxy for {{ module_name }} + * + * This file is auto-generated. Do not edit. + */ + +#include + +#include +#include +#include +#include +#include + +#include "libcamera/internal/control_serializer.h" +#include "libcamera/internal/ipa_ipc.h" +#include "libcamera/internal/ipa_ipc_unixsocket.h" +#include "libcamera/internal/ipa_data_serializer.h" +#include "libcamera/internal/ipa_module.h" +#include "libcamera/internal/ipa_proxy.h" +#include "libcamera/internal/ipc_unixsocket.h" +#include "libcamera/internal/log.h" +#include "libcamera/internal/process.h" +#include "libcamera/internal/thread.h" + +#include + +namespace libcamera { + +LOG_DECLARE_CATEGORY(IPAProxy) + +{%- if has_namespace %} +{% for ns in namespace %} +namespace {{ns}} { +{% endfor %} +{%- endif %} + +{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate) + : IPAProxy(ipam), running_(false), + isolate_(isolate) +{ + LOG(IPAProxy, Debug) + << "initializing dummy proxy: loading IPA from " + << ipam->path(); + + if (isolate_) { + const std::string proxy_worker_path = resolvePath("ipa_proxy_{{module_name}}"); + if (proxy_worker_path.empty()) { + LOG(IPAProxy, Error) + << "Failed to get proxy worker path"; + return; + } + + ipc_ = std::make_unique(ipam->path().c_str(), proxy_worker_path.c_str()); + if (!ipc_->isValid()) { + LOG(IPAProxy, Error) << "Failed to create IPAIPC"; + return; + } + +{% if interface_cb.methods|length > 0 %} + ipc_->recvIPC.connect(this, &{{proxy_name}}::recvIPC); +{% endif %} + + valid_ = true; + return; + } + + if (!ipam->load()) + return; + + IPAInterface *ipai = ipam->createInterface(); + if (!ipai) { + LOG(IPAProxy, Error) + << "Failed to create IPA context for " << ipam->path(); + return; + } + + ipa_ = std::unique_ptr<{{interface_name}}>(dynamic_cast<{{interface_name}} *>(ipai)); + proxy_.setIPA(ipa_.get()); + +{% for method in interface_cb.methods %} + ipa_->{{method.mojom_name}}.connect(this, &{{proxy_name}}::{{method.mojom_name}}Thread); +{%- endfor %} + + valid_ = true; +} + +{{proxy_name}}::~{{proxy_name}}() +{ + if (isolate_) + ipc_->sendAsync(CMD_EXIT, {}, {}); +} + +{% if interface_cb.methods|length > 0 %} +void {{proxy_name}}::recvIPC(std::vector &data, std::vector &fds) +{ + uint32_t cmd = (data[0] & 0xff) | + ((data[1] & 0xff) << 8) | + ((data[2] & 0xff) << 16) | + ((data[3] & 0xff) << 24); + + /* Need to skip another 4 bytes for the sequence number. */ + std::vector vec(data.begin() + 8, data.end()); + switch (cmd) { +{%- for method in interface_cb.methods %} + case CMD_{{method.mojom_name|upper}}: { + {{method.mojom_name}}IPC(vec, fds); + break; + } +{%- endfor %} + } +} +{%- endif %} + +{% for method in interface_main.methods %} +{{proxy_funcs.func_sig(proxy_name, method)}} +{ + if (isolate_) + {{"return " if method|method_return_value != "void"}}{{method.mojom_name}}IPC( +{%- for param in method|method_param_names -%} + {{param}}{{- ", " if not loop.last}} +{%- endfor -%} +); + else + {{"return " if method|method_return_value != "void"}}{{method.mojom_name}}Thread( +{%- for param in method|method_param_names -%} + {{param}}{{- ", " if not loop.last}} +{%- endfor -%} +); +} + +{{proxy_funcs.func_sig(proxy_name, method, "Thread")}} +{ +{%- if method.mojom_name == "init" %} + {{proxy_funcs.init_thread_body()}} +{% elif method.mojom_name == "start" %} + {{proxy_funcs.start_thread_body()}} +{% elif method.mojom_name == "stop" %} + {{proxy_funcs.stop_thread_body()}} +{% elif not method|is_async %} + ipa_->{{method.mojom_name}}( + {%- for param in method|method_param_names -%} + {{param}}{{- ", " if not loop.last}} + {%- endfor -%} +); +{% elif method|is_async %} + proxy_.invokeMethod(&ThreadProxy::{{method.mojom_name}}, ConnectionTypeQueued, + {%- for param in method|method_param_names -%} + {{param}}{{- ", " if not loop.last}} + {%- endfor -%} +); +{%- endif %} +} + +{{proxy_funcs.func_sig(proxy_name, method, "IPC")}} +{ +{%- set has_input = true if method|method_param_inputs|length > 0 %} +{%- set has_output = true if method|method_param_outputs|length > 0 or method|method_return_value != "void" %} +{% if has_input %} + std::vector _ipcInputBuf; +{%- if method|method_input_has_fd %} + std::vector _ipcInputFds; +{%- endif %} +{%- endif %} +{%- if has_output %} + std::vector _ipcOutputBuf; +{%- if method|method_output_has_fd %} + std::vector _ipcOutputFds; +{%- endif %} +{%- endif %} + +{{proxy_funcs.serialize_call(method|method_param_inputs, '_ipcInputBuf', '_ipcInputFds', base_controls)}} + +{%- set input_buf = "_ipcInputBuf" if has_input else "{}" %} +{%- set fds_buf = "_ipcInputFds" if method|method_input_has_fd else "{}" %} +{%- set cmd = "CMD_" + method.mojom_name|upper %} +{% if method|is_async %} + int ret = ipc_->sendAsync({{cmd}}, {{input_buf}}, {{fds_buf}}); +{%- else %} + int ret = ipc_->sendSync({{cmd}}, {{input_buf}}, {{fds_buf}} +{{- ", &_ipcOutputBuf" if has_output -}} +{{- ", &_ipcOutputFds" if has_output and method|method_output_has_fd -}} +); +{%- endif %} + if (ret < 0) { + LOG(IPAProxy, Error) << "Failed to call {{method.mojom_name}}"; +{%- if method|method_return_value != "void" %} + return static_cast<{{method|method_return_value}}>(ret); +{%- else %} + return; +{%- endif %} + } + LOG(IPAProxy, Debug) << "Done calling {{method.mojom_name}}"; +{% if method|method_return_value != "void" %} + return IPADataSerializer<{{method.response_parameters|first|name}}>::deserialize(_ipcOutputBuf, 0); +{% elif method|method_param_outputs|length > 0 %} +{{proxy_funcs.deserialize_call(method|method_param_outputs, '_ipcOutputBuf', '_ipcOutputFds')}} +{% endif -%} +} + +{% endfor %} + + +{% for method in interface_cb.methods %} +{{proxy_funcs.func_sig(proxy_name, method, "Thread")}} +{ + {{method.mojom_name}}.emit({{method.parameters|params_comma_sep}}); +} + +void {{proxy_name}}::{{method.mojom_name}}IPC( + std::vector &data, + [[maybe_unused]] std::vector &fds) +{ +{%- for param in method.parameters %} + {{param|name}} {{param.mojom_name}}; +{%- endfor %} +{{proxy_funcs.deserialize_call(method.parameters, 'data', 'fds', false)}} + {{method.mojom_name}}.emit({{method.parameters|params_comma_sep}}); +} +{% endfor %} + +{%- if has_namespace %} +{% for ns in namespace|reverse %} +} /* {{ns}} */ +{% endfor %} +{%- endif %} +} /* namespace libcamera */ diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl new file mode 100644 index 00000000..7d2bfb6c --- /dev/null +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl @@ -0,0 +1,117 @@ +{#- SPDX-License-Identifier: LGPL-2.1-or-later -#} +{%- import "proxy_functions.tmpl" as proxy_funcs -%} + +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) {{ year }}, Google Inc. + * + * ipa_proxy_{{ module_name }}.h - Image Processing Algorithm proxy for {{ module_name }} + * + * This file is auto-generated. Do not edit. + */ + +#ifndef __LIBCAMERA_INTERNAL_IPA_PROXY_{{ module_name|upper }}_H__ +#define __LIBCAMERA_INTERNAL_IPA_PROXY_{{ module_name|upper }}_H__ + +#include +#include +#include + +#include "libcamera/internal/control_serializer.h" +#include "libcamera/internal/ipa_ipc.h" +#include "libcamera/internal/ipa_ipc_unixsocket.h" +#include "libcamera/internal/ipa_proxy.h" +#include "libcamera/internal/ipc_unixsocket.h" +#include "libcamera/internal/thread.h" + +namespace libcamera { +{%- if has_namespace %} +{% for ns in namespace %} +namespace {{ns}} { +{% endfor %} +{%- endif %} + +class {{proxy_name}} : public IPAProxy, public {{interface_name}}, public Object +{ +public: + {{proxy_name}}(IPAModule *ipam, bool isolate); + ~{{proxy_name}}(); + +{% for method in interface_main.methods %} +{{proxy_funcs.func_sig(proxy_name, method, "", false, true)|indent(8, true)}}; +{% endfor %} + +{%- for method in interface_cb.methods %} + Signal< +{%- for param in method.parameters -%} + const {{param|name}}{{" &" if not param|is_pod}} + {{- ", " if not loop.last }} +{%- endfor -%} +> {{method.mojom_name}}; +{% endfor %} + +private: + void recvIPC(std::vector &data, std::vector &fds); + +{% for method in interface_main.methods %} +{{proxy_funcs.func_sig(proxy_name, method, "Thread", false)|indent(8, true)}}; +{{proxy_funcs.func_sig(proxy_name, method, "IPC", false)|indent(8, true)}}; +{% endfor %} +{% for method in interface_cb.methods %} +{{proxy_funcs.func_sig(proxy_name, method, "Thread", false)|indent(8, true)}}; + void {{method.mojom_name}}IPC( + std::vector &data, + std::vector &fds); +{% endfor %} + + /* Helper class to invoke async functions in another thread. */ + class ThreadProxy : public Object + { + public: + void setIPA({{interface_name}} *ipa) + { + ipa_ = ipa; + } + + int start() + { + return ipa_->start(); + } + + void stop() + { + ipa_->stop(); + } +{% for method in interface_main.methods %} +{%- if method|is_async %} + {{proxy_funcs.func_sig(proxy_name, method, "", false)|indent(16)}} + { + ipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}}); + } +{%- endif %} +{%- endfor %} + + private: + {{interface_name}} *ipa_; + }; + + bool running_; + Thread thread_; + ThreadProxy proxy_; + std::unique_ptr<{{interface_name}}> ipa_; + + const bool isolate_; + + std::unique_ptr ipc_; + + ControlSerializer controlSerializer_; +}; + +{%- if has_namespace %} +{% for ns in namespace|reverse %} +} /* {{ns}} */ +{% endfor %} +{%- endif %} +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_INTERNAL_IPA_PROXY_{{ module_name|upper }}_H__ */ diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy_worker.cpp.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy_worker.cpp.tmpl new file mode 100644 index 00000000..1647a9ca --- /dev/null +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy_worker.cpp.tmpl @@ -0,0 +1,184 @@ +{#- SPDX-License-Identifier: LGPL-2.1-or-later -#} +{%- import "proxy_functions.tmpl" as proxy_funcs -%} + +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) {{ year }}, Google Inc. + * + * ipa_proxy_{{ module_name }}_worker.cpp - Image Processing Algorithm proxy worker for {{ module_name }} + * + * This file is auto-generated. Do not edit. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "libcamera/internal/camera_sensor.h" +#include "libcamera/internal/control_serializer.h" +#include "libcamera/internal/ipa_data_serializer.h" +#include "libcamera/internal/ipa_ipc_unixsocket.h" +#include "libcamera/internal/ipa_module.h" +#include "libcamera/internal/ipa_proxy.h" +#include "libcamera/internal/ipc_unixsocket.h" +#include "libcamera/internal/log.h" +#include "libcamera/internal/thread.h" + +using namespace libcamera; + +LOG_DEFINE_CATEGORY({{proxy_name}}Worker) + +{%- if has_namespace %} +{% for ns in namespace -%} +using namespace {{ns}}; +{% endfor %} +{%- endif %} + +struct CallData { + IPCUnixSocket::Payload *response; + bool done; +}; + +{{interface_name}} *ipa_; +IPCUnixSocket socket_; +std::map callData_; + +ControlSerializer controlSerializer_; + +bool exit_ = false; + +void readyRead(IPCUnixSocket *socket) +{ + IPCUnixSocket::Payload _message, _response; + int _ret = socket->receive(&_message); + if (_ret) { + LOG({{proxy_name}}Worker, Error) + << "Receive message failed" << _ret; + return; + } + + uint32_t _cmd, _seq; + std::tie(_cmd, _seq) = IPAIPCUnixSocket::readHeader(_message); + IPAIPCUnixSocket::eraseHeader(_message); + + switch (_cmd) { + case CMD_EXIT: { + exit_ = true; + break; + } + +{% for method in interface_main.methods %} + case CMD_{{method.mojom_name|upper}}: { + {{proxy_funcs.deserialize_call(method|method_param_inputs, '_message.data', '_message.fds', false, true)|indent(8, true)}} +{% for param in method|method_param_outputs %} + {{param|name}} {{param.mojom_name}}; +{% endfor %} +{%- if method|method_return_value != "void" %} + {{method|method_return_value}} _callRet = ipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}}); +{%- else %} + ipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}} +{{- ", " if method|method_param_outputs|params_comma_sep -}} +{%- for param in method|method_param_outputs -%} +&{{param.mojom_name}}{{", " if not loop.last}} +{%- endfor -%} +); +{%- endif %} +{% if not method|is_async %} + IPAIPCUnixSocket::writeHeader(_response, _cmd, _seq); +{%- if method|method_return_value != "void" %} + std::vector _callRetBuf; + std::tie(_callRetBuf, std::ignore) = + IPADataSerializer<{{method|method_return_value}}>::serialize(_callRet); + _response.data.insert(_response.data.end(), _callRetBuf.begin(), _callRetBuf.end()); +{%- else %} + {{proxy_funcs.serialize_call(method|method_param_outputs, "_response.data", "_response.fds", base_controls)|indent(8, true)}} +{%- endif %} + int _ret = socket_.send(_response); + if (_ret < 0) { + LOG({{proxy_name}}Worker, Error) + << "Reply to {{method.mojom_name}}() failed" << _ret; + } + LOG({{proxy_name}}Worker, Debug) << "Done replying to {{method.mojom_name}}()"; +{%- endif %} + break; + } +{% endfor %} + } +} + +{% for method in interface_cb.methods %} +{{proxy_funcs.func_sig(proxy_name, method, "", false)}} +{ + IPCUnixSocket::Payload _message; + + IPAIPCUnixSocket::writeHeader(_message, CMD_{{method.mojom_name|upper}}, 0); + {{proxy_funcs.serialize_call(method|method_param_inputs, "_message.data", "_message.fds", base_controls)}} + + socket_.send(_message); + + LOG({{proxy_name}}Worker, Debug) << "{{method.mojom_name}} done"; +} +{%- endfor %} + +int main(int argc, char **argv) +{ + /* Uncomment this for debugging. */ +#if 0 + std::string logPath = "/tmp/libcamera.worker." + + std::to_string(getpid()) + ".log"; + logSetFile(logPath.c_str()); +#endif + + if (argc < 3) { + LOG({{proxy_name}}Worker, Debug) + << "Tried to start worker with no args"; + return EXIT_FAILURE; + } + + int fd = std::stoi(argv[2]); + LOG({{proxy_name}}Worker, Debug) + << "Starting worker for IPA module " << argv[1] + << " with IPC fd = " << fd; + + std::unique_ptr ipam = std::make_unique(argv[1]); + if (!ipam->isValid() || !ipam->load()) { + LOG({{proxy_name}}Worker, Error) + << "IPAModule " << argv[1] << " should be valid but isn't"; + return EXIT_FAILURE; + } + + if (socket_.bind(fd) < 0) { + LOG({{proxy_name}}Worker, Error) << "IPC socket binding failed"; + return EXIT_FAILURE; + } + socket_.readyRead.connect(&readyRead); + + ipa_ = dynamic_cast<{{interface_name}} *>(ipam->createInterface()); + if (!ipa_) { + LOG({{proxy_name}}Worker, Error) << "Failed to create IPA interface instance"; + return EXIT_FAILURE; + } +{% for method in interface_cb.methods %} + ipa_->{{method.mojom_name}}.connect(&{{method.mojom_name}}); +{%- endfor %} + + LOG({{proxy_name}}Worker, Debug) << "Proxy worker successfully started"; + + /* \todo upgrade listening loop */ + EventDispatcher *dispatcher = Thread::current()->eventDispatcher(); + while (!exit_) + dispatcher->processEvents(); + + delete ipa_; + socket_.close(); + + return 0; +} diff --git a/utils/ipc/generators/libcamera_templates/module_serializer.h.tmpl b/utils/ipc/generators/libcamera_templates/module_serializer.h.tmpl new file mode 100644 index 00000000..37352c1f --- /dev/null +++ b/utils/ipc/generators/libcamera_templates/module_serializer.h.tmpl @@ -0,0 +1,44 @@ +{#- SPDX-License-Identifier: LGPL-2.1-or-later -#} +{%- import "serializer.tmpl" as serializer -%} + +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) {{ year }}, Google Inc. + * + * {{ module_name }}_serializer.h - Image Processing Algorithm data serializer for {{ module_name }} + * + * This file is auto-generated. Do not edit. + */ + +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_{{ module_name|upper }}_H__ +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_{{ module_name|upper }}_H__ + +#include +#include + +#include "libcamera/internal/control_serializer.h" +#include "libcamera/internal/ipa_data_serializer.h" + +#include +#include + +namespace libcamera { + +LOG_DECLARE_CATEGORY(IPADataSerializer) +{% for struct in structs_nonempty %} +template<> +class IPADataSerializer<{{ struct|name_full(namespace_str) }}> +{ +public: +{{- serializer.serializer(struct, base_controls, namespace_str) }} +{%- if struct|has_fd %} +{{ serializer.deserializer_fd(struct, namespace_str) }} +{%- else %} +{{ serializer.deserializer_no_fd(struct, namespace_str) }} +{%- endif %} +}; +{% endfor %} + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_{{ module_name|upper }}_H__ */ diff --git a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl new file mode 100644 index 00000000..e1f5a20c --- /dev/null +++ b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl @@ -0,0 +1,185 @@ +{#- SPDX-License-Identifier: LGPL-2.1-or-later -#} +{# + # \brief Generate fuction prototype + # + # \param class Class name + # \param method mojom Method object + # \param suffix Suffix to append to \a method function name + # \param need_class_name True to generate class name with function + # \param override True to generate override tag after the function prototype + #} +{%- macro func_sig(class, method, suffix, need_class_name = true, override = false) -%} +{{method|method_return_value}} {{ class + "::" if need_class_name }}{{method.mojom_name}}{{suffix}}( +{%- for param in method|method_parameters %} + {{param}}{{- "," if not loop.last}} +{%- endfor -%} +){{" override" if override}} +{%- endmacro -%} + +{# + # \brief Generate function body for IPA init() function for thread + #} +{%- macro init_thread_body() -%} + int ret = ipa_->init(settings); + if (ret) + return ret; + + proxy_.moveToThread(&thread_); + + return 0; +{%- endmacro -%} + +{# + # \brief Generate function body for IPA start() function for thread + #} +{%- macro start_thread_body() -%} + running_ = true; + thread_.start(); + + return proxy_.invokeMethod(&ThreadProxy::start, ConnectionTypeBlocking); +{%- endmacro -%} + +{# + # \brief Generate function body for IPA stop() function for thread + #} +{%- macro stop_thread_body() -%} + if (!running_) + return; + + running_ = false; + + proxy_.invokeMethod(&ThreadProxy::stop, ConnectionTypeBlocking); + + thread_.exit(); + thread_.wait(); +{%- endmacro -%} + + +{# + # \brief Serialize multiple objects into data buffer and fd vector + # + # Generate code to serialize multiple objects, as specified in \a params + # (which are the parameters to some function), into \a buf data buffer and + # \a fds fd vector. + # This code is meant to be used by the proxy, for serializing prior to IPC calls. + #} +{%- macro serialize_call(params, buf, fds, base_controls) %} +{%- for param in params %} + std::vector {{param.mojom_name}}Buf; +{%- if param|has_fd %} + std::vector {{param.mojom_name}}Fds; + std::tie({{param.mojom_name}}Buf, {{param.mojom_name}}Fds) = +{%- else %} + std::tie({{param.mojom_name}}Buf, std::ignore) = +{%- endif %} +{%- if param|is_controls %} + IPADataSerializer<{{param|name}}>::serialize({{param.mojom_name}}, {{param.mojom_name}}.infoMap() ? *{{param.mojom_name}}.infoMap() : {{base_controls}} +{%- else %} + IPADataSerializer<{{param|name}}>::serialize({{param.mojom_name}} +{%- endif %} +{{- ", &controlSerializer_" if param|needs_control_serializer -}} +); +{%- endfor %} + +{%- if params|length > 1 %} +{%- for param in params %} + appendUInt({{buf}}, {{param.mojom_name}}Buf.size()); +{%- if param|has_fd %} + appendUInt({{buf}}, {{param.mojom_name}}Fds.size()); +{%- endif %} +{%- endfor %} +{%- endif %} + +{%- for param in params %} + {{buf}}.insert({{buf}}.end(), {{param.mojom_name}}Buf.begin(), {{param.mojom_name}}Buf.end()); +{%- endfor %} + +{%- for param in params %} +{%- if param|has_fd %} + {{fds}}.insert({{fds}}.end(), {{param.mojom_name}}Fds.begin(), {{param.mojom_name}}Fds.end()); +{%- endif %} +{%- endfor %} +{%- endmacro -%} + + +{# + # \brief Deserialize a single object from data buffer and fd vector + # + # \param pointer True deserializes the object into a dereferenced pointer + # + # Generate code to deserialize a single object, as specified in \a param, + # from \a buf data buffer and \a fds fd vector. + # This code is meant to be used by macro deserialize_call. + #} +{%- macro deserialize_param(param, pointer, loop, buf, fds) -%} +{{"*" if pointer}}{{param.mojom_name}} = IPADataSerializer<{{param|name}}>::deserialize( + {{buf}}.begin() + {{param.mojom_name}}Start, +{%- if loop.last %} + {{buf}}.end() +{%- else %} + {{buf}}.begin() + {{param.mojom_name}}Start + {{param.mojom_name}}BufSize +{%- endif -%} +{{- "," if param|has_fd }} +{%- if param|has_fd %} + {{fds}}.begin() + {{param.mojom_name}}FdStart, +{%- if loop.last %} + {{fds}}.end() +{%- else %} + {{fds}}.begin() + {{param.mojom_name}}FdStart + {{param.mojom_name}}FdsSize +{%- endif -%} +{%- endif -%} +{{- "," if param|needs_control_serializer }} +{%- if param|needs_control_serializer %} + &controlSerializer_ +{%- endif -%} +); +{%- endmacro -%} + + +{# + # \brief Deserialize multiple objects from data buffer and fd vector + # + # \param pointer True deserializes objects into pointers, and adds a null check. + # \param declare True declares the objects in addition to deserialization. + # + # Generate code to deserialize multiple objects, as specified in \a params + # (which are the parameters to some function), from \a buf data buffer and + # \a fds fd vector. + # This code is meant to be used by the proxy, for serializing prior to IPC calls. + #} +{%- macro deserialize_call(params, buf, fds, pointer = true, declare = false) -%} +{% set ns = namespace(size_offset = 0) %} +{%- if params|length > 1 %} +{%- for param in params %} + [[maybe_unused]] size_t {{param.mojom_name}}BufSize = readUInt({{buf}}, {{ns.size_offset}}); + {%- set ns.size_offset = ns.size_offset + 4 %} +{%- if param|has_fd %} + [[maybe_unused]] size_t {{param.mojom_name}}FdsSize = readUInt({{buf}}, {{ns.size_offset}}); + {%- set ns.size_offset = ns.size_offset + 4 %} +{%- endif %} +{%- endfor %} +{%- endif %} +{% for param in params %} +{%- if loop.first %} + size_t {{param.mojom_name}}Start = {{ns.size_offset}}; +{%- else %} + size_t {{param.mojom_name}}Start = {{loop.previtem.mojom_name}}Start + {{loop.previtem.mojom_name}}BufSize; +{%- endif %} +{%- endfor %} +{% for param in params|with_fds %} +{%- if loop.first %} + size_t {{param.mojom_name}}FdStart = 0; +{%- elif not loop.last %} + size_t {{param.mojom_name}}FdStart = {{loop.previtem.mojom_name}}FdStart + {{loop.previtem.mojom_name}}FdsSize; +{%- endif %} +{%- endfor %} +{% for param in params %} + {%- if pointer %} + if ({{param.mojom_name}}) { +{{deserialize_param(param, pointer, loop, buf, fds)|indent(16, True)}} + } + {%- else %} + {{param|name + " " if declare}}{{deserialize_param(param, pointer, loop, buf, fds)|indent(8)}} + {%- endif %} +{% endfor %} +{%- endmacro -%} diff --git a/utils/ipc/generators/libcamera_templates/serializer.tmpl b/utils/ipc/generators/libcamera_templates/serializer.tmpl new file mode 100644 index 00000000..3c071c4e --- /dev/null +++ b/utils/ipc/generators/libcamera_templates/serializer.tmpl @@ -0,0 +1,276 @@ +{#- SPDX-License-Identifier: LGPL-2.1-or-later -#} +{# Turn this into a C macro? #} +{# + # \brief Verify that there is enough bytes to deserialize + # + # Generate code that verifies that \a size is not greater than \a dataSize. + # Otherwise log an error with \a name and \a typename. + #} +{%- macro check_data_size(size, dataSize, name, typename) %} + if ({{size}} > {{dataSize}}) { + LOG(IPADataSerializer, Error) + << "Failed to deserialize {{name}}: not enough {{typename}}, expected " + << ({{size}}) << ", got " << ({{dataSize}}); + return ret; + } +{%- endmacro %} + + +{# + # \brief Serialize some field into return vector + # + # Generate code to serialize \a field into ret_data, including size of the + # field and fds (where appropriate). \a base_controls indicates the + # default ControlInfoMap in the event that the ControlList does not have one. + # This code is meant to be used by the IPADataSerializer specialization. + #} +{%- macro serializer_field(field, base_controls, namespace, loop) %} +{%- if field|is_pod or field|is_enum %} + std::vector {{field.mojom_name}}; + std::tie({{field.mojom_name}}, std::ignore) = + {%- if field|is_pod %} + IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}}_); + {%- elif field|is_enum %} + IPADataSerializer::serialize(data.{{field.mojom_name}}_); + {%- endif %} + ret_data.insert(ret_data.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end()); +{%- elif field|is_fd %} + std::vector {{field.mojom_name}}; + std::vector {{field.mojom_name}}Fds; + std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) = + IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}}_); + ret_data.insert(ret_data.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end()); + ret_fds.insert(ret_fds.end(), {{field.mojom_name}}Fds.begin(), {{field.mojom_name}}Fds.end()); +{%- elif field|is_controls %} + if (data.{{field.mojom_name}}_.size() > 0) { + std::vector {{field.mojom_name}}; + std::tie({{field.mojom_name}}, std::ignore) = + IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}}_, + data.{{field.mojom_name}}_.infoMap() ? *data.{{field.mojom_name}}_.infoMap() : {{base_controls}}, + cs); + appendUInt(ret_data, {{field.mojom_name}}.size()); + ret_data.insert(ret_data.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end()); + } else { + appendUInt(ret_data, 0); + } +{%- elif field|is_plain_struct or field|is_array or field|is_map %} + std::vector {{field.mojom_name}}; + {%- if field|has_fd %} + std::vector {{field.mojom_name}}Fds; + std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) = + {%- else %} + std::tie({{field.mojom_name}}, std::ignore) = + {%- endif %} + {%- if field|is_array or field|is_map %} + IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}}_, cs); + {%- else %} + IPADataSerializer<{{field|name_full(namespace)}}>::serialize(data.{{field.mojom_name}}_, cs); + {%- endif %} + appendUInt(ret_data, {{field.mojom_name}}.size()); + {%- if field|has_fd %} + appendUInt(ret_data, {{field.mojom_name}}Fds.size()); + {%- endif %} + ret_data.insert(ret_data.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end()); + {%- if field|has_fd %} + ret_fds.insert(ret_fds.end(), {{field.mojom_name}}Fds.begin(), {{field.mojom_name}}Fds.end()); + {%- endif %} +{%- else %} + /* Unknown serialization for {{field.mojom_name}}. */ +{%- endif %} +{%- endmacro %} + + +{# + # \brief Deserialize some field into return struct + # + # Generate code to deserialize \a field into object ret. + # This code is meant to be used by the IPADataSerializer specialization. + #} +{%- macro deserializer_field(field, namespace, loop) %} +{% if field|is_pod or field|is_enum %} + {%- set field_size = (field|bit_width|int / 8)|int %} + {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data') }} + {%- if field|is_pod %} + ret.{{field.mojom_name}}_ = static_cast<{{field|name}}>(IPADataSerializer<{{field|name}}>::deserialize(m, m + {{field_size}})); + {%- else %} + ret.{{field.mojom_name}}_ = static_cast<{{field|name}}>(IPADataSerializer::deserialize(m, m + {{field_size}})); + {%- endif %} + {%- if not loop.last %} + m += {{field_size}}; + dataSize -= {{field_size}}; + {%- endif %} +{% elif field|is_fd %} + {%- set field_size = 1 %} + {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data') }} + ret.{{field.mojom_name}}_ = IPADataSerializer<{{field|name}}>::deserialize(m, m + 1, n, n + 1, cs); + {%- if not loop.last %} + m += {{field_size}}; + dataSize -= {{field_size}}; + n += ret.{{field.mojom_name}}_.isValid() ? 1 : 0; + fdsSize -= ret.{{field.mojom_name}}_.isValid() ? 1 : 0; + {%- endif %} +{% elif field|is_controls %} + {%- set field_size = 4 %} + {{- check_data_size(field_size, 'dataSize', field.mojom_name + 'Size', 'data') }} + size_t {{field.mojom_name}}Size = readUInt(m); + {%- set field_size = '4 + ' + field.mojom_name + 'Size' -%} + {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data') }} + if ({{field.mojom_name}}Size > 0) + ret.{{field.mojom_name}}_ = + IPADataSerializer<{{field|name}}>::deserialize(m + 4, m + 4 + {{field.mojom_name}}Size, cs); + {%- if not loop.last %} + m += {{field_size}}; + dataSize -= {{field_size}}; + {%- endif %} +{% elif field|is_plain_struct or field|is_array or field|is_map%} + {%- set field_size = 4 %} + {{- check_data_size(field_size, 'dataSize', field.mojom_name + 'Size', 'data') }} + size_t {{field.mojom_name}}Size = readUInt(m); + {%- if field|has_fd %} + {%- set field_size = 8 %} + {{- check_data_size(field_size, 'dataSize', field.mojom_name + 'FdsSize', 'data') }} + size_t {{field.mojom_name}}FdsSize = readUInt(m + 4); + {{- check_data_size(field.mojom_name + 'FdsSize', 'fdsSize', field.mojom_name, 'fds') }} + {%- endif %} + {%- set field_size = field|has_fd|choose('8 + ', '4 + ') + field.mojom_name + 'Size' -%} + {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data') }} + ret.{{field.mojom_name}}_ = + {%- if field|has_fd and (field|is_array or field|is_map) %} + IPADataSerializer<{{field|name}}>::deserialize(m + 8, m + 8 + {{field.mojom_name}}Size, n, n + {{field.mojom_name}}FdsSize, cs); + {%- elif field|has_fd and (not (field|is_array or field|is_map)) %} + IPADataSerializer<{{field|name_full(namespace)}}>::deserialize(m + 8, m + 8 + {{field.mojom_name}}Size, n, n + {{field.mojom_name}}FdsSize, cs); + {%- elif (not field|has_fd) and (field|is_array or field|is_map) %} + IPADataSerializer<{{field|name}}>::deserialize(m + 4, m + 4 + {{field.mojom_name}}Size, cs); + {%- else %} + IPADataSerializer<{{field|name_full(namespace)}}>::deserialize(m + 4, m + 4 + {{field.mojom_name}}Size, cs); + {%- endif %} + {%- if not loop.last %} + m += {{field_size}}; + dataSize -= {{field_size}}; + {%- if field|has_fd %} + n += {{field.mojom_name}}FdsSize; + fdsSize -= {{field.mojom_name}}FdsSize; + {%- endif %} + {%- endif %} +{% else %} + /* Unknown deserialization for {{field.mojom_name}}. */ +{%- endif %} +{%- endmacro %} + + +{# + # \brief Serialize a struct + # + # Generate code for IPADataSerializer specialization, for serializing + # \a struct. \a base_controls indicates the default ControlInfoMap + # in the event that the ControlList does not have one. + #} +{%- macro serializer(struct, base_controls, namespace) %} + static std::tuple, std::vector> + serialize(const {{ struct|name_full(namespace) }} &data, +{%- if struct|needs_control_serializer %} + ControlSerializer *cs) +{%- else %} + [[maybe_unused]] ControlSerializer *cs = nullptr) +{%- endif %} + { + std::vector ret_data; +{%- if struct|has_fd %} + std::vector ret_fds; +{%- endif %} +{%- for field in struct.fields %} +{{serializer_field(field, base_controls, namespace, loop)}} +{%- endfor %} +{% if struct|has_fd %} + return {ret_data, ret_fds}; +{%- else %} + return {ret_data, {}}; +{%- endif %} + } +{%- endmacro %} + + +{# + # \brief Deserialize a struct that has fds + # + # Generate code for IPADataSerializer specialization, for deserializing + # \a struct, in the case that \a struct has file descriptors. + #} +{%- macro deserializer_fd(struct, namespace) %} + static {{ struct|name_full(namespace) }} + deserialize(std::vector &data, + std::vector &fds, +{%- if struct|needs_control_serializer %} + ControlSerializer *cs) +{%- else %} + [[maybe_unused]] ControlSerializer *cs = nullptr) +{%- endif %} + { + {{struct|name_full(namespace)}} ret; + std::vector::iterator m = data.begin(); + std::vector::iterator n = fds.begin(); + + size_t dataSize = data.size(); + size_t fdsSize = fds.size(); +{%- for field in struct.fields -%} +{{deserializer_field(field, namespace, loop)}} +{%- endfor %} + return ret; + } + + static {{ struct|name_full(namespace) }} + deserialize(std::vector::iterator data_it1, + std::vector::iterator data_it2, + std::vector::iterator fds_it1, + std::vector::iterator fds_it2, +{%- if struct|needs_control_serializer %} + ControlSerializer *cs) +{%- else %} + [[maybe_unused]] ControlSerializer *cs = nullptr) +{%- endif %} + { + std::vector data(data_it1, data_it2); + std::vector fds(fds_it1, fds_it2); + return IPADataSerializer<{{struct|name_full(namespace)}}>::deserialize(data, fds, cs); + } +{%- endmacro %} + + +{# + # \brief Deserialize a struct that has no fds + # + # Generate code for IPADataSerializer specialization, for deserializing + # \a struct, in the case that \a struct does not have file descriptors. + #} +{%- macro deserializer_no_fd(struct, namespace) %} + static {{ struct|name_full(namespace) }} + deserialize(std::vector &data, +{%- if struct|needs_control_serializer %} + ControlSerializer *cs) +{%- else %} + [[maybe_unused]] ControlSerializer *cs = nullptr) +{%- endif %} + { + {{struct|name_full(namespace)}} ret; + std::vector::iterator m = data.begin(); + + size_t dataSize = data.size(); +{%- for field in struct.fields -%} +{{deserializer_field(field, namespace, loop)}} +{%- endfor %} + return ret; + } + + static {{ struct|name_full(namespace) }} + deserialize(std::vector::iterator data_it1, + std::vector::iterator data_it2, +{%- if struct|needs_control_serializer %} + ControlSerializer *cs) +{%- else %} + [[maybe_unused]] ControlSerializer *cs = nullptr) +{%- endif %} + { + std::vector data(data_it1, data_it2); + return IPADataSerializer<{{struct|name_full(namespace)}}>::deserialize(data, cs); + } +{%- endmacro %} diff --git a/utils/ipc/generators/mojom_libcamera_generator.py b/utils/ipc/generators/mojom_libcamera_generator.py new file mode 100644 index 00000000..a9269c82 --- /dev/null +++ b/utils/ipc/generators/mojom_libcamera_generator.py @@ -0,0 +1,461 @@ +'''Generates libcamera files from a mojom.Module.''' + +import argparse +import ast +import datetime +import contextlib +import os +import re +import shutil +import sys +import tempfile + +from jinja2 import contextfilter + +import mojom.fileutil as fileutil +import mojom.generate.generator as generator +import mojom.generate.module as mojom +from mojom.generate.template_expander import UseJinja + + +GENERATOR_PREFIX = 'libcamera' + +_kind_to_cpp_type = { + mojom.BOOL: 'bool', + mojom.INT8: 'int8_t', + mojom.UINT8: 'uint8_t', + mojom.INT16: 'int16_t', + mojom.UINT16: 'uint16_t', + mojom.INT32: 'int32_t', + mojom.UINT32: 'uint32_t', + mojom.FLOAT: 'float', + mojom.INT64: 'int64_t', + mojom.UINT64: 'uint64_t', + mojom.DOUBLE: 'double', +} + +_bit_widths = { + mojom.BOOL: '8', + mojom.INT8: '8', + mojom.UINT8: '8', + mojom.INT16: '16', + mojom.UINT16: '16', + mojom.INT32: '32', + mojom.UINT32: '32', + mojom.FLOAT: '32', + mojom.INT64: '64', + mojom.UINT64: '64', + mojom.DOUBLE: '64', +} + +def ModuleName(path): + return path.split('/')[-1].split('.')[0] + +def ModuleClassName(module): + s = re.sub(r'^IPA', '', module.interfaces[0].mojom_name) + return re.sub(r'Interface$', '', s) + +def UpperCamelCase(name): + return ''.join([x.capitalize() for x in generator.SplitCamelCase(name)]) + +def CamelCase(name): + uccc = UpperCamelCase(name) + return uccc[0].lower() + uccc[1:] + +def ConstantStyle(name): + return generator.ToUpperSnakeCase(name) + +def Choose(cond, t, f): + return t if cond else f + +def CommaSep(l): + return ', '.join([m for m in l]) + +def ParamsCommaSep(l): + return ', '.join([m.mojom_name for m in l]) + +def GetDefaultValue(element): + if element.default is not None: + return element.default + if type(element.kind) == mojom.Kind: + return '0' + if mojom.IsEnumKind(element.kind): + return f'static_cast<{element.kind.mojom_name}>(0)' + if (isinstance(element.kind, mojom.Struct) and + element.kind.mojom_name == 'FileDescriptor'): + return '-1' + return '' + +def WithDefaultValues(element): + return [x for x in element if HasDefaultValue(x)] + +def WithFds(element): + return [x for x in element if HasFd(x)] + +def HasDefaultValue(element): + return GetDefaultValue(element) != '' + +def HasDefaultFields(element): + return True in [HasDefaultValue(x) for x in element.fields] + +def GetAllTypes(element): + if mojom.IsArrayKind(element): + return GetAllTypes(element.kind) + if mojom.IsMapKind(element): + return GetAllTypes(element.key_kind) + GetAllTypes(element.value_kind) + if isinstance(element, mojom.Parameter): + return GetAllTypes(element.kind) + if mojom.IsEnumKind(element): + return [element.mojom_name] + if not mojom.IsStructKind(element): + return [element.spec] + if len(element.fields) == 0: + return [element.mojom_name] + ret = [GetAllTypes(x.kind) for x in element.fields] + ret = [x for sublist in ret for x in sublist] + return list(set(ret)) + +def GetAllAttrs(element): + if mojom.IsArrayKind(element): + return GetAllAttrs(element.kind) + if mojom.IsMapKind(element): + return {**GetAllAttrs(element.key_kind), **GetAllAttrs(element.value_kind)} + if isinstance(element, mojom.Parameter): + return GetAllAttrs(element.kind) + if mojom.IsEnumKind(element): + return element.attributes if element.attributes is not None else {} + if mojom.IsStructKind(element) and len(element.fields) == 0: + return element.attributes if element.attributes is not None else {} + if not mojom.IsStructKind(element): + return {} + attrs = [(x.attributes) for x in element.fields] + ret = {} + for d in attrs: + if d is not None: + ret = {**ret, **d} + return ret + +def NeedsControlSerializer(element): + types = GetAllTypes(element) + return "ControlList" in types or "ControlInfoMap" in types + +def HasFd(element): + attrs = GetAllAttrs(element) + if isinstance(element, mojom.Kind): + types = GetAllTypes(element) + else: + types = GetAllTypes(element.kind) + return "FileDescriptor" in types or (attrs is not None and "hasFd" in attrs) + +def MethodInputHasFd(method): + if len([x for x in method.parameters if HasFd(x)]) > 0: + return True + return False + +def MethodOutputHasFd(method): + if (MethodReturnValue(method) != 'void' or + method.response_parameters is None): + return False + if len([x for x in method.parameters if HasFd(x)]) > 0: + return True + return False + +def MethodParamInputs(method): + return method.parameters + +def MethodParamOutputs(method): + if (MethodReturnValue(method) != 'void' or + method.response_parameters is None): + return [] + return method.response_parameters + +def MethodParamNames(method): + params = [] + for param in method.parameters: + params.append(param.mojom_name) + if MethodReturnValue(method) == 'void': + if method.response_parameters is None: + return params + for param in method.response_parameters: + params.append(param.mojom_name) + return params + +def MethodParameters(method): + params = [] + for param in method.parameters: + params.append('const %s %s%s' % (GetNameForElement(param), + ('&' if not IsPod(param) else ''), + param.mojom_name)) + if MethodReturnValue(method) == 'void': + if method.response_parameters is None: + return params + for param in method.response_parameters: + params.append(f'{GetNameForElement(param)} *{param.mojom_name}') + return params + +def MethodReturnValue(method): + if method.response_parameters is None: + return 'void' + if len(method.response_parameters) == 1 and IsPod(method.response_parameters[0]): + return GetNameForElement(method.response_parameters[0]) + return 'void' + +def IsAsync(method): + # callbacks are always async + if re.match("^IPA.*CallbackInterface$", method.interface.mojom_name): + return True + elif re.match("^IPA.*Interface$", method.interface.mojom_name): + if method.attributes is None: + return False + elif 'async' in method.attributes and method.attributes['async']: + return True + return False + +def IsArray(element): + return mojom.IsArrayKind(element.kind) + +def IsControls(element): + return mojom.IsStructKind(element.kind) and (element.kind.mojom_name == "ControlList" or + element.kind.mojom_name == "ControlInfoMap") + +def IsEnum(element): + return mojom.IsEnumKind(element.kind) + +def IsFd(element): + return mojom.IsStructKind(element.kind) and element.kind.mojom_name == "FileDescriptor" + +def IsMap(element): + return mojom.IsMapKind(element.kind) + +def IsPlainStruct(element): + return mojom.IsStructKind(element.kind) and not IsControls(element) and not IsFd(element) + +def IsPod(element): + return element.kind in _kind_to_cpp_type + +def BitWidth(element): + if element.kind in _bit_widths: + return _bit_widths[element.kind] + if mojom.IsEnumKind(element.kind): + return '32' + return '' + +def GetNameForElement(element): + if (mojom.IsEnumKind(element) or + mojom.IsInterfaceKind(element) or + mojom.IsStructKind(element)): + return element.mojom_name + if (mojom.IsArrayKind(element)): + elem_name = GetNameForElement(element.kind) + return f'std::vector<{elem_name}>' + if (mojom.IsMapKind(element)): + key_name = GetNameForElement(element.key_kind) + value_name = GetNameForElement(element.value_kind) + return f'std::map<{key_name}, {value_name}>' + if isinstance(element, (mojom.Field, mojom.Method, mojom.Parameter)): + if (mojom.IsArrayKind(element.kind) or mojom.IsMapKind(element.kind)): + return GetNameForElement(element.kind) + if (hasattr(element, 'kind') and element.kind in _kind_to_cpp_type): + return _kind_to_cpp_type[element.kind] + return element.kind.mojom_name + if isinstance(element, mojom.EnumValue): + return (GetNameForElement(element.enum) + '.' + + ConstantStyle(element.name)) + if isinstance(element, (mojom.NamedValue, + mojom.Constant, + mojom.EnumField)): + return ConstantStyle(element.name) + if (hasattr(element, '__hash__') and element in _kind_to_cpp_type): + return _kind_to_cpp_type[element] + if (hasattr(element, 'kind') and element.kind in _kind_to_cpp_type): + return _kind_to_cpp_type[element.kind] + if (hasattr(element, 'spec')): + return element.spec.split(':')[-1] + if (mojom.IsInterfaceRequestKind(element) or + mojom.IsAssociatedKind(element) or + mojom.IsPendingRemoteKind(element) or + mojom.IsPendingReceiverKind(element) or + mojom.IsUnionKind(element)): + raise Exception('Unsupported element: %s' % element) + raise Exception('Unexpected element: %s' % element) + +def GetFullNameForElement(element, namespace_str): + name = GetNameForElement(element) + if namespace_str == '': + return name + return f'{namespace_str}::{name}' + +def ValidateZeroLength(l, s, cap=True): + if len(l) > 0: + raise Exception(f'{s.capitalize() if cap else s} should be empty') + +def ValidateSingleLength(l, s, cap=True): + if len(l) > 1: + raise Exception(f'Only one {s} allowed') + if len(l) < 1: + raise Exception(f'{s.capitalize() if cap else s} is required') + +def ValidateInterfaces(interfaces): + # Validate presence of main interface + intf = [x for x in interfaces + if re.match("^IPA.*Interface", x.mojom_name) and + not re.match("^IPA.*CallbackInterface", x.mojom_name)] + ValidateSingleLength(intf, 'main interface') + + # Validate presence of callback interface + cb = [x for x in interfaces if re.match("^IPA.*CallbackInterface", x.mojom_name)] + ValidateSingleLength(cb, 'callback interface') + + # Validate required main interface functions + intf = intf[0] + f_init = [x for x in intf.methods if x.mojom_name == 'init'] + f_start = [x for x in intf.methods if x.mojom_name == 'start'] + f_stop = [x for x in intf.methods if x.mojom_name == 'stop'] + + ValidateSingleLength(f_init, 'init()', False) + ValidateSingleLength(f_start, 'start()', False) + ValidateSingleLength(f_stop, 'stop()', False) + + f_init = f_init[0] + f_start = f_start[0] + f_stop = f_stop[0] + + # Validate parameters to init() + ValidateSingleLength(f_init.parameters, 'input parameter to init()') + ValidateSingleLength(f_init.response_parameters, 'output parameter from init()') + if f_init.parameters[0].kind.mojom_name != 'IPASettings': + raise Exception('init() must have single IPASettings input parameter') + if f_init.response_parameters[0].kind.spec != 'i32': + raise Exception('init() must have single int32 output parameter') + + # Validate parameters to start() + ValidateZeroLength(f_start.parameters, 'input parameter to start()') + ValidateSingleLength(f_start.response_parameters, 'output parameter from start()') + if f_start.response_parameters[0].kind.spec != 'i32': + raise Exception('start() must have single int32 output parameter') + + # Validate parameters to stop() + ValidateZeroLength(f_stop.parameters, 'input parameter to stop()') + ValidateZeroLength(f_stop.parameters, 'output parameter from stop()') + +class Generator(generator.Generator): + + @staticmethod + def GetTemplatePrefix(): + return 'libcamera_templates' + + def GetFilters(self): + libcamera_filters = { + 'all_types': GetAllTypes, + 'bit_width': BitWidth, + 'choose': Choose, + 'comma_sep': CommaSep, + 'default_value': GetDefaultValue, + 'has_default_fields': HasDefaultFields, + 'has_fd': HasFd, + 'is_async': IsAsync, + 'is_array': IsArray, + 'is_controls': IsControls, + 'is_enum': IsEnum, + 'is_fd': IsFd, + 'is_map': IsMap, + 'is_plain_struct': IsPlainStruct, + 'is_pod': IsPod, + 'method_input_has_fd': MethodInputHasFd, + 'method_output_has_fd': MethodOutputHasFd, + 'method_param_names': MethodParamNames, + 'method_param_inputs': MethodParamInputs, + 'method_param_outputs': MethodParamOutputs, + 'method_parameters': MethodParameters, + 'method_return_value': MethodReturnValue, + 'name': GetNameForElement, + 'name_full': GetFullNameForElement, + 'needs_control_serializer': NeedsControlSerializer, + 'params_comma_sep': ParamsCommaSep, + 'with_default_values': WithDefaultValues, + 'with_fds': WithFds, + } + return libcamera_filters + + def _GetJinjaExports(self): + return { + 'base_controls': '%s::Controls' % ModuleClassName(self.module), + 'cmd_enum_name': '_%sCMD' % ModuleClassName(self.module), + 'enums': self.module.enums, + 'has_array': len([x for x in self.module.kinds.keys() if x[0] == 'a']) > 0, + 'has_map': len([x for x in self.module.kinds.keys() if x[0] == 'm']) > 0, + 'has_namespace': self.module.mojom_namespace != '', + 'imports': self.module.imports, + 'interface_cb': self.module.interfaces[1], + 'interface_main': self.module.interfaces[0], + 'interface_name': 'IPA%sInterface' % ModuleClassName(self.module), + 'ipc_name': 'IPAIPCUnixSocket', + 'kinds': self.module.kinds, + 'module': self.module, + 'module_name': ModuleName(self.module.path), + 'module_class_name': ModuleClassName(self.module), + 'namespace': self.module.mojom_namespace.split('.'), + 'namespace_str': self.module.mojom_namespace.replace('.', '::') if + self.module.mojom_namespace is not None else '', + 'proxy_name': 'IPAProxy%s' % ModuleClassName(self.module), + 'proxy_worker_name': 'IPAProxy%sWorker' % ModuleClassName(self.module), + 'structs_nonempty': [x for x in self.module.structs if len(x.fields) > 0], + 'year': datetime.datetime.now().year, + } + + + @UseJinja('module_generated.h.tmpl') + def _GenerateDataHeader(self): + return self._GetJinjaExports() + + @UseJinja('module_serializer.h.tmpl') + def _GenerateSerializer(self): + return self._GetJinjaExports() + + @UseJinja('module_ipa_proxy.cpp.tmpl') + def _GenerateProxyCpp(self): + return self._GetJinjaExports() + + @UseJinja('module_ipa_proxy.h.tmpl') + def _GenerateProxyHeader(self): + return self._GetJinjaExports() + + @UseJinja('module_ipa_proxy_worker.cpp.tmpl') + def _GenerateProxyWorker(self): + return self._GetJinjaExports() + + def GenerateFiles(self, unparsed_args): + parser = argparse.ArgumentParser() + parser.add_argument('--libcamera_generate_header', action='store_true') + parser.add_argument('--libcamera_generate_serializer', action='store_true') + parser.add_argument('--libcamera_generate_proxy_cpp', action='store_true') + parser.add_argument('--libcamera_generate_proxy_h', action='store_true') + parser.add_argument('--libcamera_generate_proxy_worker', action='store_true') + parser.add_argument('--libcamera_output_path') + args = parser.parse_args(unparsed_args) + + ValidateInterfaces(self.module.interfaces) + + fileutil.EnsureDirectoryExists(os.path.dirname(args.libcamera_output_path)) + + module_name = ModuleName(self.module.path) + + if args.libcamera_generate_header: + self.Write(self._GenerateDataHeader(), + args.libcamera_output_path) + + if args.libcamera_generate_serializer: + self.Write(self._GenerateSerializer(), + args.libcamera_output_path) + + if args.libcamera_generate_proxy_cpp: + self.Write(self._GenerateProxyCpp(), + args.libcamera_output_path) + + if args.libcamera_generate_proxy_h: + self.Write(self._GenerateProxyHeader(), + args.libcamera_output_path) + + if args.libcamera_generate_proxy_worker: + self.Write(self._GenerateProxyWorker(), + args.libcamera_output_path) From patchwork Fri Oct 2 14:31:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9915 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 3D1F1C3B5C for ; Fri, 2 Oct 2020 14:32:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0B7A763BB9; Fri, 2 Oct 2020 16:32:25 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="BBerdFa+"; 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 777DA63B6A for ; Fri, 2 Oct 2020 16:32:24 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 475672A2; Fri, 2 Oct 2020 16:32:21 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649144; bh=PEhLQUjDSK8PiE0rLGoxDb/1ddSCPi8A98JZRBBkNQc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BBerdFa+kP1lNGGjyACeF24DaSWLPddcAX+8vEa8PbErp/HlCo1fu3Qm0r6uy6Dhp qnKF5A5qID0rpgr869wGrxaH+OsAK7glprxZy3aBec+q7I3lQURO8njkomaUnVY6nv yhGKH2LXDPIfbNbH8aB900tIxIcvEX5BjwOH6LHY= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:23 +0900 Message-Id: <20201002143154.468162-8-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 07/38] utils: ipc: add generator script 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 avoid changing our copy of mojo to make updates easier. Some parameters in the mojo generator script needs to be changed though; add a wrapper script that sets these parameters. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- Changes in v3: - add SPDX and copyright and file description block - add todo for sys.pycache_prefix for python3.8+ Changes in v2: - add descriptions to python setup - disable pycache --- utils/ipc/generate.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100755 utils/ipc/generate.py diff --git a/utils/ipc/generate.py b/utils/ipc/generate.py new file mode 100755 index 00000000..8771e0a6 --- /dev/null +++ b/utils/ipc/generate.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (C) 2020, Google Inc. +# +# Author: Paul Elder +# +# generate.py - Run mojo code generator for generating libcamera IPC files + +import os +import sys + +# TODO set sys.pycache_prefix for >= python3.8 +sys.dont_write_bytecode = True + +import mojo.public.tools.bindings.mojom_bindings_generator as generator + +def _GetModulePath(path, output_dir): + return os.path.join(output_dir, path.relative_path()) + +# Override the mojo code generator's generator list to only contain our +# libcamera generator +generator._BUILTIN_GENERATORS = {'libcamera': 'mojom_libcamera_generator'} + +# Override the mojo code generator's _GetModulePath method to not add +# the '-module' suffix when searching for mojo modules, so that we can +# pass the path to the mojom module without having to trim the '-module' suffix +generator._GetModulePath = _GetModulePath + +generator.main() From patchwork Fri Oct 2 14:31:24 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9916 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 9B2BBC3B5C for ; Fri, 2 Oct 2020 14:32:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6A3CF63B6A; Fri, 2 Oct 2020 16:32:27 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="mzErEuJt"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 8F7D563B6A for ; Fri, 2 Oct 2020 16:32:26 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D00952A2; Fri, 2 Oct 2020 16:32:24 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649146; bh=RDUz/TvWA6tR1hlvwIiSWY/pC5UgrRwsMc+WYXGbbME=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mzErEuJteQtWUme94YWpn8LanEMHmAHEGuZolZpxsPQetAlpd3oQIwTnh9z7HsYiW Je5OlQlzqqb69OYWZyxGV6NYW0rdYGc+dRZTnZcqts+johH8HkGcVvtFVNWShj6PTw AYpq/XBZCvQiRtUemLG0Ev+6jflBIXzZACnzvd90= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:24 +0900 Message-Id: <20201002143154.468162-9-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 08/38] utils: ipc: add parser script 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" On some systems, python2 might still be the default python. Enforce python3 by wrapping the mojo parser script in a python3 script. This also has the benefit of not modifying mojo. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- New in v3 --- utils/ipc/parser.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 utils/ipc/parser.py diff --git a/utils/ipc/parser.py b/utils/ipc/parser.py new file mode 100755 index 00000000..f46820fa --- /dev/null +++ b/utils/ipc/parser.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (C) 2020, Google Inc. +# +# Author: Paul Elder +# +# parser.py - Run mojo parser with python3 + +import os +import sys + +# TODO set sys.pycache_prefix for >= python3.8 +sys.dont_write_bytecode = True + +# Make sure that mojom_parser.py can import mojom +sys.path.append(f'{os.path.dirname(__file__)}/mojo/public/tools/mojom') + +import mojo.public.tools.mojom.mojom_parser as parser + +parser.Run(sys.argv[1:]) From patchwork Fri Oct 2 14:31:25 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9917 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 F34E3C3B5C for ; Fri, 2 Oct 2020 14:32:30 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C00B963B72; Fri, 2 Oct 2020 16:32:30 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="IWJ9l2Lb"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2CEA563B6E for ; Fri, 2 Oct 2020 16:32:29 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 197A72A2; Fri, 2 Oct 2020 16:32:26 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649148; bh=EqwHAUDiz808DG9DkQqqHWAGTLiwUBtYRITT4JVLqek=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IWJ9l2LbzAquk3M7zHAPDUE+fWpOSoJ0BmF7xoJEiav7HMVJ29im3IMwj4oM6KX9a m8hSTub4BrJKD4aUj7L2+4lrR1vCCMkwvG3L3byBDweEpZ4Pn685fS/FoNfYwf2L03 5nymdNKCTqEu2n85gqjnmTmHRcFfVynXirRxnEV8= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:25 +0900 Message-Id: <20201002143154.468162-10-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 09/38] Documentation: skip generating documentation for generated code 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" Specify in doxygen to not generate code for the generated headers and proxy source code files. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- No change in v3 New in v2 --- Documentation/Doxyfile.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in index fa8d744b..598eca9b 100644 --- a/Documentation/Doxyfile.in +++ b/Documentation/Doxyfile.in @@ -840,7 +840,9 @@ EXCLUDE = @TOP_SRCDIR@/include/libcamera/span.h \ @TOP_SRCDIR@/src/libcamera/device_enumerator_sysfs.cpp \ @TOP_SRCDIR@/src/libcamera/device_enumerator_udev.cpp \ @TOP_SRCDIR@/src/libcamera/pipeline/ \ - @TOP_SRCDIR@/src/libcamera/proxy/ + @TOP_SRCDIR@/src/libcamera/proxy/ \ + @TOP_BUILDDIR@/include/libcamera/ipa/ \ + @TOP_BUILDDIR@/src/libcamera/proxy/ # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded From patchwork Fri Oct 2 14:31:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9918 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 56CF6C3B5C for ; Fri, 2 Oct 2020 14:32:32 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 21B3463BC1; Fri, 2 Oct 2020 16:32:32 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="vpMgIXJ8"; 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 7E03563B6A for ; Fri, 2 Oct 2020 16:32:31 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A25712A2; Fri, 2 Oct 2020 16:32:29 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649151; bh=bPamY0YG2eER8ZcJeZR2OlYIh7JqQDvy+8G6k5LBqbo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vpMgIXJ8Y+gCDa6iy88SCwneRXhCS28nK36VIWqPHH5vkwRTyT5GwkYmR6GT6XfjD EpYK3nBpVwWGxDnTk5Ok0XotSNW/5Zkd4lb96AYKMwfCt9M1+x0nTnCEx90g9ARy4C S9/Mr67Orjy1rjVZ0kSwJeUc8TgbZvJIFHnTxiLk= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:26 +0900 Message-Id: <20201002143154.468162-11-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 10/38] libcamera: Add IPADataSerializer 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 IPADataSerializer which implments de/serialization of built-in (PODs, vector, map, string) and libcamera data structures. This is intended to be used by the proxy and the proxy worker in the IPC layer. Signed-off-by: Paul Elder --- Changes in v3: - reimplement append/readUInt with memcpy (intead of bit shifting) - change DECLARE_INTEGRAL_SERIALIZER with DECLARE_POD_SERIALIZER - use this for int64_t, bool, float, and double - fix comment style Changes in v2: - added serializers for all integer types, bool, and string - use string serializer for IPASettings serializer - add documentation - add serializer for const ControlList --- .../libcamera/internal/ipa_data_serializer.h | 1014 +++++++++++++++++ src/libcamera/ipa_data_serializer.cpp | 154 +++ src/libcamera/meson.build | 1 + 3 files changed, 1169 insertions(+) create mode 100644 include/libcamera/internal/ipa_data_serializer.h create mode 100644 src/libcamera/ipa_data_serializer.cpp diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h new file mode 100644 index 00000000..9cd35c4c --- /dev/null +++ b/include/libcamera/internal/ipa_data_serializer.h @@ -0,0 +1,1014 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * ipa_data_serializer.h - Image Processing Algorithm data serializer + */ +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__ +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "libcamera/internal/byte_stream_buffer.h" +#include "libcamera/internal/camera_sensor.h" +#include "libcamera/internal/control_serializer.h" +#include "libcamera/internal/log.h" + +namespace libcamera { + +LOG_DECLARE_CATEGORY(IPADataSerializer) + +template +void appendUInt(std::vector &vec, T val) +{ + size_t byteWidth = sizeof(val); + std::vector v(byteWidth); + memcpy(&v[0], &val, byteWidth); + + vec.insert(vec.end(), v.begin(), v.end()); +} + +template +T readUInt(std::vector &vec, size_t pos) +{ + T ret = 0; + size_t byteWidth = sizeof(ret); + if (pos + byteWidth > vec.size()) + return ret; + + memcpy(&ret, &vec.data()[pos], byteWidth); + return ret; +} + +template +T readUInt(std::vector::iterator it) +{ + T ret = 0; + size_t byteWidth = sizeof(ret); + + std::vector v(it, it + byteWidth); + memcpy(&ret, v.data(), byteWidth); + return ret; +} + +template +class IPADataSerializer +{ +#ifdef __DOXYGEN__ +public: + static std::tuple, std::vector> + serialize(T data, ControlSerializer *cs); + + static T deserialize(std::vector &data, + ControlSerializer *cs); + static T deserialize(std::vector::iterator it1, + std::vector::iterator it2, + ControlSerializer *cs); + + static T deserialize(std::vector &data, + std::vector &fds, + ControlSerializer *cs); + static T deserialize(std::vector::iterator data_it1, + std::vector::iterator data_it2, + std::vector::iterator fds_it1, + std::vector::iterator fds_it2, + ControlSerializer *cs); +#endif /* __DOXYGEN__ */ +}; + +#ifndef __DOXYGEN__ + +template +class IPADataSerializer> +{ +public: + static std::tuple, std::vector> + serialize(const std::vector &data, ControlSerializer *cs = nullptr) + { + std::vector data_vec; + std::vector fds_vec; + + /* Serialize the length. */ + uint32_t vec_len = data.size(); + appendUInt(data_vec, vec_len); + + /* Serialize the members. */ + for (auto it = data.begin(); it != data.end(); ++it) { + std::vector dvec; + std::vector fvec; + + std::tie(dvec, fvec) = + IPADataSerializer::serialize(*it, cs); + + appendUInt(data_vec, dvec.size()); + appendUInt(data_vec, fvec.size()); + + data_vec.insert(data_vec.end(), dvec.begin(), dvec.end()); + fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end()); + } + + return {data_vec, fds_vec}; + } + + static std::vector deserialize(std::vector &data, ControlSerializer *cs = nullptr) + { + return IPADataSerializer>::deserialize(data.begin(), data.end(), cs); + } + + static std::vector deserialize(std::vector::iterator it1, + std::vector::iterator it2, + ControlSerializer *cs = nullptr) + { + std::vector fds; + return IPADataSerializer>::deserialize(it1, it2, + fds.begin(), fds.end(), + cs); + } + + static std::vector deserialize(std::vector &data, std::vector &fds, + ControlSerializer *cs = nullptr) + { + return IPADataSerializer>::deserialize(data.begin(), data.end(), + fds.begin(), fds.end(), + cs); + } + + static std::vector deserialize(std::vector::iterator data_it1, + [[maybe_unused]] std::vector::iterator data_it2, + std::vector::iterator fds_it1, + [[maybe_unused]] std::vector::iterator fds_it2, + ControlSerializer *cs = nullptr) + { + uint32_t vec_len = readUInt(data_it1); + std::vector ret(vec_len); + + std::vector::iterator data_it = data_it1 + 4; + std::vector::iterator fd_it = fds_it1; + for (uint32_t i = 0; i < vec_len; i++) { + uint32_t sizeof_data = readUInt(data_it); + uint32_t sizeof_fds = readUInt(data_it + 4); + + ret[i] = IPADataSerializer::deserialize(data_it + 8, + data_it + 8 + sizeof_data, + fd_it, + fd_it + sizeof_fds, + cs); + + data_it += 8 + sizeof_data; + fd_it += sizeof_fds; + } + + return ret; + } +}; + +template +class IPADataSerializer> +{ +public: + static std::tuple, std::vector> + serialize(const std::map &data, ControlSerializer *cs = nullptr) + { + std::vector data_vec; + std::vector fds_vec; + + /* Serialize the length. */ + uint32_t map_len = data.size(); + appendUInt(data_vec, map_len); + + /* Serialize the members. */ + for (auto it = data.begin(); it != data.end(); ++it) { + std::vector dvec; + std::vector fvec; + + std::tie(dvec, fvec) = + IPADataSerializer::serialize(it->first, cs); + + appendUInt(data_vec, dvec.size()); + appendUInt(data_vec, fvec.size()); + + data_vec.insert(data_vec.end(), dvec.begin(), dvec.end()); + fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end()); + + std::tie(dvec, fvec) = + IPADataSerializer::serialize(it->second, cs); + + appendUInt(data_vec, dvec.size()); + appendUInt(data_vec, fvec.size()); + + data_vec.insert(data_vec.end(), dvec.begin(), dvec.end()); + fds_vec.insert(fds_vec.end(), fvec.begin(), fvec.end()); + } + + return {data_vec, fds_vec}; + } + + static std::map deserialize(std::vector &data, ControlSerializer *cs = nullptr) + { + return IPADataSerializer>::deserialize(data.begin(), data.end(), cs); + } + + static std::map deserialize(std::vector::iterator it1, + std::vector::iterator it2, + ControlSerializer *cs = nullptr) + { + std::vector fds; + return IPADataSerializer>::deserialize(it1, it2, + fds.begin(), fds.end(), + cs); + } + + static std::map deserialize(std::vector &data, std::vector &fds, + ControlSerializer *cs = nullptr) + { + return IPADataSerializer>::deserialize(data.begin(), data.end(), + fds.begin(), fds.end(), + cs); + } + + static std::map deserialize(std::vector::iterator data_it1, + [[maybe_unused]] std::vector::iterator data_it2, + std::vector::iterator fds_it1, + [[maybe_unused]] std::vector::iterator fds_it2, + ControlSerializer *cs = nullptr) + { + std::map ret; + + uint32_t map_len = readUInt(data_it1); + + std::vector::iterator data_it = data_it1 + 4; + std::vector::iterator fd_it = fds_it1; + for (uint32_t i = 0; i < map_len; i++) { + uint32_t sizeof_data = readUInt(data_it); + uint32_t sizeof_fds = readUInt(data_it + 4); + + K key = IPADataSerializer::deserialize(data_it + 8, + data_it + 8 + sizeof_data, + fd_it, + fd_it + sizeof_fds, + cs); + + data_it += 8 + sizeof_data; + fd_it += sizeof_fds; + sizeof_data = readUInt(data_it); + sizeof_fds = readUInt(data_it + 4); + + const V value = IPADataSerializer::deserialize(data_it + 8, + data_it + 8 + sizeof_data, + fd_it, + fd_it + sizeof_fds, + cs); + ret.insert({key, value}); + + data_it += 8 + sizeof_data; + fd_it += sizeof_fds; + } + + return ret; + } +}; + +#define DECLARE_POD_SERIALIZER(type) \ +template<> \ +class IPADataSerializer \ +{ \ +public: \ + static std::tuple, std::vector> \ + serialize(const type data, \ + [[maybe_unused]] ControlSerializer *cs = nullptr) \ + { \ + std::vector data_vec; \ + appendUInt(data_vec, data); \ + \ + return {data_vec, {}}; \ + } \ + \ + static type deserialize(std::vector &data, \ + [[maybe_unused]] ControlSerializer *cs = nullptr)\ + { \ + return IPADataSerializer::deserialize(data.begin(),\ + data.end());\ + } \ + \ + static type deserialize(std::vector::iterator it1, \ + [[maybe_unused]] std::vector::iterator it2,\ + [[maybe_unused]] ControlSerializer *cs = nullptr)\ + { \ + return readUInt(it1); \ + } \ + \ + static type deserialize(std::vector &data, \ + [[maybe_unused]] std::vector &fds,\ + [[maybe_unused]] ControlSerializer *cs = nullptr)\ + { \ + return IPADataSerializer::deserialize(data.begin(),\ + data.end());\ + } \ + \ + static type deserialize(std::vector::iterator data_it1,\ + std::vector::iterator data_it2,\ + [[maybe_unused]] std::vector::iterator fds_it1,\ + [[maybe_unused]] std::vector::iterator fds_it2,\ + [[maybe_unused]] ControlSerializer *cs = nullptr)\ + { \ + return IPADataSerializer::deserialize(data_it1, \ + data_it2); \ + } \ +}; + +DECLARE_POD_SERIALIZER(bool) +DECLARE_POD_SERIALIZER(uint8_t) +DECLARE_POD_SERIALIZER(uint16_t) +DECLARE_POD_SERIALIZER(uint32_t) +DECLARE_POD_SERIALIZER(uint64_t) +DECLARE_POD_SERIALIZER(int8_t) +DECLARE_POD_SERIALIZER(int16_t) +DECLARE_POD_SERIALIZER(int32_t) +DECLARE_POD_SERIALIZER(int64_t) +DECLARE_POD_SERIALIZER(float) +DECLARE_POD_SERIALIZER(double) + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const std::string &data, [[maybe_unused]] ControlSerializer *cs = nullptr) + { + std::vector data_vec(data.begin(), data.end()); + + return {data_vec, {}}; + } + + static std::string deserialize(std::vector &data, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return IPADataSerializer::deserialize(data.begin(), data.end()); + } + + static std::string deserialize(std::vector::iterator it1, + std::vector::iterator it2, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + std::string str(it1, it2); + + return str; + } + + static std::string deserialize(std::vector &data, + [[maybe_unused]] std::vector &fds, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return IPADataSerializer::deserialize(data.begin(), data.end()); + } + + static std::string deserialize(std::vector::iterator data_it1, + std::vector::iterator data_it2, + [[maybe_unused]] std::vector::iterator fds_it1, + [[maybe_unused]] std::vector::iterator fds_it2, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return IPADataSerializer::deserialize(data_it1, data_it2); + } +}; + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const FileDescriptor &data, [[maybe_unused]] ControlSerializer *cs = nullptr) + { + std::vector data_vec = { data.isValid() }; + std::vector fd_vec; + if (data.isValid()) + fd_vec.push_back(data.fd()); + + return {data_vec, fd_vec}; + } + + static FileDescriptor deserialize(std::vector &data, std::vector &fds, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return IPADataSerializer::deserialize(data.begin(), data.end(), + fds.begin(), fds.end()); + } + + static FileDescriptor deserialize(std::vector::iterator data_it1, + std::vector::iterator data_it2, + std::vector::iterator fds_it1, + std::vector::iterator fds_it2, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + if (std::distance(data_it1, data_it2) < 1) + LOG(IPADataSerializer, Fatal) + << "Invalid data to deserialize FileDescriptor"; + + bool valid = *data_it1; + + if (valid && std::distance(fds_it1, fds_it2) < 1) + LOG(IPADataSerializer, Fatal) + << "Invalid fds to deserialize FileDescriptor"; + + return valid ? FileDescriptor(*fds_it1) : FileDescriptor(); + } +}; + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const IPASettings &data, [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return IPADataSerializer::serialize(data.configurationFile); + } + + static IPASettings deserialize(std::vector &data, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return IPADataSerializer::deserialize(data.begin(), data.end()); + } + + static IPASettings deserialize(std::vector::iterator it1, + std::vector::iterator it2, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + IPASettings ret; + ret.configurationFile = IPADataSerializer::deserialize(it1, it2); + + return ret; + } + + static IPASettings deserialize(std::vector &data, + [[maybe_unused]] std::vector &fds, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return IPADataSerializer::deserialize(data.begin(), data.end()); + } + + static IPASettings deserialize(std::vector::iterator data_it1, + std::vector::iterator data_it2, + [[maybe_unused]] std::vector::iterator fds_it1, + [[maybe_unused]] std::vector::iterator fds_it2, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return IPADataSerializer::deserialize(data_it1, data_it2); + } +}; + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const CameraSensorInfo &data, [[maybe_unused]] ControlSerializer *cs = nullptr) + { + std::vector data_vec; + + uint32_t str_len = data.model.size(); + appendUInt(data_vec, str_len); + + data_vec.insert(data_vec.end(), data.model.begin(), data.model.end()); + + appendUInt(data_vec, data.bitsPerPixel); + + appendUInt(data_vec, data.activeAreaSize.width); + appendUInt(data_vec, data.activeAreaSize.height); + + appendUInt(data_vec, static_cast(data.analogCrop.x)); + appendUInt(data_vec, static_cast(data.analogCrop.y)); + appendUInt(data_vec, data.analogCrop.width); + appendUInt(data_vec, data.analogCrop.height); + + appendUInt(data_vec, data.outputSize.width); + appendUInt(data_vec, data.outputSize.height); + + appendUInt(data_vec, data.pixelRate); + + appendUInt(data_vec, data.lineLength); + + return {data_vec, {}}; + } + + static CameraSensorInfo deserialize(std::vector &data, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return IPADataSerializer::deserialize(data.begin(), data.end()); + } + + static CameraSensorInfo deserialize(std::vector::iterator it1, + [[maybe_unused]] std::vector::iterator it2, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + CameraSensorInfo ret; + + uint32_t str_len = readUInt(it1); + std::string str(it1 + 4, it1 + 4 + str_len); + ret.model = str; + + std::vector::iterator it = it1 + 4 + str_len; + + ret.bitsPerPixel = readUInt(it); + + ret.activeAreaSize.width = readUInt(it + 4); + ret.activeAreaSize.height = readUInt(it + 8); + + ret.analogCrop.x = static_cast(readUInt(it + 12)); + ret.analogCrop.y = static_cast(readUInt(it + 16)); + ret.analogCrop.width = readUInt(it + 20); + ret.analogCrop.height = readUInt(it + 24); + + ret.outputSize.width = readUInt(it + 28); + ret.outputSize.height = readUInt(it + 32); + + ret.pixelRate = readUInt(it + 36); + + ret.lineLength = readUInt(it + 44); + + return ret; + } + + static CameraSensorInfo deserialize(std::vector &data, + [[maybe_unused]] std::vector &fds, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return IPADataSerializer::deserialize(data.begin(), data.end()); + } + + static CameraSensorInfo deserialize(std::vector::iterator data_it1, + std::vector::iterator data_it2, + [[maybe_unused]] std::vector::iterator fds_it1, + [[maybe_unused]] std::vector::iterator fds_it2, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return IPADataSerializer::deserialize(data_it1, data_it2); + } +}; + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const IPAStream &data, [[maybe_unused]] ControlSerializer *cs = nullptr) + { + std::vector data_vec; + + appendUInt(data_vec, data.pixelFormat); + + appendUInt(data_vec, data.size.width); + appendUInt(data_vec, data.size.height); + + return {data_vec, {}}; + } + + static IPAStream deserialize(std::vector &data, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return IPADataSerializer::deserialize(data.begin(), data.end()); + } + + static IPAStream deserialize(std::vector::iterator it1, + [[maybe_unused]] std::vector::iterator it2, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + IPAStream ret; + + ret.pixelFormat = readUInt(it1); + + ret.size.width = readUInt(it1 + 4); + ret.size.height = readUInt(it1 + 8); + + return ret; + } + + static IPAStream deserialize(std::vector &data, + [[maybe_unused]] std::vector &fds, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return IPADataSerializer::deserialize(data.begin(), data.end()); + } + + static IPAStream deserialize(std::vector::iterator data_it1, + std::vector::iterator data_it2, + [[maybe_unused]] std::vector::iterator fds_it1, + [[maybe_unused]] std::vector::iterator fds_it2, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + return IPADataSerializer::deserialize(data_it1, data_it2); + } +}; + +template<> +class IPADataSerializer +{ +public: + /* map arg will be generated, since it's per-pipeline anyway. */ + static std::tuple, std::vector> + serialize(const ControlList &data, const ControlInfoMap &map, + ControlSerializer *cs) + { + if (!cs) + LOG(IPADataSerializer, Fatal) + << "ControlSerializer not provided for serialization of ControlList"; + + size_t size = cs->binarySize(map); + std::vector infoData(size); + ByteStreamBuffer buffer(infoData.data(), infoData.size()); + int ret = cs->serialize(map, buffer); + + if (ret < 0 || buffer.overflow()) { + LOG(IPADataSerializer, Error) << "Failed to serialize ControlList's ControlInfoMap"; + return {{}, {}}; + } + + size = cs->binarySize(data); + std::vector listData(size); + buffer = ByteStreamBuffer(listData.data(), listData.size()); + ret = cs->serialize(data, buffer); + + if (ret < 0 || buffer.overflow()) { + LOG(IPADataSerializer, Error) << "Failed to serialize ControlList"; + return {{}, {}}; + } + + std::vector data_vec; + appendUInt(data_vec, infoData.size()); + appendUInt(data_vec, listData.size()); + data_vec.insert(data_vec.end(), infoData.begin(), infoData.end()); + data_vec.insert(data_vec.end(), listData.begin(), listData.end()); + + return {data_vec, {}}; + } + + static const ControlList deserialize(std::vector &data, ControlSerializer *cs) + { + return IPADataSerializer::deserialize(data.begin(), data.end(), cs); + } + + static const ControlList deserialize(std::vector::iterator it1, + [[maybe_unused]] std::vector::iterator it2, + ControlSerializer *cs) + { + if (!cs) + LOG(IPADataSerializer, Fatal) + << "ControlSerializer not provided for deserialization of ControlList"; + + uint32_t infoDataSize = readUInt(it1); + uint32_t listDataSize = readUInt(it1 + 4); + + std::vector::iterator it = it1 + 8; + + std::vector infoData(it, it + infoDataSize); + std::vector listData(it + infoDataSize, it + infoDataSize + listDataSize); + + ByteStreamBuffer buffer(const_cast(infoData.data()), infoData.size()); + ControlInfoMap map = cs->deserialize(buffer); + /* It's fine if map is empty. */ + if (buffer.overflow()) { + LOG(IPADataSerializer, Error) + << "Failed to deserialize ControlLists's ControlInfoMap: buffer overflow"; + return ControlList(); + } + + buffer = ByteStreamBuffer(const_cast(listData.data()), listData.size()); + ControlList list = cs->deserialize(buffer); + if (buffer.overflow()) + LOG(IPADataSerializer, Error) << "Failed to deserialize ControlList: buffer overflow"; + if (list.empty()) + LOG(IPADataSerializer, Error) << "Failed to deserialize ControlList: empty list"; + + return list; + } + + static const ControlList deserialize(std::vector &data, + [[maybe_unused]] std::vector &fds, + ControlSerializer *cs) + { + return IPADataSerializer::deserialize(data.begin(), data.end(), cs); + } + + static const ControlList deserialize(std::vector::iterator data_it1, + std::vector::iterator data_it2, + [[maybe_unused]] std::vector::iterator fds_it1, + [[maybe_unused]] std::vector::iterator fds_it2, + ControlSerializer *cs) + { + return IPADataSerializer::deserialize(data_it1, data_it2, cs); + } +}; + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const ControlList &data, const ControlInfoMap &map, + ControlSerializer *cs) + { + const ControlList &list_const = const_cast(data); + + std::vector data_vec; + std::tie(data_vec, std::ignore) = + IPADataSerializer::serialize(list_const, map, cs); + + return {data_vec, {}}; + } + + static ControlList deserialize(std::vector &data, + ControlSerializer *cs) + { + ControlList ret; + const ControlList &list = ret; + const_cast(list) = + IPADataSerializer::deserialize(data, cs); + + return ret; + } + + static ControlList deserialize(std::vector::iterator it1, + [[maybe_unused]] std::vector::iterator it2, + ControlSerializer *cs) + { + ControlList ret; + const ControlList &list = ret; + const_cast(list) = + IPADataSerializer::deserialize(it1, it2, cs); + + return ret; + } + + static ControlList deserialize(std::vector &data, + [[maybe_unused]] std::vector &fds, + ControlSerializer *cs) + { + ControlList ret; + const ControlList &list = ret; + const_cast(list) = + IPADataSerializer::deserialize(data, fds, cs); + + return ret; + } + + static ControlList deserialize(std::vector::iterator data_it1, + std::vector::iterator data_it2, + [[maybe_unused]] std::vector::iterator fds_it1, + [[maybe_unused]] std::vector::iterator fds_it2, + ControlSerializer *cs) + { + ControlList ret; + const ControlList &list = ret; + const_cast(list) = + IPADataSerializer::deserialize(data_it1, data_it2, + fds_it1, fds_it2, cs); + + return ret; + } +}; + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const ControlInfoMap &map, ControlSerializer *cs) + { + if (!cs) + LOG(IPADataSerializer, Fatal) + << "ControlSerializer not provided for serialization of ControlInfoMap"; + + size_t size = cs->binarySize(map); + std::vector infoData(size); + ByteStreamBuffer buffer(infoData.data(), infoData.size()); + int ret = cs->serialize(map, buffer); + + if (ret < 0 || buffer.overflow()) + return {{}, {}}; + + std::vector data_vec; + appendUInt(data_vec, infoData.size()); + data_vec.insert(data_vec.end(), infoData.begin(), infoData.end()); + + return {data_vec, {}}; + } + + static const ControlInfoMap deserialize(std::vector &data, + ControlSerializer *cs) + { + return IPADataSerializer::deserialize(data.begin(), data.end(), cs); + } + + static const ControlInfoMap deserialize(std::vector::iterator it1, + [[maybe_unused]] std::vector::iterator it2, + ControlSerializer *cs) + { + if (!cs) + LOG(IPADataSerializer, Fatal) + << "ControlSerializer not provided for deserialization of ControlInfoMap"; + + uint32_t infoDataSize = readUInt(it1); + + std::vector::iterator it = it1 + 4; + + std::vector infoData(it, it + infoDataSize); + + ByteStreamBuffer buffer(const_cast(infoData.data()), infoData.size()); + const ControlInfoMap map = cs->deserialize(buffer); + + return map; + } + + static const ControlInfoMap deserialize(std::vector &data, + [[maybe_unused]] std::vector &fds, + ControlSerializer *cs) + { + return IPADataSerializer::deserialize(data.begin(), data.end(), cs); + } + + static const ControlInfoMap deserialize(std::vector::iterator data_it1, + std::vector::iterator data_it2, + [[maybe_unused]] std::vector::iterator fds_it1, + [[maybe_unused]] std::vector::iterator fds_it2, + ControlSerializer *cs) + { + return IPADataSerializer::deserialize(data_it1, data_it2, cs); + } +}; + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const ControlInfoMap &map, [[maybe_unused]] ControlSerializer *cs) + { + const ControlInfoMap &map_const = const_cast(map); + + std::vector data_vec; + std::tie(data_vec, std::ignore) = + IPADataSerializer::serialize(map_const, cs); + + return {data_vec, {}}; + } + + static ControlInfoMap deserialize(std::vector &data, + ControlSerializer *cs) + { + ControlInfoMap ret; + const ControlInfoMap &map = ret; + const_cast(map) = + IPADataSerializer::deserialize(data, cs); + + return ret; + } + + static ControlInfoMap deserialize(std::vector::iterator it1, + [[maybe_unused]] std::vector::iterator it2, + ControlSerializer *cs) + { + ControlInfoMap ret; + const ControlInfoMap &map = ret; + const_cast(map) = + IPADataSerializer::deserialize(it1, it2, cs); + + return ret; + } + + static ControlInfoMap deserialize(std::vector &data, + [[maybe_unused]] std::vector &fds, + ControlSerializer *cs) + { + ControlInfoMap ret; + const ControlInfoMap &map = ret; + const_cast(map) = + IPADataSerializer::deserialize(data, fds, cs); + + return ret; + } + + static ControlInfoMap deserialize(std::vector::iterator data_it1, + std::vector::iterator data_it2, + [[maybe_unused]] std::vector::iterator fds_it1, + [[maybe_unused]] std::vector::iterator fds_it2, + ControlSerializer *cs) + { + ControlInfoMap ret; + const ControlInfoMap &map = ret; + const_cast(map) = + IPADataSerializer::deserialize(data_it1, data_it2, + fds_it1, fds_it2, cs); + + return ret; + } +}; + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const FrameBuffer::Plane &data, [[maybe_unused]] ControlSerializer *cs = nullptr) + { + std::vector data_vec; + std::vector fds_vec; + + std::vector fdBuf; + std::vector fdFds; + std::tie(fdBuf, fdFds) = + IPADataSerializer::serialize(data.fd); + data_vec.insert(data_vec.end(), fdBuf.begin(), fdBuf.end()); + fds_vec.insert(fds_vec.end(), fdFds.begin(), fdFds.end()); + + appendUInt(data_vec, data.length); + + return {data_vec, fds_vec}; + } + + static FrameBuffer::Plane deserialize(std::vector &data, + std::vector &fds, + ControlSerializer *cs = nullptr) + { + return IPADataSerializer::deserialize(data.begin(), data.end(), + fds.begin(), fds.end(), + cs); + } + + static FrameBuffer::Plane deserialize(std::vector::iterator data_it1, + [[maybe_unused]] std::vector::iterator data_it2, + std::vector::iterator fds_it1, + [[maybe_unused]] std::vector::iterator fds_it2, + [[maybe_unused]] ControlSerializer *cs = nullptr) + { + FrameBuffer::Plane ret; + + ret.fd = IPADataSerializer::deserialize(data_it1, data_it1 + 1, + fds_it1, fds_it1 + 1); + ret.length = readUInt(data_it1 + 1); + + return ret; + } +}; + + +template<> +class IPADataSerializer +{ +public: + static std::tuple, std::vector> + serialize(const IPABuffer &data, ControlSerializer *cs = nullptr) + { + std::vector data_vec; + + appendUInt(data_vec, data.id); + + std::vector planes_data_vec; + std::vector planes_fds_vec; + std::tie(planes_data_vec, planes_fds_vec) = + IPADataSerializer>::serialize(data.planes, cs); + + data_vec.insert(data_vec.end(), planes_data_vec.begin(), planes_data_vec.end()); + + return {data_vec, planes_fds_vec}; + } + + static IPABuffer deserialize(std::vector &data, + std::vector &fds, + ControlSerializer *cs = nullptr) + { + return IPADataSerializer::deserialize(data.begin(), data.end(), + fds.begin(), fds.end(), cs); + } + + static IPABuffer deserialize(std::vector::iterator data_it1, + std::vector::iterator data_it2, + std::vector::iterator fds_it1, + std::vector::iterator fds_it2, + ControlSerializer *cs = nullptr) + { + IPABuffer ret; + + ret.id = readUInt(data_it1); + + ret.planes = + IPADataSerializer>::deserialize( + data_it1 + 4, data_it2, fds_it1, fds_it2, cs); + + return ret; + } +}; + +#endif /* __DOXYGEN__ */ + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_H__ */ diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp new file mode 100644 index 00000000..5029cdf6 --- /dev/null +++ b/src/libcamera/ipa_data_serializer.cpp @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * ipa_data_serializer.cpp - Image Processing Algorithm data serializer + */ + +#include "libcamera/internal/ipa_data_serializer.h" + +#include "libcamera/internal/log.h" + +/** + * \file ipa_data_serializer.h + * \brief IPA Data Serializer + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(IPADataSerializer) + +/** + * \class IPADataSerializer + * \brief IPA Data Serializer + * + * Static template class that provides functions for serializing and + * deserializing IPA data. + */ + +/** + * \fn template void appendUInt(std::vector &vec, T val) + * \brief Append uint to end of byte vector, in little-endian order + * \tparam T Type of uint to append + * \param[in] vec Byte vector to append to + * \param[in] val Value of uint to append + */ + +/** + * \fn template T readUInt(std::vector &vec, size_t pos) + * \brief Read uint from byte vector, in little-endian order + * \tparam T Type of uint to read + * \param[in] vec Byte vector to read from + * \param[in] pos Index in vec to start reading from + * + * \return The uint read from \a vec, or 0 if reading goes past end of \a vec + */ + +/** + * \fn template T readUInt(std::vector::iterator it) + * \brief Read uint from byte vector, in little-endian order + * \tparam T Type of uint to read + * \param[in] it Iterator of byte vector to read from + * + * \return The uint read from \a vec + */ + +/** + * \fn template IPADataSerializer::serialize( + * T data, + * ControlSerializer *cs = nullptr) + * \brief Serialize an object into byte vector and fd vector + * \tparam T Type of object to serialize + * \param[in] data Object to serialize + * \param[in] cs ControlSerializer + * + * \a cs is only necessary if the object type \a T or its members contain + * ControlList or ControlInfoMap. + * + * \return Tuple of byte vector and fd vector, that is the serialized form + * of \a data + */ + +/** + * \fn template IPADataSerializer::deserialize( + * std::vector &data, + * ControlSerializer *cs = nullptr) + * \brief Deserialize byte vector into an object + * \tparam T Type of object to deserialize to + * \param[in] data Byte vector to deserialize from + * \param[in] cs ControlSerializer + * + * This version of deserialize() can be used if the object type \a T and its + * members don't have any FileDescriptor. + * + * \a cs is only necessary if the object type \a T or its members contain + * ControlList or ControlInfoMap. + * + * \return The deserialized object + */ + +/** + * \fn template IPADataSerializer::deserialize( + * std::vector::iterator it1, + * std::vector::iterator it2, + * ControlSerializer *cs = nullptr) + * \brief Deserialize byte vector into an object + * \tparam T Type of object to deserialize to + * \param[in] it1 Begin iterator of byte vector to deserialize from + * \param[in] it2 End iterator of byte vector to deserialize from + * \param[in] cs ControlSerializer + * + * This version of deserialize() can be used if the object type \a T and its + * members don't have any FileDescriptor. + * + * \a cs is only necessary if the object type \a T or its members contain + * ControlList or ControlInfoMap. + * + * \return The deserialized object + */ + +/** + * \fn template IPADataSerializer::deserialize( + * std::vector &data, + * std::vector &fds, + * ControlSerializer *cs = nullptr) + * \brief Deserialize byte vector and fd vector into an object + * \tparam T Type of object to deserialize to + * \param[in] data Byte vector to deserialize from + * \param[in] fds Fd vector to deserialize from + * \param[in] cs ControlSerializer + * + * This version of deserialize() (or the iterator version) must be used if + * the object type \a T or its members contain FileDescriptor. + * + * \a cs is only necessary if the object type \a T or its members contain + * ControlList or ControlInfoMap. + * + * \return The deserialized object + */ + +/** + * \fn template IPADataSerializer::deserialize( + * std::vector::iterator data_it1, + * std::vector::iterator data_it2, + * std::vector::iterator fds_it1, + * std::vector::iterator fds_it2, + * ControlSerializer *cs = nullptr) + * \brief Deserialize byte vector and fd vector into an object + * \tparam T Type of object to deserialize to + * \param[in] data_it1 Begin iterator of byte vector to deserialize from + * \param[in] data_it2 End iterator of byte vector to deserialize from + * \param[in] fds_it1 Begin iterator of fd vector to deserialize from + * \param[in] fds_it2 End iterator of fd vector to deserialize from + * \param[in] cs ControlSerializer + * + * This version of deserialize() (or the vector version) must be used if + * the object type \a T or its members contain FileDescriptor. + * + * \a cs is only necessary if the object type \a T or its members contain + * ControlList or ControlInfoMap. + * + * \return The deserialized object + */ + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 07711b5f..190d7490 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -24,6 +24,7 @@ libcamera_sources = files([ 'geometry.cpp', 'ipa_context_wrapper.cpp', 'ipa_controls.cpp', + 'ipa_data_serializer.cpp', 'ipa_interface.cpp', 'ipa_manager.cpp', 'ipa_module.cpp', From patchwork Fri Oct 2 14:31:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9919 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 F3492C3B5C for ; Fri, 2 Oct 2020 14:32:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BB80C63B6E; Fri, 2 Oct 2020 16:32:34 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="kspCDVoQ"; 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 AA6EC63B6E for ; Fri, 2 Oct 2020 16:32:33 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E00EE2A2; Fri, 2 Oct 2020 16:32:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649153; bh=kaNKMD3AjVHE+16afzcqNrZh8mELyQJUprSl13QdFLw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kspCDVoQkVqGr3dZ1H7/goHuUhCBcjVXF4ZH9Tgepwq0SwzQkP9E2KVFsYKj3DIOK p3FaPAl/esZ9+IcghcswW1+tn/qmgxIJplwDv4agM0YiweGWRVzwxZF3DGmb/sbpq8 SQCcw8dr/ltJw7GG1filREnKIgwImnQT7JfmY8ZU= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:27 +0900 Message-Id: <20201002143154.468162-12-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 11/38] libcamera: Add IPAIPC 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" Create a virtual IPAIPC class that models an IPC/RPC system. IPA proxies and proxy workers will call into the IPAIPC, rather than implementing the IPC themselves. Signed-off-by: Paul Elder Reviewed-by: Jacopo Mondi --- Changes in v3: - expand documentation a bit - move [[maybe_unused]] to after the parameters in the IPAIPC constructor, to appease gcc <= 9.1 Changes in v2: - add documentation --- include/libcamera/internal/ipa_ipc.h | 41 ++++++++++ src/libcamera/ipa_ipc.cpp | 114 +++++++++++++++++++++++++++ src/libcamera/meson.build | 1 + 3 files changed, 156 insertions(+) create mode 100644 include/libcamera/internal/ipa_ipc.h create mode 100644 src/libcamera/ipa_ipc.cpp diff --git a/include/libcamera/internal/ipa_ipc.h b/include/libcamera/internal/ipa_ipc.h new file mode 100644 index 00000000..09de36a5 --- /dev/null +++ b/include/libcamera/internal/ipa_ipc.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * ipa_ipc.h - Image Processing Algorithm IPC module for IPA proxies + */ +#ifndef __LIBCAMERA_INTERNAL_IPA_IPC_H__ +#define __LIBCAMERA_INTERNAL_IPA_IPC_H__ + +#include + +namespace libcamera { + +class IPAIPC +{ +public: + IPAIPC(const char *ipa_module_path [[maybe_unused]], + const char *ipa_proxy_worker_path [[maybe_unused]]); + virtual ~IPAIPC(); + + bool isValid() const { return valid_; } + + virtual int sendSync(uint32_t cmd, + const std::vector &data_in, + const std::vector &fds_in, + std::vector *data_out = nullptr, + std::vector *fds_out = nullptr) = 0; + + virtual int sendAsync(uint32_t cmd, + const std::vector &data_in, + const std::vector &fds_in) = 0; + + Signal &, std::vector &> recvIPC; + +protected: + bool valid_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_INTERNAL_IPA_IPC_H__ */ diff --git a/src/libcamera/ipa_ipc.cpp b/src/libcamera/ipa_ipc.cpp new file mode 100644 index 00000000..b963351f --- /dev/null +++ b/src/libcamera/ipa_ipc.cpp @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * ipa_ipc.cpp - Image Processing Algorithm IPC module for IPA proxies + */ + +#include + +#include "libcamera/internal/ipc_unixsocket.h" +#include "libcamera/internal/log.h" +#include "libcamera/internal/process.h" +#include "libcamera/internal/thread.h" + +#include +#include + +#include "libcamera/internal/ipa_ipc.h" + +/** + * \file ipa_ipc.h + * \brief IPC mechanism for IPA isolation + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(IPAIPC) + +/** + * \class IPAIPC + * \brief IPC mechanism for IPA isolation + * + * Virtual class to model an IPC mechanism for use by IPA proxies for IPA + * isolation. sendSync() and sendAsync() must be implemented, and the recvIPC + * signal must be emitted whenever new data is available. + */ + +/** + * \brief Construct an IPAIPC instance + * \param[in] ipa_module_path Path to the IPA module shared object + * \param[in] ipa_proxy_worker_path Path to the IPA proxy worker shared object + */ +IPAIPC::IPAIPC([[maybe_unused]] const char *ipa_module_path, + [[maybe_unused]] const char *ipa_proxy_worker_path) + : valid_(false) +{ +} + +IPAIPC::~IPAIPC() +{ +} + +/** + * \fn IPAIPC::isValid() + * \brief Check if the IPAIPC instance is valid + * + * An IPAIPC instance is valid if the IPA interface is successfully created in + * isolation, and IPC is successfully set up. + * + * \return True if the IPAIPC is valid, false otherwise + */ + +/** + * \fn IPAIPC::sendSync() + * \brief Send a message over IPC synchronously + * \param[in] cmd Command ID + * \param[in] data_in Data to send, as a byte vector + * \param[in] fds_in Fds to send, in a vector + * \param[in] data_out Byte vector in which to receive data, if applicable + * \param[in] fds_out Fds vector in which to receive fds, if applicable + * + * This function will not return until a response is received. The event loop + * will still continue to execute, however. + * + * \return Zero on success, negative error code otherwise + */ + +/** + * \fn IPAIPC::sendAsync() + * \brief Send a message over IPC asynchronously + * \param[in] cmd Command ID + * \param[in] data_in Data to send, as a byte vector + * \param[in] fds_in Fds to send, in a vector + * + * This function will return immediately after sending the message. + * + * \return Zero on success, negative error code otherwise + */ + +/** + * \var IPAIPC::recvIPC + * \brief Signal to be emitted when data is received over IPC + * + * When data is received over IPC, this signal shall be emitted. Users must + * connect to this to receive new data. + * + * The parameters of the signal are a vector of bytes and a vector of file + * descriptors. + */ + +/** + * \var IPAIPC::valid_ + * \brief Flag to indicate if the IPAIPC instance is valid + * + * An IPAIPC instance is valid if the IPA interface is successfully created in + * isolation, and IPC is successfully set up. + * + * This flag can be read via IPAIPC::isValid(). + * + * Implementations of the IPAIPC class should set this flag upon successful + * construction. + */ + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 190d7490..e0a2ab47 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -25,6 +25,7 @@ libcamera_sources = files([ 'ipa_context_wrapper.cpp', 'ipa_controls.cpp', 'ipa_data_serializer.cpp', + 'ipa_ipc.cpp', 'ipa_interface.cpp', 'ipa_manager.cpp', 'ipa_module.cpp', From patchwork Fri Oct 2 14:31:28 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9920 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 5702BC3B5C for ; Fri, 2 Oct 2020 14:32:37 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 263EE63BBF; Fri, 2 Oct 2020 16:32:37 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="aJCamqGm"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C451763B6A for ; Fri, 2 Oct 2020 16:32:35 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 11F3C2A2; Fri, 2 Oct 2020 16:32:33 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649155; bh=LZwf6NYnL3vlSnPlY9j50q0aJohdnszn10mByNlmNBY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=aJCamqGmcl9FVTrSgCz7C2noO0sXVjN0AjDo1Za24xzvpqIP+s74F+kgLrB9awx66 jfpeAHzvDUHnep8GLqmBsTf7tCJ+I2Lu7KGhLA8HFzMx9ikS4ah0EP3VLP0P0VgMZx O8G/vyVWBTBZXS4WtiR6avkpLWN/kxp+Vza0it0w= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:28 +0900 Message-Id: <20201002143154.468162-13-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 12/38] libcamera: Add IPAIPC implementation based on unix socket 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 implementation of IPAIPC using unix socket. Signed-off-by: Paul Elder --- Changes in v3: - remove unused writeUInt32() and readUInt32() - remove redundant definition of IPAIPCUnixSocket::isValid() - remove & 0xff in writeHeader() - make readHeader, writeHeader, and eraseHeader static class functions of IPAIPCUnixSocket instead of globals Changes in v2: - specify in doxygen to skip generating documentation for IPAIPCUnixSocket --- Documentation/Doxyfile.in | 2 + .../libcamera/internal/ipa_ipc_unixsocket.h | 62 +++++ src/libcamera/ipa_ipc_unixsocket.cpp | 211 ++++++++++++++++++ src/libcamera/meson.build | 1 + 4 files changed, 276 insertions(+) create mode 100644 include/libcamera/internal/ipa_ipc_unixsocket.h create mode 100644 src/libcamera/ipa_ipc_unixsocket.cpp diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in index 598eca9b..71509ff7 100644 --- a/Documentation/Doxyfile.in +++ b/Documentation/Doxyfile.in @@ -837,8 +837,10 @@ RECURSIVE = YES EXCLUDE = @TOP_SRCDIR@/include/libcamera/span.h \ @TOP_SRCDIR@/include/libcamera/internal/device_enumerator_sysfs.h \ @TOP_SRCDIR@/include/libcamera/internal/device_enumerator_udev.h \ + @TOP_SRCDIR@/include/libcamera/internal/ipa_ipc_unixsocket.h \ @TOP_SRCDIR@/src/libcamera/device_enumerator_sysfs.cpp \ @TOP_SRCDIR@/src/libcamera/device_enumerator_udev.cpp \ + @TOP_SRCDIR@/src/libcamera/ipa_ipc_unixsocket.cpp \ @TOP_SRCDIR@/src/libcamera/pipeline/ \ @TOP_SRCDIR@/src/libcamera/proxy/ \ @TOP_BUILDDIR@/include/libcamera/ipa/ \ diff --git a/include/libcamera/internal/ipa_ipc_unixsocket.h b/include/libcamera/internal/ipa_ipc_unixsocket.h new file mode 100644 index 00000000..11f05b12 --- /dev/null +++ b/include/libcamera/internal/ipa_ipc_unixsocket.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * ipa_ipc_unixsocket.h - Image Processing Algorithm IPC module using unix socket + */ +#ifndef __LIBCAMERA_INTERNAL_IPA_IPC_UNIXSOCKET_H__ +#define __LIBCAMERA_INTERNAL_IPA_IPC_UNIXSOCKET_H__ + +#include + +#include + +#include "libcamera/internal/ipa_ipc.h" +#include "libcamera/internal/ipa_module.h" +#include "libcamera/internal/ipc_unixsocket.h" + +namespace libcamera { + +class Process; + +class IPAIPCUnixSocket : public IPAIPC +{ +public: + IPAIPCUnixSocket(const char *ipa_module_path, const char *ipa_proxy_worker_path); + ~IPAIPCUnixSocket(); + + int sendSync(uint32_t cmd, + const std::vector &data_in, + const std::vector &fds_in, + std::vector *data_out = nullptr, + std::vector *fds_out = nullptr) override; + + int sendAsync(uint32_t cmd, + const std::vector &data_in, + const std::vector &fds_in) override; + + static void writeHeader(IPCUnixSocket::Payload &payload, uint32_t cmd, uint32_t seq); + static std::tuple readHeader(IPCUnixSocket::Payload &payload); + static void eraseHeader(IPCUnixSocket::Payload &payload); + +private: + struct CallData { + IPCUnixSocket::Payload *response; + bool done; + }; + + void readyRead(IPCUnixSocket *socket); + int call(const IPCUnixSocket::Payload &message, IPCUnixSocket::Payload *response, uint32_t seq); + + uint32_t seq_; + + Process *proc_; + + IPCUnixSocket *socket_; + + std::map callData_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_INTERNAL_IPA_IPC_UNIXSOCKET_H__ */ diff --git a/src/libcamera/ipa_ipc_unixsocket.cpp b/src/libcamera/ipa_ipc_unixsocket.cpp new file mode 100644 index 00000000..6dc92768 --- /dev/null +++ b/src/libcamera/ipa_ipc_unixsocket.cpp @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * ipa_ipc_unixsocket.cpp - Image Processing Algorithm IPC module using unix socket + */ + +#include + +#include "libcamera/internal/ipc_unixsocket.h" +#include "libcamera/internal/log.h" +#include "libcamera/internal/process.h" +#include "libcamera/internal/thread.h" + +#include +#include + +#include "libcamera/internal/ipa_ipc.h" +#include "libcamera/internal/ipa_ipc_unixsocket.h" + +namespace libcamera { + +LOG_DECLARE_CATEGORY(IPAIPC) + +IPAIPCUnixSocket::IPAIPCUnixSocket(const char *ipa_module_path, + const char *ipa_proxy_worker_path) + : IPAIPC(ipa_module_path, ipa_proxy_worker_path), seq_(0), + proc_(nullptr), socket_(nullptr) +{ + std::vector fds; + std::vector args; + args.push_back(ipa_module_path); + + socket_ = new IPCUnixSocket(); + int fd = socket_->create(); + if (fd < 0) { + LOG(IPAIPC, Error) + << "Failed to create socket"; + return; + } + socket_->readyRead.connect(this, &IPAIPCUnixSocket::readyRead); + args.push_back(std::to_string(fd)); + fds.push_back(fd); + + proc_ = new Process(); + int ret = proc_->start(ipa_proxy_worker_path, args, fds); + if (ret) { + LOG(IPAIPC, Error) + << "Failed to start proxy worker process"; + return; + } + + valid_ = true; +} + +IPAIPCUnixSocket::~IPAIPCUnixSocket() +{ + delete proc_; + delete socket_; +} + +int IPAIPCUnixSocket::sendSync(uint32_t cmd, + const std::vector &data_in, + const std::vector &fds_in, + std::vector *data_out, + std::vector *fds_out) +{ + IPCUnixSocket::Payload message, response; + int ret; + + /* It's fine if seq_ overflows; that'll just be the new epoch. */ + seq_++; + writeHeader(message, cmd, seq_); + message.data.insert(message.data.end(), data_in.begin(), data_in.end()); + + message.fds = const_cast &>(fds_in); + + ret = call(message, &response, seq_); + if (ret) { + LOG(IPAIPC, Error) << "Failed to call sync"; + callData_.erase(seq_); + return ret; + } + + if (data_out) + data_out->insert(data_out->end(), response.data.begin(), response.data.end()); + + if (fds_out) + fds_out->insert(fds_out->end(), response.fds.begin(), response.fds.end()); + + return 0; +} + +int IPAIPCUnixSocket::sendAsync(uint32_t cmd, + const std::vector &data_in, + const std::vector &fds_in) +{ + IPCUnixSocket::Payload message; + int ret; + + writeHeader(message, cmd, 0); + message.data.insert(message.data.end(), data_in.begin(), data_in.end()); + + message.fds = const_cast &>(fds_in); + + ret = socket_->send(message); + if (ret) { + LOG(IPAIPC, Error) << "Failed to call async"; + return ret; + } + + return 0; +} + + +void IPAIPCUnixSocket::writeHeader(IPCUnixSocket::Payload &payload, + uint32_t cmd, uint32_t seq) +{ + uint8_t cmd_arr[] = {static_cast(cmd), + static_cast((cmd >> 8)), + static_cast((cmd >> 16)), + static_cast((cmd >> 24))}; + uint8_t seq_arr[] = {static_cast(seq), + static_cast((seq >> 8)), + static_cast((seq >> 16)), + static_cast((seq >> 24))}; + payload.data.insert(payload.data.begin(), cmd_arr, cmd_arr+4); + payload.data.insert(payload.data.begin() + 4, seq_arr, seq_arr+4); +} + +std::tuple +IPAIPCUnixSocket::readHeader(IPCUnixSocket::Payload &payload) +{ + uint32_t cmd = payload.data[0] | + (payload.data[1] << 8) | + (payload.data[2] << 16) | + (payload.data[3] << 24); + uint32_t seq = payload.data[4] | + (payload.data[5] << 8) | + (payload.data[6] << 16) | + (payload.data[7] << 24); + + return {cmd, seq}; +} + +void IPAIPCUnixSocket::eraseHeader(IPCUnixSocket::Payload &payload) +{ + payload.data.erase(payload.data.begin(), payload.data.begin() + 8); +} + +void IPAIPCUnixSocket::readyRead(IPCUnixSocket *socket) +{ + IPCUnixSocket::Payload message; + int ret = socket->receive(&message); + if (ret) { + LOG(IPAIPC, Error) << "Receive message failed" << ret; + return; + } + + uint32_t cmd, seq; + std::tie(cmd, seq) = readHeader(message); + + auto callData = callData_.find(seq); + if (callData != callData_.end()) { + eraseHeader(message); + /* Is there any way to avoid this copy? */ + *callData->second.response = message; + callData->second.done = true; + return; + } + + /* + * Received unexpected data, this means it's a call from the IPA. + * We can't return anything to the IPA (gotta keep them under *our* + * control, plus returning would require blocking the caller, and we + * can't afford to do that). Let the proxy do switch-case on cmd. + */ + recvIPC.emit(message.data, message.fds); + + return; +} + +int IPAIPCUnixSocket::call(const IPCUnixSocket::Payload &message, IPCUnixSocket::Payload *response, uint32_t seq) +{ + Timer timeout; + int ret; + + callData_[seq].response = response; + callData_[seq].done = false; + + ret = socket_->send(message); + if (ret) + return ret; + + timeout.start(200); + while (!callData_[seq].done) { + if (!timeout.isRunning()) { + LOG(IPAIPC, Error) << "Call timeout!"; + callData_.erase(seq); + return -ETIMEDOUT; + } + + Thread::current()->eventDispatcher()->processEvents(); + } + + callData_.erase(seq); + + return 0; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index e0a2ab47..59417403 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -26,6 +26,7 @@ libcamera_sources = files([ 'ipa_controls.cpp', 'ipa_data_serializer.cpp', 'ipa_ipc.cpp', + 'ipa_ipc_unixsocket.cpp', 'ipa_interface.cpp', 'ipa_manager.cpp', 'ipa_module.cpp', From patchwork Fri Oct 2 14:31:29 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9921 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 C27F7C3B5C for ; Fri, 2 Oct 2020 14:32:39 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 90D2E63BBF; Fri, 2 Oct 2020 16:32:39 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="WiGQh+o8"; 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 6430F63B6A for ; Fri, 2 Oct 2020 16:32:38 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 4E5852A2; Fri, 2 Oct 2020 16:32:36 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649158; bh=gzqffvHSm3Rfkei1rFCAjpqHSqAQW+rDK0X9tA0ukEQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WiGQh+o8JTzoMLZPNWvKDaWvyjYNrPTm4yFV+5XJVo+ynFe+2TcFYHBEbIYG4bbap rDtIj6QSsVfPs4H1OO4dbytCZYZlFfbeMLe3jZ+T7/86YxJbCTTz/vCQrd71NOa7uy NDbVC8iLk8voERbDrdlFeCPenCl9+erMyEosv0yw= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:29 +0900 Message-Id: <20201002143154.468162-14-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 13/38] meson: ipa, proxy: Generate headers and proxy with mojo 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" Run mojo from meson to generate the header, serializer, and proxy files for every pipeline's mojom data definition file. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- Changes in v3: - change parser path to our wrapper parser script, instead of running mojo's directly Changes in v2: - clean up loops - remove non-literal keys in maps - move loading the mojom scripts to utils/ - declare the template files in meson - add the generated headers into libcamera_generated_headers instead of libcamera_internal_headers - add libcamera_generated_headers to libcamera_sources --- include/libcamera/ipa/meson.build | 78 +++++++++++++++++++ src/libcamera/meson.build | 1 + src/libcamera/proxy/meson.build | 18 +++++ src/libcamera/proxy/worker/meson.build | 19 ++++- .../libcamera_templates/meson.build | 11 +++ utils/ipc/generators/meson.build | 3 + utils/ipc/meson.build | 14 ++++ utils/meson.build | 1 + 8 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 utils/ipc/generators/libcamera_templates/meson.build create mode 100644 utils/ipc/generators/meson.build create mode 100644 utils/ipc/meson.build diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build index 508c6bd1..337948d2 100644 --- a/include/libcamera/ipa/meson.build +++ b/include/libcamera/ipa/meson.build @@ -8,3 +8,81 @@ libcamera_ipa_headers = files([ install_headers(libcamera_ipa_headers, subdir: join_paths(libcamera_include_dir, 'ipa')) + +# +# Prepare IPA/IPC generation components +# + +ipa_mojom_files = [] + +ipa_mojoms = [] + +# +# Generate headers from templates. +# + +libcamera_generated_headers = [] + +foreach file : ipa_mojom_files + name = file.split('.')[0] + + # {pipeline}.mojom-module + mojom = custom_target(file.split('.')[0] + '_mojom_module', + input : file, + output : file + '-module', + command : [ + mojom_parser, + '--output-root', meson.build_root(), + '--input-root', meson.source_root(), + '--mojoms', '@INPUT@' + ]) + + # {pipeline}_generated.h + header = custom_target(name + '_generated_h', + input : mojom, + output : name + '_generated.h', + depends : mojom_templates, + command : [ + mojom_generator, 'generate', + '-g', 'libcamera', + '--bytecode_path', mojom_templates_dir, + '--libcamera_generate_header', + '--libcamera_output_path=@OUTPUT@', + './' +'@INPUT@' + ]) + + # {pipeline}_serializer.h + serializer = custom_target(name + '_serializer_h', + input : mojom, + output : name + '_serializer.h', + depends : mojom_templates, + command : [ + mojom_generator, 'generate', + '-g', 'libcamera', + '--bytecode_path', mojom_templates_dir, + '--libcamera_generate_serializer', + '--libcamera_output_path=@OUTPUT@', + './' +'@INPUT@' + ]) + + # ipa_proxy_{pipeline}.h + proxy_header = custom_target(name + '_proxy_h', + input : mojom, + output : 'ipa_proxy_' + name + '.h', + depends : mojom_templates, + command : [ + mojom_generator, 'generate', + '-g', 'libcamera', + '--bytecode_path', mojom_templates_dir, + '--libcamera_generate_proxy_h', + '--libcamera_output_path=@OUTPUT@', + './' +'@INPUT@' + ]) + + ipa_mojoms += { + 'name': name, + 'mojom': mojom, + } + + libcamera_generated_headers += [header, serializer, proxy_header] +endforeach diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 59417403..796e381a 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -58,6 +58,7 @@ libcamera_sources = files([ ]) libcamera_sources += libcamera_public_headers +libcamera_sources += libcamera_generated_headers includes = [ libcamera_includes, diff --git a/src/libcamera/proxy/meson.build b/src/libcamera/proxy/meson.build index bd804750..ac68ad08 100644 --- a/src/libcamera/proxy/meson.build +++ b/src/libcamera/proxy/meson.build @@ -4,3 +4,21 @@ libcamera_sources += files([ 'ipa_proxy_linux.cpp', 'ipa_proxy_thread.cpp', ]) + +# generate ipa_proxy_{pipeline}.cpp +foreach mojom : ipa_mojoms + proxy = custom_target(mojom['name'] + '_proxy_cpp', + input : mojom['mojom'], + output : 'ipa_proxy_' + mojom['name'] + '.cpp', + depends : [mojom_templates], + command : [ + mojom_generator, 'generate', + '-g', 'libcamera', + '--bytecode_path', mojom_templates_dir, + '--libcamera_generate_proxy_cpp', + '--libcamera_output_path=@OUTPUT@', + './' + '@INPUT@' + ]) + + libcamera_sources += proxy +endforeach diff --git a/src/libcamera/proxy/worker/meson.build b/src/libcamera/proxy/worker/meson.build index ac0310a7..5490811b 100644 --- a/src/libcamera/proxy/worker/meson.build +++ b/src/libcamera/proxy/worker/meson.build @@ -6,8 +6,23 @@ ipa_proxy_sources = [ proxy_install_dir = join_paths(get_option('libexecdir'), 'libcamera') -foreach t : ipa_proxy_sources - proxy = executable(t[0], t[1], +# generate ipa_proxy_{pipeline}_worker.cpp +foreach mojom : ipa_mojoms + worker = custom_target(mojom['name'] + '_proxy_worker', + input : mojom['mojom'], + output : 'ipa_proxy_' + mojom['name'] + '_worker.cpp', + depends : [mojom_templates], + command : [ + mojom_generator, 'generate', + '-g', 'libcamera', + '--bytecode_path', mojom_templates_dir, + '--libcamera_generate_proxy_worker', + '--libcamera_output_path=@OUTPUT@', + './' + '@INPUT@' + ]) + + proxy = executable('ipa_proxy_' + mojom['name'], + [worker, libcamera_generated_headers], install : true, install_dir : proxy_install_dir, dependencies : libcamera_dep) diff --git a/utils/ipc/generators/libcamera_templates/meson.build b/utils/ipc/generators/libcamera_templates/meson.build new file mode 100644 index 00000000..d0a7449f --- /dev/null +++ b/utils/ipc/generators/libcamera_templates/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: CC0-1.0 + +mojom_template_files = files([ + 'module_generated.h.tmpl', + 'module_ipa_proxy.cpp.tmpl', + 'module_ipa_proxy.h.tmpl', + 'module_ipa_proxy_worker.cpp.tmpl', + 'module_serializer.h.tmpl', + 'proxy_functions.tmpl', + 'serializer.tmpl', +]) diff --git a/utils/ipc/generators/meson.build b/utils/ipc/generators/meson.build new file mode 100644 index 00000000..504f1a46 --- /dev/null +++ b/utils/ipc/generators/meson.build @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: CC0-1.0 + +subdir('libcamera_templates') diff --git a/utils/ipc/meson.build b/utils/ipc/meson.build new file mode 100644 index 00000000..ca214633 --- /dev/null +++ b/utils/ipc/meson.build @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: CC0-1.0 + +subdir('generators') + +mojom_parser = find_program('./parser.py') + +mojom_generator = find_program('./generate.py') + +mojom_templates = custom_target('mojom_templates', + input : mojom_template_files, + output : 'libcamera_templates.zip', + command : [mojom_generator, '-o', '@OUTDIR@', 'precompile']) + +mojom_templates_dir = meson.current_build_dir() diff --git a/utils/meson.build b/utils/meson.build index 3d83b975..383d5285 100644 --- a/utils/meson.build +++ b/utils/meson.build @@ -1,5 +1,6 @@ # SPDX-License-Identifier: CC0-1.0 +subdir('ipc') subdir('ipu3') ## Code generation From patchwork Fri Oct 2 14:31:30 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9922 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 60E72C3B5C for ; Fri, 2 Oct 2020 14:32:43 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 2FC0363B98; Fri, 2 Oct 2020 16:32:43 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="L54cxO6+"; 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 EBD3E63B6A for ; Fri, 2 Oct 2020 16:32:40 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D70D8528; Fri, 2 Oct 2020 16:32:38 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649160; bh=krYBpsCFXAgmhYpzglB2Rz8MJTleaeNEHSvcXW7oij4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=L54cxO6+kvW6vcsaF09uXuA1qwgygU5t5LPg7Ei8bVSQeU6O1Vzz8mb53vJgLBuoO LjFiokyuTc5kToreWYT6i69jWUxv3wRAXNYh5m1SoFDpEfBAen5EWFFdj3CYM5lp6a pytqkFoSyrjcv1CZWiZODvIQp9WprQ410pZg9jYw= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:30 +0900 Message-Id: <20201002143154.468162-15-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 14/38] ipa: raspberrypi: meson: Add dependency on generated headers 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" The raspberrypi IPA will depend on the raspberrypi IPA header generated from the mojo definition file. Add the dependency. Simply add all generated headers as a dependency, instead of walking the list of headers, to simplify the implementation. This will have the effect of generating all headers before compiling the IPA, which has no drawbacks. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- Changes in v3: - expand changelog New in v2 --- src/ipa/raspberrypi/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ipa/raspberrypi/meson.build b/src/ipa/raspberrypi/meson.build index 9445cd09..27c29249 100644 --- a/src/ipa/raspberrypi/meson.build +++ b/src/ipa/raspberrypi/meson.build @@ -42,7 +42,7 @@ rpi_ipa_sources = files([ ]) mod = shared_module(ipa_name, - rpi_ipa_sources, + [rpi_ipa_sources, libcamera_generated_headers], name_prefix : '', include_directories : rpi_ipa_includes, dependencies : rpi_ipa_deps, From patchwork Fri Oct 2 14:31:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9923 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 B1F03C3B5C for ; Fri, 2 Oct 2020 14:32:44 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7F9CC63BCA; Fri, 2 Oct 2020 16:32:44 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="FMr0Ec4h"; 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 803DA63BA1 for ; Fri, 2 Oct 2020 16:32:43 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 6BDC21227; Fri, 2 Oct 2020 16:32:41 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649163; bh=p0PQMXfmG5X0veOq8ApmjQ4021i0zabNkxPaWsShCMk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FMr0Ec4hTFwl0unoedrkg0a10TNXOSh2WIQZYTU4qK5FWtZy7paSUQWzTrqvZAVOu cPDZYGvRoT1orKRBrMoDdll+yfQE2cl9oOmnzRdt8dBc+W65QkrWSZGWxm6utndbd9 IeQOujB79wEdUYjl8uqUaO7SnHAzIPkr3aTeLi9g= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:31 +0900 Message-Id: <20201002143154.468162-16-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 15/38] libcamera: IPAModule: Replace ipa_context with IPAInterface 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" With the new IPC infrastructure, we no longer need the C interface as provided by struct ipa_context. Make ipaCreate_() and createInterface() return IPAInterface. Signed-off-by: Paul Elder Reviewed-by: Niklas Söderlund --- No change in v3 Changes in v2: - add documentation for IPAModule::createInterface() --- include/libcamera/internal/ipa_module.h | 4 ++-- src/libcamera/ipa_module.cpp | 10 ++++------ src/libcamera/meson.build | 1 - 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/include/libcamera/internal/ipa_module.h b/include/libcamera/internal/ipa_module.h index c2df2476..19fc5827 100644 --- a/include/libcamera/internal/ipa_module.h +++ b/include/libcamera/internal/ipa_module.h @@ -33,7 +33,7 @@ public: bool load(); - struct ipa_context *createContext(); + IPAInterface *createInterface(); bool match(PipelineHandler *pipe, uint32_t minVersion, uint32_t maxVersion) const; @@ -52,7 +52,7 @@ private: bool loaded_; void *dlHandle_; - typedef struct ipa_context *(*IPAIntfFactory)(); + typedef IPAInterface *(*IPAIntfFactory)(void); IPAIntfFactory ipaCreate_; }; diff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp index de512a7f..d5ac9820 100644 --- a/src/libcamera/ipa_module.cpp +++ b/src/libcamera/ipa_module.cpp @@ -439,20 +439,18 @@ bool IPAModule::load() } /** - * \brief Instantiate an IPA context + * \brief Instantiate an IPA interface * * After loading the IPA module with load(), this method creates an instance of - * the IPA module context. Ownership of the context is passed to the caller, and - * the context shall be destroyed by calling the \ref ipa_context_ops::destroy - * "ipa_context::ops::destroy()" function. + * the IPA module interface. * * Calling this function on a module that has not yet been loaded, or an * invalid module (as returned by load() and isValid(), respectively) is * an error. * - * \return The IPA context on success, or nullptr on error + * \return The IPA interface on success, or nullptr on error */ -struct ipa_context *IPAModule::createContext() +IPAInterface *IPAModule::createInterface() { if (!valid_ || !loaded_) return nullptr; diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 796e381a..42340eae 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -22,7 +22,6 @@ libcamera_sources = files([ 'formats.cpp', 'framebuffer_allocator.cpp', 'geometry.cpp', - 'ipa_context_wrapper.cpp', 'ipa_controls.cpp', 'ipa_data_serializer.cpp', 'ipa_ipc.cpp', From patchwork Fri Oct 2 14:31:32 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9924 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 256E6C3B5C for ; Fri, 2 Oct 2020 14:32:47 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id E629A63BC1; Fri, 2 Oct 2020 16:32:46 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="hp4kEIHR"; 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 C4C8863B6A for ; Fri, 2 Oct 2020 16:32:45 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 0016D2A2; Fri, 2 Oct 2020 16:32:43 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649165; bh=JvGMlYaqUIe5BF6K0ewSJVUQBeEu2EeUJpV0QMvItmk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hp4kEIHR/rEFRWwVkyl4XWLPBtiASqZSclYLQ6jQyDQfLK7L0PVSQZEcHvP9vf58A ie3GNWG7IVtTNlZ6Mdi9YmxLuLaicX7d6JHamYGIvA5MSX6z1VXlBXnwuG9yZ1HeiB joZh3ywQFpUxa30DNaynlXNizN2Xf7wA9kLSef6A= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:32 +0900 Message-Id: <20201002143154.468162-17-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 16/38] libcamera: ipa_context_wrapper: Remove ipa_context_wrapper 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" Since ipa_context has been replaced with custom IPAInterfaces, it is not longer needed. Remove it. Signed-off-by: Paul Elder --- No change in v3 New in v2 --- .../libcamera/internal/ipa_context_wrapper.h | 52 --- include/libcamera/internal/meson.build | 1 - include/libcamera/ipa/ipa_interface.h | 106 ------- src/libcamera/ipa_context_wrapper.cpp | 297 ------------------ 4 files changed, 456 deletions(-) delete mode 100644 include/libcamera/internal/ipa_context_wrapper.h delete mode 100644 src/libcamera/ipa_context_wrapper.cpp diff --git a/include/libcamera/internal/ipa_context_wrapper.h b/include/libcamera/internal/ipa_context_wrapper.h deleted file mode 100644 index 8f767e84..00000000 --- a/include/libcamera/internal/ipa_context_wrapper.h +++ /dev/null @@ -1,52 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * ipa_context_wrapper.h - Image Processing Algorithm context wrapper - */ -#ifndef __LIBCAMERA_INTERNAL_IPA_CONTEXT_WRAPPER_H__ -#define __LIBCAMERA_INTERNAL_IPA_CONTEXT_WRAPPER_H__ - -#include - -#include "libcamera/internal/control_serializer.h" - -namespace libcamera { - -class IPAContextWrapper final : public IPAInterface -{ -public: - IPAContextWrapper(struct ipa_context *context); - ~IPAContextWrapper(); - - int init(const IPASettings &settings) override; - int start() override; - void stop() override; - void configure(const CameraSensorInfo &sensorInfo, - const std::map &streamConfig, - const std::map &entityControls, - const IPAOperationData &ipaConfig, - IPAOperationData *result) override; - - void mapBuffers(const std::vector &buffers) override; - void unmapBuffers(const std::vector &ids) override; - - virtual void processEvent(const IPAOperationData &data) override; - -private: - static void queue_frame_action(void *ctx, unsigned int frame, - struct ipa_operation_data &data); - static const struct ipa_callback_ops callbacks_; - - void doQueueFrameAction(unsigned int frame, - const IPAOperationData &data); - - struct ipa_context *ctx_; - IPAInterface *intf_; - - ControlSerializer serializer_; -}; - -} /* namespace libcamera */ - -#endif /* __LIBCAMERA_INTERNAL_IPA_CONTEXT_WRAPPER_H__ */ diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build index 15010338..8dd744fd 100644 --- a/include/libcamera/internal/meson.build +++ b/include/libcamera/internal/meson.build @@ -12,7 +12,6 @@ libcamera_internal_headers = files([ 'event_dispatcher_poll.h', 'file.h', 'formats.h', - 'ipa_context_wrapper.h', 'ipa_manager.h', 'ipa_module.h', 'ipa_proxy.h', diff --git a/include/libcamera/ipa/ipa_interface.h b/include/libcamera/ipa/ipa_interface.h index 5016ec25..38334ab9 100644 --- a/include/libcamera/ipa/ipa_interface.h +++ b/include/libcamera/ipa/ipa_interface.h @@ -10,111 +10,6 @@ #include #include -#ifdef __cplusplus -extern "C" { -#endif - -struct ipa_context { - const struct ipa_context_ops *ops; -}; - -struct ipa_settings { - const char *configuration_file; -}; - -struct ipa_sensor_info { - const char *model; - uint8_t bits_per_pixel; - struct { - uint32_t width; - uint32_t height; - } active_area; - struct { - int32_t left; - int32_t top; - uint32_t width; - uint32_t height; - } analog_crop; - struct { - uint32_t width; - uint32_t height; - } output_size; - uint64_t pixel_rate; - uint32_t line_length; -}; - -struct ipa_stream { - unsigned int id; - unsigned int pixel_format; - unsigned int width; - unsigned int height; -}; - -struct ipa_control_info_map { - unsigned int id; - const uint8_t *data; - size_t size; -}; - -struct ipa_buffer_plane { - int dmabuf; - size_t length; -}; - -struct ipa_buffer { - unsigned int id; - unsigned int num_planes; - struct ipa_buffer_plane planes[3]; -}; - -struct ipa_control_list { - const uint8_t *data; - unsigned int size; -}; - -struct ipa_operation_data { - unsigned int operation; - const uint32_t *data; - unsigned int num_data; - const struct ipa_control_list *lists; - unsigned int num_lists; -}; - -struct ipa_callback_ops { - void (*queue_frame_action)(void *cb_ctx, unsigned int frame, - struct ipa_operation_data &data); -}; - -struct ipa_context_ops { - void (*destroy)(struct ipa_context *ctx); - void *(*get_interface)(struct ipa_context *ctx); - void (*init)(struct ipa_context *ctx, - const struct ipa_settings *settings); - int (*start)(struct ipa_context *ctx); - void (*stop)(struct ipa_context *ctx); - void (*register_callbacks)(struct ipa_context *ctx, - const struct ipa_callback_ops *callbacks, - void *cb_ctx); - void (*configure)(struct ipa_context *ctx, - const struct ipa_sensor_info *sensor_info, - const struct ipa_stream *streams, - unsigned int num_streams, - const struct ipa_control_info_map *maps, - unsigned int num_maps); - void (*map_buffers)(struct ipa_context *ctx, - const struct ipa_buffer *buffers, - size_t num_buffers); - void (*unmap_buffers)(struct ipa_context *ctx, const unsigned int *ids, - size_t num_buffers); - void (*process_event)(struct ipa_context *ctx, - const struct ipa_operation_data *data); -}; - -struct ipa_context *ipaCreate(); - -#ifdef __cplusplus -} - #include #include @@ -170,6 +65,5 @@ public: }; } /* namespace libcamera */ -#endif #endif /* __LIBCAMERA_IPA_INTERFACE_H__ */ diff --git a/src/libcamera/ipa_context_wrapper.cpp b/src/libcamera/ipa_context_wrapper.cpp deleted file mode 100644 index 231300ce..00000000 --- a/src/libcamera/ipa_context_wrapper.cpp +++ /dev/null @@ -1,297 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * ipa_context_wrapper.cpp - Image Processing Algorithm context wrapper - */ - -#include "libcamera/internal/ipa_context_wrapper.h" - -#include - -#include - -#include "libcamera/internal/byte_stream_buffer.h" -#include "libcamera/internal/camera_sensor.h" -#include "libcamera/internal/utils.h" - -/** - * \file ipa_context_wrapper.h - * \brief Image Processing Algorithm context wrapper - */ - -namespace libcamera { - -/** - * \class IPAContextWrapper - * \brief Wrap an ipa_context and expose it as an IPAInterface - * - * The IPAContextWrapper class wraps an ipa_context, provided by an IPA module, and - * exposes an IPAInterface. This mechanism is used for IPAs that are not - * isolated in a separate process to allow direct calls from pipeline handler - * using the IPAInterface API instead of the lower-level ipa_context API. - * - * The IPAInterface methods are converted to the ipa_context API by translating - * all C++ arguments into plain C structures or byte arrays that contain no - * pointer, as required by the ipa_context API. - */ - -/** - * \brief Construct an IPAContextWrapper instance that wraps the \a context - * \param[in] context The IPA module context - * - * Ownership of the \a context is passed to the IPAContextWrapper. The context remains - * valid for the whole lifetime of the wrapper and is destroyed automatically - * with it. - */ -IPAContextWrapper::IPAContextWrapper(struct ipa_context *context) - : ctx_(context), intf_(nullptr) -{ - if (!ctx_) - return; - - bool forceCApi = !!utils::secure_getenv("LIBCAMERA_IPA_FORCE_C_API"); - - if (!forceCApi && ctx_ && ctx_->ops->get_interface) { - intf_ = reinterpret_cast(ctx_->ops->get_interface(ctx_)); - intf_->queueFrameAction.connect(this, &IPAContextWrapper::doQueueFrameAction); - return; - } - - ctx_->ops->register_callbacks(ctx_, &IPAContextWrapper::callbacks_, - this); -} - -IPAContextWrapper::~IPAContextWrapper() -{ - if (!ctx_) - return; - - ctx_->ops->destroy(ctx_); -} - -int IPAContextWrapper::init(const IPASettings &settings) -{ - if (intf_) - return intf_->init(settings); - - if (!ctx_) - return 0; - - struct ipa_settings c_settings; - c_settings.configuration_file = settings.configurationFile.c_str(); - - ctx_->ops->init(ctx_, &c_settings); - - return 0; -} - -int IPAContextWrapper::start() -{ - if (intf_) - return intf_->start(); - - if (!ctx_) - return 0; - - return ctx_->ops->start(ctx_); -} - -void IPAContextWrapper::stop() -{ - if (intf_) - return intf_->stop(); - - if (!ctx_) - return; - - ctx_->ops->stop(ctx_); -} - -void IPAContextWrapper::configure(const CameraSensorInfo &sensorInfo, - const std::map &streamConfig, - const std::map &entityControls, - const IPAOperationData &ipaConfig, - IPAOperationData *result) -{ - if (intf_) - return intf_->configure(sensorInfo, streamConfig, - entityControls, ipaConfig, result); - - if (!ctx_) - return; - - serializer_.reset(); - - /* Translate the camera sensor info. */ - struct ipa_sensor_info sensor_info = {}; - sensor_info.model = sensorInfo.model.c_str(); - sensor_info.bits_per_pixel = sensorInfo.bitsPerPixel; - sensor_info.active_area.width = sensorInfo.activeAreaSize.width; - sensor_info.active_area.height = sensorInfo.activeAreaSize.height; - sensor_info.analog_crop.left = sensorInfo.analogCrop.x; - sensor_info.analog_crop.top = sensorInfo.analogCrop.y; - sensor_info.analog_crop.width = sensorInfo.analogCrop.width; - sensor_info.analog_crop.height = sensorInfo.analogCrop.height; - sensor_info.output_size.width = sensorInfo.outputSize.width; - sensor_info.output_size.height = sensorInfo.outputSize.height; - sensor_info.pixel_rate = sensorInfo.pixelRate; - sensor_info.line_length = sensorInfo.lineLength; - - /* Translate the IPA stream configurations map. */ - struct ipa_stream c_streams[streamConfig.size()]; - - unsigned int i = 0; - for (const auto &stream : streamConfig) { - struct ipa_stream *c_stream = &c_streams[i]; - unsigned int id = stream.first; - const IPAStream &ipaStream = stream.second; - - c_stream->id = id; - c_stream->pixel_format = ipaStream.pixelFormat; - c_stream->width = ipaStream.size.width; - c_stream->height = ipaStream.size.height; - - ++i; - } - - /* Translate the IPA entity controls map. */ - struct ipa_control_info_map c_info_maps[entityControls.size()]; - std::vector> data(entityControls.size()); - - i = 0; - for (const auto &info : entityControls) { - struct ipa_control_info_map &c_info_map = c_info_maps[i]; - unsigned int id = info.first; - const ControlInfoMap &infoMap = info.second; - - size_t infoMapSize = serializer_.binarySize(infoMap); - data[i].resize(infoMapSize); - ByteStreamBuffer byteStream(data[i].data(), data[i].size()); - serializer_.serialize(infoMap, byteStream); - - c_info_map.id = id; - c_info_map.data = byteStream.base(); - c_info_map.size = byteStream.size(); - - ++i; - } - - /* \todo Translate the ipaConfig and reponse */ - ctx_->ops->configure(ctx_, &sensor_info, c_streams, streamConfig.size(), - c_info_maps, entityControls.size()); -} - -void IPAContextWrapper::mapBuffers(const std::vector &buffers) -{ - if (intf_) - return intf_->mapBuffers(buffers); - - if (!ctx_) - return; - - struct ipa_buffer c_buffers[buffers.size()]; - - for (unsigned int i = 0; i < buffers.size(); ++i) { - struct ipa_buffer &c_buffer = c_buffers[i]; - const IPABuffer &buffer = buffers[i]; - const std::vector &planes = buffer.planes; - - c_buffer.id = buffer.id; - c_buffer.num_planes = planes.size(); - - for (unsigned int j = 0; j < planes.size(); ++j) { - const FrameBuffer::Plane &plane = planes[j]; - c_buffer.planes[j].dmabuf = plane.fd.fd(); - c_buffer.planes[j].length = plane.length; - } - } - - ctx_->ops->map_buffers(ctx_, c_buffers, buffers.size()); -} - -void IPAContextWrapper::unmapBuffers(const std::vector &ids) -{ - if (intf_) - return intf_->unmapBuffers(ids); - - if (!ctx_) - return; - - ctx_->ops->unmap_buffers(ctx_, ids.data(), ids.size()); -} - -void IPAContextWrapper::processEvent(const IPAOperationData &data) -{ - if (intf_) - return intf_->processEvent(data); - - if (!ctx_) - return; - - struct ipa_operation_data c_data; - c_data.operation = data.operation; - c_data.data = data.data.data(); - c_data.num_data = data.data.size(); - - struct ipa_control_list control_lists[data.controls.size()]; - c_data.lists = control_lists; - c_data.num_lists = data.controls.size(); - - std::size_t listsSize = 0; - for (const auto &list : data.controls) - listsSize += serializer_.binarySize(list); - - std::vector binaryData(listsSize); - ByteStreamBuffer byteStreamBuffer(binaryData.data(), listsSize); - - unsigned int i = 0; - for (const auto &list : data.controls) { - struct ipa_control_list &c_list = control_lists[i]; - c_list.size = serializer_.binarySize(list); - ByteStreamBuffer b = byteStreamBuffer.carveOut(c_list.size); - - serializer_.serialize(list, b); - - c_list.data = b.base(); - } - - ctx_->ops->process_event(ctx_, &c_data); -} - -void IPAContextWrapper::doQueueFrameAction(unsigned int frame, - const IPAOperationData &data) -{ - IPAInterface::queueFrameAction.emit(frame, data); -} - -void IPAContextWrapper::queue_frame_action(void *ctx, unsigned int frame, - struct ipa_operation_data &data) -{ - IPAContextWrapper *_this = static_cast(ctx); - IPAOperationData opData; - - opData.operation = data.operation; - for (unsigned int i = 0; i < data.num_data; ++i) - opData.data.push_back(data.data[i]); - - for (unsigned int i = 0; i < data.num_lists; ++i) { - const struct ipa_control_list &c_list = data.lists[i]; - ByteStreamBuffer b(c_list.data, c_list.size); - opData.controls.push_back(_this->serializer_.deserialize(b)); - } - - _this->doQueueFrameAction(frame, opData); -} - -#ifndef __DOXYGEN__ -/* - * This construct confuses Doxygen and makes it believe that all members of the - * operations is a member of IPAContextWrapper. It must thus be hidden. - */ -const struct ipa_callback_ops IPAContextWrapper::callbacks_ = { - .queue_frame_action = &IPAContextWrapper::queue_frame_action, -}; -#endif - -} /* namespace libcamera */ From patchwork Fri Oct 2 14:31:33 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9925 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 88258C3B5C for ; Fri, 2 Oct 2020 14:32:49 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 56A7963BBF; Fri, 2 Oct 2020 16:32:49 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="dm4bDJ0/"; 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 5169863B6A for ; Fri, 2 Oct 2020 16:32:48 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 3DFCD528; Fri, 2 Oct 2020 16:32:45 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649168; bh=aCVjjgWCMqyAScCKuuTgqVyFMyRyBU9lWtsQqDJc9u0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dm4bDJ0/Acl4c6sKl+u8+fPeUxI8dvhVvmeLz08crRpomNs0yEqKrhpgpo4vN3Q79 KG68pMkprjhV9T0F983LeqBm7/dQJ1sEHpVbbS+7QD/4kRE9cnqGE/pLSEeWD4UNdA u3X6/mVPCoAhNDIJjlRRrmAKKnkwOVavAXX+OyLU= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:33 +0900 Message-Id: <20201002143154.468162-18-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 17/38] libcamera: IPAProxy: Remove stop() override 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" Since stop() is part of the IPA interface, and the IPA interface is now generated based on the data definition file per pipeline, this no longer needs to be overrided by the base IPAProxy. Remove it. Signed-off-by: Paul Elder Reviewed-by: Niklas Söderlund --- No change in v3 Changes in v2: - remove documentation --- include/libcamera/internal/ipa_proxy.h | 2 -- src/libcamera/ipa_proxy.cpp | 10 ---------- 2 files changed, 12 deletions(-) diff --git a/include/libcamera/internal/ipa_proxy.h b/include/libcamera/internal/ipa_proxy.h index b429ce5a..aec8f04f 100644 --- a/include/libcamera/internal/ipa_proxy.h +++ b/include/libcamera/internal/ipa_proxy.h @@ -27,8 +27,6 @@ public: std::string configurationFile(const std::string &file) const; - void stop() override = 0; - protected: std::string resolvePath(const std::string &file) const; diff --git a/src/libcamera/ipa_proxy.cpp b/src/libcamera/ipa_proxy.cpp index ff4d7fd1..23be24ad 100644 --- a/src/libcamera/ipa_proxy.cpp +++ b/src/libcamera/ipa_proxy.cpp @@ -145,16 +145,6 @@ std::string IPAProxy::configurationFile(const std::string &name) const return std::string(); } -/** - * \fn IPAProxy::stop() - * \brief Stop the IPA proxy - * - * This function stops the IPA and releases all the resources acquired by the - * proxy in start(). Calling stop() when the IPA proxy hasn't been started or - * has already been stopped is valid, the proxy shall treat this as a no-op and - * shall not forward the call to the IPA. - */ - /** * \brief Find a valid full path for a proxy worker for a given executable name * \param[in] file File name of proxy worker executable From patchwork Fri Oct 2 14:31:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9926 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 E0BECC3B5C for ; Fri, 2 Oct 2020 14:32:51 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id AC59963BCE; Fri, 2 Oct 2020 16:32:51 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="jwkp4Y+C"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CB9A563B98 for ; Fri, 2 Oct 2020 16:32:50 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id ADF6D1227; Fri, 2 Oct 2020 16:32:48 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649170; bh=jbChiQCCC0JPyF051wl2sCbKY2v7RYZrFmkAjH/9oIw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jwkp4Y+CdGIZS/K/7ZIqkAa1eFbdFHNNBLEfBBYFiuioZHkrvFkT+M6Kp84BOBppt M0P+O6TxQU30TNOF7Yu4kJoY+6LYhcA9N3bcErt7kQXTX/RQPxQ7l1X5CqPtb5RYwf GdHJrroa8t47oF0aZ/BfxGlN+xiDKnq3v/cOObPM= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:34 +0900 Message-Id: <20201002143154.468162-19-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 18/38] libcamera: IPAProxy: Add isolate parameter to create() 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" Since IPAProxy implementations now always encapsulate IPA modules, add a parameter to create() to signal if the proxy should isolate the IPA or not. Signed-off-by: Paul Elder Reviewed-by: Niklas Söderlund --- No change in v3 Changes in v2: - document isolate argument --- include/libcamera/internal/ipa_proxy.h | 22 +++++++++++----------- src/libcamera/ipa_proxy.cpp | 4 +++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/include/libcamera/internal/ipa_proxy.h b/include/libcamera/internal/ipa_proxy.h index aec8f04f..1903150e 100644 --- a/include/libcamera/internal/ipa_proxy.h +++ b/include/libcamera/internal/ipa_proxy.h @@ -42,7 +42,7 @@ public: IPAProxyFactory(const char *name); virtual ~IPAProxyFactory() {} - virtual std::unique_ptr create(IPAModule *ipam) = 0; + virtual std::unique_ptr create(IPAModule *ipam, bool isolate) = 0; const std::string &name() const { return name_; } @@ -53,16 +53,16 @@ private: std::string name_; }; -#define REGISTER_IPA_PROXY(proxy) \ -class proxy##Factory final : public IPAProxyFactory \ -{ \ -public: \ - proxy##Factory() : IPAProxyFactory(#proxy) {} \ - std::unique_ptr create(IPAModule *ipam) \ - { \ - return std::make_unique(ipam); \ - } \ -}; \ +#define REGISTER_IPA_PROXY(proxy) \ +class proxy##Factory final : public IPAProxyFactory \ +{ \ +public: \ + proxy##Factory() : IPAProxyFactory(#proxy) {} \ + std::unique_ptr create(IPAModule *ipam, bool isolate) \ + { \ + return std::make_unique(ipam, isolate); \ + } \ +}; \ static proxy##Factory global_##proxy##Factory; } /* namespace libcamera */ diff --git a/src/libcamera/ipa_proxy.cpp b/src/libcamera/ipa_proxy.cpp index 23be24ad..ee5e6f3e 100644 --- a/src/libcamera/ipa_proxy.cpp +++ b/src/libcamera/ipa_proxy.cpp @@ -250,10 +250,12 @@ IPAProxyFactory::IPAProxyFactory(const char *name) * \fn IPAProxyFactory::create() * \brief Create an instance of the IPAProxy corresponding to the factory * \param[in] ipam The IPA module + * \param[in] isolate Flag to isolate the IPA module * * This virtual function is implemented by the REGISTER_IPA_PROXY() macro. * It creates a IPAProxy instance that isolates an IPA interface designated - * by the IPA module \a ipam. + * by the IPA module \a ipam. If \a isolate is true, then the IPA module will + * be isolated. * * \return A pointer to a newly constructed instance of the IPAProxy subclass * corresponding to the factory From patchwork Fri Oct 2 14:31:35 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9927 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 7187AC3B5C for ; Fri, 2 Oct 2020 14:32:55 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3F56863B98; Fri, 2 Oct 2020 16:32:55 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="uRiBHuIh"; 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 5656563B98 for ; Fri, 2 Oct 2020 16:32:53 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 425602A2; Fri, 2 Oct 2020 16:32:50 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649173; bh=fK5Vjs9cnu2vAvz0e9Ui4IOvN79/pOjLAmoEeROM1g4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uRiBHuIhO97guOPOQ/1sCTbA2EsIEZ1EORMuuGYtZ+vnDyujT2Ef2EUAANDqZG5Ph yWbJZkRVpTmi7O24u1I+/I+6DPI3CJX/26gCHVPXNuJpYjfkn9r4ljWsLQcffe5YNZ StnHydIHBc69ip05bEbSWnQz18m7hWkNGcbMtpwQ= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:35 +0900 Message-Id: <20201002143154.468162-20-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 19/38] libcamera: PipelineHandler: Remove IPA from base class 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" Since pipline handlers now have their own IPA interface types, it can no longer be defined in the base class, and each pipline handler implementation must declare it and its type themselves. Remove it from the base class. Signed-off-by: Paul Elder Reviewed-by: Niklas Söderlund --- No change in v3 Changes in v2: - remove documentation --- include/libcamera/internal/pipeline_handler.h | 1 - src/libcamera/pipeline_handler.cpp | 8 -------- 2 files changed, 9 deletions(-) diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h index a4e1b529..2018bef0 100644 --- a/include/libcamera/internal/pipeline_handler.h +++ b/include/libcamera/internal/pipeline_handler.h @@ -47,7 +47,6 @@ public: std::list queuedRequests_; ControlInfoMap controlInfo_; ControlList properties_; - std::unique_ptr ipa_; private: CameraData(const CameraData &) = delete; diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp index 894200ee..b893b0e7 100644 --- a/src/libcamera/pipeline_handler.cpp +++ b/src/libcamera/pipeline_handler.cpp @@ -106,14 +106,6 @@ LOG_DEFINE_CATEGORY(Pipeline) * when creating the camera, and shall not be modified afterwards. */ -/** - * \var CameraData::ipa_ - * \brief The IPA module used by the camera - * - * Reference to the Image Processing Algorithms (IPA) operating on the camera's - * stream(s). If no IPA exists for the camera, this field is set to nullptr. - */ - /** * \class PipelineHandler * \brief Create and manage cameras based on a set of media devices From patchwork Fri Oct 2 14:31:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9928 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 C84B0C3B5C for ; Fri, 2 Oct 2020 14:32:56 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 8D4C963BD9; Fri, 2 Oct 2020 16:32:56 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="NnkllJ4b"; 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 977BE63BBF for ; Fri, 2 Oct 2020 16:32:55 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id B22CFFD1; Fri, 2 Oct 2020 16:32:53 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649175; bh=qcqCyccBHvkyaRral2w9MjvwGzk9SDzrQQfh151xDYQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NnkllJ4bpif7tFirh0gyn0ay9e2fHvzH8IdeAQwf2ZBMNYFnKA6r2FLiVyY7sXLrE 2FvhhFuAIy+iSxmWi7lgWprTiYj6dzCXg91eoIrXjipis1oW6VTQpPRpPfJ/4ln0Dv umuTgXIPYdT9W9BCruP0COh1qSaEjHxWdRUe56oI= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:36 +0900 Message-Id: <20201002143154.468162-21-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 20/38] libcamera: IPAInterface: Remove all functions from IPAInterface 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" Now that all the functions in the IPA interface are defined in the data definition file and a specialized IPAInterface is generated per pipeline handler, remove all the functions from the case IPAInterface. Signed-off-by: Paul Elder --- No change in v3 No change in v2 --- include/libcamera/ipa/ipa_interface.h | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/include/libcamera/ipa/ipa_interface.h b/include/libcamera/ipa/ipa_interface.h index 38334ab9..97f14477 100644 --- a/include/libcamera/ipa/ipa_interface.h +++ b/include/libcamera/ipa/ipa_interface.h @@ -46,22 +46,6 @@ class IPAInterface { public: virtual ~IPAInterface() {} - - virtual int init(const IPASettings &settings) = 0; - virtual int start() = 0; - virtual void stop() = 0; - - virtual void configure(const CameraSensorInfo &sensorInfo, - const std::map &streamConfig, - const std::map &entityControls, - const IPAOperationData &ipaConfig, - IPAOperationData *result) = 0; - - virtual void mapBuffers(const std::vector &buffers) = 0; - virtual void unmapBuffers(const std::vector &ids) = 0; - - virtual void processEvent(const IPAOperationData &data) = 0; - Signal queueFrameAction; }; } /* namespace libcamera */ From patchwork Fri Oct 2 14:31:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9929 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 2FE90C3B5C for ; Fri, 2 Oct 2020 14:32:59 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F1A1563B98; Fri, 2 Oct 2020 16:32:58 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="EhZ/BZfG"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B801B63B6A for ; Fri, 2 Oct 2020 16:32:57 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 14BE5528; Fri, 2 Oct 2020 16:32:55 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649177; bh=jMtFVVXbGkNtmeghaKJKP7wic+BsqhUYF6yRv7gTK0Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EhZ/BZfG/DLhweyuKyvEsVWMZudRlS/9NLl4RGvl7jiqZ7jGZbEOBiMEu9lEALZWj V4SflEgZut2xxWaaL++YdCHws8K7pgOhk3IpVWFUruVULnByC1yNXH0QTkzSvt7EeY ceTmd2yd1fHrAA3qU2i0iN0isvnJSTSrJBPXaVeY= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:37 +0900 Message-Id: <20201002143154.468162-22-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 21/38] libcamera: IPAInterface: make ipaCreate return IPAInterface 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" With the new IPC infrastructure, we no longer need the C interface as provided by struct ipa_context. Make ipaCreate return IPAinterface. Signed-off-by: Paul Elder --- No change in v3 No change in v2 --- include/libcamera/ipa/ipa_interface.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/libcamera/ipa/ipa_interface.h b/include/libcamera/ipa/ipa_interface.h index 97f14477..546fb57c 100644 --- a/include/libcamera/ipa/ipa_interface.h +++ b/include/libcamera/ipa/ipa_interface.h @@ -50,4 +50,8 @@ public: } /* namespace libcamera */ +extern "C" { +libcamera::IPAInterface *ipaCreate(); +} + #endif /* __LIBCAMERA_IPA_INTERFACE_H__ */ From patchwork Fri Oct 2 14:31:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9930 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 9B93DC3B5C for ; Fri, 2 Oct 2020 14:33:01 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 6A16063BE1; Fri, 2 Oct 2020 16:33:01 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="bZ/msbyt"; 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 0368063B6A for ; Fri, 2 Oct 2020 16:33:00 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 39594528; Fri, 2 Oct 2020 16:32:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649179; bh=5rclY+Nly8KiYHuWAE1XBEjyvnExK79v7APNFLJPIEs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bZ/msbytmGex+entWWAiqZRL7QbNNVrwQ7kYDA2B2DZiHs6g7qEbRZQM327jtoHYP VHaz4V5eqUYUmyQadREf7iM7bvWC8B7Kf108VM/ZkFZj5b6nrENZCjjRUh7yN3D6pT yVNFNLgzPvoWWTJMbWikrVtnwT2m2Y7UAs/sSm50= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:38 +0900 Message-Id: <20201002143154.468162-23-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 22/38] libcamera: IPAInterface: remove ipa_context and functions from documentation 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 all the documentation related to ipa_context and the C IPA API, as well as the documentation about the functions in the IPAInterface. Signed-off-by: Paul Elder --- No change in v3 New in v2 --- src/libcamera/ipa_interface.cpp | 517 +++----------------------------- 1 file changed, 34 insertions(+), 483 deletions(-) diff --git a/src/libcamera/ipa_interface.cpp b/src/libcamera/ipa_interface.cpp index 23fc56d7..d989c75d 100644 --- a/src/libcamera/ipa_interface.cpp +++ b/src/libcamera/ipa_interface.cpp @@ -15,371 +15,52 @@ * an Image Processing Algorithm (IPA) module. An IPA module is developed for a * specific pipeline handler and each pipeline handler may be compatible with * multiple IPA implementations, both open and closed source. To support this, - * libcamera communicates with IPA modules through a standard plain C interface. + * libcamera communicates with IPA modules through a customizable C++ interface. * * IPA modules shall expose a public function named ipaCreate() with the * following prototype. * * \code{.c} - * struct ipa_context *ipaCreate(); + * IPAInterface *ipaCreate(); * \endcode * - * The ipaCreate() function creates an instance of an IPA context, which models + * The ipaCreate() function creates an instance of an IPA interface, which models * a context of execution for the IPA. IPA modules shall support creating one * context per camera, as required by their associated pipeline handler. * - * The IPA module context operations are defined in the struct ipa_context_ops. - * They model a low-level interface to configure the IPA, notify it of events, - * and receive IPA actions through callbacks. An IPA module stores a pointer to - * the operations corresponding to its context in the ipa_context::ops field. - * That pointer is immutable for the lifetime of the context, and may differ - * between different contexts created by the same IPA module. + * The IPA module interface operations are defined in the mojom file + * corresponding to the pipeline handler, in + * include/libcamera/ipa/{pipeline_name}.mojom. These interface operations and + * their parameters are completely customizable, and are meant to be written + * by the pipeline author. * * The IPA interface defines base data types and functions to exchange data. On * top of this, each pipeline handler is responsible for defining the set of * events and actions used to communicate with their IPA. These are collectively * referred to as IPA operations and define the pipeline handler-specific IPA * protocol. Each operation defines the data that it carries, and how that data - * is encoded in the ipa_context_ops functions arguments. + * is encoded in the mojom file. * * \todo Add reference to how pipelines shall document their protocol. * * IPAs can be isolated in a separate process. This implies that arguments to - * the IPA interface functions may need to be transferred over IPC. All - * arguments use Plain Old Data types and are documented either in the form of C - * data types, or as a textual description of byte arrays for types that can't - * be expressed using C data types (such as arrays of mixed data types). IPA - * modules can thus use the C API without calling into libcamera to access the - * data passed to the IPA context operations. + * the IPA interface functions may need to be transferred over IPC. An IPA + * proxy is auto-generated based on the mojom file, which abstracts away the + * de/serialization from the pipeline handler and the IPA implementation. Thus + * any C++ struct that is defined in the mojom file, or the C++ libcamera + * objects that are mentioned in core.mojom, can be used directly. * * Due to IPC, synchronous communication between pipeline handlers and IPAs can - * be costly. For that reason, the interface operates asynchronously. This - * implies that methods don't return a status, and that all methods may copy - * their arguments. + * be costly. For that reason, functions that cannot afford the high cost + * should be marked as [async] in the mojom file, and they will operate + * asynchronously. This implies that these methods don't return a status, and + * that all methods may copy their arguments. Synchronous functions are still + * allowed, but should be used with caution. * - * The IPAInterface class is a C++ representation of the ipa_context_ops, using - * C++ data classes provided by libcamera. This is the API exposed to pipeline - * handlers to communicate with IPA modules. IPA modules may use the - * IPAInterface API internally if they want to benefit from the data and helper - * classes offered by libcamera. - * - * When an IPA module is loaded directly into the libcamera process and uses - * the IPAInterface API internally, short-circuiting the path to the - * ipa_context_ops and back to IPAInterface is desirable. To support this, IPA - * modules may implement the ipa_context_ops::get_interface function to return a - * pointer to their internal IPAInterface. - */ - -/** - * \struct ipa_context - * \brief IPA module context of execution - * - * This structure models a context of execution for an IPA module. It is - * instantiated by the IPA module ipaCreate() function. IPA modules allocate - * context instances in an implementation-defined way, contexts shall thus be - * destroyed using the ipa_operation::destroy function only. - * - * The ipa_context structure provides a pointer to the IPA context operations. - * It shall otherwise be treated as a constant black-box cookie and passed - * unmodified to the functions defined in struct ipa_context_ops. - * - * IPA modules are expected to extend struct ipa_context by inheriting from it, - * either through structure embedding to model inheritance in plain C, or - * through C++ class inheritance. A simple example of the latter is available - * in the IPAContextWrapper class implementation. - * - * \var ipa_context::ops - * \brief The IPA context operations - */ - -/** - * \struct ipa_settings - * \brief IPA initialization settings for the IPA context operations - * \sa IPASettings - * - * \var ipa_settings::configuration_file - * \brief The name of the IPA configuration file (may be null or point to an - * empty string) - */ - -/** - * \struct ipa_sensor_info - * \brief Camera sensor information for the IPA context operations - * \sa libcamera::CameraSensorInfo - * - * \var ipa_sensor_info::model - * \brief The camera sensor model name - * \todo Remove this field as soon as no IPA depends on it anymore - * - * \var ipa_sensor_info::bits_per_pixel - * \brief The camera sensor image format bit depth - * \sa libcamera::CameraSensorInfo::bitsPerPixel - * - * \var ipa_sensor_info::active_area.width - * \brief The camera sensor pixel array active area width - * \sa libcamera::CameraSensorInfo::activeAreaSize - * - * \var ipa_sensor_info::active_area.height - * \brief The camera sensor pixel array active area height - * \sa libcamera::CameraSensorInfo::activeAreaSize - * - * \var ipa_sensor_info::active_area - * \brief The camera sensor pixel array active size - * \sa libcamera::CameraSensorInfo::activeAreaSize - * - * \var ipa_sensor_info::analog_crop.left - * \brief The left coordinate of the analog crop rectangle, relative to the - * pixel array active area - * \sa libcamera::CameraSensorInfo::analogCrop - * - * \var ipa_sensor_info::analog_crop.top - * \brief The top coordinate of the analog crop rectangle, relative to the pixel - * array active area - * \sa libcamera::CameraSensorInfo::analogCrop - * - * \var ipa_sensor_info::analog_crop.width - * \brief The horizontal size of the analog crop rectangle - * \sa libcamera::CameraSensorInfo::analogCrop - * - * \var ipa_sensor_info::analog_crop.height - * \brief The vertical size of the analog crop rectangle - * \sa libcamera::CameraSensorInfo::analogCrop - * - * \var ipa_sensor_info::analog_crop - * \brief The analog crop rectangle - * \sa libcamera::CameraSensorInfo::analogCrop - * - * \var ipa_sensor_info::output_size.width - * \brief The horizontal size of the output image - * \sa libcamera::CameraSensorInfo::outputSize - * - * \var ipa_sensor_info::output_size.height - * \brief The vertical size of the output image - * \sa libcamera::CameraSensorInfo::outputSize - * - * \var ipa_sensor_info::output_size - * \brief The size of the output image - * \sa libcamera::CameraSensorInfo::outputSize - * - * \var ipa_sensor_info::pixel_rate - * \brief The number of pixel produced in a second - * \sa libcamera::CameraSensorInfo::pixelRate - * - * \var ipa_sensor_info::line_length - * \brief The full line length, including blanking, in pixel units - * \sa libcamera::CameraSensorInfo::lineLength - */ - -/** - * \struct ipa_stream - * \brief Stream information for the IPA context operations - * - * \var ipa_stream::id - * \brief Identifier for the stream, defined by the IPA protocol - * - * \var ipa_stream::pixel_format - * \brief The stream pixel format, as defined by the PixelFormat class - * - * \var ipa_stream::width - * \brief The stream width in pixels - * - * \var ipa_stream::height - * \brief The stream height in pixels - */ - -/** - * \struct ipa_control_info_map - * \brief ControlInfoMap description for the IPA context operations - * - * \var ipa_control_info_map::id - * \brief Identifier for the ControlInfoMap, defined by the IPA protocol - * - * \var ipa_control_info_map::data - * \brief Pointer to a control packet for the ControlInfoMap - * \sa ipa_controls.h - * - * \var ipa_control_info_map::size - * \brief The size of the control packet in bytes - */ - -/** - * \struct ipa_buffer_plane - * \brief A plane for an ipa_buffer - * - * \var ipa_buffer_plane::dmabuf - * \brief The dmabuf file descriptor for the plane (-1 for unused planes) - * - * \var ipa_buffer_plane::length - * \brief The plane length in bytes (0 for unused planes) - */ - -/** - * \struct ipa_buffer - * \brief Buffer information for the IPA context operations - * - * \var ipa_buffer::id - * \brief The buffer unique ID (see \ref libcamera::IPABuffer::id) - * - * \var ipa_buffer::num_planes - * \brief The number of used planes in the ipa_buffer::planes array - * - * \var ipa_buffer::planes - * \brief The buffer planes (up to 3) - */ - -/** - * \struct ipa_control_list - * \brief ControlList description for the IPA context operations - * - * \var ipa_control_list::data - * \brief Pointer to a control packet for the ControlList - * \sa ipa_controls.h - * - * \var ipa_control_list::size - * \brief The size of the control packet in bytes - */ - -/** - * \struct ipa_operation_data - * \brief IPA operation data for the IPA context operations - * \sa libcamera::IPAOperationData - * - * \var ipa_operation_data::operation - * \brief IPA protocol operation - * - * \var ipa_operation_data::data - * \brief Pointer to the operation data array - * - * \var ipa_operation_data::num_data - * \brief Number of entries in the ipa_operation_data::data array - * - * \var ipa_operation_data::lists - * \brief Pointer to an array of ipa_control_list - * - * \var ipa_operation_data::num_lists - * \brief Number of entries in the ipa_control_list array - */ - -/** - * \struct ipa_callback_ops - * \brief IPA context operations as a set of function pointers - */ - -/** - * \var ipa_callback_ops::queue_frame_action - * \brief Queue an action associated with a frame to the pipeline handler - * \param[in] cb_ctx The callback context registered with - * ipa_context_ops::register_callbacks - * \param[in] frame The frame number - * - * \sa libcamera::IPAInterface::queueFrameAction - */ - -/** - * \struct ipa_context_ops - * \brief IPA context operations as a set of function pointers - * - * To allow for isolation of IPA modules in separate processes, the functions - * defined in the ipa_context_ops structure return only data related to the - * libcamera side of the operations. In particular, error related to the - * libcamera side of the IPC may be returned. Data returned by the IPA, - * including status information, shall be provided through callbacks from the - * IPA to libcamera. - */ - -/** - * \var ipa_context_ops::destroy - * \brief Destroy the IPA context created by the module's ipaCreate() function - * \param[in] ctx The IPA context - */ - -/** - * \var ipa_context_ops::get_interface - * \brief Retrieve the IPAInterface implemented by the ipa_context (optional) - * \param[in] ctx The IPA context - * - * IPA modules may implement this function to expose their internal - * IPAInterface, if any. When implemented, libcamera may at its sole discretion - * call it and then bypass the ipa_context_ops API by calling the IPAInterface - * methods directly. IPA modules shall still implement and support the full - * ipa_context_ops API. - */ - -/** - * \var ipa_context_ops::init - * \brief Initialise the IPA context - * \param[in] ctx The IPA context - * \param[in] settings The IPA initialization settings - * - * \sa libcamera::IPAInterface::init() - */ - -/** - * \var ipa_context_ops::start - * \brief Start the IPA context - * - * \sa libcamera::IPAInterface::start() - */ - -/** - * \var ipa_context_ops::stop - * \brief Stop the IPA context - * - * \sa libcamera::IPAInterface::stop() - */ - -/** - * \var ipa_context_ops::register_callbacks - * \brief Register callback operation from the IPA to the pipeline handler - * \param[in] ctx The IPA context - * \param[in] callback The IPA callback operations - * \param[in] cb_ctx The callback context, passed to all callback operations - */ - -/** - * \var ipa_context_ops::configure - * \brief Configure the IPA stream and sensor settings - * \param[in] ctx The IPA context - * \param[in] sensor_info Camera sensor information - * \param[in] streams Configuration of all active streams - * \param[in] num_streams The number of entries in the \a streams array - * \param[in] maps Controls provided by the pipeline entities - * \param[in] num_maps The number of entries in the \a maps array - * - * \sa libcamera::IPAInterface::configure() - */ - -/** - * \var ipa_context_ops::map_buffers - * \brief Map buffers shared between the pipeline handler and the IPA - * \param[in] ctx The IPA context - * \param[in] buffers The buffers to map - * \param[in] num_buffers The number of entries in the \a buffers array - * - * The dmabuf file descriptors provided in \a buffers are borrowed from the - * caller and are only guaranteed to be valid during the map_buffers() call. - * Should the callee need to store a copy of the file descriptors, it shall - * duplicate them first with ::%dup(). - * - * \sa libcamera::IPAInterface::mapBuffers() - */ - -/** - * \var ipa_context_ops::unmap_buffers - * \brief Unmap buffers shared by the pipeline to the IPA - * \param[in] ctx The IPA context - * \param[in] ids The IDs of the buffers to unmap - * \param[in] num_buffers The number of entries in the \a ids array - * - * \sa libcamera::IPAInterface::unmapBuffers() - */ - -/** - * \var ipa_context_ops::process_event - * \brief Process an event from the pipeline handler - * \param[in] ctx The IPA context - * - * \sa libcamera::IPAInterface::processEvent() + * The IPA interface, as defined in the respective mojom file, is the API + * exposed to pipeline handlers to communicate with IPA modules. IPA modules + * may use the IPAInterface API internally if they want to benefit from the + * data and helper classes offered by libcamera. */ /** @@ -387,9 +68,10 @@ * \brief Entry point to the IPA modules * * This function is the entry point to the IPA modules. It is implemented by - * every IPA module, and called by libcamera to create a new IPA context. + * every IPA module, and called by libcamera to create a new IPA interface + * instance. * - * \return A newly created IPA context + * \return A newly created IPA interface instance */ namespace libcamera { @@ -499,157 +181,26 @@ namespace libcamera { * \class IPAInterface * \brief C++ Interface for IPA implementation * - * This pure virtual class defines a C++ API corresponding to the ipa_context, - * ipa_context_ops and ipa_callback_ops API. It is used by pipeline handlers to + * This pure virtual class defines a skeletal C++ API for IPA modules. + * Specializations of this class must be defined in a mojom file in + * include/libcamera/ipa/ (see include/libcamera/ipa/core.mojom for details + * on how to do so). The specialized class is used by pipeline handlers to * interact with IPA modules, and may be used internally in IPA modules if * desired to benefit from the data and helper classes provided by libcamera. * - * Functions defined in the ipa_context_ops structure are mapped to IPAInterface - * methods, while functions defined in the ipa_callback_ops are mapped to - * IPAInterface signals. As with the C API, the IPA C++ interface uses - * serializable data types only. It reuses structures defined by the C API, or - * defines corresponding classes using C++ containers when required. - * * Due to process isolation all arguments to the IPAInterface methods and * signals may need to be transferred over IPC. The class thus uses serializable * data types only. The IPA C++ interface defines custom data structures that * mirror core libcamera structures when the latter are not suitable, such as * IPAStream to carry StreamConfiguration data. * - * As for the functions defined in struct ipa_context_ops, the methods defined - * by this class shall not return data from the IPA. + * Custom data structures may also be defined in the mojom file, in which case + * the de/serialization will automatically be generated. If any other libcamera + * structures are to be used as parameters, then a de/serializer for them must + * be implemented in IPADataSerializer. * * The pipeline handler shall use the IPAManager to locate a compatible * IPAInterface. The interface may then be used to interact with the IPA module. */ -/** - * \fn IPAInterface::init() - * \brief Initialise the IPAInterface - * \param[in] settings The IPA initialization settings - * - * This function initializes the IPA interface. It shall be called before any - * other function of the IPAInterface. The \a settings carry initialization - * parameters that are valid for the whole life time of the IPA interface. - */ - -/** - * \fn IPAInterface::start() - * \brief Start the IPA - * - * This method informs the IPA module that the camera is about to be started. - * The IPA module shall prepare any resources it needs to operate. - * - * \return 0 on success or a negative error code otherwise - */ - -/** - * \fn IPAInterface::stop() - * \brief Stop the IPA - * - * This method informs the IPA module that the camera is stopped. The IPA module - * shall release resources prepared in start(). - */ - -/** - * \fn IPAInterface::configure() - * \brief Configure the IPA stream and sensor settings - * \param[in] sensorInfo Camera sensor information - * \param[in] streamConfig Configuration of all active streams - * \param[in] entityControls Controls provided by the pipeline entities - * \param[in] ipaConfig Pipeline-handler-specific configuration data - * \param[out] result Pipeline-handler-specific configuration result - * - * This method shall be called when the camera is started to inform the IPA of - * the camera's streams and the sensor settings. The meaning of the numerical - * keys in the \a streamConfig and \a entityControls maps is defined by the IPA - * protocol. - * - * The \a sensorInfo conveys information about the camera sensor settings that - * the pipeline handler has selected for the configuration. The IPA may use - * that information to tune its algorithms. - * - * The \a ipaConfig and \a result parameters carry custom data passed by the - * pipeline handler to the IPA and back. The pipeline handler may set the \a - * result parameter to null if the IPA protocol doesn't need to pass a result - * back through the configure() function. - */ - -/** - * \fn IPAInterface::mapBuffers() - * \brief Map buffers shared between the pipeline handler and the IPA - * \param[in] buffers List of buffers to map - * - * This method informs the IPA module of memory buffers set up by the pipeline - * handler that the IPA needs to access. It provides dmabuf file handles for - * each buffer, and associates the buffers with unique numerical IDs. - * - * IPAs shall map the dmabuf file handles to their address space and keep a - * cache of the mappings, indexed by the buffer numerical IDs. The IDs are used - * in all other IPA interface methods to refer to buffers, including the - * unmapBuffers() method. - * - * All buffers that the pipeline handler wishes to share with an IPA shall be - * mapped with this method. Buffers may be mapped all at once with a single - * call, or mapped and unmapped dynamically at runtime, depending on the IPA - * protocol. Regardless of the protocol, all buffers mapped at a given time - * shall have unique numerical IDs. - * - * The numerical IDs have no meaning defined by the IPA interface, and IPA - * protocols shall not give them any specific meaning either. They should be - * treated as opaque handles by IPAs, with the only exception that ID zero is - * invalid. - * - * \sa unmapBuffers() - * - * \todo Provide a generic implementation of mapBuffers and unmapBuffers for - * IPAs - */ - -/** - * \fn IPAInterface::unmapBuffers() - * \brief Unmap buffers shared by the pipeline to the IPA - * \param[in] ids List of buffer IDs to unmap - * - * This method removes mappings set up with mapBuffers(). Buffers may be - * unmapped all at once with a single call, or selectively at runtime, depending - * on the IPA protocol. Numerical IDs of unmapped buffers may be reused when - * mapping new buffers. - * - * \sa mapBuffers() - */ - -/** - * \fn IPAInterface::processEvent() - * \brief Process an event from the pipeline handler - * \param[in] data IPA operation data - * - * This operation is used by pipeline handlers to inform the IPA module of - * events that occurred during the on-going capture operation. - * - * The event notified by the pipeline handler with this method is handled by the - * IPA, which interprets the operation parameters according to the separately - * documented IPA protocol. - */ - -/** - * \var IPAInterface::queueFrameAction - * \brief Queue an action associated with a frame to the pipeline handler - * \param[in] frame The frame number for the action - * \param[in] data IPA operation data - * - * This signal is emitted when the IPA wishes to queue a FrameAction on the - * pipeline. The pipeline is still responsible for the scheduling of the action - * on its timeline. - * - * This signal is emitted by the IPA to queue an action to be executed by the - * pipeline handler on a frame. The type of action is identified by the - * \a data.operation field, as defined by the IPA protocol, and the rest of the - * \a data is interpreted accordingly. The pipeline handler shall queue the - * action and execute it as appropriate. - * - * The signal is only emitted when the IPA is running, that is after start() and - * before stop() have been called. - */ - } /* namespace libcamera */ From patchwork Fri Oct 2 14:31:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9931 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 34F76C3B5C for ; Fri, 2 Oct 2020 14:33:04 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F36BC63B98; Fri, 2 Oct 2020 16:33:03 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="sGnblb/Q"; 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 1E1BF63B6A for ; Fri, 2 Oct 2020 16:33:02 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 5E0A4FD1; Fri, 2 Oct 2020 16:33:00 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649181; bh=wbglJOpkI7fXzFAqCmH1fs9ToXFUUSe1oK5N+zLl1a8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=sGnblb/QYE5q27PSScXi5nFmbyBX1I3ePzgqgI8s/THhlXsnnP0z/MZdpnIY9IAMY KHMptlxMpOthlgUOp8jNcelA7q0PK4bi26UNBi3ULxc4l0bRgtOjw05GyJ4/DXaLwC u//6x51gJQ9jE0YhT7EsbmfoAAQyCqbPI3O+mrfQ= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:39 +0900 Message-Id: <20201002143154.468162-24-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 23/38] libcamera: IPAManager: Fetch IPAProxy corresponding to pipeline 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" Now that each pipeline handler has its own IPAProxy implementation, make the IPAManager fetch the IPAProxy based on the pipeline handler name. Also, since the IPAProxy is used regardless of isolation or no isolation, remove the isolation check from the proxy selection. Signed-off-by: Paul Elder --- No change in v3 No change in v2 --- src/libcamera/ipa_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp index 046fd5c6..2d0ea242 100644 --- a/src/libcamera/ipa_manager.cpp +++ b/src/libcamera/ipa_manager.cpp @@ -275,8 +275,8 @@ std::unique_ptr IPAManager::createIPA(PipelineHandler *pipe, * * \todo Implement a better proxy selection */ - const char *proxyName = self_->isSignatureValid(m) - ? "IPAProxyThread" : "IPAProxyLinux"; + std::string pipeName(pipe->name()); + const char *proxyName = pipeName.replace(0, 15, "IPAProxy").c_str(); IPAProxyFactory *pf = nullptr; for (IPAProxyFactory *factory : IPAProxyFactory::factories()) { From patchwork Fri Oct 2 14:31:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9932 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 8143BC3B5C for ; Fri, 2 Oct 2020 14:33:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 4E9CC63B98; Fri, 2 Oct 2020 16:33:06 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="clslyrXC"; 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 97DD763B6A for ; Fri, 2 Oct 2020 16:33:04 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 82DEA2A2; Fri, 2 Oct 2020 16:33:02 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649184; bh=Mq/Z3+CehNw4TKz172O3dCHMkBTycrA/uhEHOW1B21o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=clslyrXCAG/SWJ3XHa553aCeeqV8ezhX9pdZKKvii9SPd1OUklbRE5jGKkjIPQVV5 Jj3PoFF4EnwZe2pepNQbpb2uNbts7KKr06zcUhJppsNKZLzJFAsea58tuPOa71KH2V Zn8N+N+WssMswi6N2w3A51tPqD0UD+rf0/MM1NUg= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:40 +0900 Message-Id: <20201002143154.468162-25-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 24/38] libcamera: IPAManager: add isolation flag to proxy creation 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" When the IPA proxy is created, it needs to know whether to isolate or not. Feed the flag at creation of the IPA proxy. Signed-off-by: Paul Elder Reviewed-by: Niklas Söderlund --- No change in v3 No change in v2 --- src/libcamera/ipa_manager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp index 2d0ea242..26458153 100644 --- a/src/libcamera/ipa_manager.cpp +++ b/src/libcamera/ipa_manager.cpp @@ -291,7 +291,8 @@ std::unique_ptr IPAManager::createIPA(PipelineHandler *pipe, return nullptr; } - std::unique_ptr proxy = pf->create(m); + std::unique_ptr proxy = + pf->create(m, !self_->isSignatureValid(m)); if (!proxy->isValid()) { LOG(IPAManager, Error) << "Failed to load proxy"; return nullptr; From patchwork Fri Oct 2 14:31:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9933 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 D3035C3B5C for ; Fri, 2 Oct 2020 14:33:08 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9FC2963B98; Fri, 2 Oct 2020 16:33:08 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="fSyA3pBc"; 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 42C8563B6A for ; Fri, 2 Oct 2020 16:33:07 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id F2817FD1; Fri, 2 Oct 2020 16:33:04 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649187; bh=A988jWffaGguysURX5q6vJmOfC/W8RbtoBtlOGoIIw0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=fSyA3pBc+w+ymu3HeM63Gn3fKHRbrS0O1n88N37kMfGoQ8rRp7ygZMf/96qNRfPhk lOC7c+ZmM/ahn/PT4XSEIOkNF0cLSPYTxcW0elPYp/XebAe8EbblCKN2N71qcB6dSq oo0YOooYGdNZyw2xWOi5xrw9EiirTFhkInVMC/QI= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:41 +0900 Message-Id: <20201002143154.468162-26-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 25/38] libcamera: IPAManager: Make createIPA return proxy directly 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" Since every pipeline knows the type of the proxy that it needs, and since all IPAs are to be wrapped in a proxy, IPAManager no longer needs to search in the factory list to fetch the proxy factory to construct a factory. Instead, we define createIPA as a template function, and the pipeline handler can declare the proxy type when it calls createIPA. Signed-off-by: Paul Elder Reviewed-by: Niklas Söderlund --- No change in v3 No change in v2 --- include/libcamera/internal/ipa_manager.h | 31 +++++++++++++-- src/libcamera/ipa_manager.cpp | 48 +----------------------- 2 files changed, 29 insertions(+), 50 deletions(-) diff --git a/include/libcamera/internal/ipa_manager.h b/include/libcamera/internal/ipa_manager.h index 4a143b6a..297a8a58 100644 --- a/include/libcamera/internal/ipa_manager.h +++ b/include/libcamera/internal/ipa_manager.h @@ -14,20 +14,45 @@ #include #include "libcamera/internal/ipa_module.h" +#include "libcamera/internal/log.h" #include "libcamera/internal/pipeline_handler.h" #include "libcamera/internal/pub_key.h" namespace libcamera { +LOG_DECLARE_CATEGORY(IPAManager) + class IPAManager { public: IPAManager(); ~IPAManager(); - static std::unique_ptr createIPA(PipelineHandler *pipe, - uint32_t maxVersion, - uint32_t minVersion); + template + static std::unique_ptr

createIPA(PipelineHandler *pipe, + uint32_t maxVersion, + uint32_t minVersion) + { + IPAModule *m = nullptr; + + for (IPAModule *module : self_->modules_) { + if (module->match(pipe, minVersion, maxVersion)) { + m = module; + break; + } + } + + if (!m) + return nullptr; + + std::unique_ptr

proxy = std::make_unique

(m, !self_->isSignatureValid(m)); + if (!proxy->isValid()) { + LOG(IPAManager, Error) << "Failed to load proxy"; + return nullptr; + } + + return proxy; + } private: static IPAManager *self_; diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp index 26458153..0d518443 100644 --- a/src/libcamera/ipa_manager.cpp +++ b/src/libcamera/ipa_manager.cpp @@ -245,6 +245,7 @@ unsigned int IPAManager::addDir(const char *libDir, unsigned int maxDepth) } /** + * \fn IPAManager::createIPA() * \brief Create an IPA proxy that matches a given pipeline handler * \param[in] pipe The pipeline handler that wants a matching IPA proxy * \param[in] minVersion Minimum acceptable version of IPA module @@ -253,53 +254,6 @@ unsigned int IPAManager::addDir(const char *libDir, unsigned int maxDepth) * \return A newly created IPA proxy, or nullptr if no matching IPA module is * found or if the IPA proxy fails to initialize */ -std::unique_ptr IPAManager::createIPA(PipelineHandler *pipe, - uint32_t maxVersion, - uint32_t minVersion) -{ - IPAModule *m = nullptr; - - for (IPAModule *module : self_->modules_) { - if (module->match(pipe, minVersion, maxVersion)) { - m = module; - break; - } - } - - if (!m) - return nullptr; - - /* - * Load and run the IPA module in a thread if it has a valid signature, - * or isolate it in a separate process otherwise. - * - * \todo Implement a better proxy selection - */ - std::string pipeName(pipe->name()); - const char *proxyName = pipeName.replace(0, 15, "IPAProxy").c_str(); - IPAProxyFactory *pf = nullptr; - - for (IPAProxyFactory *factory : IPAProxyFactory::factories()) { - if (!strcmp(factory->name().c_str(), proxyName)) { - pf = factory; - break; - } - } - - if (!pf) { - LOG(IPAManager, Error) << "Failed to get proxy factory"; - return nullptr; - } - - std::unique_ptr proxy = - pf->create(m, !self_->isSignatureValid(m)); - if (!proxy->isValid()) { - LOG(IPAManager, Error) << "Failed to load proxy"; - return nullptr; - } - - return proxy; -} bool IPAManager::isSignatureValid([[maybe_unused]] IPAModule *ipa) const { From patchwork Fri Oct 2 14:31:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9934 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 36EE9C3B5C for ; Fri, 2 Oct 2020 14:33:11 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 02D3463BCA; Fri, 2 Oct 2020 16:33:11 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="iyxn+pWb"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 917D763B6A for ; Fri, 2 Oct 2020 16:33:09 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D35A12A2; Fri, 2 Oct 2020 16:33:07 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649189; bh=flR82/ko2y3q0dHo0vSOzCMaGIiLuiuG9kh32ONMbk0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=iyxn+pWbb8qfWooF3hMMyqHmMFW87H/OcJ0NiJQGYKZwKszFcR6bfp66hRZdnNCbo cos8/GLhdh5s1mOLa6Rv90VJcokDRDP7oe6S2m9D+7YBRu/XbdnT5RHuzxmftkJeJX DuBi2oUKpJFDp2jP/Udk2t+PmJAqZQE86Aw7qu/c= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:42 +0900 Message-Id: <20201002143154.468162-27-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 26/38] ipa: Add core.mojom 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 base mojom file to contain empty mojom definitions of libcamera objects, as well as documentation for the IPA interfaces that need to be defined in the mojom files. Signed-off-by: Paul Elder --- Changes in v3: - add doc that structs need to be defined - add doc to recommend namespacing - change indentation - add requirement that base controls *must* be defined in libcamera::{pipeline_name}::Controls New in v2 --- include/libcamera/ipa/core.mojom | 83 ++++++++++++++++++++++++++ include/libcamera/ipa/meson.build | 18 +++++- src/libcamera/proxy/meson.build | 2 +- src/libcamera/proxy/worker/meson.build | 2 +- 4 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 include/libcamera/ipa/core.mojom diff --git a/include/libcamera/ipa/core.mojom b/include/libcamera/ipa/core.mojom new file mode 100644 index 00000000..81d68bae --- /dev/null +++ b/include/libcamera/ipa/core.mojom @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +/* + * Any libcamera objects that are used by any interfaces must be declared + * here, and a de/serializer be implemented in IPADataSerializer. In addition, + * the corresponding header file (or forward-declarations) must be placed in + * {pipeline_name}.h. + * + * For libcamera types, the [hasFd] attribute is needed to notify the compiler + * that the struct has an fd. + */ +struct CameraSensorInfo {}; +struct ControlInfoMap {}; +struct ControlList {}; +struct FileDescriptor {}; +[hasFd] struct IPABuffer {}; +struct IPASettings {}; +struct IPAStream {}; + +/* + * Any custom structs that the IPA interfaces use must be defined here, + * usin the mojo interface definition language. + * + * It is recommended to use namespacing, to avoid potential collisions with + * libcamera types. Use mojom's module directive for this. + */ + +/* + * \class IPACoreInterface + * + * This mojo interface describes the main interface of the IPA. It must be + * defined. + * + * The main interface must be named as IPA{pipeline_name}Interface. + * IPACoreInterface serves as an example and documentation, where + * {pipeline_name} is Core. + * + * At minimum, the following three functions must be present (and implemented): + * init(IPASettings settings) => (int32 ret); + * start() => (int32 ret); + * stop(); + * + * All input parameters will become const references, except for primitives, + * which will simply become const. Output parameters will become pointers, + * unless there is only one primitive output parameter, in which case it will + * become a regular return value. + * + * const is not allowed inside of arrays and maps. mojo arrays will become C++ + * vectors. + * + * By default, all methods defined in the main interface are synchronous. This + * means that in the case of IPC (ie. isolated IPA), the function call will not + * return until the return value or output parameters are ready. To specify an + * asynchronous function, the [async] attribute can be used. Asynchronous + * methods must not have any return value or output parameters, since in the + * case of IPC the call needs to return immediately. + * + * In addition the following must be defined in {pipeline_name}.h in the + * libcamera namespace: + * + * static ControlInfoMap {pipeline_name}::Controls; + * + * It may be empty. + */ + +/* + * \class IPACoreCallbackInterface + * + * This mojo interface describes the callback interface of the IPA. It must be + * defined. If there are no callback functions, then it may be empty. + * + * The callback interface must be named as IPA{pipeline_name}CallbackInterface. + * IPACoreCallbackInterface serves as an example and documentation, where + * {pipeline_name} is Core. + * + * Methods defined in the callback interface shall not return anything. This + * also means that these methods must be asynchronous and not synchronous. + * Therefore the [async] tag is not necessary. + * + * Methods defined in the callback interface will become Signals in the IPA + * interface. The IPA can emit signals, while the pipeline handler can connect + * slots to them. + */ diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build index 337948d2..590a1464 100644 --- a/include/libcamera/ipa/meson.build +++ b/include/libcamera/ipa/meson.build @@ -13,6 +13,17 @@ install_headers(libcamera_ipa_headers, # Prepare IPA/IPC generation components # +core_mojom_file = 'core.mojom' +ipa_mojom_core = custom_target(core_mojom_file.split('.')[0] + '_mojom_module', + input : core_mojom_file, + output : core_mojom_file + '-module', + command : [ + mojom_parser, + '--output-root', meson.build_root(), + '--input-root', meson.source_root(), + '--mojoms', '@INPUT@' + ]) + ipa_mojom_files = [] ipa_mojoms = [] @@ -30,6 +41,7 @@ foreach file : ipa_mojom_files mojom = custom_target(file.split('.')[0] + '_mojom_module', input : file, output : file + '-module', + depends : ipa_mojom_core, command : [ mojom_parser, '--output-root', meson.build_root(), @@ -41,7 +53,7 @@ foreach file : ipa_mojom_files header = custom_target(name + '_generated_h', input : mojom, output : name + '_generated.h', - depends : mojom_templates, + depends : [mojom_templates, ipa_mojom_core], command : [ mojom_generator, 'generate', '-g', 'libcamera', @@ -55,7 +67,7 @@ foreach file : ipa_mojom_files serializer = custom_target(name + '_serializer_h', input : mojom, output : name + '_serializer.h', - depends : mojom_templates, + depends : [mojom_templates, ipa_mojom_core], command : [ mojom_generator, 'generate', '-g', 'libcamera', @@ -69,7 +81,7 @@ foreach file : ipa_mojom_files proxy_header = custom_target(name + '_proxy_h', input : mojom, output : 'ipa_proxy_' + name + '.h', - depends : mojom_templates, + depends : [mojom_templates, ipa_mojom_core], command : [ mojom_generator, 'generate', '-g', 'libcamera', diff --git a/src/libcamera/proxy/meson.build b/src/libcamera/proxy/meson.build index ac68ad08..f27b453a 100644 --- a/src/libcamera/proxy/meson.build +++ b/src/libcamera/proxy/meson.build @@ -10,7 +10,7 @@ foreach mojom : ipa_mojoms proxy = custom_target(mojom['name'] + '_proxy_cpp', input : mojom['mojom'], output : 'ipa_proxy_' + mojom['name'] + '.cpp', - depends : [mojom_templates], + depends : [mojom_templates, ipa_mojom_core], command : [ mojom_generator, 'generate', '-g', 'libcamera', diff --git a/src/libcamera/proxy/worker/meson.build b/src/libcamera/proxy/worker/meson.build index 5490811b..353e5cf1 100644 --- a/src/libcamera/proxy/worker/meson.build +++ b/src/libcamera/proxy/worker/meson.build @@ -11,7 +11,7 @@ foreach mojom : ipa_mojoms worker = custom_target(mojom['name'] + '_proxy_worker', input : mojom['mojom'], output : 'ipa_proxy_' + mojom['name'] + '_worker.cpp', - depends : [mojom_templates], + depends : [mojom_templates, ipa_mojom_core], command : [ mojom_generator, 'generate', '-g', 'libcamera', From patchwork Fri Oct 2 14:31:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9935 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 A8D3AC3B5C for ; Fri, 2 Oct 2020 14:33:13 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 7613A63BE8; Fri, 2 Oct 2020 16:33:13 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="m4ypgw7T"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DA26263B98 for ; Fri, 2 Oct 2020 16:33:11 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1BB9C2A2; Fri, 2 Oct 2020 16:33:09 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649191; bh=Pm1OZ25w8/uLGTSJKJLWmLNWSCbcp2Fn+Rh3AQ46Uc8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=m4ypgw7TXDh9r8rGRH2eEjR02qngv8+7qBjObNMgj5fm8NoItuHyckfmnifCyTXwr 5uG2s+yFoYYkRUB2PVh7//kFbmHDqZjF0Ar88vzCyFPgeKvhmB0AydXzOPfOG2KGo3 szv0lDLROcvfGKnW68hbJuKitx1cKo7/Q58DTv4c= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:43 +0900 Message-Id: <20201002143154.468162-28-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 27/38] ipa: raspberrypi: Add mojom data definition file 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 mojom data definition for raspberrypi pipeline handler's IPAs. This is a direct translation of what the raspberrypi pipeline handler and IPA was using before with IPAOperationData. Also move the enums from raspberrypi.h to raspberrypi.mojom Signed-off-by: Paul Elder --- Changes in v3: - remove stray comment about how our compiler will generate code - add ipa.rpi namespace - not ipa.RPi, because namespace conflict - remove RPi prefix from struct and enum names - add transform parameter to struct ConfigInput Changes in v2: - rebased on "libcamera: pipeline: ipa: raspberrypi: Rework drop frame signalling" - add license - move generic documentation to core.mojom - move documentation from IPA interface - customize the RPi IPA interface to make the pipeline and IPA code cleaner --- include/libcamera/ipa/meson.build | 4 +- include/libcamera/ipa/raspberrypi.h | 18 --- include/libcamera/ipa/raspberrypi.mojom | 158 ++++++++++++++++++++++++ 3 files changed, 161 insertions(+), 19 deletions(-) create mode 100644 include/libcamera/ipa/raspberrypi.mojom diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build index 590a1464..55c97fa5 100644 --- a/include/libcamera/ipa/meson.build +++ b/include/libcamera/ipa/meson.build @@ -24,7 +24,9 @@ ipa_mojom_core = custom_target(core_mojom_file.split('.')[0] + '_mojom_module', '--mojoms', '@INPUT@' ]) -ipa_mojom_files = [] +ipa_mojom_files = [ + 'raspberrypi.mojom', +] ipa_mojoms = [] diff --git a/include/libcamera/ipa/raspberrypi.h b/include/libcamera/ipa/raspberrypi.h index b3041591..58262f6c 100644 --- a/include/libcamera/ipa/raspberrypi.h +++ b/include/libcamera/ipa/raspberrypi.h @@ -14,24 +14,6 @@ namespace libcamera { namespace RPi { -enum ConfigParameters { - IPA_CONFIG_LS_TABLE = (1 << 0), - IPA_CONFIG_STAGGERED_WRITE = (1 << 1), - IPA_CONFIG_SENSOR = (1 << 2), - IPA_CONFIG_DROP_FRAMES = (1 << 3), -}; - -enum Operations { - IPA_ACTION_V4L2_SET_STAGGERED = 1, - IPA_ACTION_V4L2_SET_ISP, - IPA_ACTION_STATS_METADATA_COMPLETE, - IPA_ACTION_RUN_ISP, - IPA_ACTION_EMBEDDED_COMPLETE, - IPA_EVENT_SIGNAL_STAT_READY, - IPA_EVENT_SIGNAL_ISP_PREPARE, - IPA_EVENT_QUEUE_REQUEST, -}; - enum BufferMask { ID = 0x00ffff, STATS = 0x010000, diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom new file mode 100644 index 00000000..bbef5178 --- /dev/null +++ b/include/libcamera/ipa/raspberrypi.mojom @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +module ipa.rpi; + +import "include/libcamera/ipa/core.mojom"; + +interface IPARPiInterface { +/** + * \fn init() + * \brief Initialise the IPAInterface + * \param[in] settings The IPA initialization settings + * + * This function initializes the IPA interface. It shall be called before any + * other function of the IPAInterface. The \a settings carry initialization + * parameters that are valid for the whole life time of the IPA interface. + */ + init(IPASettings settings) => (int32 ret); + +/** + * \fn start() + * \brief Start the IPA + * + * This method informs the IPA module that the camera is about to be started. + * The IPA module shall prepare any resources it needs to operate. + * + * \return 0 on success or a negative error code otherwise + */ + start() => (int32 ret); + +/** + * \fn stop() + * \brief Stop the IPA + * + * This method informs the IPA module that the camera is stopped. The IPA module + * shall release resources prepared in start(). + */ + stop(); + +/** + * \fn configure() + * \brief Configure the IPA stream and sensor settings + * \param[in] sensorInfo Camera sensor information + * \param[in] streamConfig Configuration of all active streams + * \param[in] entityControls Controls provided by the pipeline entities + * \param[in] ipaConfig Pipeline-handler-specific configuration data + * \param[out] result Pipeline-handler-specific configuration result + * + * This method shall be called when the camera is started to inform the IPA of + * the camera's streams and the sensor settings. The meaning of the numerical + * keys in the \a streamConfig and \a entityControls maps is defined by the IPA + * protocol. + * + * The \a sensorInfo conveys information about the camera sensor settings that + * the pipeline handler has selected for the configuration. The IPA may use + * that information to tune its algorithms. + * + * The \a ipaConfig and \a result parameters carry custom data passed by the + * pipeline handler to the IPA and back. The pipeline handler may set the \a + * result parameter to null if the IPA protocol doesn't need to pass a result + * back through the configure() function. + */ + configure(CameraSensorInfo sensorInfo, + map streamConfig, + map entityControls, + ConfigInput ipaConfig) + => (ConfigOutput results); + +/** + * \fn mapBuffers() + * \brief Map buffers shared between the pipeline handler and the IPA + * \param[in] buffers List of buffers to map + * + * This method informs the IPA module of memory buffers set up by the pipeline + * handler that the IPA needs to access. It provides dmabuf file handles for + * each buffer, and associates the buffers with unique numerical IDs. + * + * IPAs shall map the dmabuf file handles to their address space and keep a + * cache of the mappings, indexed by the buffer numerical IDs. The IDs are used + * in all other IPA interface methods to refer to buffers, including the + * unmapBuffers() method. + * + * All buffers that the pipeline handler wishes to share with an IPA shall be + * mapped with this method. Buffers may be mapped all at once with a single + * call, or mapped and unmapped dynamically at runtime, depending on the IPA + * protocol. Regardless of the protocol, all buffers mapped at a given time + * shall have unique numerical IDs. + * + * The numerical IDs have no meaning defined by the IPA interface, and IPA + * protocols shall not give them any specific meaning either. They should be + * treated as opaque handles by IPAs, with the only exception that ID zero is + * invalid. + * + * \sa unmapBuffers() + * + * \todo Provide a generic implementation of mapBuffers and unmapBuffers for + * IPAs + */ + mapBuffers(array buffers); + +/** + * \fn unmapBuffers() + * \brief Unmap buffers shared by the pipeline to the IPA + * \param[in] ids List of buffer IDs to unmap + * + * This method removes mappings set up with mapBuffers(). Buffers may be + * unmapped all at once with a single call, or selectively at runtime, depending + * on the IPA protocol. Numerical IDs of unmapped buffers may be reused when + * mapping new buffers. + * + * \sa mapBuffers() + */ + unmapBuffers(array ids); + + [async] signalStatReady(uint32 bufferId); + [async] signalQueueRequest(ControlList controls); + [async] signalIspPrepare(IspPreparePayload data); +}; + +interface IPARPiCallbackInterface { + statsMetadataComplete(uint32 bufferId, ControlList controls); + runIsp(uint32 bufferId); + embeddedComplete(uint32 bufferId); + setIsp(ControlList controls); + setStaggered(ControlList controls); +}; + + +enum ConfigParameters { + RPI_IPA_CONFIG_LS_TABLE = 0x01, + RPI_IPA_CONFIG_STAGGERED_WRITE = 0x02, + RPI_IPA_CONFIG_SENSOR = 0x04, + RPI_IPA_CONFIG_DROP_FRAMES = 0x08, +}; + +struct StaggeredWritePayload { + uint32 gainDelay; + uint32 exposureDelay; + uint32 sensorMetadata; +}; + +struct IspPreparePayload { + uint32 embeddedbufferId; + uint32 bayerbufferId; +}; + +struct ConfigInput { + uint32 op; + uint32 transform; + FileDescriptor lsTableHandle; + int32 lsTableHandleStatic = -1; +}; + +struct ConfigOutput { + uint32 op; + StaggeredWritePayload staggeredWriteResult; + ControlList controls; + int32 dropFrameCount; +}; From patchwork Fri Oct 2 14:31:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9936 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 409D3C3B5C for ; Fri, 2 Oct 2020 14:33:15 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0E09963BEB; Fri, 2 Oct 2020 16:33:15 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="bjMpCGWo"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 2469C63B98 for ; Fri, 2 Oct 2020 16:33:14 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 59870FD1; Fri, 2 Oct 2020 16:33:12 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649193; bh=S5Rg7iFnqfc+mBlGOhMRPE6Kt8lTSXx0AE4MBIRiwb4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bjMpCGWoM1suodHyEKC/jTQe83ySZJN+MIbMWY23Mkm3NxHoDwLOC7qPLv0KIHPIS i5P7iytjb0xRhn8OyUmZlnzpz3pREc20bvtYtlLrwI2X2BrKkF6wpVI9jDC2/dR/oO P2qWT0So1bjaaXfpTi58PPJj92/qfDIUBNVU48Us= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:44 +0900 Message-Id: <20201002143154.468162-29-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 28/38] libcamera: pipeline, ipa: raspberrypi: Use new data definition 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" Now that we can generate custom functions and data structures with mojo, switch the raspberrypi pipeline handler and IPA to use the custom data structures as defined in the mojom data definition file. Signed-off-by: Paul Elder --- Changes in v3: - use ipa::rpi namespace - rebase on the RPi namespace patch series newly merged into master Changes in v2: - rebased on "libcamera: pipeline: ipa: raspberrypi: Rework drop frame signalling" - use newly customized RPi IPA interface --- include/libcamera/ipa/raspberrypi.h | 37 ++-- src/ipa/raspberrypi/raspberrypi.cpp | 160 +++++++---------- .../pipeline/raspberrypi/raspberrypi.cpp | 164 +++++++++--------- 3 files changed, 167 insertions(+), 194 deletions(-) diff --git a/include/libcamera/ipa/raspberrypi.h b/include/libcamera/ipa/raspberrypi.h index 58262f6c..211fd1ca 100644 --- a/include/libcamera/ipa/raspberrypi.h +++ b/include/libcamera/ipa/raspberrypi.h @@ -26,22 +26,27 @@ enum BufferMask { static constexpr unsigned int MaxLsGridSize = 32 << 10; /* List of controls handled by the Raspberry Pi IPA */ -static const ControlInfoMap Controls = { - { &controls::AeEnable, ControlInfo(false, true) }, - { &controls::ExposureTime, ControlInfo(0, 999999) }, - { &controls::AnalogueGain, ControlInfo(1.0f, 32.0f) }, - { &controls::AeMeteringMode, ControlInfo(0, static_cast(controls::MeteringModeMax)) }, - { &controls::AeConstraintMode, ControlInfo(0, static_cast(controls::ConstraintModeMax)) }, - { &controls::AeExposureMode, ControlInfo(0, static_cast(controls::ExposureModeMax)) }, - { &controls::ExposureValue, ControlInfo(0.0f, 16.0f) }, - { &controls::AwbEnable, ControlInfo(false, true) }, - { &controls::ColourGains, ControlInfo(0.0f, 32.0f) }, - { &controls::AwbMode, ControlInfo(0, static_cast(controls::AwbModeMax)) }, - { &controls::Brightness, ControlInfo(-1.0f, 1.0f) }, - { &controls::Contrast, ControlInfo(0.0f, 32.0f) }, - { &controls::Saturation, ControlInfo(0.0f, 32.0f) }, - { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) }, - { &controls::ColourCorrectionMatrix, ControlInfo(-16.0f, 16.0f) }, +static ControlInfoMap Controls; + +inline void initializeRPiControls() +{ + Controls = { + { &controls::AeEnable, ControlInfo(false, true) }, + { &controls::ExposureTime, ControlInfo(0, 999999) }, + { &controls::AnalogueGain, ControlInfo(1.0f, 32.0f) }, + { &controls::AeMeteringMode, ControlInfo(0, static_cast(controls::MeteringModeMax)) }, + { &controls::AeConstraintMode, ControlInfo(0, static_cast(controls::ConstraintModeMax)) }, + { &controls::AeExposureMode, ControlInfo(0, static_cast(controls::ExposureModeMax)) }, + { &controls::ExposureValue, ControlInfo(0.0f, 16.0f) }, + { &controls::AwbEnable, ControlInfo(false, true) }, + { &controls::ColourGains, ControlInfo(0.0f, 32.0f) }, + { &controls::AwbMode, ControlInfo(0, static_cast(controls::AwbModeMax)) }, + { &controls::Brightness, ControlInfo(-1.0f, 1.0f) }, + { &controls::Contrast, ControlInfo(0.0f, 32.0f) }, + { &controls::Saturation, ControlInfo(0.0f, 32.0f) }, + { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) }, + { &controls::ColourCorrectionMatrix, ControlInfo(-16.0f, 16.0f) }, + }; }; } /* namespace RPi */ diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index b0c7d1c1..0e23a476 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -60,7 +61,7 @@ constexpr unsigned int DefaultExposureTime = 20000; LOG_DEFINE_CATEGORY(IPARPI) -class IPARPi : public IPAInterface +class IPARPi : public ipa::rpi::IPARPiInterface { public: IPARPi() @@ -68,6 +69,7 @@ public: frameCount_(0), checkCount_(0), mistrustCount_(0), lsTable_(nullptr) { + RPi::initializeRPiControls(); } ~IPARPi() @@ -82,12 +84,14 @@ public: void configure(const CameraSensorInfo &sensorInfo, const std::map &streamConfig, - const std::map &entityControls, - const IPAOperationData &data, - IPAOperationData *response) override; + const std::map &entityControls, + const ipa::rpi::ConfigInput &data, + ipa::rpi::ConfigOutput *response) override; void mapBuffers(const std::vector &buffers) override; void unmapBuffers(const std::vector &ids) override; - void processEvent(const IPAOperationData &event) override; + void signalStatReady(const uint32_t bufferId) override; + void signalQueueRequest(const ControlList &controls) override; + void signalIspPrepare(const ipa::rpi::IspPreparePayload &data) override; private: void setMode(const CameraSensorInfo &sensorInfo); @@ -144,6 +148,11 @@ private: /* LS table allocation passed in from the pipeline handler. */ FileDescriptor lsTableHandle_; + /* + * LS table allocation passed in from the pipeline handler, + * in the context of the pipeline handler. + */ + int32_t lsTableHandlePH_; void *lsTable_; }; @@ -193,15 +202,13 @@ void IPARPi::setMode(const CameraSensorInfo &sensorInfo) void IPARPi::configure(const CameraSensorInfo &sensorInfo, [[maybe_unused]] const std::map &streamConfig, - const std::map &entityControls, - const IPAOperationData &ipaConfig, - IPAOperationData *result) + const std::map &entityControls, + const ipa::rpi::ConfigInput &ipaConfig, + ipa::rpi::ConfigOutput *result) { if (entityControls.empty()) return; - result->operation = 0; - unicamCtrls_ = entityControls.at(0); ispCtrls_ = entityControls.at(1); @@ -225,32 +232,28 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo, helper_->GetDelays(exposureDelay, gainDelay); sensorMetadata = helper_->SensorEmbeddedDataPresent(); - result->data.push_back(gainDelay); - result->data.push_back(exposureDelay); - result->data.push_back(sensorMetadata); - - result->operation |= RPi::IPA_CONFIG_STAGGERED_WRITE; + result->op_ |= ipa::rpi::RPI_IPA_CONFIG_STAGGERED_WRITE; + result->staggeredWriteResult_.gainDelay_ = gainDelay; + result->staggeredWriteResult_.exposureDelay_ = exposureDelay; + result->staggeredWriteResult_.sensorMetadata_ = sensorMetadata; } /* Re-assemble camera mode using the sensor info. */ setMode(sensorInfo); - /* - * The ipaConfig.data always gives us the user transform first. Note that - * this will always make the LS table pointer (if present) element 1. - */ - mode_.transform = static_cast(ipaConfig.data[0]); + mode_.transform = static_cast(ipaConfig.transform_); /* Store the lens shading table pointer and handle if available. */ - if (ipaConfig.operation & RPi::IPA_CONFIG_LS_TABLE) { + if (ipaConfig.op_ & ipa::rpi::RPI_IPA_CONFIG_LS_TABLE) { /* Remove any previous table, if there was one. */ if (lsTable_) { munmap(lsTable_, RPi::MaxLsGridSize); lsTable_ = nullptr; } - /* Map the LS table buffer into user space (now element 1). */ - lsTableHandle_ = FileDescriptor(ipaConfig.data[1]); + /* Map the LS table buffer into user space. */ + lsTableHandle_ = FileDescriptor(ipaConfig.lsTableHandle_); + lsTableHandlePH_ = ipaConfig.lsTableHandleStatic_; if (lsTableHandle_.isValid()) { lsTable_ = mmap(nullptr, RPi::MaxLsGridSize, PROT_READ | PROT_WRITE, MAP_SHARED, lsTableHandle_.fd(), 0); @@ -272,18 +275,15 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo, */ frameCount_ = 0; checkCount_ = 0; - unsigned int dropFrame = 0; + result->op_ |= ipa::rpi::RPI_IPA_CONFIG_DROP_FRAMES; if (controllerInit_) { - dropFrame = helper_->HideFramesModeSwitch(); + result->dropFrameCount_ = helper_->HideFramesModeSwitch(); mistrustCount_ = helper_->MistrustFramesModeSwitch(); } else { - dropFrame = helper_->HideFramesStartup(); + result->dropFrameCount_ = helper_->HideFramesStartup(); mistrustCount_ = helper_->MistrustFramesStartup(); } - result->data.push_back(dropFrame); - result->operation |= RPi::IPA_CONFIG_DROP_FRAMES; - /* These zero values mean not program anything (unless overwritten). */ struct AgcStatus agcStatus; agcStatus.shutter_time = 0.0; @@ -308,9 +308,9 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo, if (agcStatus.shutter_time != 0.0 && agcStatus.analogue_gain != 0.0) { ControlList ctrls(unicamCtrls_); applyAGC(&agcStatus, ctrls); - result->controls.push_back(ctrls); - result->operation |= RPi::IPA_CONFIG_SENSOR; + result->op_ |= ipa::rpi::RPI_IPA_CONFIG_SENSOR; + result->controls_ = ctrls; } lastMode_ = mode_; @@ -348,56 +348,38 @@ void IPARPi::unmapBuffers(const std::vector &ids) } } -void IPARPi::processEvent(const IPAOperationData &event) +void IPARPi::signalStatReady(const uint32_t bufferId) { - switch (event.operation) { - case RPi::IPA_EVENT_SIGNAL_STAT_READY: { - unsigned int bufferId = event.data[0]; - - if (++checkCount_ != frameCount_) /* assert here? */ - LOG(IPARPI, Error) << "WARNING: Prepare/Process mismatch!!!"; - if (frameCount_ > mistrustCount_) - processStats(bufferId); - - reportMetadata(); - - IPAOperationData op; - op.operation = RPi::IPA_ACTION_STATS_METADATA_COMPLETE; - op.data = { bufferId & RPi::BufferMask::ID }; - op.controls = { libcameraMetadata_ }; - queueFrameAction.emit(0, op); - break; - } + if (++checkCount_ != frameCount_) /* assert here? */ + LOG(IPARPI, Error) << "WARNING: Prepare/Process mismatch!!!"; + if (frameCount_ > mistrustCount_) + processStats(bufferId); - case RPi::IPA_EVENT_SIGNAL_ISP_PREPARE: { - unsigned int embeddedbufferId = event.data[0]; - unsigned int bayerbufferId = event.data[1]; + reportMetadata(); - /* - * At start-up, or after a mode-switch, we may want to - * avoid running the control algos for a few frames in case - * they are "unreliable". - */ - prepareISP(embeddedbufferId); - frameCount_++; - - /* Ready to push the input buffer into the ISP. */ - IPAOperationData op; - op.operation = RPi::IPA_ACTION_RUN_ISP; - op.data = { bayerbufferId & RPi::BufferMask::ID }; - queueFrameAction.emit(0, op); - break; - } + statsMetadataComplete.emit(bufferId & RPi::BufferMask::ID, libcameraMetadata_); +} - case RPi::IPA_EVENT_QUEUE_REQUEST: { - queueRequest(event.controls[0]); - break; - } +void IPARPi::signalQueueRequest(const ControlList &controls) +{ + queueRequest(controls); +} - default: - LOG(IPARPI, Error) << "Unknown event " << event.operation; - break; - } +void IPARPi::signalIspPrepare(const ipa::rpi::IspPreparePayload &data) +{ + unsigned int embeddedbufferId = data.embeddedbufferId_; + unsigned int bayerbufferId = data.bayerbufferId_; + + /* + * At start-up, or after a mode-switch, we may want to + * avoid running the control algos for a few frames in case + * they are "unreliable". + */ + prepareISP(embeddedbufferId); + frameCount_++; + + /* Ready to push the input buffer into the ISP. */ + runIsp.emit(bayerbufferId & RPi::BufferMask::ID); } void IPARPi::reportMetadata() @@ -498,6 +480,8 @@ void IPARPi::queueRequest(const ControlList &controls) /* Clear the return metadata buffer. */ libcameraMetadata_.clear(); + LOG(IPARPI, Info) << "Request ctrl length: " << controls.size(); + for (auto const &ctrl : controls) { LOG(IPARPI, Info) << "Request ctrl: " << controls::controls.at(ctrl.first)->name() @@ -710,10 +694,7 @@ void IPARPi::queueRequest(const ControlList &controls) void IPARPi::returnEmbeddedBuffer(unsigned int bufferId) { - IPAOperationData op; - op.operation = RPi::IPA_ACTION_EMBEDDED_COMPLETE; - op.data = { bufferId & RPi::BufferMask::ID }; - queueFrameAction.emit(0, op); + embeddedComplete.emit(bufferId & RPi::BufferMask::ID); } void IPARPi::prepareISP(unsigned int bufferId) @@ -774,12 +755,8 @@ void IPARPi::prepareISP(unsigned int bufferId) if (dpcStatus) applyDPC(dpcStatus, ctrls); - if (!ctrls.empty()) { - IPAOperationData op; - op.operation = RPi::IPA_ACTION_V4L2_SET_ISP; - op.controls.push_back(ctrls); - queueFrameAction.emit(0, op); - } + if (!ctrls.empty()) + setIsp.emit(ctrls); } } @@ -835,10 +812,7 @@ void IPARPi::processStats(unsigned int bufferId) ControlList ctrls(unicamCtrls_); applyAGC(&agcStatus, ctrls); - IPAOperationData op; - op.operation = RPi::IPA_ACTION_V4L2_SET_STAGGERED; - op.controls.push_back(ctrls); - queueFrameAction.emit(0, op); + setStaggered.emit(ctrls); } } @@ -1068,7 +1042,7 @@ void IPARPi::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls) .grid_width = w, .grid_stride = w, .grid_height = h, - .dmabuf = lsTableHandle_.fd(), + .dmabuf = lsTableHandlePH_, .ref_transform = 0, .corner_sampled = 1, .gain_format = GAIN_FORMAT_U4P10 @@ -1145,9 +1119,9 @@ const struct IPAModuleInfo ipaModuleInfo = { "raspberrypi", }; -struct ipa_context *ipaCreate() +IPAInterface *ipaCreate() { - return new IPAInterfaceWrapper(std::make_unique()); + return new IPARPi(); } }; /* extern "C" */ diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index d4d04c0d..60ec07ea 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -16,7 +16,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -144,7 +146,11 @@ public: int loadIPA(); int configureIPA(const CameraConfiguration *config); - void queueFrameAction(unsigned int frame, const IPAOperationData &action); + void statsMetadataComplete(uint32_t bufferId, const ControlList &controls); + void runIsp(uint32_t bufferId); + void embeddedComplete(uint32_t bufferId); + void setIsp(const ControlList &controls); + void setStaggered(const ControlList &controls); /* bufferComplete signal handlers. */ void unicamBufferDequeue(FrameBuffer *buffer); @@ -156,6 +162,8 @@ public: void handleExternalBuffer(FrameBuffer *buffer, RPi::Stream *stream); void handleState(); + std::unique_ptr ipa_; + CameraSensor *sensor_; /* Array of Unicam and ISP device streams and associated buffers/streams. */ RPi::Device unicam_; @@ -441,6 +449,7 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() PipelineHandlerRPi::PipelineHandlerRPi(CameraManager *manager) : PipelineHandler(manager), unicam_(nullptr), isp_(nullptr) { + RPi::initializeRPiControls(); } CameraConfiguration *PipelineHandlerRPi::generateConfiguration(Camera *camera, @@ -1093,11 +1102,16 @@ void RPiCameraData::frameStarted(uint32_t sequence) int RPiCameraData::loadIPA() { - ipa_ = IPAManager::createIPA(pipe_, 1, 1); + ipa_ = IPAManager::createIPA(pipe_, 1, 1); + if (!ipa_) return -ENOENT; - ipa_->queueFrameAction.connect(this, &RPiCameraData::queueFrameAction); + ipa_->statsMetadataComplete.connect(this, &RPiCameraData::statsMetadataComplete); + ipa_->runIsp.connect(this, &RPiCameraData::runIsp); + ipa_->embeddedComplete.connect(this, &RPiCameraData::embeddedComplete); + ipa_->setIsp.connect(this, &RPiCameraData::setIsp); + ipa_->setStaggered.connect(this, &RPiCameraData::setStaggered); IPASettings settings{ .configurationFile = ipa_->configurationFile(sensor_->model() + ".json") @@ -1113,8 +1127,8 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config) static_cast(config); std::map streamConfig; - std::map entityControls; - IPAOperationData ipaConfig = {}; + std::map entityControls; + ipa::rpi::ConfigInput ipaConfig; /* Get the device format to pass to the IPA. */ V4L2DeviceFormat sensorFormat; @@ -1134,7 +1148,7 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config) entityControls.emplace(1, isp_[Isp::Input].dev()->controls()); /* Always send the user transform to the IPA. */ - ipaConfig.data = { static_cast(config->transform) }; + ipaConfig.transform_ = static_cast(config->transform); /* Allocate the lens shading table via dmaHeap and pass to the IPA. */ if (!lsTable_.isValid()) { @@ -1143,8 +1157,9 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config) return -ENOMEM; /* Allow the IPA to mmap the LS table via the file descriptor. */ - ipaConfig.operation = RPi::IPA_CONFIG_LS_TABLE; - ipaConfig.data.push_back(static_cast(lsTable_.fd())); + ipaConfig.op_ |= ipa::rpi::RPI_IPA_CONFIG_LS_TABLE; + ipaConfig.lsTableHandle_ = lsTable_; + ipaConfig.lsTableHandleStatic_ = lsTable_.fd(); } CameraSensorInfo sensorInfo = {}; @@ -1155,22 +1170,23 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config) } /* Ready the IPA - it must know about the sensor resolution. */ - IPAOperationData result; + ipa::rpi::ConfigOutput result; ipa_->configure(sensorInfo, streamConfig, entityControls, ipaConfig, &result); - unsigned int resultIdx = 0; - if (result.operation & RPi::IPA_CONFIG_STAGGERED_WRITE) { + if (result.op_ & ipa::rpi::RPI_IPA_CONFIG_STAGGERED_WRITE) { /* * Setup our staggered control writer with the sensor default * gain and exposure delays. */ if (!staggeredCtrl_) { staggeredCtrl_.init(unicam_[Unicam::Image].dev(), - { { V4L2_CID_ANALOGUE_GAIN, result.data[resultIdx++] }, - { V4L2_CID_EXPOSURE, result.data[resultIdx++] } }); - sensorMetadata_ = result.data[resultIdx++]; + { { V4L2_CID_ANALOGUE_GAIN, + result.staggeredWriteResult_.gainDelay_ }, + { V4L2_CID_EXPOSURE, + result.staggeredWriteResult_.exposureDelay_ } }); + sensorMetadata_ = result.staggeredWriteResult_.sensorMetadata_; } /* @@ -1187,86 +1203,70 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config) } } - if (result.operation & RPi::IPA_CONFIG_SENSOR) { - const ControlList &ctrls = result.controls[0]; + if (result.op_ & ipa::rpi::RPI_IPA_CONFIG_SENSOR) { + const ControlList &ctrls = result.controls_; if (!staggeredCtrl_.set(ctrls)) LOG(RPI, Error) << "V4L2 staggered set failed"; } - if (result.operation & RPi::IPA_CONFIG_DROP_FRAMES) { + if (result.op_ & ipa::rpi::RPI_IPA_CONFIG_DROP_FRAMES) { /* Configure the number of dropped frames required on startup. */ - dropFrameCount_ = result.data[resultIdx++]; + dropFrameCount_ = result.dropFrameCount_; } return 0; } -void RPiCameraData::queueFrameAction([[maybe_unused]] unsigned int frame, - const IPAOperationData &action) +void RPiCameraData::statsMetadataComplete(uint32_t bufferId, const ControlList &controls) { - /* - * The following actions can be handled when the pipeline handler is in - * a stopped state. - */ - switch (action.operation) { - case RPi::IPA_ACTION_V4L2_SET_STAGGERED: { - const ControlList &controls = action.controls[0]; - if (!staggeredCtrl_.set(controls)) - LOG(RPI, Error) << "V4L2 staggered set failed"; - goto done; - } + if (state_ == State::Stopped) + handleState(); - case RPi::IPA_ACTION_V4L2_SET_ISP: { - ControlList controls = action.controls[0]; - isp_[Isp::Input].dev()->setControls(&controls); - goto done; - } - } + FrameBuffer *buffer = isp_[Isp::Stats].getBuffers().at(bufferId); + handleStreamBuffer(buffer, &isp_[Isp::Stats]); + /* Fill the Request metadata buffer with what the IPA has provided */ + requestQueue_.front()->metadata() = std::move(controls); + state_ = State::IpaComplete; + handleState(); +} + +void RPiCameraData::runIsp(uint32_t bufferId) +{ if (state_ == State::Stopped) - goto done; + handleState(); - /* - * The following actions must not be handled when the pipeline handler - * is in a stopped state. - */ - switch (action.operation) { - case RPi::IPA_ACTION_STATS_METADATA_COMPLETE: { - unsigned int bufferId = action.data[0]; - FrameBuffer *buffer = isp_[Isp::Stats].getBuffers().at(bufferId); - - handleStreamBuffer(buffer, &isp_[Isp::Stats]); - /* Fill the Request metadata buffer with what the IPA has provided */ - requestQueue_.front()->metadata() = std::move(action.controls[0]); - state_ = State::IpaComplete; - break; - } + FrameBuffer *buffer = unicam_[Unicam::Image].getBuffers().at(bufferId); - case RPi::IPA_ACTION_EMBEDDED_COMPLETE: { - unsigned int bufferId = action.data[0]; - FrameBuffer *buffer = unicam_[Unicam::Embedded].getBuffers().at(bufferId); - handleStreamBuffer(buffer, &unicam_[Unicam::Embedded]); - break; - } + LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << bufferId + << ", timestamp: " << buffer->metadata().timestamp; - case RPi::IPA_ACTION_RUN_ISP: { - unsigned int bufferId = action.data[0]; - FrameBuffer *buffer = unicam_[Unicam::Image].getBuffers().at(bufferId); + isp_[Isp::Input].queueBuffer(buffer); + ispOutputCount_ = 0; + handleState(); +} - LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << bufferId - << ", timestamp: " << buffer->metadata().timestamp; +void RPiCameraData::embeddedComplete(uint32_t bufferId) +{ + if (state_ == State::Stopped) + handleState(); - isp_[Isp::Input].queueBuffer(buffer); - ispOutputCount_ = 0; - break; - } + FrameBuffer *buffer = unicam_[Unicam::Embedded].getBuffers().at(bufferId); + handleStreamBuffer(buffer, &unicam_[Unicam::Embedded]); + handleState(); +} - default: - LOG(RPI, Error) << "Unknown action " << action.operation; - break; - } +void RPiCameraData::setIsp(const ControlList &controls) +{ + ControlList ctrls = controls; + isp_[Isp::Input].dev()->setControls(&ctrls); + handleState(); +} -done: +void RPiCameraData::setStaggered(const ControlList &controls) +{ + if (!staggeredCtrl_.set(controls)) + LOG(RPI, Error) << "V4L2 staggered set failed"; handleState(); } @@ -1366,10 +1366,7 @@ void RPiCameraData::ispOutputDequeue(FrameBuffer *buffer) * application until after the IPA signals so. */ if (stream == &isp_[Isp::Stats]) { - IPAOperationData op; - op.operation = RPi::IPA_EVENT_SIGNAL_STAT_READY; - op.data = { RPi::BufferMask::STATS | static_cast(index) }; - ipa_->processEvent(op); + ipa_->signalStatReady(RPi::BufferMask::STATS | static_cast(index)); } else { /* Any other ISP output can be handed back to the application now. */ handleStreamBuffer(buffer, stream); @@ -1542,7 +1539,6 @@ void RPiCameraData::checkRequestCompleted() void RPiCameraData::tryRunPipeline() { FrameBuffer *bayerBuffer, *embeddedBuffer; - IPAOperationData op; /* If any of our request or buffer queues are empty, we cannot proceed. */ if (state_ != State::Idle || requestQueue_.empty() || @@ -1593,9 +1589,7 @@ void RPiCameraData::tryRunPipeline() * queue the ISP output buffer listed in the request to start the HW * pipeline. */ - op.operation = RPi::IPA_EVENT_QUEUE_REQUEST; - op.controls = { request->controls() }; - ipa_->processEvent(op); + ipa_->signalQueueRequest(request->controls()); /* Ready to use the buffers, pop them off the queue. */ bayerQueue_.pop(); @@ -1611,10 +1605,10 @@ void RPiCameraData::tryRunPipeline() << " Bayer buffer id: " << bayerId << " Embedded buffer id: " << embeddedId; - op.operation = RPi::IPA_EVENT_SIGNAL_ISP_PREPARE; - op.data = { RPi::BufferMask::EMBEDDED_DATA | embeddedId, - RPi::BufferMask::BAYER_DATA | bayerId }; - ipa_->processEvent(op); + ipa::rpi::IspPreparePayload ispPrepare; + ispPrepare.embeddedbufferId_ = RPi::BufferMask::EMBEDDED_DATA | embeddedId; + ispPrepare.bayerbufferId_ = RPi::BufferMask::BAYER_DATA | bayerId; + ipa_->signalIspPrepare(ispPrepare); } void RPiCameraData::tryFlushQueues() From patchwork Fri Oct 2 14:31:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9937 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 AACE4C3B5C for ; Fri, 2 Oct 2020 14:33:17 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 79D9063BE6; Fri, 2 Oct 2020 16:33:17 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="LIGcDMpF"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 704D463B98 for ; Fri, 2 Oct 2020 16:33:16 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id B032B528; Fri, 2 Oct 2020 16:33:14 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649196; bh=18plUL17/5AwZRQMCkqwSWzUuA62UmxIhg6h6gwlEMs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LIGcDMpFwR4F1OCEzFov86rqw94vXymTD1VbV0pQa1xUjhClGt0ehFZza8aYyXU6Y o9Sbwd2c/16RKNZW/H/Si3bjU4kenxhcz5CVmcYWx837PMVZAzPBeehTmMr2x6jRQA ZpSI71rhb2N3ROAxoC1OryXQD6lVI9P8VQ1e7YNA= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:45 +0900 Message-Id: <20201002143154.468162-30-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 29/38] libcamera: pipeline, ipa: vimc: Support the new IPC mechanism 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 support to vimc pipeline handler and IPA for the new IPC mechanism. Signed-off-by: Paul Elder --- Changes in v3: - change namespacing of base ControlInfoMap structure New in v2 --- include/libcamera/ipa/meson.build | 1 + include/libcamera/ipa/{ipa_vimc.h => vimc.h} | 8 ++++++++ include/libcamera/ipa/vimc.mojom | 12 ++++++++++++ src/ipa/vimc/meson.build | 2 +- src/ipa/vimc/vimc.cpp | 18 +++++------------- src/libcamera/pipeline/vimc/vimc.cpp | 8 +++++++- 6 files changed, 34 insertions(+), 15 deletions(-) rename include/libcamera/ipa/{ipa_vimc.h => vimc.h} (84%) create mode 100644 include/libcamera/ipa/vimc.mojom diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build index 55c97fa5..886908ad 100644 --- a/include/libcamera/ipa/meson.build +++ b/include/libcamera/ipa/meson.build @@ -26,6 +26,7 @@ ipa_mojom_core = custom_target(core_mojom_file.split('.')[0] + '_mojom_module', ipa_mojom_files = [ 'raspberrypi.mojom', + 'vimc.mojom', ] ipa_mojoms = [] diff --git a/include/libcamera/ipa/ipa_vimc.h b/include/libcamera/ipa/vimc.h similarity index 84% rename from include/libcamera/ipa/ipa_vimc.h rename to include/libcamera/ipa/vimc.h index 8e82dd94..d93e10b8 100644 --- a/include/libcamera/ipa/ipa_vimc.h +++ b/include/libcamera/ipa/vimc.h @@ -8,6 +8,8 @@ #ifndef __LIBCAMERA_IPA_VIMC_H__ #define __LIBCAMERA_IPA_VIMC_H__ +#include + namespace libcamera { #define VIMC_IPA_FIFO_PATH "/tmp/libcamera_ipa_vimc_fifo" @@ -19,6 +21,12 @@ enum IPAOperationCode { IPAOperationStop, }; +namespace Vimc { + +static ControlInfoMap Controls; + +} + } /* namespace libcamera */ #endif /* __LIBCAMERA_IPA_VIMC_H__ */ diff --git a/include/libcamera/ipa/vimc.mojom b/include/libcamera/ipa/vimc.mojom new file mode 100644 index 00000000..9ab6d005 --- /dev/null +++ b/include/libcamera/ipa/vimc.mojom @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +import "include/libcamera/ipa/core.mojom"; + +interface IPAVimcInterface { + init(IPASettings settings) => (int32 ret); + start() => (int32 ret); + stop(); +}; + +interface IPAVimcCallbackInterface { +}; diff --git a/src/ipa/vimc/meson.build b/src/ipa/vimc/meson.build index 8c9df854..b2b7a09d 100644 --- a/src/ipa/vimc/meson.build +++ b/src/ipa/vimc/meson.build @@ -3,7 +3,7 @@ ipa_name = 'ipa_vimc' mod = shared_module(ipa_name, - 'vimc.cpp', + ['vimc.cpp', libcamera_generated_headers], name_prefix : '', include_directories : [ipa_includes, libipa_includes], dependencies : libcamera_dep, diff --git a/src/ipa/vimc/vimc.cpp b/src/ipa/vimc/vimc.cpp index ef257762..00ec7b80 100644 --- a/src/ipa/vimc/vimc.cpp +++ b/src/ipa/vimc/vimc.cpp @@ -5,7 +5,8 @@ * ipa_vimc.cpp - Vimc Image Processing Algorithm module */ -#include +#include +#include #include #include @@ -26,7 +27,7 @@ namespace libcamera { LOG_DEFINE_CATEGORY(IPAVimc) -class IPAVimc : public IPAInterface +class IPAVimc : public IPAVimcInterface { public: IPAVimc(); @@ -37,15 +38,6 @@ public: int start() override; void stop() override; - void configure([[maybe_unused]] const CameraSensorInfo &sensorInfo, - [[maybe_unused]] const std::map &streamConfig, - [[maybe_unused]] const std::map &entityControls, - [[maybe_unused]] const IPAOperationData &ipaConfig, - [[maybe_unused]] IPAOperationData *result) override {} - void mapBuffers([[maybe_unused]] const std::vector &buffers) override {} - void unmapBuffers([[maybe_unused]] const std::vector &ids) override {} - void processEvent([[maybe_unused]] const IPAOperationData &event) override {} - private: void initTrace(); void trace(enum IPAOperationCode operation); @@ -141,9 +133,9 @@ const struct IPAModuleInfo ipaModuleInfo = { "vimc", }; -struct ipa_context *ipaCreate() +IPAInterface *ipaCreate() { - return new IPAInterfaceWrapper(std::make_unique()); + return new IPAVimc(); } } diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp index fc8085f1..146025c0 100644 --- a/src/libcamera/pipeline/vimc/vimc.cpp +++ b/src/libcamera/pipeline/vimc/vimc.cpp @@ -34,6 +34,10 @@ #include "libcamera/internal/v4l2_subdevice.h" #include "libcamera/internal/v4l2_videodevice.h" +#include +#include +#include + namespace libcamera { LOG_DEFINE_CATEGORY(VIMC) @@ -67,6 +71,8 @@ public: V4L2VideoDevice *video_; V4L2VideoDevice *raw_; Stream stream_; + + std::unique_ptr ipa_; }; class VimcCameraConfiguration : public CameraConfiguration @@ -428,7 +434,7 @@ bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator) std::unique_ptr data = std::make_unique(this, media); - data->ipa_ = IPAManager::createIPA(this, 0, 0); + data->ipa_ = IPAManager::createIPA(this, 0, 0); if (data->ipa_ != nullptr) { std::string conf = data->ipa_->configurationFile("vimc.conf"); data->ipa_->init(IPASettings{ conf }); From patchwork Fri Oct 2 14:31:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9938 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 458A9C3B5C for ; Fri, 2 Oct 2020 14:33:20 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 07BFD63BEB; Fri, 2 Oct 2020 16:33:20 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="gD32idcq"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 9D97463B98 for ; Fri, 2 Oct 2020 16:33:18 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D533D528; Fri, 2 Oct 2020 16:33:16 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649198; bh=Gqh9QvK3Bz3MWys9flkv82+i8FHoEgR802cEUCwnb7k=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=gD32idcqsmpIHPXPF0/8Q57jT1QhQEKZTzoqCS8TFEznMdQJKGSQdc+hdRUG7zvDf itvBPP1NBYH6E3vlnnUrLETlJ9aMI44rvlTYAtNh7fExsYE0Ll60hnErwBb/AB73VR 2fG4i5DZ24yAG/RbUQG4jU1wW22D8HVEUQ5uUl7k= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:46 +0900 Message-Id: <20201002143154.468162-31-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 30/38] libcamera: pipeline, ipa: rkisp1: Support the new IPC mechanism 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 support to the rkisp1 pipeline handler and IPA for the new IPC mechanism. Signed-off-by: Paul Elder --- Changes in v3: - change namespacing of base ControlInfoMap structure New in v2 --- include/libcamera/ipa/meson.build | 1 + include/libcamera/ipa/rkisp1.h | 18 ++++---- include/libcamera/ipa/rkisp1.mojom | 42 +++++++++++++++++++ src/ipa/rkisp1/meson.build | 2 +- src/ipa/rkisp1/rkisp1.cpp | 53 +++++++++++------------- src/libcamera/pipeline/rkisp1/rkisp1.cpp | 44 +++++++++++--------- 6 files changed, 104 insertions(+), 56 deletions(-) create mode 100644 include/libcamera/ipa/rkisp1.mojom diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build index 886908ad..32f9e321 100644 --- a/include/libcamera/ipa/meson.build +++ b/include/libcamera/ipa/meson.build @@ -26,6 +26,7 @@ ipa_mojom_core = custom_target(core_mojom_file.split('.')[0] + '_mojom_module', ipa_mojom_files = [ 'raspberrypi.mojom', + 'rkisp1.mojom', 'vimc.mojom', ] diff --git a/include/libcamera/ipa/rkisp1.h b/include/libcamera/ipa/rkisp1.h index 4fe0482b..bbe6e95e 100644 --- a/include/libcamera/ipa/rkisp1.h +++ b/include/libcamera/ipa/rkisp1.h @@ -7,12 +7,16 @@ #ifndef __LIBCAMERA_IPA_INTERFACE_RKISP1_H__ #define __LIBCAMERA_IPA_INTERFACE_RKISP1_H__ -enum RkISP1Operations { - RKISP1_IPA_ACTION_V4L2_SET = 1, - RKISP1_IPA_ACTION_PARAM_FILLED = 2, - RKISP1_IPA_ACTION_METADATA = 3, - RKISP1_IPA_EVENT_SIGNAL_STAT_BUFFER = 4, - RKISP1_IPA_EVENT_QUEUE_REQUEST = 5, -}; +#include + +namespace libcamera { + +namespace RkISP1 { + +static ControlInfoMap Controls; + +} + +} /* namespace libcamera */ #endif /* __LIBCAMERA_IPA_INTERFACE_RKISP1_H__ */ diff --git a/include/libcamera/ipa/rkisp1.mojom b/include/libcamera/ipa/rkisp1.mojom new file mode 100644 index 00000000..df76ee6f --- /dev/null +++ b/include/libcamera/ipa/rkisp1.mojom @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +import "include/libcamera/ipa/core.mojom"; + +interface IPARkISP1Interface { + init(IPASettings settings) => (int32 ret); + start() => (int32 ret); + stop(); + + configure(CameraSensorInfo sensorInfo, + map streamConfig, + map entityControls) => (); + + mapBuffers(array buffers); + unmapBuffers(array ids); + + [async] processEvent(RkISP1Event ev); +}; + +interface IPARkISP1CallbackInterface { + queueFrameAction(uint32 frame, RkISP1Action action); +}; + +enum RkISP1Operations { + RKISP1_IPA_ACTION_V4L2_SET, + RKISP1_IPA_ACTION_PARAM_FILLED, + RKISP1_IPA_ACTION_METADATA, + RKISP1_IPA_EVENT_SIGNAL_STAT_BUFFER, + RKISP1_IPA_EVENT_QUEUE_REQUEST, +}; + +struct RkISP1Event { + RkISP1Operations op; + uint32 frame; + uint32 bufferId; + ControlList controls; +}; + +struct RkISP1Action { + RkISP1Operations op; + ControlList controls; +}; diff --git a/src/ipa/rkisp1/meson.build b/src/ipa/rkisp1/meson.build index ed9a6b6b..30953c13 100644 --- a/src/ipa/rkisp1/meson.build +++ b/src/ipa/rkisp1/meson.build @@ -3,7 +3,7 @@ ipa_name = 'ipa_rkisp1' mod = shared_module(ipa_name, - 'rkisp1.cpp', + ['rkisp1.cpp', libcamera_generated_headers], name_prefix : '', include_directories : [ipa_includes, libipa_includes], dependencies : libcamera_dep, diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp index 07d7f1b2..4fec397b 100644 --- a/src/ipa/rkisp1/rkisp1.cpp +++ b/src/ipa/rkisp1/rkisp1.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -30,7 +31,7 @@ namespace libcamera { LOG_DEFINE_CATEGORY(IPARkISP1) -class IPARkISP1 : public IPAInterface +class IPARkISP1 : public IPARkISP1Interface { public: int init([[maybe_unused]] const IPASettings &settings) override @@ -41,13 +42,11 @@ public: void stop() override {} void configure(const CameraSensorInfo &info, - const std::map &streamConfig, - const std::map &entityControls, - const IPAOperationData &ipaConfig, - IPAOperationData *response) override; + const std::map &streamConfig, + const std::map &entityControls) override; void mapBuffers(const std::vector &buffers) override; void unmapBuffers(const std::vector &ids) override; - void processEvent(const IPAOperationData &event) override; + void processEvent(const RkISP1Event &event) override; private: void queueRequest(unsigned int frame, rkisp1_params_cfg *params, @@ -80,10 +79,8 @@ private: * before accessing them. */ void IPARkISP1::configure([[maybe_unused]] const CameraSensorInfo &info, - [[maybe_unused]] const std::map &streamConfig, - const std::map &entityControls, - [[maybe_unused]] const IPAOperationData &ipaConfig, - [[maybe_unused]] IPAOperationData *result) + [[maybe_unused]] const std::map &streamConfig, + const std::map &entityControls) { if (entityControls.empty()) return; @@ -159,12 +156,12 @@ void IPARkISP1::unmapBuffers(const std::vector &ids) } } -void IPARkISP1::processEvent(const IPAOperationData &event) +void IPARkISP1::processEvent(const RkISP1Event &event) { - switch (event.operation) { + switch (event.op_) { case RKISP1_IPA_EVENT_SIGNAL_STAT_BUFFER: { - unsigned int frame = event.data[0]; - unsigned int bufferId = event.data[1]; + unsigned int frame = event.frame_; + unsigned int bufferId = event.bufferId_; const rkisp1_stat_buffer *stats = static_cast(buffersMemory_[bufferId]); @@ -173,17 +170,17 @@ void IPARkISP1::processEvent(const IPAOperationData &event) break; } case RKISP1_IPA_EVENT_QUEUE_REQUEST: { - unsigned int frame = event.data[0]; - unsigned int bufferId = event.data[1]; + unsigned int frame = event.frame_; + unsigned int bufferId = event.bufferId_; rkisp1_params_cfg *params = static_cast(buffersMemory_[bufferId]); - queueRequest(frame, params, event.controls[0]); + queueRequest(frame, params, event.controls_); break; } default: - LOG(IPARkISP1, Error) << "Unknown event " << event.operation; + LOG(IPARkISP1, Error) << "Unknown event " << event.op_; break; } } @@ -203,8 +200,8 @@ void IPARkISP1::queueRequest(unsigned int frame, rkisp1_params_cfg *params, params->module_en_update = RKISP1_CIF_ISP_MODULE_AEC; } - IPAOperationData op; - op.operation = RKISP1_IPA_ACTION_PARAM_FILLED; + RkISP1Action op; + op.op_ = RKISP1_IPA_ACTION_PARAM_FILLED; queueFrameAction.emit(frame, op); } @@ -256,13 +253,13 @@ void IPARkISP1::updateStatistics(unsigned int frame, void IPARkISP1::setControls(unsigned int frame) { - IPAOperationData op; - op.operation = RKISP1_IPA_ACTION_V4L2_SET; + RkISP1Action op; + op.op_ = RKISP1_IPA_ACTION_V4L2_SET; ControlList ctrls(ctrls_); ctrls.set(V4L2_CID_EXPOSURE, static_cast(exposure_)); ctrls.set(V4L2_CID_ANALOGUE_GAIN, static_cast(gain_)); - op.controls.push_back(ctrls); + op.controls_ = ctrls; queueFrameAction.emit(frame, op); } @@ -274,9 +271,9 @@ void IPARkISP1::metadataReady(unsigned int frame, unsigned int aeState) if (aeState) ctrls.set(controls::AeLocked, aeState == 2); - IPAOperationData op; - op.operation = RKISP1_IPA_ACTION_METADATA; - op.controls.push_back(ctrls); + RkISP1Action op; + op.op_ = RKISP1_IPA_ACTION_METADATA; + op.controls_ = ctrls; queueFrameAction.emit(frame, op); } @@ -293,9 +290,9 @@ const struct IPAModuleInfo ipaModuleInfo = { "rkisp1", }; -struct ipa_context *ipaCreate() +IPAInterface *ipaCreate() { - return new IPAInterfaceWrapper(std::make_unique()); + return new IPARkISP1(); } } diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp index aec590ff..74812302 100644 --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -144,9 +146,11 @@ public: RkISP1MainPath *mainPath_; RkISP1SelfPath *selfPath_; + std::unique_ptr ipa_; + private: void queueFrameAction(unsigned int frame, - const IPAOperationData &action); + const RkISP1Action &action); void metadataReady(unsigned int frame, const ControlList &metadata); }; @@ -412,7 +416,7 @@ private: int RkISP1CameraData::loadIPA() { - ipa_ = IPAManager::createIPA(pipe_, 1, 1); + ipa_ = IPAManager::createIPA(pipe_, 1, 1); if (!ipa_) return -ENOENT; @@ -425,11 +429,11 @@ int RkISP1CameraData::loadIPA() } void RkISP1CameraData::queueFrameAction(unsigned int frame, - const IPAOperationData &action) + const RkISP1Action &action) { - switch (action.operation) { + switch (action.op_) { case RKISP1_IPA_ACTION_V4L2_SET: { - const ControlList &controls = action.controls[0]; + const ControlList &controls = action.controls_; timeline_.scheduleAction(std::make_unique(frame, sensor_, controls)); @@ -442,10 +446,10 @@ void RkISP1CameraData::queueFrameAction(unsigned int frame, break; } case RKISP1_IPA_ACTION_METADATA: - metadataReady(frame, action.controls[0]); + metadataReady(frame, action.controls_); break; default: - LOG(RkISP1, Error) << "Unknown action " << action.operation; + LOG(RkISP1, Error) << "Unknown action " << action.op_; break; } } @@ -908,12 +912,10 @@ int PipelineHandlerRkISP1::start(Camera *camera) ret = 0; } - std::map entityControls; + std::map entityControls; entityControls.emplace(0, data->sensor_->controls()); - IPAOperationData ipaConfig; - data->ipa_->configure(sensorInfo, streamConfig, entityControls, - ipaConfig, nullptr); + data->ipa_->configure(sensorInfo, streamConfig, entityControls); return ret; } @@ -955,11 +957,12 @@ int PipelineHandlerRkISP1::queueRequestDevice(Camera *camera, Request *request) if (!info) return -ENOENT; - IPAOperationData op; - op.operation = RKISP1_IPA_EVENT_QUEUE_REQUEST; - op.data = { data->frame_, info->paramBuffer->cookie() }; - op.controls = { request->controls() }; - data->ipa_->processEvent(op); + RkISP1Event ev; + ev.op_ = RKISP1_IPA_EVENT_QUEUE_REQUEST; + ev.frame_ = data->frame_; + ev.bufferId_ = info->paramBuffer->cookie(); + ev.controls_ = request->controls(); + data->ipa_->processEvent(ev); data->timeline_.scheduleAction(std::make_unique(data->frame_, data, @@ -1178,10 +1181,11 @@ void PipelineHandlerRkISP1::statReady(FrameBuffer *buffer) if (data->frame_ <= buffer->metadata().sequence) data->frame_ = buffer->metadata().sequence + 1; - IPAOperationData op; - op.operation = RKISP1_IPA_EVENT_SIGNAL_STAT_BUFFER; - op.data = { info->frame, info->statBuffer->cookie() }; - data->ipa_->processEvent(op); + RkISP1Event ev; + ev.op_ = RKISP1_IPA_EVENT_SIGNAL_STAT_BUFFER; + ev.frame_ = info->frame; + ev.bufferId_ = info->statBuffer->cookie(); + data->ipa_->processEvent(ev); } REGISTER_PIPELINE_HANDLER(PipelineHandlerRkISP1); From patchwork Fri Oct 2 14:31:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9939 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 8B8B7C3B5C for ; Fri, 2 Oct 2020 14:33:22 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 57F6A63BE6; Fri, 2 Oct 2020 16:33:22 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="gk9clwKO"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1098063B98 for ; Fri, 2 Oct 2020 16:33:21 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 05B7B528; Fri, 2 Oct 2020 16:33:18 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649200; bh=suC8FhA7ExotBNs3cFppm5Y1UW8OWFfLvA8kVH+GnyI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=gk9clwKOUQ/gDMHblzl2HGojVEK/6JH+AQWQre0w+OWyeVIn5xnmyYbGtdWsNL0au jCP++rCf7AiO8j5yFDFlYc1jRNt93NDHdrsGzRbYtH2vp+Jidc+cA+Kf1b7QQdKTu7 A8IzEnOKYkM9iGwLRcAFkXbqy+O8fnwnXGp6kHuY= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:47 +0900 Message-Id: <20201002143154.468162-32-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 31/38] libcamera: IPAProxy: Remove registration mechanism 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" Implementations of IPA proxies use a registration mechanism to register themselves with the main IPA proxy factory. This registration declares static objects, causing a risk of things being constructed before the proper libcamera facilities are ready. Since each pipeline handler has its own IPA proxy and knows the type, it isn't necessary to have a proxy factory. Remove it to alleviate the risk of early construction. Signed-off-by: Paul Elder Reviewed-by: Niklas Söderlund --- No change in v3 Changes in v2: - remove documentation --- include/libcamera/internal/ipa_proxy.h | 29 -------- src/libcamera/ipa_proxy.cpp | 93 -------------------------- 2 files changed, 122 deletions(-) diff --git a/include/libcamera/internal/ipa_proxy.h b/include/libcamera/internal/ipa_proxy.h index 1903150e..f651a3ae 100644 --- a/include/libcamera/internal/ipa_proxy.h +++ b/include/libcamera/internal/ipa_proxy.h @@ -36,35 +36,6 @@ private: IPAModule *ipam_; }; -class IPAProxyFactory -{ -public: - IPAProxyFactory(const char *name); - virtual ~IPAProxyFactory() {} - - virtual std::unique_ptr create(IPAModule *ipam, bool isolate) = 0; - - const std::string &name() const { return name_; } - - static void registerType(IPAProxyFactory *factory); - static std::vector &factories(); - -private: - std::string name_; -}; - -#define REGISTER_IPA_PROXY(proxy) \ -class proxy##Factory final : public IPAProxyFactory \ -{ \ -public: \ - proxy##Factory() : IPAProxyFactory(#proxy) {} \ - std::unique_ptr create(IPAModule *ipam, bool isolate) \ - { \ - return std::make_unique(ipam, isolate); \ - } \ -}; \ -static proxy##Factory global_##proxy##Factory; - } /* namespace libcamera */ #endif /* __LIBCAMERA_INTERNAL_IPA_PROXY_H__ */ diff --git a/src/libcamera/ipa_proxy.cpp b/src/libcamera/ipa_proxy.cpp index ee5e6f3e..29c0e9e0 100644 --- a/src/libcamera/ipa_proxy.cpp +++ b/src/libcamera/ipa_proxy.cpp @@ -30,17 +30,11 @@ LOG_DEFINE_CATEGORY(IPAProxy) * \brief IPA Proxy * * Isolate IPA into separate process. - * - * Every subclass of proxy shall be registered with libcamera using - * the REGISTER_IPA_PROXY() macro. */ /** * \brief Construct an IPAProxy instance * \param[in] ipam The IPA module - * - * IPAProxy instances shall be constructed through the IPAProxyFactory::create() - * method implemented by the respective factories. */ IPAProxy::IPAProxy(IPAModule *ipam) : valid_(false), ipam_(ipam) @@ -219,91 +213,4 @@ std::string IPAProxy::resolvePath(const std::string &file) const * construction. */ -/** - * \class IPAProxyFactory - * \brief Registration of IPAProxy classes and creation of instances - * - * To facilitate discovery and instantiation of IPAProxy classes, the - * IPAProxyFactory class maintains a registry of IPAProxy classes. Each - * IPAProxy subclass shall register itself using the REGISTER_IPA_PROXY() - * macro, which will create a corresponding instance of a IPAProxyFactory - * subclass and register it with the static list of factories. - */ - -/** - * \brief Construct a IPAProxy factory - * \param[in] name Name of the IPAProxy class - * - * Creating an instance of the factory registers is with the global list of - * factories, accessible through the factories() function. - * - * The factory \a name is used for debugging and IPAProxy matching purposes - * and shall be unique. - */ -IPAProxyFactory::IPAProxyFactory(const char *name) - : name_(name) -{ - registerType(this); -} - -/** - * \fn IPAProxyFactory::create() - * \brief Create an instance of the IPAProxy corresponding to the factory - * \param[in] ipam The IPA module - * \param[in] isolate Flag to isolate the IPA module - * - * This virtual function is implemented by the REGISTER_IPA_PROXY() macro. - * It creates a IPAProxy instance that isolates an IPA interface designated - * by the IPA module \a ipam. If \a isolate is true, then the IPA module will - * be isolated. - * - * \return A pointer to a newly constructed instance of the IPAProxy subclass - * corresponding to the factory - */ - -/** - * \fn IPAProxyFactory::name() - * \brief Retrieve the factory name - * \return The factory name - */ - -/** - * \brief Add a IPAProxy class to the registry - * \param[in] factory Factory to use to construct the IPAProxy - * - * The caller is responsible to guarantee the uniqueness of the IPAProxy name. - */ -void IPAProxyFactory::registerType(IPAProxyFactory *factory) -{ - std::vector &factories = IPAProxyFactory::factories(); - - factories.push_back(factory); - - LOG(IPAProxy, Debug) - << "Registered proxy \"" << factory->name() << "\""; -} - -/** - * \brief Retrieve the list of all IPAProxy factories - * - * The static factories map is defined inside the function to ensure it gets - * initialized on first use, without any dependency on link order. - * - * \return The list of pipeline handler factories - */ -std::vector &IPAProxyFactory::factories() -{ - static std::vector factories; - return factories; -} - -/** - * \def REGISTER_IPA_PROXY - * \brief Register a IPAProxy with the IPAProxy factory - * \param[in] proxy Class name of IPAProxy derived class to register - * - * Register a proxy subclass with the factory and make it available to - * isolate IPA modules. - */ - } /* namespace libcamera */ From patchwork Fri Oct 2 14:31:48 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9940 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 0133EC3B5C for ; Fri, 2 Oct 2020 14:33:25 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id C4E1563BE6; Fri, 2 Oct 2020 16:33:24 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="h+dXY9R4"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A26F363B98 for ; Fri, 2 Oct 2020 16:33:23 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 8E5B42A2; Fri, 2 Oct 2020 16:33:21 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649203; bh=LtmQawoyLfRlD7u//6P2ZybnBD1vjVKoLvOIpdB/3Gg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=h+dXY9R4kFz8L6gA9L9DTecQbXXB56dcBtb2QKaB66Hbx+J1/EVt5bt3L5+5rq6/Z lD+YdL1hUvraDtvlZOvRalYysKkTsLbl4TtnuOZwPAnc7xNXUp9AbmHbl+Onwy7QDC GzO2r7951y2je0k000DGixnROeEUJJbAqW7XGo5k= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:48 +0900 Message-Id: <20201002143154.468162-33-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 32/38] libcamera: proxy: Remove IPAProxyLinux and IPAProxyThread 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 have now changed the proxy from per-IPC mechanism to per-pipeline. The per-IPC mechanism proxies are thus no longer needed; remove them. Signed-off-by: Paul Elder Reviewed-by: Niklas Söderlund --- No change in v3 No change in v2 --- src/libcamera/proxy/ipa_proxy_linux.cpp | 103 ----------- src/libcamera/proxy/ipa_proxy_thread.cpp | 172 ------------------ src/libcamera/proxy/meson.build | 5 - .../proxy/worker/ipa_proxy_linux_worker.cpp | 90 --------- src/libcamera/proxy/worker/meson.build | 4 - 5 files changed, 374 deletions(-) delete mode 100644 src/libcamera/proxy/ipa_proxy_linux.cpp delete mode 100644 src/libcamera/proxy/ipa_proxy_thread.cpp delete mode 100644 src/libcamera/proxy/worker/ipa_proxy_linux_worker.cpp diff --git a/src/libcamera/proxy/ipa_proxy_linux.cpp b/src/libcamera/proxy/ipa_proxy_linux.cpp deleted file mode 100644 index b78a0e45..00000000 --- a/src/libcamera/proxy/ipa_proxy_linux.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * ipa_proxy_linux.cpp - Default Image Processing Algorithm proxy for Linux - */ - -#include - -#include -#include - -#include "libcamera/internal/ipa_module.h" -#include "libcamera/internal/ipa_proxy.h" -#include "libcamera/internal/ipc_unixsocket.h" -#include "libcamera/internal/log.h" -#include "libcamera/internal/process.h" - -namespace libcamera { - -LOG_DECLARE_CATEGORY(IPAProxy) - -class IPAProxyLinux : public IPAProxy -{ -public: - IPAProxyLinux(IPAModule *ipam); - ~IPAProxyLinux(); - - int init([[maybe_unused]] const IPASettings &settings) override - { - return 0; - } - int start() override { return 0; } - void stop() override {} - void configure([[maybe_unused]] const CameraSensorInfo &sensorInfo, - [[maybe_unused]] const std::map &streamConfig, - [[maybe_unused]] const std::map &entityControls, - [[maybe_unused]] const IPAOperationData &ipaConfig, - [[maybe_unused]] IPAOperationData *result) override {} - void mapBuffers([[maybe_unused]] const std::vector &buffers) override {} - void unmapBuffers([[maybe_unused]] const std::vector &ids) override {} - void processEvent([[maybe_unused]] const IPAOperationData &event) override {} - -private: - void readyRead(IPCUnixSocket *ipc); - - Process *proc_; - - IPCUnixSocket *socket_; -}; - -IPAProxyLinux::IPAProxyLinux(IPAModule *ipam) - : IPAProxy(ipam), proc_(nullptr), socket_(nullptr) -{ - LOG(IPAProxy, Debug) - << "initializing dummy proxy: loading IPA from " - << ipam->path(); - - std::vector fds; - std::vector args; - args.push_back(ipam->path()); - const std::string path = resolvePath("ipa_proxy_linux"); - if (path.empty()) { - LOG(IPAProxy, Error) - << "Failed to get proxy worker path"; - return; - } - - socket_ = new IPCUnixSocket(); - int fd = socket_->create(); - if (fd < 0) { - LOG(IPAProxy, Error) - << "Failed to create socket"; - return; - } - socket_->readyRead.connect(this, &IPAProxyLinux::readyRead); - args.push_back(std::to_string(fd)); - fds.push_back(fd); - - proc_ = new Process(); - int ret = proc_->start(path, args, fds); - if (ret) { - LOG(IPAProxy, Error) - << "Failed to start proxy worker process"; - return; - } - - valid_ = true; -} - -IPAProxyLinux::~IPAProxyLinux() -{ - delete proc_; - delete socket_; -} - -void IPAProxyLinux::readyRead([[maybe_unused]] IPCUnixSocket *ipc) -{ -} - -REGISTER_IPA_PROXY(IPAProxyLinux) - -} /* namespace libcamera */ diff --git a/src/libcamera/proxy/ipa_proxy_thread.cpp b/src/libcamera/proxy/ipa_proxy_thread.cpp deleted file mode 100644 index eead2883..00000000 --- a/src/libcamera/proxy/ipa_proxy_thread.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2020, Google Inc. - * - * ipa_proxy_thread.cpp - Proxy running an Image Processing Algorithm in a thread - */ - -#include - -#include -#include - -#include "libcamera/internal/ipa_context_wrapper.h" -#include "libcamera/internal/ipa_module.h" -#include "libcamera/internal/ipa_proxy.h" -#include "libcamera/internal/log.h" -#include "libcamera/internal/thread.h" - -namespace libcamera { - -LOG_DECLARE_CATEGORY(IPAProxy) - -class IPAProxyThread : public IPAProxy, public Object -{ -public: - IPAProxyThread(IPAModule *ipam); - - int init(const IPASettings &settings) override; - int start() override; - void stop() override; - - void configure(const CameraSensorInfo &sensorInfo, - const std::map &streamConfig, - const std::map &entityControls, - const IPAOperationData &ipaConfig, - IPAOperationData *result) override; - void mapBuffers(const std::vector &buffers) override; - void unmapBuffers(const std::vector &ids) override; - void processEvent(const IPAOperationData &event) override; - -private: - void queueFrameAction(unsigned int frame, const IPAOperationData &data); - - /* Helper class to invoke processEvent() in another thread. */ - class ThreadProxy : public Object - { - public: - void setIPA(IPAInterface *ipa) - { - ipa_ = ipa; - } - - int start() - { - return ipa_->start(); - } - - void stop() - { - ipa_->stop(); - } - - void processEvent(const IPAOperationData &event) - { - ipa_->processEvent(event); - } - - private: - IPAInterface *ipa_; - }; - - bool running_; - Thread thread_; - ThreadProxy proxy_; - std::unique_ptr ipa_; -}; - -IPAProxyThread::IPAProxyThread(IPAModule *ipam) - : IPAProxy(ipam), running_(false) -{ - if (!ipam->load()) - return; - - struct ipa_context *ctx = ipam->createContext(); - if (!ctx) { - LOG(IPAProxy, Error) - << "Failed to create IPA context for " << ipam->path(); - return; - } - - ipa_ = std::make_unique(ctx); - proxy_.setIPA(ipa_.get()); - - /* - * Proxy the queueFrameAction signal to dispatch it in the caller's - * thread. - */ - ipa_->queueFrameAction.connect(this, &IPAProxyThread::queueFrameAction); - - valid_ = true; -} - -int IPAProxyThread::init(const IPASettings &settings) -{ - int ret = ipa_->init(settings); - if (ret) - return ret; - - proxy_.moveToThread(&thread_); - - return 0; -} - -int IPAProxyThread::start() -{ - running_ = true; - thread_.start(); - - return proxy_.invokeMethod(&ThreadProxy::start, ConnectionTypeBlocking); -} - -void IPAProxyThread::stop() -{ - if (!running_) - return; - - running_ = false; - - proxy_.invokeMethod(&ThreadProxy::stop, ConnectionTypeBlocking); - - thread_.exit(); - thread_.wait(); -} - -void IPAProxyThread::configure(const CameraSensorInfo &sensorInfo, - const std::map &streamConfig, - const std::map &entityControls, - const IPAOperationData &ipaConfig, - IPAOperationData *result) -{ - ipa_->configure(sensorInfo, streamConfig, entityControls, ipaConfig, - result); -} - -void IPAProxyThread::mapBuffers(const std::vector &buffers) -{ - ipa_->mapBuffers(buffers); -} - -void IPAProxyThread::unmapBuffers(const std::vector &ids) -{ - ipa_->unmapBuffers(ids); -} - -void IPAProxyThread::processEvent(const IPAOperationData &event) -{ - if (!running_) - return; - - /* Dispatch the processEvent() call to the thread. */ - proxy_.invokeMethod(&ThreadProxy::processEvent, ConnectionTypeQueued, - event); -} - -void IPAProxyThread::queueFrameAction(unsigned int frame, const IPAOperationData &data) -{ - IPAInterface::queueFrameAction.emit(frame, data); -} - -REGISTER_IPA_PROXY(IPAProxyThread) - -} /* namespace libcamera */ diff --git a/src/libcamera/proxy/meson.build b/src/libcamera/proxy/meson.build index f27b453a..8ad78115 100644 --- a/src/libcamera/proxy/meson.build +++ b/src/libcamera/proxy/meson.build @@ -1,10 +1,5 @@ # SPDX-License-Identifier: CC0-1.0 -libcamera_sources += files([ - 'ipa_proxy_linux.cpp', - 'ipa_proxy_thread.cpp', -]) - # generate ipa_proxy_{pipeline}.cpp foreach mojom : ipa_mojoms proxy = custom_target(mojom['name'] + '_proxy_cpp', diff --git a/src/libcamera/proxy/worker/ipa_proxy_linux_worker.cpp b/src/libcamera/proxy/worker/ipa_proxy_linux_worker.cpp deleted file mode 100644 index 0c4687f7..00000000 --- a/src/libcamera/proxy/worker/ipa_proxy_linux_worker.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * ipa_proxy_linux_worker.cpp - Default Image Processing Algorithm proxy worker for Linux - */ - -#include -#include -#include - -#include -#include -#include - -#include "libcamera/internal/ipa_module.h" -#include "libcamera/internal/ipc_unixsocket.h" -#include "libcamera/internal/log.h" -#include "libcamera/internal/thread.h" - -using namespace libcamera; - -LOG_DEFINE_CATEGORY(IPAProxyLinuxWorker) - -void readyRead(IPCUnixSocket *ipc) -{ - IPCUnixSocket::Payload message; - int ret; - - ret = ipc->receive(&message); - if (ret) { - LOG(IPAProxyLinuxWorker, Error) - << "Receive message failed: " << ret; - return; - } - - LOG(IPAProxyLinuxWorker, Debug) << "Received a message!"; -} - -int main(int argc, char **argv) -{ - /* Uncomment this for debugging. */ -#if 0 - std::string logPath = "/tmp/libcamera.worker." + - std::to_string(getpid()) + ".log"; - logSetFile(logPath.c_str()); -#endif - - if (argc < 3) { - LOG(IPAProxyLinuxWorker, Debug) - << "Tried to start worker with no args"; - return EXIT_FAILURE; - } - - int fd = std::stoi(argv[2]); - LOG(IPAProxyLinuxWorker, Debug) - << "Starting worker for IPA module " << argv[1] - << " with IPC fd = " << fd; - - std::unique_ptr ipam = std::make_unique(argv[1]); - if (!ipam->isValid() || !ipam->load()) { - LOG(IPAProxyLinuxWorker, Error) - << "IPAModule " << argv[1] << " should be valid but isn't"; - return EXIT_FAILURE; - } - - IPCUnixSocket socket; - if (socket.bind(fd) < 0) { - LOG(IPAProxyLinuxWorker, Error) << "IPC socket binding failed"; - return EXIT_FAILURE; - } - socket.readyRead.connect(&readyRead); - - struct ipa_context *ipac = ipam->createContext(); - if (!ipac) { - LOG(IPAProxyLinuxWorker, Error) << "Failed to create IPA context"; - return EXIT_FAILURE; - } - - LOG(IPAProxyLinuxWorker, Debug) << "Proxy worker successfully started"; - - /* \todo upgrade listening loop */ - EventDispatcher *dispatcher = Thread::current()->eventDispatcher(); - while (1) - dispatcher->processEvents(); - - ipac->ops->destroy(ipac); - - return 0; -} diff --git a/src/libcamera/proxy/worker/meson.build b/src/libcamera/proxy/worker/meson.build index 353e5cf1..6279fe00 100644 --- a/src/libcamera/proxy/worker/meson.build +++ b/src/libcamera/proxy/worker/meson.build @@ -1,9 +1,5 @@ # SPDX-License-Identifier: CC0-1.0 -ipa_proxy_sources = [ - ['ipa_proxy_linux', 'ipa_proxy_linux_worker.cpp'] -] - proxy_install_dir = join_paths(get_option('libexecdir'), 'libcamera') # generate ipa_proxy_{pipeline}_worker.cpp From patchwork Fri Oct 2 14:31:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9941 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 7856FC3B5C for ; Fri, 2 Oct 2020 14:33:27 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 44EA463BCA; Fri, 2 Oct 2020 16:33:27 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="FIhmPEUx"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 371A263B98 for ; Fri, 2 Oct 2020 16:33:26 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 22E43FD1; Fri, 2 Oct 2020 16:33:23 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649205; bh=CWvGQOwsETu2PiugNCLEbxflcHtP4qbRCth8pLez6yc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FIhmPEUxXHTF6JaNJHPbdZFOxZPFtJSSgsKDJfDuzDGq5d4TA/XE/0oU6+MQuQmRF JMJ5bWK8Ps8mqfpbnCo+IjOXgjA48pcc0+M57QsJu/jVUnE2/MgvXdKsUP8uHcZnZ9 QduEG/nH4z7TjNJuONqVINBgJnY3au5JsfO5jsSQ= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:49 +0900 Message-Id: <20201002143154.468162-34-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 33/38] ipa: remove libipa 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" As every pipeline and have its own proxy, IPAInterfaceWrapper is no longer necessary. Since it's the only member of libipa, remove libipa completely. Signed-off-by: Paul Elder Reviewed-by: Niklas Söderlund --- No change in v3 No change in v2 --- Documentation/Doxyfile.in | 1 - Documentation/meson.build | 2 - src/ipa/libipa/ipa_interface_wrapper.cpp | 285 ----------------------- src/ipa/libipa/ipa_interface_wrapper.h | 61 ----- src/ipa/libipa/meson.build | 15 -- src/ipa/meson.build | 2 - src/ipa/raspberrypi/meson.build | 2 - src/ipa/raspberrypi/raspberrypi.cpp | 2 - src/ipa/rkisp1/meson.build | 3 +- src/ipa/rkisp1/rkisp1.cpp | 2 - src/ipa/vimc/meson.build | 3 +- src/ipa/vimc/vimc.cpp | 2 - test/ipa/ipa_wrappers_test.cpp | 1 - test/ipa/meson.build | 4 +- 14 files changed, 4 insertions(+), 381 deletions(-) delete mode 100644 src/ipa/libipa/ipa_interface_wrapper.cpp delete mode 100644 src/ipa/libipa/ipa_interface_wrapper.h delete mode 100644 src/ipa/libipa/meson.build diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in index 71509ff7..5ccd0d05 100644 --- a/Documentation/Doxyfile.in +++ b/Documentation/Doxyfile.in @@ -789,7 +789,6 @@ WARN_LOGFILE = # Note: If this tag is empty the current directory is searched. INPUT = "@TOP_SRCDIR@/include/libcamera" \ - "@TOP_SRCDIR@/src/ipa/libipa" \ "@TOP_SRCDIR@/src/libcamera" \ "@TOP_BUILDDIR@/include/libcamera" \ "@TOP_BUILDDIR@/src/libcamera" diff --git a/Documentation/meson.build b/Documentation/meson.build index d3d64f71..7d850cd8 100644 --- a/Documentation/meson.build +++ b/Documentation/meson.build @@ -27,8 +27,6 @@ if doxygen.found() and dot.found() libcamera_ipa_headers, libcamera_public_headers, libcamera_sources, - libipa_headers, - libipa_sources, ], output : 'api-html', command : [doxygen, doxyfile], diff --git a/src/ipa/libipa/ipa_interface_wrapper.cpp b/src/ipa/libipa/ipa_interface_wrapper.cpp deleted file mode 100644 index cee532e3..00000000 --- a/src/ipa/libipa/ipa_interface_wrapper.cpp +++ /dev/null @@ -1,285 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * ipa_interface_wrapper.cpp - Image Processing Algorithm interface wrapper - */ - -#include "ipa_interface_wrapper.h" - -#include -#include -#include -#include - -#include - -#include "libcamera/internal/byte_stream_buffer.h" -#include "libcamera/internal/camera_sensor.h" - -/** - * \file ipa_interface_wrapper.h - * \brief Image Processing Algorithm interface wrapper - */ - -namespace libcamera { - -/** - * \class IPAInterfaceWrapper - * \brief Wrap an IPAInterface and expose it as an ipa_context - * - * This class implements the ipa_context API based on a provided IPAInterface. - * It helps IPAs that implement the IPAInterface API to provide the external - * ipa_context API. - * - * To use the wrapper, an IPA module simple creates a new instance of its - * IPAInterface implementation, and passes it to the constructor of the - * IPAInterfaceWrapper. As IPAInterfaceWrapper inherits from ipa_context, the - * constructed wrapper can then be directly returned from the IPA module's - * ipaCreate() function. - * - * \code{.cpp} - * class MyIPA : public IPAInterface - * { - * ... - * }; - * - * struct ipa_context *ipaCreate() - * { - * return new IPAInterfaceWrapper(std::make_unique()); - * } - * \endcode - * - * The wrapper takes ownership of the IPAInterface and will automatically - * delete it when the wrapper is destroyed. - */ - -/** - * \brief Construct an IPAInterfaceWrapper wrapping \a interface - * \param[in] interface The interface to wrap - */ -IPAInterfaceWrapper::IPAInterfaceWrapper(std::unique_ptr interface) - : ipa_(std::move(interface)), callbacks_(nullptr), cb_ctx_(nullptr) -{ - ops = &operations_; - - ipa_->queueFrameAction.connect(this, &IPAInterfaceWrapper::queueFrameAction); -} - -void IPAInterfaceWrapper::destroy(struct ipa_context *_ctx) -{ - IPAInterfaceWrapper *ctx = static_cast(_ctx); - - delete ctx; -} - -void *IPAInterfaceWrapper::get_interface(struct ipa_context *_ctx) -{ - IPAInterfaceWrapper *ctx = static_cast(_ctx); - - return ctx->ipa_.get(); -} - -void IPAInterfaceWrapper::init(struct ipa_context *_ctx, - const struct ipa_settings *settings) -{ - IPAInterfaceWrapper *ctx = static_cast(_ctx); - - IPASettings ipaSettings{ - .configurationFile = settings->configuration_file - }; - ctx->ipa_->init(ipaSettings); -} - -int IPAInterfaceWrapper::start(struct ipa_context *_ctx) -{ - IPAInterfaceWrapper *ctx = static_cast(_ctx); - - return ctx->ipa_->start(); -} - -void IPAInterfaceWrapper::stop(struct ipa_context *_ctx) -{ - IPAInterfaceWrapper *ctx = static_cast(_ctx); - - ctx->ipa_->stop(); -} - -void IPAInterfaceWrapper::register_callbacks(struct ipa_context *_ctx, - const struct ipa_callback_ops *callbacks, - void *cb_ctx) -{ - IPAInterfaceWrapper *ctx = static_cast(_ctx); - - ctx->callbacks_ = callbacks; - ctx->cb_ctx_ = cb_ctx; -} - -void IPAInterfaceWrapper::configure(struct ipa_context *_ctx, - const struct ipa_sensor_info *sensor_info, - const struct ipa_stream *streams, - unsigned int num_streams, - const struct ipa_control_info_map *maps, - unsigned int num_maps) -{ - IPAInterfaceWrapper *ctx = static_cast(_ctx); - - ctx->serializer_.reset(); - - /* Translate the IPA sensor info. */ - CameraSensorInfo sensorInfo{}; - sensorInfo.model = sensor_info->model; - sensorInfo.bitsPerPixel = sensor_info->bits_per_pixel; - sensorInfo.activeAreaSize = { sensor_info->active_area.width, - sensor_info->active_area.height }; - sensorInfo.analogCrop = { sensor_info->analog_crop.left, - sensor_info->analog_crop.top, - sensor_info->analog_crop.width, - sensor_info->analog_crop.height }; - sensorInfo.outputSize = { sensor_info->output_size.width, - sensor_info->output_size.height }; - sensorInfo.pixelRate = sensor_info->pixel_rate; - sensorInfo.lineLength = sensor_info->line_length; - - /* Translate the IPA stream configurations map. */ - std::map ipaStreams; - - for (unsigned int i = 0; i < num_streams; ++i) { - const struct ipa_stream &stream = streams[i]; - - ipaStreams[stream.id] = { - stream.pixel_format, - Size(stream.width, stream.height), - }; - } - - /* Translate the IPA entity controls map. */ - std::map entityControls; - std::map infoMaps; - - for (unsigned int i = 0; i < num_maps; ++i) { - const struct ipa_control_info_map &ipa_map = maps[i]; - ByteStreamBuffer byteStream(ipa_map.data, ipa_map.size); - unsigned int id = ipa_map.id; - - infoMaps[id] = ctx->serializer_.deserialize(byteStream); - entityControls.emplace(id, infoMaps[id]); - } - - /* \todo Translate the ipaConfig and result. */ - IPAOperationData ipaConfig; - ctx->ipa_->configure(sensorInfo, ipaStreams, entityControls, ipaConfig, - nullptr); -} - -void IPAInterfaceWrapper::map_buffers(struct ipa_context *_ctx, - const struct ipa_buffer *_buffers, - size_t num_buffers) -{ - IPAInterfaceWrapper *ctx = static_cast(_ctx); - std::vector buffers(num_buffers); - - for (unsigned int i = 0; i < num_buffers; ++i) { - const struct ipa_buffer &_buffer = _buffers[i]; - IPABuffer &buffer = buffers[i]; - std::vector &planes = buffer.planes; - - buffer.id = _buffer.id; - - planes.resize(_buffer.num_planes); - for (unsigned int j = 0; j < _buffer.num_planes; ++j) { - planes[j].fd = FileDescriptor(_buffer.planes[j].dmabuf); - planes[j].length = _buffer.planes[j].length; - } - } - - ctx->ipa_->mapBuffers(buffers); -} - -void IPAInterfaceWrapper::unmap_buffers(struct ipa_context *_ctx, - const unsigned int *_ids, - size_t num_buffers) -{ - IPAInterfaceWrapper *ctx = static_cast(_ctx); - std::vector ids(_ids, _ids + num_buffers); - ctx->ipa_->unmapBuffers(ids); -} - -void IPAInterfaceWrapper::process_event(struct ipa_context *_ctx, - const struct ipa_operation_data *data) -{ - IPAInterfaceWrapper *ctx = static_cast(_ctx); - IPAOperationData opData; - - opData.operation = data->operation; - - opData.data.resize(data->num_data); - memcpy(opData.data.data(), data->data, - data->num_data * sizeof(*data->data)); - - opData.controls.resize(data->num_lists); - for (unsigned int i = 0; i < data->num_lists; ++i) { - const struct ipa_control_list *c_list = &data->lists[i]; - ByteStreamBuffer byteStream(c_list->data, c_list->size); - opData.controls[i] = ctx->serializer_.deserialize(byteStream); - } - - ctx->ipa_->processEvent(opData); -} - -void IPAInterfaceWrapper::queueFrameAction(unsigned int frame, - const IPAOperationData &data) -{ - if (!callbacks_) - return; - - struct ipa_operation_data c_data; - c_data.operation = data.operation; - c_data.data = data.data.data(); - c_data.num_data = data.data.size(); - - struct ipa_control_list control_lists[data.controls.size()]; - c_data.lists = control_lists; - c_data.num_lists = data.controls.size(); - - std::size_t listsSize = 0; - for (const auto &list : data.controls) - listsSize += serializer_.binarySize(list); - - std::vector binaryData(listsSize); - ByteStreamBuffer byteStreamBuffer(binaryData.data(), listsSize); - - unsigned int i = 0; - for (const auto &list : data.controls) { - struct ipa_control_list &c_list = control_lists[i]; - c_list.size = serializer_.binarySize(list); - - ByteStreamBuffer b = byteStreamBuffer.carveOut(c_list.size); - serializer_.serialize(list, b); - - c_list.data = b.base(); - } - - callbacks_->queue_frame_action(cb_ctx_, frame, c_data); -} - -#ifndef __DOXYGEN__ -/* - * This construct confuses Doygen and makes it believe that all members of the - * operations is a member of IPAInterfaceWrapper. It must thus be hidden. - */ -const struct ipa_context_ops IPAInterfaceWrapper::operations_ = { - .destroy = &IPAInterfaceWrapper::destroy, - .get_interface = &IPAInterfaceWrapper::get_interface, - .init = &IPAInterfaceWrapper::init, - .start = &IPAInterfaceWrapper::start, - .stop = &IPAInterfaceWrapper::stop, - .register_callbacks = &IPAInterfaceWrapper::register_callbacks, - .configure = &IPAInterfaceWrapper::configure, - .map_buffers = &IPAInterfaceWrapper::map_buffers, - .unmap_buffers = &IPAInterfaceWrapper::unmap_buffers, - .process_event = &IPAInterfaceWrapper::process_event, -}; -#endif - -} /* namespace libcamera */ diff --git a/src/ipa/libipa/ipa_interface_wrapper.h b/src/ipa/libipa/ipa_interface_wrapper.h deleted file mode 100644 index a1c70159..00000000 --- a/src/ipa/libipa/ipa_interface_wrapper.h +++ /dev/null @@ -1,61 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * ipa_interface_wrapper.h - Image Processing Algorithm interface wrapper - */ -#ifndef __LIBCAMERA_IPA_INTERFACE_WRAPPER_H__ -#define __LIBCAMERA_IPA_INTERFACE_WRAPPER_H__ - -#include - -#include - -#include "libcamera/internal/control_serializer.h" - -namespace libcamera { - -class IPAInterfaceWrapper : public ipa_context -{ -public: - IPAInterfaceWrapper(std::unique_ptr interface); - -private: - static void destroy(struct ipa_context *ctx); - static void *get_interface(struct ipa_context *ctx); - static void init(struct ipa_context *ctx, - const struct ipa_settings *settings); - static int start(struct ipa_context *ctx); - static void stop(struct ipa_context *ctx); - static void register_callbacks(struct ipa_context *ctx, - const struct ipa_callback_ops *callbacks, - void *cb_ctx); - static void configure(struct ipa_context *ctx, - const struct ipa_sensor_info *sensor_info, - const struct ipa_stream *streams, - unsigned int num_streams, - const struct ipa_control_info_map *maps, - unsigned int num_maps); - static void map_buffers(struct ipa_context *ctx, - const struct ipa_buffer *c_buffers, - size_t num_buffers); - static void unmap_buffers(struct ipa_context *ctx, - const unsigned int *ids, - size_t num_buffers); - static void process_event(struct ipa_context *ctx, - const struct ipa_operation_data *data); - - static const struct ipa_context_ops operations_; - - void queueFrameAction(unsigned int frame, const IPAOperationData &data); - - std::unique_ptr ipa_; - const struct ipa_callback_ops *callbacks_; - void *cb_ctx_; - - ControlSerializer serializer_; -}; - -} /* namespace libcamera */ - -#endif /* __LIBCAMERA_IPA_INTERFACE_WRAPPER_H__ */ diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build deleted file mode 100644 index 22626405..00000000 --- a/src/ipa/libipa/meson.build +++ /dev/null @@ -1,15 +0,0 @@ -# SPDX-License-Identifier: CC0-1.0 - -libipa_headers = files([ - 'ipa_interface_wrapper.h', -]) - -libipa_sources = files([ - 'ipa_interface_wrapper.cpp', -]) - -libipa_includes = include_directories('..') - -libipa = static_library('ipa', libipa_sources, - include_directories : ipa_includes, - dependencies : libcamera_dep) diff --git a/src/ipa/meson.build b/src/ipa/meson.build index 5a5de267..b11e5d23 100644 --- a/src/ipa/meson.build +++ b/src/ipa/meson.build @@ -15,8 +15,6 @@ config_h.set('IPA_CONFIG_DIR', config_h.set('IPA_MODULE_DIR', '"' + join_paths(get_option('prefix'), ipa_install_dir) + '"') -subdir('libipa') - ipa_sign = files('ipa-sign.sh') ipas = ['raspberrypi', 'rkisp1', 'vimc'] diff --git a/src/ipa/raspberrypi/meson.build b/src/ipa/raspberrypi/meson.build index 27c29249..57596166 100644 --- a/src/ipa/raspberrypi/meson.build +++ b/src/ipa/raspberrypi/meson.build @@ -10,7 +10,6 @@ rpi_ipa_deps = [ rpi_ipa_includes = [ ipa_includes, - libipa_includes, include_directories('controller') ] @@ -46,7 +45,6 @@ mod = shared_module(ipa_name, name_prefix : '', include_directories : rpi_ipa_includes, dependencies : rpi_ipa_deps, - link_with : libipa, install : true, install_dir : ipa_install_dir) diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index 0e23a476..fcaf9b6c 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -23,8 +23,6 @@ #include #include -#include - #include "libcamera/internal/camera_sensor.h" #include "libcamera/internal/log.h" #include "libcamera/internal/utils.h" diff --git a/src/ipa/rkisp1/meson.build b/src/ipa/rkisp1/meson.build index 30953c13..f0f0a1a2 100644 --- a/src/ipa/rkisp1/meson.build +++ b/src/ipa/rkisp1/meson.build @@ -5,9 +5,8 @@ ipa_name = 'ipa_rkisp1' mod = shared_module(ipa_name, ['rkisp1.cpp', libcamera_generated_headers], name_prefix : '', - include_directories : [ipa_includes, libipa_includes], + include_directories : ipa_includes, dependencies : libcamera_dep, - link_with : libipa, install : true, install_dir : ipa_install_dir) diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp index 4fec397b..89a24e5b 100644 --- a/src/ipa/rkisp1/rkisp1.cpp +++ b/src/ipa/rkisp1/rkisp1.cpp @@ -23,8 +23,6 @@ #include #include -#include - #include "libcamera/internal/log.h" namespace libcamera { diff --git a/src/ipa/vimc/meson.build b/src/ipa/vimc/meson.build index b2b7a09d..c9cbdf57 100644 --- a/src/ipa/vimc/meson.build +++ b/src/ipa/vimc/meson.build @@ -5,9 +5,8 @@ ipa_name = 'ipa_vimc' mod = shared_module(ipa_name, ['vimc.cpp', libcamera_generated_headers], name_prefix : '', - include_directories : [ipa_includes, libipa_includes], + include_directories : ipa_includes, dependencies : libcamera_dep, - link_with : libipa, install : true, install_dir : ipa_install_dir) diff --git a/src/ipa/vimc/vimc.cpp b/src/ipa/vimc/vimc.cpp index 00ec7b80..1693ffac 100644 --- a/src/ipa/vimc/vimc.cpp +++ b/src/ipa/vimc/vimc.cpp @@ -18,8 +18,6 @@ #include #include -#include - #include "libcamera/internal/file.h" #include "libcamera/internal/log.h" diff --git a/test/ipa/ipa_wrappers_test.cpp b/test/ipa/ipa_wrappers_test.cpp index 59d991cb..0133d8d2 100644 --- a/test/ipa/ipa_wrappers_test.cpp +++ b/test/ipa/ipa_wrappers_test.cpp @@ -13,7 +13,6 @@ #include #include -#include #include "libcamera/internal/camera_sensor.h" #include "libcamera/internal/device_enumerator.h" diff --git a/test/ipa/meson.build b/test/ipa/meson.build index ba672f3f..b25bfcf4 100644 --- a/test/ipa/meson.build +++ b/test/ipa/meson.build @@ -9,8 +9,8 @@ ipa_test = [ foreach t : ipa_test exe = executable(t[0], t[1], dependencies : libcamera_dep, - link_with : [libipa, test_libraries], - include_directories : [libipa_includes, test_includes_internal]) + link_with : test_libraries, + include_directories : test_includes_internal) test(t[0], exe, suite : 'ipa') endforeach From patchwork Fri Oct 2 14:31:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9942 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 CB7C4C3B5C for ; Fri, 2 Oct 2020 14:33:29 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 9CCAA63BE8; Fri, 2 Oct 2020 16:33:29 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="LryNyTSF"; 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 5B1FE63B98 for ; Fri, 2 Oct 2020 16:33:28 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 92673528; Fri, 2 Oct 2020 16:33:26 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649208; bh=H2JIAoSUznJO114Svyw/DBrw9YK4KvJqqZMj1g8baek=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LryNyTSFK6lj92IIxviRDFfKWzn50dXHW2prmSm3nIYI1Fpj0juldeGV1+Wt/Nzma CziDFGAN/2kPAqMELVMo1+mzO6cfH4oa2o0M2X38QHWnth8ywluA2Zk4zvnt5l9vxZ DEhnnRbbzJPlfInwM/82wDc74IbEMWMJgXAmm2XA= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:50 +0900 Message-Id: <20201002143154.468162-35-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 34/38] tests: ipa_interface_test: Update to use new createIPA 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" Update the IPA interface test to use the new createIPA function from IPAManager. Also declare ProcessManager, as it is not longer globally defined. Update meson.build to depend on the generated IPA interface headers. Signed-off-by: Paul Elder --- Changes in v3: - declare ProcessManager - add libcamera_generated_headers as dependency to meson - otherwise test might build before the generated IPA headers and #include will fail New in v2 --- test/ipa/ipa_interface_test.cpp | 10 +++++++--- test/ipa/meson.build | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/test/ipa/ipa_interface_test.cpp b/test/ipa/ipa_interface_test.cpp index 1bc93a63..db95a9e5 100644 --- a/test/ipa/ipa_interface_test.cpp +++ b/test/ipa/ipa_interface_test.cpp @@ -14,13 +14,15 @@ #include #include -#include +#include +#include #include #include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/ipa_manager.h" #include "libcamera/internal/ipa_module.h" #include "libcamera/internal/pipeline_handler.h" +#include "libcamera/internal/process.h" #include "libcamera/internal/thread.h" #include "test.h" @@ -95,7 +97,7 @@ protected: EventDispatcher *dispatcher = thread()->eventDispatcher(); Timer timer; - ipa_ = IPAManager::createIPA(pipe_.get(), 0, 0); + ipa_ = IPAManager::createIPA(pipe_.get(), 0, 0); if (!ipa_) { cerr << "Failed to create VIMC IPA interface" << endl; return TestFail; @@ -163,8 +165,10 @@ private: } } + ProcessManager processManager_; + std::shared_ptr pipe_; - std::unique_ptr ipa_; + std::unique_ptr ipa_; std::unique_ptr ipaManager_; enum IPAOperationCode trace_; EventNotifier *notifier_; diff --git a/test/ipa/meson.build b/test/ipa/meson.build index b25bfcf4..928987db 100644 --- a/test/ipa/meson.build +++ b/test/ipa/meson.build @@ -7,7 +7,7 @@ ipa_test = [ ] foreach t : ipa_test - exe = executable(t[0], t[1], + exe = executable(t[0], [t[1], libcamera_generated_headers], dependencies : libcamera_dep, link_with : test_libraries, include_directories : test_includes_internal) From patchwork Fri Oct 2 14:31:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9943 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 2D055C3B5C for ; Fri, 2 Oct 2020 14:33:32 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id EF3A863BF4; Fri, 2 Oct 2020 16:33:31 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="wp9dfwKo"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 990E263B98 for ; Fri, 2 Oct 2020 16:33:30 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id D064B528; Fri, 2 Oct 2020 16:33:28 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649210; bh=U7XZ6G7+qC01pOIUREiXgFeqLPCwXFU49SMu2lSvSHU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=wp9dfwKo4/sIVPLvJpayItLqDQJ5ZmOhmqyllo5Tiu9jp6IlPyaLW5ceaHaXCQWgj 9h9957CSraHoiMloWeEyd6DFtB/MFfENDG5JaMOuR8lPaYq3cJcyRVnlmx313K7tm/ LZOOsNkGgO48qE/uNDcUjtO49QLJ96DzxkgGdG1c= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:51 +0900 Message-Id: <20201002143154.468162-36-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 35/38] tests: Remove IPA wrappers test 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" Since we no longer use IPA wrappers, remove the test for it. Signed-off-by: Paul Elder --- No change in v3 New in v2 --- test/ipa/ipa_wrappers_test.cpp | 451 --------------------------------- test/ipa/meson.build | 1 - 2 files changed, 452 deletions(-) delete mode 100644 test/ipa/ipa_wrappers_test.cpp diff --git a/test/ipa/ipa_wrappers_test.cpp b/test/ipa/ipa_wrappers_test.cpp deleted file mode 100644 index 0133d8d2..00000000 --- a/test/ipa/ipa_wrappers_test.cpp +++ /dev/null @@ -1,451 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2019, Google Inc. - * - * ipa_wrappers_test.cpp - Test the IPA interface and context wrappers - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include "libcamera/internal/camera_sensor.h" -#include "libcamera/internal/device_enumerator.h" -#include "libcamera/internal/ipa_context_wrapper.h" -#include "libcamera/internal/media_device.h" -#include "libcamera/internal/v4l2_subdevice.h" - -#include "test.h" - -using namespace libcamera; -using namespace std; - -enum Operation { - Op_init, - Op_start, - Op_stop, - Op_configure, - Op_mapBuffers, - Op_unmapBuffers, - Op_processEvent, -}; - -class TestIPAInterface : public IPAInterface -{ -public: - TestIPAInterface() - : sequence_(0) - { - } - - int init(const IPASettings &settings) override - { - if (settings.configurationFile != "/ipa/configuration/file") { - cerr << "init(): Invalid configuration file" << endl; - report(Op_init, TestFail); - return 0; - } - - report(Op_init, TestPass); - return 0; - } - - int start() override - { - report(Op_start, TestPass); - return 0; - } - - void stop() override - { - report(Op_stop, TestPass); - } - - void configure(const CameraSensorInfo &sensorInfo, - const std::map &streamConfig, - const std::map &entityControls, - [[maybe_unused]] const IPAOperationData &ipaConfig, - [[maybe_unused]] IPAOperationData *result) override - { - /* Verify sensorInfo. */ - if (sensorInfo.outputSize.width != 2560 || - sensorInfo.outputSize.height != 1940) { - cerr << "configure(): Invalid sensor info size " - << sensorInfo.outputSize.toString(); - } - - /* Verify streamConfig. */ - if (streamConfig.size() != 2) { - cerr << "configure(): Invalid number of streams " - << streamConfig.size() << endl; - return report(Op_configure, TestFail); - } - - auto iter = streamConfig.find(1); - if (iter == streamConfig.end()) { - cerr << "configure(): No configuration for stream 1" << endl; - return report(Op_configure, TestFail); - } - const IPAStream *stream = &iter->second; - if (stream->pixelFormat != V4L2_PIX_FMT_YUYV || - stream->size != Size{ 1024, 768 }) { - cerr << "configure(): Invalid configuration for stream 1" << endl; - return report(Op_configure, TestFail); - } - - iter = streamConfig.find(2); - if (iter == streamConfig.end()) { - cerr << "configure(): No configuration for stream 2" << endl; - return report(Op_configure, TestFail); - } - stream = &iter->second; - if (stream->pixelFormat != V4L2_PIX_FMT_NV12 || - stream->size != Size{ 800, 600 }) { - cerr << "configure(): Invalid configuration for stream 2" << endl; - return report(Op_configure, TestFail); - } - - /* Verify entityControls. */ - auto ctrlIter = entityControls.find(42); - if (ctrlIter == entityControls.end()) { - cerr << "configure(): Controls not found" << endl; - return report(Op_configure, TestFail); - } - - const ControlInfoMap &infoMap = ctrlIter->second; - - if (infoMap.count(V4L2_CID_BRIGHTNESS) != 1 || - infoMap.count(V4L2_CID_CONTRAST) != 1 || - infoMap.count(V4L2_CID_SATURATION) != 1) { - cerr << "configure(): Invalid control IDs" << endl; - return report(Op_configure, TestFail); - } - - report(Op_configure, TestPass); - } - - void mapBuffers(const std::vector &buffers) override - { - if (buffers.size() != 2) { - cerr << "mapBuffers(): Invalid number of buffers " - << buffers.size() << endl; - return report(Op_mapBuffers, TestFail); - } - - if (buffers[0].id != 10 || - buffers[1].id != 11) { - cerr << "mapBuffers(): Invalid buffer IDs" << endl; - return report(Op_mapBuffers, TestFail); - } - - if (buffers[0].planes.size() != 3 || - buffers[1].planes.size() != 3) { - cerr << "mapBuffers(): Invalid number of planes" << endl; - return report(Op_mapBuffers, TestFail); - } - - if (buffers[0].planes[0].length != 4096 || - buffers[0].planes[1].length != 0 || - buffers[0].planes[2].length != 0 || - buffers[0].planes[0].length != 4096 || - buffers[1].planes[1].length != 4096 || - buffers[1].planes[2].length != 0) { - cerr << "mapBuffers(): Invalid length" << endl; - return report(Op_mapBuffers, TestFail); - } - - if (buffers[0].planes[0].fd.fd() == -1 || - buffers[0].planes[1].fd.fd() != -1 || - buffers[0].planes[2].fd.fd() != -1 || - buffers[0].planes[0].fd.fd() == -1 || - buffers[1].planes[1].fd.fd() == -1 || - buffers[1].planes[2].fd.fd() != -1) { - cerr << "mapBuffers(): Invalid dmabuf" << endl; - return report(Op_mapBuffers, TestFail); - } - - report(Op_mapBuffers, TestPass); - } - - void unmapBuffers(const std::vector &ids) override - { - if (ids.size() != 2) { - cerr << "unmapBuffers(): Invalid number of ids " - << ids.size() << endl; - return report(Op_unmapBuffers, TestFail); - } - - if (ids[0] != 10 || ids[1] != 11) { - cerr << "unmapBuffers(): Invalid buffer IDs" << endl; - return report(Op_unmapBuffers, TestFail); - } - - report(Op_unmapBuffers, TestPass); - } - - void processEvent(const IPAOperationData &data) override - { - /* Verify operation and data. */ - if (data.operation != Op_processEvent) { - cerr << "processEvent(): Invalid operation " - << data.operation << endl; - return report(Op_processEvent, TestFail); - } - - if (data.data != std::vector{ 1, 2, 3, 4 }) { - cerr << "processEvent(): Invalid data" << endl; - return report(Op_processEvent, TestFail); - } - - /* Verify controls. */ - if (data.controls.size() != 1) { - cerr << "processEvent(): Controls not found" << endl; - return report(Op_processEvent, TestFail); - } - - const ControlList &controls = data.controls[0]; - if (controls.get(V4L2_CID_BRIGHTNESS).get() != 10 || - controls.get(V4L2_CID_CONTRAST).get() != 20 || - controls.get(V4L2_CID_SATURATION).get() != 30) { - cerr << "processEvent(): Invalid controls" << endl; - return report(Op_processEvent, TestFail); - } - - report(Op_processEvent, TestPass); - } - -private: - void report(Operation op, int status) - { - IPAOperationData data; - data.operation = op; - data.data.resize(1); - data.data[0] = status; - queueFrameAction.emit(sequence_++, data); - } - - unsigned int sequence_; -}; - -#define INVOKE(method, ...) \ - invoke(&IPAInterface::method, Op_##method, #method, ##__VA_ARGS__) - -class IPAWrappersTest : public Test -{ -public: - IPAWrappersTest() - : subdev_(nullptr), wrapper_(nullptr), sequence_(0), fd_(-1) - { - } - -protected: - int init() override - { - /* Locate the VIMC Sensor B subdevice. */ - enumerator_ = unique_ptr(DeviceEnumerator::create()); - if (!enumerator_) { - cerr << "Failed to create device enumerator" << endl; - return TestFail; - } - - if (enumerator_->enumerate()) { - cerr << "Failed to enumerate media devices" << endl; - return TestFail; - } - - DeviceMatch dm("vimc"); - media_ = enumerator_->search(dm); - if (!media_) { - cerr << "No VIMC media device found: skip test" << endl; - return TestSkip; - } - - MediaEntity *entity = media_->getEntityByName("Sensor A"); - if (!entity) { - cerr << "Unable to find media entity 'Sensor A'" << endl; - return TestFail; - } - - subdev_ = new V4L2Subdevice(entity); - if (subdev_->open() < 0) { - cerr << "Unable to open 'Sensor A' subdevice" << endl; - return TestFail; - } - - /* Force usage of the C API as that's what we want to test. */ - int ret = setenv("LIBCAMERA_IPA_FORCE_C_API", "", 1); - if (ret) - return TestFail; - - std::unique_ptr intf = std::make_unique(); - wrapper_ = new IPAContextWrapper(new IPAInterfaceWrapper(std::move(intf))); - wrapper_->queueFrameAction.connect(this, &IPAWrappersTest::queueFrameAction); - - /* Create a file descriptor for the buffer-related operations. */ - fd_ = open("/tmp", O_TMPFILE | O_RDWR, 0600); - if (fd_ == -1) - return TestFail; - - ret = ftruncate(fd_, 4096); - if (ret < 0) - return TestFail; - - return TestPass; - } - - int run() override - { - int ret; - - /* Test configure(). */ - CameraSensorInfo sensorInfo{ - .model = "sensor", - .bitsPerPixel = 8, - .activeAreaSize = { 2576, 1956 }, - .analogCrop = { 8, 8, 2560, 1940 }, - .outputSize = { 2560, 1940 }, - .pixelRate = 96000000, - .lineLength = 2918, - }; - std::map config{ - { 1, { V4L2_PIX_FMT_YUYV, { 1024, 768 } } }, - { 2, { V4L2_PIX_FMT_NV12, { 800, 600 } } }, - }; - std::map controlInfo; - controlInfo.emplace(42, subdev_->controls()); - IPAOperationData ipaConfig; - ret = INVOKE(configure, sensorInfo, config, controlInfo, - ipaConfig, nullptr); - if (ret == TestFail) - return TestFail; - - /* Test mapBuffers(). */ - std::vector buffers(2); - buffers[0].planes.resize(3); - buffers[0].id = 10; - buffers[0].planes[0].fd = FileDescriptor(fd_); - buffers[0].planes[0].length = 4096; - buffers[1].id = 11; - buffers[1].planes.resize(3); - buffers[1].planes[0].fd = FileDescriptor(fd_); - buffers[1].planes[0].length = 4096; - buffers[1].planes[1].fd = FileDescriptor(fd_); - buffers[1].planes[1].length = 4096; - - ret = INVOKE(mapBuffers, buffers); - if (ret == TestFail) - return TestFail; - - /* Test unmapBuffers(). */ - std::vector bufferIds = { 10, 11 }; - ret = INVOKE(unmapBuffers, bufferIds); - if (ret == TestFail) - return TestFail; - - /* Test processEvent(). */ - IPAOperationData data; - data.operation = Op_processEvent; - data.data = { 1, 2, 3, 4 }; - data.controls.emplace_back(subdev_->controls()); - - ControlList &controls = data.controls.back(); - controls.set(V4L2_CID_BRIGHTNESS, static_cast(10)); - controls.set(V4L2_CID_CONTRAST, static_cast(20)); - controls.set(V4L2_CID_SATURATION, static_cast(30)); - - ret = INVOKE(processEvent, data); - if (ret == TestFail) - return TestFail; - - /* - * Test init(), start() and stop() last to ensure nothing in the - * wrappers or serializer depends on them being called first. - */ - IPASettings settings{ - .configurationFile = "/ipa/configuration/file" - }; - ret = INVOKE(init, settings); - if (ret == TestFail) { - cerr << "Failed to run init()"; - return TestFail; - } - - ret = INVOKE(start); - if (ret == TestFail) { - cerr << "Failed to run start()"; - return TestFail; - } - - ret = INVOKE(stop); - if (ret == TestFail) { - cerr << "Failed to run stop()"; - return TestFail; - } - - return TestPass; - } - - void cleanup() override - { - delete wrapper_; - delete subdev_; - - if (fd_ != -1) - close(fd_); - } - -private: - template - int invoke(T (IPAInterface::*func)(Args1...), Operation op, - const char *name, Args2... args) - { - data_ = IPAOperationData(); - (wrapper_->*func)(args...); - - if (frame_ != sequence_) { - cerr << "IPAInterface::" << name - << "(): invalid frame number " << frame_ - << ", expected " << sequence_; - return TestFail; - } - - sequence_++; - - if (data_.operation != op) { - cerr << "IPAInterface::" << name - << "(): failed to propagate" << endl; - return TestFail; - } - - if (data_.data[0] != TestPass) { - cerr << "IPAInterface::" << name - << "(): reported an error" << endl; - return TestFail; - } - - return TestPass; - } - - void queueFrameAction(unsigned int frame, const IPAOperationData &data) - { - frame_ = frame; - data_ = data; - } - - std::shared_ptr media_; - std::unique_ptr enumerator_; - V4L2Subdevice *subdev_; - - IPAContextWrapper *wrapper_; - IPAOperationData data_; - unsigned int sequence_; - unsigned int frame_; - int fd_; -}; - -TEST_REGISTER(IPAWrappersTest) diff --git a/test/ipa/meson.build b/test/ipa/meson.build index 928987db..e4892fb2 100644 --- a/test/ipa/meson.build +++ b/test/ipa/meson.build @@ -3,7 +3,6 @@ ipa_test = [ ['ipa_module_test', 'ipa_module_test.cpp'], ['ipa_interface_test', 'ipa_interface_test.cpp'], - ['ipa_wrappers_test', 'ipa_wrappers_test.cpp'], ] foreach t : ipa_test From patchwork Fri Oct 2 14:31:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9944 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 B40E2C3B5C for ; Fri, 2 Oct 2020 14:33:34 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 83FDB63BE8; Fri, 2 Oct 2020 16:33:34 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="etmUnyMr"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id CE18D63B98 for ; Fri, 2 Oct 2020 16:33:32 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 19D0A528; Fri, 2 Oct 2020 16:33:30 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649212; bh=wsDwssVOlx+/AHcucNUCp+e5s9UcfWheZg8OfWNb7ds=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=etmUnyMrtUDO2FNNSJoosoEWqLLwcnKUG37iN/Lidhf7RA8qxJuS9b3755bYMURV3 HEH1Ojc/ihOpr802SWEPDt22yqGpDOkA1fHmFz2GaR5LcQiK/b7cIUdCRCRob6F4gg 3z0GCdtgKRDI+0dOpEYyqxXA5caLKh4s5E7kgNd8= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:52 +0900 Message-Id: <20201002143154.468162-37-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 36/38] tests: Add IPADataSerializer test 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" Test the IPADataSerializer for controls, vectors, maps, and PODs of built-in types. Signed-off-by: Paul Elder --- Changes in v3: - use re-namespaced RPi::Controls New in v2 --- .../ipa_data_serializer_test.cpp | 464 ++++++++++++++++++ test/serialization/meson.build | 3 +- 2 files changed, 466 insertions(+), 1 deletion(-) create mode 100644 test/serialization/ipa_data_serializer_test.cpp diff --git a/test/serialization/ipa_data_serializer_test.cpp b/test/serialization/ipa_data_serializer_test.cpp new file mode 100644 index 00000000..c634a363 --- /dev/null +++ b/test/serialization/ipa_data_serializer_test.cpp @@ -0,0 +1,464 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * ipa_data_serializer_test.cpp - Test serializing/deserializing with IPADataSerializer + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libcamera/internal/device_enumerator.h" +#include "libcamera/internal/ipa_data_serializer.h" +#include "libcamera/internal/ipa_manager.h" +#include "libcamera/internal/ipa_module.h" +#include "libcamera/internal/pipeline_handler.h" +#include "libcamera/internal/thread.h" + +#include "serialization_test.h" +#include "test.h" + +using namespace std; +using namespace libcamera; + +template +bool isVectorEqual(vector &vecA, vector &vecB) +{ + if (vecA.size() != vecB.size()) + return false; + + size_t len = vecA.size(); + for (unsigned int i = 0; i < len; i++) + if (vecA[i] != vecB[i]) + goto nequal; + + return true; + +nequal: + cerr << "lhs: { "; + for (const auto &value : vecA) + cerr << value << ", "; + cerr << "}" << endl; + + cerr << "rhs: { "; + for (const auto &value : vecB) + cerr << value << ", "; + cerr << "}" << endl; + + return false; +} + +template<> +bool isVectorEqual(vector &vecA, + vector &vecB) +{ + if (vecA.size() != vecB.size()) + return false; + + size_t len = vecA.size(); + for (unsigned int i = 0; i < len; i++) + if (!SerializationTest::equals(vecA[i], vecB[i])) + return false; + + return true; +} + +template +bool isMapEqual(map &mapA, map &mapB) +{ + if (mapA == mapB) + return true; + + cerr << "lhs: { "; + for (const auto &value : mapA) + cerr << value.first << " : " << value.second << ", "; + cerr << "}" << endl; + + cerr << "rhs: { "; + for (const auto &value : mapB) + cerr << value.first << " : " << value.second << ", "; + cerr << "}" << endl; + + return false; +} + +template +bool isMapToCimEqual(map &mapA, map &mapB) +{ + bool isEqual = true; + + auto itA = mapA.begin(); + auto itB = mapB.begin(); + while (true) { + bool endA = (itA == mapA.end()); + bool endB = (itB == mapB.end()); + + if (endA and endB) + break; + + if (!endA && (endB || itA->first < itB->first)) { + cerr << "key: " << itA->first << " not in mapB" << endl; + isEqual = false; + itA++; + continue; + } + + if (endA || itB->first < itA->first) { + cerr << "key: " << itB->first << " not in mapA" << endl; + isEqual = false; + itB++; + continue; + } + + if (!SerializationTest::equals(itA->second, itB->second)) { + cerr << "key " << itA->first + << " has different values" << endl; + isEqual = false; + } + + itA++; + itB++; + } + + return isEqual; +} + +template +bool isMapToVecEqual(map &mapA, map &mapB) +{ + if (mapA == mapB) + return true; + + cerr << "lhs: { "; + for (const auto &value : mapA) { + cerr << value.first << " : { "; + for (const auto &v : value.second) + cerr << v << ", "; + cerr << "}" << endl; + } + cerr << "}" << endl; + + cerr << "rhs: { "; + for (const auto &value : mapB) { + cerr << value.first << " : { "; + for (const auto &v : value.second) + cerr << v << ", "; + cerr << "}" << endl; + } + cerr << "}" << endl; + + return false; +} + +class IPADataSerializerTest : public CameraTest, public Test +{ +public: + IPADataSerializerTest() + : CameraTest("platform/vimc.0 Sensor B") + { + RPi::initializeRPiControls(); + } + +protected: + int init() override + { + return status_; + } + + int run() override + { + int finalRet = TestPass; + int ret; + + ret = testControls(); + if (ret != TestPass) + finalRet = ret; + + ret = testVector(); + if (ret != TestPass) + finalRet = ret; + + ret = testMap(); + if (ret != TestPass) + finalRet = ret; + + ret = testPod(); + if (ret != TestPass) + finalRet = ret; + + return finalRet; + } + +private: + ControlList generateControlListA() + { + /* Create a control list with three controls. */ + const ControlInfoMap &infoMap = camera_->controls(); + ControlList list(infoMap); + + list.set(controls::Brightness, 0.5f); + list.set(controls::Contrast, 1.2f); + list.set(controls::Saturation, 0.2f); + + return list; + } + + int testControls() + { + ControlSerializer cs; + + const ControlInfoMap &infoMap = camera_->controls(); + ControlList list = generateControlListA(); + + vector infoMapBuf; + tie(infoMapBuf, ignore) = IPADataSerializer::serialize(infoMap, &cs); + + vector listBuf; + tie(listBuf, ignore) = IPADataSerializer::serialize(list, *list.infoMap(), &cs); + + + const ControlInfoMap infoMapOut = IPADataSerializer::deserialize(infoMapBuf, &cs); + + ControlList listOut = IPADataSerializer::deserialize(listBuf, &cs); + + + if (!SerializationTest::equals(infoMap, infoMapOut)) { + cerr << "Deserialized map doesn't match original" << endl; + return TestFail; + } + + if (!SerializationTest::equals(list, listOut)) { + cerr << "Deserialized list doesn't match original" << endl; + return TestFail; + } + + return TestPass; + } + + int testVector() + { + +#define TEST_VEC_SERDES(type, vec, cs) \ +tie(buf, fds) = IPADataSerializer>::serialize(vec, cs); \ +vector vec##Out = \ + IPADataSerializer>::deserialize(buf, fds, cs); \ +ret = isVectorEqual(vec, vec##Out); \ +if (!ret) { \ + cerr << "Deserialized vector " << #vec << " doesn't match original" << endl;\ + finalRet = TestFail; \ +} + + ControlSerializer cs; + + /* + * We don't test FileDescriptor serdes because it dup()s, so we + * can't check for equality. + */ + vector vecUint8 = {1, 2, 3, 4, 5, 6}; + vector vecUint16 = {1, 2, 3, 4, 5, 6}; + vector vecUint32 = {1, 2, 3, 4, 5, 6}; + vector vecUint64 = {1, 2, 3, 4, 5, 6}; + vector vecInt8 = {1, 2, 3, -4, 5, -6}; + vector vecInt16 = {1, 2, 3, -4, 5, -6}; + vector vecInt32 = {1, 2, 3, -4, 5, -6}; + vector vecInt64 = {1, 2, 3, -4, 5, -6}; + vector vecFloat = {1.1, 2.2, 3.3, -4.4, 5.5, -6.6}; + vector vecDouble = {1.1, 2.2, 3.3, -4.4, 5.5, -6.6}; + vector vecBool = {true, true, false, false, true, false}; + vector vecString = {"foo", "bar", "baz"}; + vector vecControlInfoMap = { + camera_->controls(), + RPi::Controls, + }; + + vector buf; + vector fds; + int finalRet = TestPass; + int ret; + + TEST_VEC_SERDES(uint8_t, vecUint8, nullptr); + TEST_VEC_SERDES(uint16_t, vecUint16, nullptr); + TEST_VEC_SERDES(uint32_t, vecUint32, nullptr); + TEST_VEC_SERDES(uint64_t, vecUint64, nullptr); + TEST_VEC_SERDES(int8_t, vecInt8, nullptr); + TEST_VEC_SERDES(int16_t, vecInt16, nullptr); + TEST_VEC_SERDES(int32_t, vecInt32, nullptr); + TEST_VEC_SERDES(int64_t, vecInt64, nullptr); + TEST_VEC_SERDES(float, vecFloat, nullptr); + TEST_VEC_SERDES(double, vecDouble, nullptr); + TEST_VEC_SERDES(bool, vecBool, nullptr); + TEST_VEC_SERDES(string, vecString, nullptr); + TEST_VEC_SERDES(ControlInfoMap, vecControlInfoMap, &cs); + + return finalRet; + } + + int testMap() + { + +#define TEST_MAP_SERDES(ktype, vtype, m, cs) \ +tie(buf, fds) = IPADataSerializer>::serialize(m, cs); \ +map m##Out = \ + IPADataSerializer>::deserialize(buf, fds, cs);\ +ret = isMapEqual(m, m##Out); \ +if (!ret) { \ + cerr << "Deserialized map " << #m << " doesn't match original" << endl;\ + finalRet = TestFail; \ +} + +#define TEST_MAP_CIM_SERDES(ktype, vtype, m, cs) \ +tie(buf, fds) = IPADataSerializer>::serialize(m, cs); \ +map m##Out = \ + IPADataSerializer>::deserialize(buf, fds, cs);\ +ret = isMapToCimEqual(m, m##Out); \ +if (!ret) { \ + cerr << "Deserialized map " << #m << " doesn't match original" << endl;\ + finalRet = TestFail; \ +} + +#define TEST_MAP_VEC_SERDES(ktype, vtype, m, cs) \ +tie(buf, fds) = IPADataSerializer>::serialize(m, cs); \ +map m##Out = \ + IPADataSerializer>::deserialize(buf, fds, cs);\ +ret = isMapToVecEqual(m, m##Out); \ +if (!ret) { \ + cerr << "Deserialized map " << #m << " doesn't match original" << endl;\ + finalRet = TestFail; \ +} + + ControlSerializer cs; + + /* + * Realistically, only string and integral keys. + * Test simple, complex, and nested compound value. + */ + map mapUintStr = + { {101, "foo"}, {102, "bar"}, {103, "baz"} }; + map mapIntStr = + { {101, "foo"}, {-102, "bar"}, {-103, "baz"} }; + map mapStrStr = + { {"a", "foo"}, {"b", "bar"}, {"c", "baz"} }; + map mapUintCIM = + { {201, camera_->controls()}, {202, RPi::Controls} }; + map mapIntCIM = + { {201, camera_->controls()}, {-202, RPi::Controls} }; + map mapStrCIM = + { {"a", camera_->controls()}, {"b", RPi::Controls} }; + map> mapUintBVec = + { {301, { 1, 2, 3 }}, {302, {4, 5, 6}}, {303, {7, 8, 9}} }; + map> mapIntBVec = + { {301, { 1, 2, 3 }}, {-302, {4, 5, 6}}, {-303, {7, 8, 9}} }; + map> mapStrBVec = + { {"a", { 1, 2, 3 }}, {"b", {4, 5, 6}}, {"c", {7, 8, 9}} }; + + vector buf; + vector fds; + int finalRet = TestPass; + int ret; + + TEST_MAP_SERDES(uint64_t, string, mapUintStr, nullptr); + TEST_MAP_SERDES(int64_t, string, mapIntStr, nullptr); + TEST_MAP_SERDES(string, string, mapStrStr, nullptr); + TEST_MAP_CIM_SERDES(uint64_t, ControlInfoMap, mapUintCIM, &cs); + TEST_MAP_CIM_SERDES(int64_t, ControlInfoMap, mapIntCIM, &cs); + TEST_MAP_CIM_SERDES(string, ControlInfoMap, mapStrCIM, &cs); + TEST_MAP_VEC_SERDES(uint64_t, vector, mapUintBVec, nullptr); + TEST_MAP_VEC_SERDES(int64_t, vector, mapIntBVec, nullptr); + TEST_MAP_VEC_SERDES(string, vector, mapStrBVec, nullptr); + + return finalRet; + } + + int testPod() + { + +#define TEST_POD_SERDES(type, var) \ +tie(buf, fds) = IPADataSerializer::serialize(var); \ +type var##Out = \ + IPADataSerializer::deserialize(buf, fds); \ +ret = (var == var##Out); \ +if (!ret) { \ + cerr << "Deserialized " << #var << " as " << var##Out \ + << ", expected " << var << endl; \ + finalRet = TestFail; \ +} + + uint32_t u32min = numeric_limits::min(); + uint32_t u32max = numeric_limits::max(); + uint32_t u32one = 1; + int32_t i32min = numeric_limits::min(); + int32_t i32max = numeric_limits::max(); + int32_t i32one = 1; + + uint64_t u64min = numeric_limits::min(); + uint64_t u64max = numeric_limits::max(); + uint64_t u64one = 1; + int64_t i64min = numeric_limits::min(); + int64_t i64max = numeric_limits::max(); + int64_t i64one = 1; + + float flow = numeric_limits::lowest(); + float fmin = numeric_limits::min(); + float fmax = numeric_limits::max(); + float falmostOne = 1 + 1.0e-37; + double dlow = numeric_limits::lowest(); + double dmin = numeric_limits::min(); + double dmax = numeric_limits::max(); + double dalmostOne = 1 + 1.0e-307; + + bool t = true; + bool f = false; + + stringstream ss; + for (unsigned int i = 0; i < (1 << 21); i++) + ss << "0123456789"; + + string strLong = ss.str(); + string strEmpty = ""; + + vector buf; + vector fds; + int finalRet = TestPass; + int ret; + + TEST_POD_SERDES(uint32_t, u32min); + TEST_POD_SERDES(uint32_t, u32max); + TEST_POD_SERDES(uint32_t, u32one); + TEST_POD_SERDES(int32_t, i32min); + TEST_POD_SERDES(int32_t, i32max); + TEST_POD_SERDES(int32_t, i32one); + TEST_POD_SERDES(uint64_t, u64min); + TEST_POD_SERDES(uint64_t, u64max); + TEST_POD_SERDES(uint64_t, u64one); + TEST_POD_SERDES(int64_t, i64min); + TEST_POD_SERDES(int64_t, i64max); + TEST_POD_SERDES(int64_t, i64one); + TEST_POD_SERDES(float, flow); + TEST_POD_SERDES(float, fmin); + TEST_POD_SERDES(float, fmax); + TEST_POD_SERDES(float, falmostOne); + TEST_POD_SERDES(double, dlow); + TEST_POD_SERDES(double, dmin); + TEST_POD_SERDES(double, dmax); + TEST_POD_SERDES(double, dalmostOne); + TEST_POD_SERDES(bool, t); + TEST_POD_SERDES(bool, f); + TEST_POD_SERDES(string, strLong); + TEST_POD_SERDES(string, strEmpty); + + return finalRet; + } +}; + +TEST_REGISTER(IPADataSerializerTest) diff --git a/test/serialization/meson.build b/test/serialization/meson.build index a9d9cbcb..c140a31c 100644 --- a/test/serialization/meson.build +++ b/test/serialization/meson.build @@ -1,7 +1,8 @@ # SPDX-License-Identifier: CC0-1.0 serialization_tests = [ - [ 'control_serialization', 'control_serialization.cpp' ], + [ 'control_serialization', 'control_serialization.cpp' ], + [ 'ipa_data_serializer_test', 'ipa_data_serializer_test.cpp' ], ] foreach t : serialization_tests From patchwork Fri Oct 2 14:31:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9945 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 16AECC3B5C for ; Fri, 2 Oct 2020 14:33:37 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D1B7763B98; Fri, 2 Oct 2020 16:33:36 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="jUt2sEN5"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1D3C463B59 for ; Fri, 2 Oct 2020 16:33:35 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 578C52A2; Fri, 2 Oct 2020 16:33:33 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649214; bh=W+hIZSzDJwkml444MOCjeYIPyFK1UWFVYXSq9WKmEBI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jUt2sEN5Hwgj7j9fiXZtxwrT9NwbsLTpeUlg0wpVvc8GjHf6dAIuGn9YOJeFUltwp zI2P1IoHvLMHN3yLTRcRaZzA692qNJTGinLNoKztasCqQk5gfJxfbvEWHXHCf2zbxP MDiAZt5WTZUrhYzfQBTX+JZMQ8yjPgB5FC4CAw3A= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:53 +0900 Message-Id: <20201002143154.468162-38-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 37/38] tests: Add test for IPAIPCUnixSocket 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" Test the IPC functions of IPAIPCUnixSocket. Signed-off-by: Paul Elder --- Changes in v3: - use readHeader, writeHeader, and eraseHeader as static class functions of IPAIPCUnixSocket New in v2 --- test/ipc/meson.build | 3 +- test/ipc/unixsocket_ipc.cpp | 238 ++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 test/ipc/unixsocket_ipc.cpp diff --git a/test/ipc/meson.build b/test/ipc/meson.build index 650df1d6..ecb6d022 100644 --- a/test/ipc/meson.build +++ b/test/ipc/meson.build @@ -1,7 +1,8 @@ # SPDX-License-Identifier: CC0-1.0 ipc_tests = [ - [ 'unixsocket', 'unixsocket.cpp' ], + [ 'unixsocket_ipc', 'unixsocket_ipc.cpp' ], + [ 'unixsocket', 'unixsocket.cpp' ], ] foreach t : ipc_tests diff --git a/test/ipc/unixsocket_ipc.cpp b/test/ipc/unixsocket_ipc.cpp new file mode 100644 index 00000000..467c26ca --- /dev/null +++ b/test/ipc/unixsocket_ipc.cpp @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * unixsocket_ipc.cpp - Unix socket IPC test + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libcamera/internal/ipa_data_serializer.h" +#include "libcamera/internal/ipa_ipc_unixsocket.h" +#include "libcamera/internal/process.h" +#include "libcamera/internal/thread.h" +#include "libcamera/internal/utils.h" + +#include "test.h" + +#define CMD_EXIT 0 +#define CMD_GET_SYNC 1 +#define CMD_SET_ASYNC 2 + +using namespace std; +using namespace libcamera; + +class UnixSocketTestIPCSlave +{ +public: + UnixSocketTestIPCSlave() + : value_(1337), exitCode_(EXIT_FAILURE), exit_(false) + { + dispatcher_ = Thread::current()->eventDispatcher(); + ipc_.readyRead.connect(this, &UnixSocketTestIPCSlave::readyRead); + } + + int run(int fd) + { + if (ipc_.bind(fd)) { + cerr << "Failed to connect to IPC channel" << endl; + return EXIT_FAILURE; + } + + while (!exit_) + dispatcher_->processEvents(); + + ipc_.close(); + + return exitCode_; + } + +private: + void readyRead(IPCUnixSocket *ipc) + { + IPCUnixSocket::Payload message, response; + int ret; + + ret = ipc->receive(&message); + if (ret) { + cerr << "Receive message failed: " << ret << endl; + return; + } + + uint32_t cmd, seq; + std::tie(cmd, seq) = IPAIPCUnixSocket::readHeader(message); + IPAIPCUnixSocket::eraseHeader(message); + + switch (cmd) { + case CMD_EXIT: { + exit_ = true; + break; + } + + case CMD_GET_SYNC: { + IPAIPCUnixSocket::writeHeader(response, cmd, seq); + + vector buf; + tie(buf, ignore) = IPADataSerializer::serialize(value_); + response.data.insert(response.data.end(), buf.begin(), buf.end()); + + ret = ipc_.send(response); + if (ret < 0) { + cerr << "Reply failed" << endl; + stop(ret); + } + break; + } + + case CMD_SET_ASYNC: { + value_ = IPADataSerializer::deserialize(message.data); + break; + } + } + } + + void stop(int code) + { + exitCode_ = code; + exit_ = true; + } + + int32_t value_; + + IPCUnixSocket ipc_; + EventDispatcher *dispatcher_; + int exitCode_; + bool exit_; +}; + +class UnixSocketTestIPC : public Test +{ +protected: + int init() + { + return 0; + } + + int setVal(int32_t val) + { + vector buf; + tie(buf, ignore) = IPADataSerializer::serialize(val); + + int ret = ipc_->sendAsync(CMD_SET_ASYNC, buf, {}); + if (ret < 0) { + cerr << "Failed to call set value" << endl; + return ret; + } + + return 0; + } + + int getVal() + { + vector buf; + + int ret = ipc_->sendSync(CMD_GET_SYNC, {}, {}, &buf); + if (ret < 0) { + cerr << "Failed to call get value" << endl; + return ret; + } + + return IPADataSerializer::deserialize(buf); + } + + int exit() + { + int ret = ipc_->sendAsync(CMD_EXIT, {}, {}); + if (ret < 0) { + cerr << "Failed to call exit" << endl; + return ret; + } + + return 0; + } + + int run() + { + char selfpath[100]; + memset(selfpath, 0, sizeof(selfpath)); + int ret = readlink("/proc/self/exe", selfpath, sizeof(selfpath)); + if (ret < 0) { + int err = errno; + cerr << "Failed to get path: " << strerror(err) << endl; + return TestFail; + } + + ipc_ = std::make_unique("", selfpath); + if (!ipc_->isValid()) { + cerr << "Failed to create IPAIPC" << endl; + return TestFail; + } + + + ret = getVal(); + if (ret < 0) { + cerr << "Failed to get initial value: " << strerror(-ret) << endl; + return TestFail; + } + if (ret != 1337) { + cerr << "Wrong inital value, expected 1337, got " << ret << endl; + return TestFail; + } + + ret = setVal(9001); + if (ret < 0) { + cerr << "Failed to set value: " << strerror(-ret) << endl; + return TestFail; + } + + ret = getVal(); + if (ret < 0) { + cerr << "Failed to get value: " << strerror(-ret) << endl; + return TestFail; + } + if (ret != 9001) { + cerr << "Wrong set value, expected 9001, got " << ret << endl; + return TestFail; + } + + ret = exit(); + if (ret < 0) { + cerr << "Failed to exit: " << strerror(-ret) << endl; + return TestFail; + } + + return TestPass; + } + +private: + ProcessManager processManager_; + + std::unique_ptr ipc_; +}; + +/* + * Can't use TEST_REGISTER() as single binary needs to act as both proxy + * master and slave. + */ +int main(int argc, char **argv) +{ + /* IPAIPCUnixSocket passes IPA module path in argv[1] */ + if (argc == 3) { + int ipcfd = std::stoi(argv[2]); + UnixSocketTestIPCSlave slave; + return slave.run(ipcfd); + } + + return UnixSocketTestIPC().execute(); +} From patchwork Fri Oct 2 14:31:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9946 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 74BDEC3B5C for ; Fri, 2 Oct 2020 14:33:39 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3752B63B98; Fri, 2 Oct 2020 16:33:39 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="OOhDi5Pl"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 571E763B59 for ; Fri, 2 Oct 2020 16:33:37 +0200 (CEST) Received: from pyrite.rasen.tech (unknown [IPv6:2400:4051:61:600:2c71:1b79:d06d:5032]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 953D42A2; Fri, 2 Oct 2020 16:33:35 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1601649217; bh=xMugxmNj+F09XenfBNW9Zq8M5HHoGJ9Ua4treiDOjzs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OOhDi5PlRrBNuYaIeIyvoG0xb98/l0+uG821vvMUQg8D59U2nWxcCovx46b0KOxLD etAFrz/T5OJgMYotzcW+X3mpF9ZPfFM/2srJn/1wHVv3EtUULZxYY1kWD2opMsnD/L hN7O3/12c9WpTDHKU15SpAdMgZYsrWPhhlIRI0fA= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Fri, 2 Oct 2020 23:31:54 +0900 Message-Id: <20201002143154.468162-39-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20201002143154.468162-1-paul.elder@ideasonboard.com> References: <20201002143154.468162-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 38/38] README: Add dependency on python3-ply for IPA interface generation 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" Specify in the readme that we depend on python3-ply for generating the IPA interface. Signed-off-by: Paul Elder Reviewed-by: Laurent Pinchart --- New in v3 --- README.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.rst b/README.rst index 74babaf7..a4279a4b 100644 --- a/README.rst +++ b/README.rst @@ -60,6 +60,9 @@ Meson Build system: [required] for the libcamera core: [required] python3-yaml +for generating IPA interfaces: [required] + python3-ply + for IPA module signing: [required] libgnutls28-dev openssl