Show a patch.

GET /api/patches/2016/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2016,
    "url": "https://patchwork.libcamera.org/api/patches/2016/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/2016/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20190924172503.30864-18-jacopo@jmondi.org>",
    "date": "2019-09-24T17:24:59",
    "name": "[libcamera-devel,17/21] ipa: Convert the IPA API to plain C",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "fa33f4c05ad1bdee6cca7a8d8c1f236a89ae674c",
    "submitter": {
        "id": 3,
        "url": "https://patchwork.libcamera.org/api/people/3/?format=api",
        "name": "Jacopo Mondi",
        "email": "jacopo@jmondi.org"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/2016/mbox/",
    "series": [
        {
            "id": 506,
            "url": "https://patchwork.libcamera.org/api/series/506/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=506",
            "date": "2019-09-24T17:24:42",
            "name": "Implement control serialization",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/506/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/2016/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/2016/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<jacopo@jmondi.org>",
        "Received": [
            "from relay3-d.mail.gandi.net (relay3-d.mail.gandi.net\n\t[217.70.183.195])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id EEAAF62391\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 24 Sep 2019 19:23:39 +0200 (CEST)",
            "from uno.homenet.telecomitalia.it\n\t(host89-248-dynamic.45-213-r.retail.telecomitalia.it [213.45.248.89])\n\t(Authenticated sender: jacopo@jmondi.org)\n\tby relay3-d.mail.gandi.net (Postfix) with ESMTPSA id C6E8160005\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 24 Sep 2019 17:23:38 +0000 (UTC)"
        ],
        "X-Originating-IP": "213.45.248.89",
        "From": "Jacopo Mondi <jacopo@jmondi.org>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Tue, 24 Sep 2019 19:24:59 +0200",
        "Message-Id": "<20190924172503.30864-18-jacopo@jmondi.org>",
        "X-Mailer": "git-send-email 2.23.0",
        "In-Reply-To": "<20190924172503.30864-1-jacopo@jmondi.org>",
        "References": "<20190924172503.30864-1-jacopo@jmondi.org>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=UTF-8",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH 17/21] ipa: Convert the IPA API to plain C",
        "X-BeenThere": "libcamera-devel@lists.libcamera.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "<libcamera-devel.lists.libcamera.org>",
        "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>",
        "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>",
        "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>",
        "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>",
        "X-List-Received-Date": "Tue, 24 Sep 2019 17:23:40 -0000"
    },
    "content": "The C++ objects that are expected to convey data through the IPA API\nwill have associated methods that would require IPAs to link to\nlibcamera. Even though the libcamera license allows this, suppliers of\nclosed-source IPAs may have a different interpretation. To ease their\nmind and clearly separate vendor code and libcamera code, turn the IPA\nAPI to plain C. The corresponding C objects will be stored in plain C\nstructures or have their binary format documented, removing the need for\nlinking to libcamera code on the IPA side.\n\nThis is implemented by adding two new C structures, ipa_context and\nipa_operations. The ipa_operations contains function pointers for all\nthe IPA API operations. The ipa_context represents a context of\noperation for the IPA, and is passed to the IPA oparations. The\nIPAInterface class is retained as it is easier to use than a plain C API\nfor pipeline handlers, with a new IPAWrapper class that wraps the\nipa_context and ipa_operations into and IPAInterface.\n\nOn the IPA module side usage of IPAInterface may be desired for IPAs\nimplemented in C++ that want to link to libcamera. For those IPAs, a new\nIPAWrapperContext helper class is introduce to wrap the IPAInterface\nimplemented internally by the IPA module into an ipa_context and\nipa_operations.\n\nSigned-off-by: Jacopo Mondi <jacopo@jmondi.org>\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\nReviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n---\n Documentation/Doxyfile.in                     |  1 +\n Documentation/meson.build                     |  2 +\n include/ipa/ipa_interface.h                   | 16 ++++\n src/ipa/ipa_dummy.cpp                         |  6 +-\n src/ipa/libipa/ipa_interface_wrapper.cpp      | 74 ++++++++++++++++++\n src/ipa/libipa/ipa_interface_wrapper.h        | 29 +++++++\n src/ipa/libipa/meson.build                    | 10 +++\n src/ipa/meson.build                           |  3 +\n src/libcamera/include/ipa_context_wrapper.h   | 26 +++++++\n src/libcamera/include/ipa_module.h            |  5 +-\n src/libcamera/include/meson.build             |  1 +\n src/libcamera/ipa_context_wrapper.cpp         | 52 +++++++++++++\n src/libcamera/ipa_interface.cpp               | 77 ++++++++++++++++++-\n src/libcamera/ipa_manager.cpp                 | 67 +++++++++++++++-\n src/libcamera/ipa_module.cpp                  | 23 +++---\n src/libcamera/meson.build                     |  1 +\n src/libcamera/pipeline/vimc.cpp               |  2 +-\n .../proxy/worker/ipa_proxy_linux_worker.cpp   |  8 +-\n 18 files changed, 381 insertions(+), 22 deletions(-)\n create mode 100644 src/ipa/libipa/ipa_interface_wrapper.cpp\n create mode 100644 src/ipa/libipa/ipa_interface_wrapper.h\n create mode 100644 src/ipa/libipa/meson.build\n create mode 100644 src/libcamera/include/ipa_context_wrapper.h\n create mode 100644 src/libcamera/ipa_context_wrapper.cpp",
    "diff": "diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in\nindex 28a9c2da1ad4..fb822998a374 100644\n--- a/Documentation/Doxyfile.in\n+++ b/Documentation/Doxyfile.in\n@@ -793,6 +793,7 @@ WARN_LOGFILE           =\n \n INPUT                  = \"@TOP_SRCDIR@/include/ipa\" \\\n \t\t\t \"@TOP_SRCDIR@/include/libcamera\" \\\n+\t\t\t \"@TOP_SRCDIR@/src/ipa/libipa\" \\\n \t\t\t \"@TOP_SRCDIR@/src/libcamera\"\n \n # This tag can be used to specify the character encoding of the source files\ndiff --git a/Documentation/meson.build b/Documentation/meson.build\nindex 4ff3fbeb0674..9136506f5d9c 100644\n--- a/Documentation/meson.build\n+++ b/Documentation/meson.build\n@@ -24,6 +24,8 @@ if doxygen.found()\n                       libcamera_ipa_api,\n                       libcamera_headers,\n                       libcamera_sources,\n+                      libipa_headers,\n+                      libipa_sources,\n                   ],\n                   output : 'api-html',\n                   command : [doxygen, doxyfile],\ndiff --git a/include/ipa/ipa_interface.h b/include/ipa/ipa_interface.h\nindex 9bbc4cf58ec6..f1ebac20f151 100644\n--- a/include/ipa/ipa_interface.h\n+++ b/include/ipa/ipa_interface.h\n@@ -7,6 +7,21 @@\n #ifndef __LIBCAMERA_IPA_INTERFACE_H__\n #define __LIBCAMERA_IPA_INTERFACE_H__\n \n+#ifdef __cplusplus\n+extern \"C\" {\n+#endif\n+\n+struct ipa_context {\n+\tconst struct ipa_operations *ops;\n+};\n+\n+struct ipa_operations {\n+\tvoid (*destroy)(struct ipa_context *ctx);\n+};\n+\n+#ifdef __cplusplus\n+}\n+\n namespace libcamera {\n \n class IPAInterface\n@@ -16,5 +31,6 @@ public:\n };\n \n } /* namespace libcamera */\n+#endif\n \n #endif /* __LIBCAMERA_IPA_INTERFACE_H__ */\ndiff --git a/src/ipa/ipa_dummy.cpp b/src/ipa/ipa_dummy.cpp\nindex c833e5fb0b2d..6dc9448a3f56 100644\n--- a/src/ipa/ipa_dummy.cpp\n+++ b/src/ipa/ipa_dummy.cpp\n@@ -8,6 +8,8 @@\n #include <ipa/ipa_interface.h>\n #include <ipa/ipa_module_info.h>\n \n+#include \"libipa/ipa_interface_wrapper.h\"\n+\n namespace libcamera {\n \n class IPADummy : public IPAInterface\n@@ -27,9 +29,9 @@ const struct IPAModuleInfo ipaModuleInfo = {\n \tLICENSE,\n };\n \n-IPAInterface *ipaCreate()\n+struct ipa_context *ipaCreate()\n {\n-\treturn new IPADummy();\n+\treturn new IPAInterfaceWrapper(new IPADummy());\n }\n };\n \ndiff --git a/src/ipa/libipa/ipa_interface_wrapper.cpp b/src/ipa/libipa/ipa_interface_wrapper.cpp\nnew file mode 100644\nindex 000000000000..aacd189851c3\n--- /dev/null\n+++ b/src/ipa/libipa/ipa_interface_wrapper.cpp\n@@ -0,0 +1,74 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * ipa_interface_wrapper.cpp - Image Processing Algorithm interface wrapper\n+ */\n+\n+#include \"ipa_interface_wrapper.h\"\n+\n+#include <ipa/ipa_interface.h>\n+\n+/**\n+ * \\file ipa_interface_wrapper.h\n+ * \\brief Image Processing Algorithm interface wrapper\n+ */\n+\n+namespace libcamera {\n+\n+/**\n+ * \\class IPAInterfaceWrapper\n+ * \\brief Wrap an IPAInterface and expose it as an ipa_context\n+ *\n+ * This class implements the ipa_context API based on a provided IPAInterface.\n+ * It helps IPAs that implement the IPAInterface API to provide the external\n+ * ipa_context API.\n+ *\n+ * To use the wrapper, an IPA module simple creates a new instance of its\n+ * IPAInterface implementation, and passes it to the constructor of the\n+ * IPAInterfaceWrapper. As IPAInterfaceWrapper inherits from ipa_context, the\n+ * constructed wrapper can then be directly returned from the IPA module's\n+ * ipaCreate() function.\n+ *\n+ * \\code{.cpp}\n+ * class MyIPA : public IPAInterface\n+ * {\n+ * \t...\n+ * };\n+ *\n+ * struct ipa_context *ipaCreate()\n+ * {\n+ * \treturn new IPAInterfaceWrapper(new MyIPA());\n+ * }\n+ * \\endcode\n+ */\n+\n+/**\n+ * \\brief Construct an IPAInterfaceWrapper wrapping \\a interface\n+ * \\param[in] interface The interface to wrap\n+ */\n+IPAInterfaceWrapper::IPAInterfaceWrapper(IPAInterface *interface)\n+\t: ipa(interface)\n+{\n+\tops = &operations;\n+}\n+\n+void IPAInterfaceWrapper::destroy(struct ipa_context *_ctx)\n+{\n+\tIPAInterfaceWrapper *ctx = static_cast<IPAInterfaceWrapper *>(_ctx);\n+\n+\tdelete ctx->ipa;\n+\tdelete ctx;\n+}\n+\n+#ifndef __DOXYGEN__\n+/*\n+ * This construct confuses Doygen and makes it believe that all members of the\n+ * operations is a member of IPAInterfaceWrapper. It must thus be hidden.\n+ */\n+const struct ipa_operations IPAInterfaceWrapper::operations = {\n+\t.destroy = &IPAInterfaceWrapper::destroy,\n+};\n+#endif\n+\n+}; /* namespace libcamera */\ndiff --git a/src/ipa/libipa/ipa_interface_wrapper.h b/src/ipa/libipa/ipa_interface_wrapper.h\nnew file mode 100644\nindex 000000000000..d2ab46f50d3c\n--- /dev/null\n+++ b/src/ipa/libipa/ipa_interface_wrapper.h\n@@ -0,0 +1,29 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * ipa_interface_wrapper.h - Image Processing Algorithm interface wrapper\n+ */\n+#ifndef __LIBCAMERA_IPA_INTERFACE_WRAPPER_H__\n+#define __LIBCAMERA_IPA_INTERFACE_WRAPPER_H__\n+\n+#include <ipa/ipa_interface.h>\n+\n+namespace libcamera {\n+\n+class IPAInterfaceWrapper : public ipa_context\n+{\n+public:\n+\tIPAInterfaceWrapper(IPAInterface *interface);\n+\n+private:\n+\tstatic void destroy(struct ipa_context *ctx);\n+\n+\tstatic const struct ipa_operations operations;\n+\n+\tIPAInterface *ipa;\n+};\n+\n+} /* namespace libcamera */\n+\n+#endif /* __LIBCAMERA_IPA_INTERFACE_WRAPPER_H__ */\ndiff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\nnew file mode 100644\nindex 000000000000..455bac977029\n--- /dev/null\n+++ b/src/ipa/libipa/meson.build\n@@ -0,0 +1,10 @@\n+libipa_headers = files([\n+    'ipa_interface_wrapper.h',\n+])\n+\n+libipa_sources = files([\n+    'ipa_interface_wrapper.cpp',\n+])\n+\n+libipa = static_library('ipa', libipa_sources,\n+                        dependencies : libcamera_dep)\ndiff --git a/src/ipa/meson.build b/src/ipa/meson.build\nindex f09915bc1388..6483ea66a478 100644\n--- a/src/ipa/meson.build\n+++ b/src/ipa/meson.build\n@@ -1,3 +1,5 @@\n+subdir('libipa')\n+\n ipa_dummy_sources = [\n     ['ipa_dummy',         'LGPL-2.1-or-later'],\n     ['ipa_dummy_isolate', 'Proprietary'],\n@@ -9,6 +11,7 @@ foreach t : ipa_dummy_sources\n     ipa = shared_module(t[0], 'ipa_dummy.cpp',\n                         name_prefix : '',\n                         include_directories : libcamera_includes,\n+                        link_with : libipa,\n                         install : true,\n                         install_dir : ipa_install_dir,\n                         cpp_args : '-DLICENSE=\"' + t[1] + '\"')\ndiff --git a/src/libcamera/include/ipa_context_wrapper.h b/src/libcamera/include/ipa_context_wrapper.h\nnew file mode 100644\nindex 000000000000..12894ac6885e\n--- /dev/null\n+++ b/src/libcamera/include/ipa_context_wrapper.h\n@@ -0,0 +1,26 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * ipa_context_wrapper.h - Image Processing Algorithm context wrapper\n+ */\n+#ifndef __LIBCAMERA_IPA_CONTEXT_WRAPPER_H__\n+#define __LIBCAMERA_IPA_CONTEXT_WRAPPER_H__\n+\n+#include <ipa/ipa_interface.h>\n+\n+namespace libcamera {\n+\n+class IPAContextWrapper final : public IPAInterface\n+{\n+public:\n+\tIPAContextWrapper(struct ipa_context *context);\n+\t~IPAContextWrapper();\n+\n+private:\n+\tstruct ipa_context *ctx_;\n+};\n+\n+} /* namespace libcamera */\n+\n+#endif /* __LIBCAMERA_IPA_CONTEXT_WRAPPER_H__ */\ndiff --git a/src/libcamera/include/ipa_module.h b/src/libcamera/include/ipa_module.h\nindex 97737587ab3a..2028b76a1913 100644\n--- a/src/libcamera/include/ipa_module.h\n+++ b/src/libcamera/include/ipa_module.h\n@@ -7,7 +7,6 @@\n #ifndef __LIBCAMERA_IPA_MODULE_H__\n #define __LIBCAMERA_IPA_MODULE_H__\n \n-#include <memory>\n #include <string>\n \n #include <ipa/ipa_interface.h>\n@@ -30,7 +29,7 @@ public:\n \n \tbool load();\n \n-\tstd::unique_ptr<IPAInterface> createInstance();\n+\tstruct ipa_context *createContext();\n \n \tbool match(PipelineHandler *pipe,\n \t\t   uint32_t minVersion, uint32_t maxVersion) const;\n@@ -45,7 +44,7 @@ private:\n \tbool loaded_;\n \n \tvoid *dlHandle_;\n-\ttypedef IPAInterface *(*IPAIntfFactory)(void);\n+\ttypedef struct ipa_context *(*IPAIntfFactory)(void);\n \tIPAIntfFactory ipaCreate_;\n \n \tint loadIPAModuleInfo();\ndiff --git a/src/libcamera/include/meson.build b/src/libcamera/include/meson.build\nindex 3bec594c3b3d..1e1445a9ab2f 100644\n--- a/src/libcamera/include/meson.build\n+++ b/src/libcamera/include/meson.build\n@@ -5,6 +5,7 @@ libcamera_headers = files([\n     'device_enumerator_udev.h',\n     'event_dispatcher_poll.h',\n     'formats.h',\n+    'ipa_context_wrapper.h',\n     'ipa_manager.h',\n     'ipa_module.h',\n     'ipa_proxy.h',\ndiff --git a/src/libcamera/ipa_context_wrapper.cpp b/src/libcamera/ipa_context_wrapper.cpp\nnew file mode 100644\nindex 000000000000..87ff98d45c99\n--- /dev/null\n+++ b/src/libcamera/ipa_context_wrapper.cpp\n@@ -0,0 +1,52 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2019, Google Inc.\n+ *\n+ * ipa_context_wrapper.cpp - Image Processing Algorithm context wrapper\n+ */\n+\n+#include \"ipa_context_wrapper.h\"\n+\n+#include <libcamera/controls.h>\n+\n+/**\n+ * \\file ipa_context_wrapper.h\n+ * \\brief Image Processing Algorithm context wrapper\n+ */\n+\n+namespace libcamera {\n+\n+/**\n+ * \\class IPAContextWrapper\n+ * \\brief Wrap an ipa_context and expose it as an IPAInterface\n+ *\n+ * The IPAContextWrapper class wraps an ipa_context, provided by an IPA module, and\n+ * exposes an IPAInterface. This mechanism is used for IPAs that are not\n+ * isolated in a separate process to allow direct calls from pipeline handler\n+ * using the IPAInterface API instead of the lower-level ipa_context API.\n+ *\n+ * The IPAInterface methods are converted to the ipa_context API by serialising\n+ * all C++ arguments into plain C structures or byte arrays that contain no\n+ * pointer, as required by the ipa_context API.\n+ */\n+\n+/**\n+ * \\brief Construct an IPAContextWrapper instance that wraps the \\a context\n+ * \\param[in] context The IPA module context\n+ *\n+ * Ownership of the \\a context is passed to the IPAContextWrapper. The context remains\n+ * valid for the whole lifetime of the wrapper and is destroyed automatically\n+ * with it.\n+ */\n+IPAContextWrapper::IPAContextWrapper(struct ipa_context *context)\n+\t: ctx_(context)\n+{\n+}\n+\n+IPAContextWrapper::~IPAContextWrapper()\n+{\n+\tif (ctx_)\n+\t\tctx_->ops->destroy(ctx_);\n+}\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/ipa_interface.cpp b/src/libcamera/ipa_interface.cpp\nindex f70d91ded1ab..6e83aab0fb73 100644\n--- a/src/libcamera/ipa_interface.cpp\n+++ b/src/libcamera/ipa_interface.cpp\n@@ -10,13 +10,88 @@\n /**\n  * \\file ipa_interface.h\n  * \\brief Image Processing Algorithm interface\n+ *\n+ * libcamera interfaces with IPA modules through a plain C interface. IPA\n+ * modules shall expose a public function name ipaCreate() with the following\n+ * prototype.\n+ *\n+ * \\code{.c}\n+ * struct ipa_context *ipaCreate();\n+ * \\endcode\n+ *\n+ * The ipaCreate() function creates an instance of an IPA context, which models\n+ * a context of execution for the IPA. IPA modules shall support creating one\n+ * or multiple contexts, as required by their associated pipeline handler.\n+ *\n+ * The IPA module operations are defined in the struct ipa_operations. An IPA\n+ * module stores a pointer to the operations corresponding to its context in\n+ * the ipa_context::ops field. That pointer is immutable for the lifetime of\n+ * the context, and may be different for multiple contexts created by the same\n+ * IPA module.\n+ *\n+ * All argument to ipa_operations members are Plain Old Data and are documented\n+ * either in the form of C data types, or as a textual description for byte\n+ * arrays that can't be expressed using C data types (such as variable-length\n+ * arrays). IPA modules can thus use the C API without calling into libcamera\n+ * to access the data passed to the IPA operations.\n+ *\n+ * The IPAInterface class is a C++ representation of the ipa_operations, using\n+ * C++ data classes provided by libcamera. This is the API exposed to pipeline\n+ * handlers to communicate with IPA modules. IPA modules may use the\n+ * IPAInterface API internally if they want to benefit from the data and helper\n+ * classes offered by libcamera.\n+ */\n+\n+/**\n+ * \\struct ipa_context\n+ * \\brief IPA module context of execution\n+ *\n+ * This structure models a context of execution for an IPA module. It is\n+ * instantiated by the IPA module ipaCreate() function. IPA modules allocate\n+ * context instances in an implementation-defined way, contexts shall thus be\n+ * destroyed using the ipa_operation::destroy operation only.\n+ *\n+ * The ipa_context structure provides a pointer to the IPA operations. It shall\n+ * otherwise be treated as a constant black-box cookie and passed unmodified to\n+ * the operations defined in struct ipa_operations.\n+ *\n+ * IPA modules are expected to extend struct ipa_context by inheriting from it,\n+ * either through structure embedding to model inheritance in plain C, or\n+ * through C++ class inheritance. A simple example of the latter is available\n+ * in the IPAContextWrapper class implementation.\n+ *\n+ * \\var ipa_context::ops\n+ * \\brief The IPA context operations\n+ */\n+\n+/**\n+ * \\struct ipa_operations\n+ * \\brief IPA context operations as a set of function pointers\n+ *\n+ * To allow for isolation of IPA modules in separate processes, the functions\n+ * defined in the ipa_operations structure return only data related to the\n+ * libcamera side of the operations. In particular, error related to the\n+ * libcamera side of the IPC may be returned. Data returned by the IPA,\n+ * including status information, shall be provided through callbacks from the\n+ * IPA to libcamera.\n+ *\n+ * \\var ipa_operations::destroy\n+ * \\brief Destroy the ipa_context created by the module's ipaCreate() function\n  */\n \n namespace libcamera {\n \n /**\n  * \\class IPAInterface\n- * \\brief Interface for IPA implementation\n+ * \\brief IPA module interface\n+ *\n+ * This pure virtual class defines a C++ API corresponding to the ipa_context\n+ * and ipa_operations API. It is used by pipeline handlers to interact with IPA\n+ * modules, and may be used internally in IPA modules if desired to benefit\n+ * from the data and helper classes provided by libcamera.\n+ *\n+ * As for the operations defined in struct ipa_operations, the methods defined\n+ * by this class shall not return data from the IPA.\n  */\n \n } /* namespace libcamera */\ndiff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp\nindex 708233e8a9c7..53c2c7859e79 100644\n--- a/src/libcamera/ipa_manager.cpp\n+++ b/src/libcamera/ipa_manager.cpp\n@@ -11,6 +11,7 @@\n #include <string.h>\n #include <sys/types.h>\n \n+#include \"ipa_context_wrapper.h\"\n #include \"ipa_module.h\"\n #include \"ipa_proxy.h\"\n #include \"log.h\"\n@@ -29,6 +30,66 @@ LOG_DEFINE_CATEGORY(IPAManager)\n /**\n  * \\class IPAManager\n  * \\brief Manager for IPA modules\n+ *\n+ * The IPA module manager discovers IPA modules from disk, queries and loads\n+ * them, and creates IPA contexts. It supports isolation of the modules in a\n+ * separate process with IPC communication and offers a unified IPAInterface\n+ * view of the IPA contexts to pipeline handlers regardless of whether the\n+ * modules are isolated or loaded in the same process.\n+ *\n+ * Module isolation is based on the module licence. Open-source modules are\n+ * loaded without isolation, while closed-source module are forcefully isolated.\n+ * The isolation mechanism ensures that no code from a closed-source module is\n+ * ever run in the libcamera process.\n+ *\n+ * To create an IPA context, pipeline handlers call the IPAManager::ipaCreate()\n+ * method. For a directly loaded module, the manager calls the module's\n+ * ipaCreate() function directly and wraps the returned context in an\n+ * IPAContextWrapper that exposes an IPAInterface.\n+ *\n+ * ~~~~\n+ * +---------------+\n+ * |   Pipeline    |\n+ * |    Handler    |\n+ * +---------------+\n+ *         |\n+ *         v\n+ * +---------------+                   +---------------+\n+ * |      IPA      |                   |  Open Source  |\n+ * |   Interface   |                   |  IPA Module   |\n+ * | - - - - - - - |                   | - - - - - - - |\n+ * |  IPA Context  |  ipa_operations   |  ipa_context  |\n+ * |    Wrapper    | ----------------> |               |\n+ * +---------------+                   +---------------+\n+ * ~~~~\n+ *\n+ * For an isolated module, the manager instantiates an IPAProxy which spawns a\n+ * new process for an IPA proxy worker. The worker loads the IPA module and\n+ * creates the IPA context. The IPAProxy alse exposes an IPAInterface.\n+ *\n+ * ~~~~\n+ * +---------------+                   +---------------+\n+ * |   Pipeline    |                   | Closed Source |\n+ * |    Handler    |                   |  IPA Module   |\n+ * +---------------+                   | - - - - - - - |\n+ *         |                           |  ipa_context  |\n+ *         v                           |               |\n+ * +---------------+                   +---------------+\n+ * |      IPA      |            ipa_operations ^\n+ * |   Interface   |                           |\n+ * | - - - - - - - |                   +---------------+\n+ * |   IPA Proxy   |    operations     |   IPA Proxy   |\n+ * |               | ----------------> |    Worker     |\n+ * +---------------+     over IPC      +---------------+\n+ * ~~~~\n+ *\n+ * The IPAInterface implemented by the IPAContextWrapper or IPAProxy is\n+ * returned to the pipeline handler, and all interactions with the IPA context\n+ * go the same interface regarless of process isolation.\n+ *\n+ * In all cases the data passed to the IPAInterface methods is serialised to\n+ * Plain Old Data, either for the purpose of passing it to the IPA context\n+ * plain C API, or to transmit the data to the isolated process through IPC.\n  */\n \n IPAManager::IPAManager()\n@@ -189,7 +250,11 @@ std::unique_ptr<IPAInterface> IPAManager::createIPA(PipelineHandler *pipe,\n \tif (!m->load())\n \t\treturn nullptr;\n \n-\treturn m->createInstance();\n+\tstruct ipa_context *ctx = m->createContext();\n+\tif (!ctx)\n+\t\treturn nullptr;\n+\n+\treturn utils::make_unique<IPAContextWrapper>(ctx);\n }\n \n } /* namespace libcamera */\ndiff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp\nindex 99d308efd47b..9f5a01418f73 100644\n--- a/src/libcamera/ipa_module.cpp\n+++ b/src/libcamera/ipa_module.cpp\n@@ -385,13 +385,13 @@ const std::string &IPAModule::path() const\n /**\n  * \\brief Load the IPA implementation factory from the shared object\n  *\n- * The IPA module shared object implements an IPAInterface class to be used\n+ * The IPA module shared object implements an ipa_context object to be used\n  * by pipeline handlers. This method loads the factory function from the\n- * shared object. Later, createInstance() can be called to instantiate the\n- * IPAInterface.\n+ * shared object. Later, createContext() can be called to instantiate the\n+ * ipa_context.\n  *\n  * This method only needs to be called successfully once, after which\n- * createInstance() can be called as many times as IPAInterface instances are\n+ * createContext() can be called as many times as ipa_context instances are\n  * needed.\n  *\n  * Calling this function on an invalid module (as returned by isValid()) is\n@@ -433,24 +433,25 @@ bool IPAModule::load()\n }\n \n /**\n- * \\brief Instantiate an IPAInterface\n+ * \\brief Instantiate an IPA context\n  *\n- * After loading the IPA module with load(), this method creates an\n- * instance of the IPA module interface.\n+ * After loading the IPA module with load(), this method creates an instance of\n+ * the IPA module context. Ownership of the context is passed to the caller, and\n+ * the context shall be destroyed by calling the \\ref ipa_operations::destroy\n+ * \"ipa_context::ops::destroy()\" operation.\n  *\n  * Calling this function on a module that has not yet been loaded, or an\n  * invalid module (as returned by load() and isValid(), respectively) is\n  * an error.\n  *\n- * \\return The IPA implementation as a new IPAInterface instance on success,\n- * or nullptr on error\n+ * \\return The IPA context on success, or nullptr on error\n  */\n-std::unique_ptr<IPAInterface> IPAModule::createInstance()\n+struct ipa_context *IPAModule::createContext()\n {\n \tif (!valid_ || !loaded_)\n \t\treturn nullptr;\n \n-\treturn std::unique_ptr<IPAInterface>(ipaCreate_());\n+\treturn ipaCreate_();\n }\n \n /**\ndiff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\nindex 0f6f97305e4f..3c7f890b3359 100644\n--- a/src/libcamera/meson.build\n+++ b/src/libcamera/meson.build\n@@ -13,6 +13,7 @@ libcamera_sources = files([\n     'event_notifier.cpp',\n     'formats.cpp',\n     'geometry.cpp',\n+    'ipa_context_wrapper.cpp',\n     'ipa_interface.cpp',\n     'ipa_manager.cpp',\n     'ipa_module.cpp',\ndiff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp\nindex 499ce59181c5..9ba620a7562a 100644\n--- a/src/libcamera/pipeline/vimc.cpp\n+++ b/src/libcamera/pipeline/vimc.cpp\n@@ -362,7 +362,7 @@ bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator)\n \t\treturn false;\n \n \tipa_ = IPAManager::instance()->createIPA(this, 0, 0);\n-\tif (ipa_ == nullptr)\n+\tif (!ipa_)\n \t\tLOG(VIMC, Warning) << \"no matching IPA found\";\n \n \tstd::unique_ptr<VimcCameraData> data = utils::make_unique<VimcCameraData>(this);\ndiff --git a/src/libcamera/proxy/worker/ipa_proxy_linux_worker.cpp b/src/libcamera/proxy/worker/ipa_proxy_linux_worker.cpp\nindex a10761e52b32..07380c16e2d5 100644\n--- a/src/libcamera/proxy/worker/ipa_proxy_linux_worker.cpp\n+++ b/src/libcamera/proxy/worker/ipa_proxy_linux_worker.cpp\n@@ -72,9 +72,9 @@ int main(int argc, char **argv)\n \t}\n \tsocket.readyRead.connect(&readyRead);\n \n-\tstd::unique_ptr<IPAInterface> ipa = ipam->createInstance();\n-\tif (!ipa) {\n-\t\tLOG(IPAProxyLinuxWorker, Error) << \"Failed to create IPA interface\";\n+\tstruct ipa_context *ipac = ipam->createContext();\n+\tif (!ipac) {\n+\t\tLOG(IPAProxyLinuxWorker, Error) << \"Failed to create IPA context\";\n \t\treturn EXIT_FAILURE;\n \t}\n \n@@ -85,5 +85,7 @@ int main(int argc, char **argv)\n \twhile (1)\n \t\tdispatcher->processEvents();\n \n+\tipac->ops->destroy(ipac);\n+\n \treturn 0;\n }\n",
    "prefixes": [
        "libcamera-devel",
        "17/21"
    ]
}