Patch Detail
Show a patch.
GET /api/patches/9389/?format=api
{ "id": 9389, "url": "https://patchwork.libcamera.org/api/patches/9389/?format=api", "web_url": "https://patchwork.libcamera.org/patch/9389/", "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": "<20200826110926.67192-6-paul.elder@ideasonboard.com>", "date": "2020-08-26T11:09:14", "name": "[libcamera-devel,RFC,05/17] IPA: IPC: add IPAProxyRPi and IPAProxyRPiWorker", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "5453418a98f065ae286ae1309f9956806d67d843", "submitter": { "id": 17, "url": "https://patchwork.libcamera.org/api/people/17/?format=api", "name": "Paul Elder", "email": "paul.elder@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/9389/mbox/", "series": [ { "id": 1243, "url": "https://patchwork.libcamera.org/api/series/1243/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=1243", "date": "2020-08-26T11:09:09", "name": "[libcamera-devel,RFC,01/17] IPA: IPC: raspberrypi: Add data definition and generated header", "version": 1, "mbox": "https://patchwork.libcamera.org/series/1243/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/9389/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/9389/checks/", "tags": {}, "headers": { "Return-Path": "<libcamera-devel-bounces@lists.libcamera.org>", "X-Original-To": "parsemail@patchwork.libcamera.org", "Delivered-To": "parsemail@patchwork.libcamera.org", "Received": [ "from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 4E259BD87E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 26 Aug 2020 11:10:03 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1A773628F9;\n\tWed, 26 Aug 2020 13:10:03 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 43794628E6\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 26 Aug 2020 13:10:01 +0200 (CEST)", "from pyrite.rasen.tech (unknown\n\t[IPv6:2400:4051:61:600:2c71:1b79:d06d:5032])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 188719CE;\n\tWed, 26 Aug 2020 13:09:58 +0200 (CEST)" ], "Authentication-Results": "lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"FCqb1Hz2\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1598440201;\n\tbh=YjoOuE7v0XfTU6DmTt/8gVf6mg1AqOc4JKPOkzG9Ipg=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=FCqb1Hz2GSbLlSmRj7t1CoPVa0tWFmmaMpQoQduAhwOVsAtn2kSGRPgMzJhykn27J\n\tIt3T3Mq3MVbPflv6xDNpShJdSC1Y+/U2Drsoh1cEqdWF0xyVJtWJ1P+A/v/s9+OsHS\n\tpZnGbXLa738DfHW7+cDFBsPlhpHcV+I1Kl902m2g=", "From": "Paul Elder <paul.elder@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Wed, 26 Aug 2020 20:09:14 +0900", "Message-Id": "<20200826110926.67192-6-paul.elder@ideasonboard.com>", "X-Mailer": "git-send-email 2.27.0", "In-Reply-To": "<20200826110926.67192-1-paul.elder@ideasonboard.com>", "References": "<20200826110926.67192-1-paul.elder@ideasonboard.com>", "MIME-Version": "1.0", "Subject": "[libcamera-devel] [RFC PATCH 05/17] IPA: IPC: add IPAProxyRPi and\n\tIPAProxyRPiWorker", "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>", "Content-Type": "text/plain; charset=\"us-ascii\"", "Content-Transfer-Encoding": "7bit", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "This patch implements IPAProxyRPi and the corresponding proxy worker.\nBoth of these are meant to be generated based on some template proxy,\nand the RPi IPA interface mojo definition.\n\nThe IPAProxyRPi implements an IPARPiInterface, and internally does a\nswitch on if isolation is enabled or not, and does either a thread call\ninto the IPARPi directly, or sends a message over IPC. The IPC mechanism\nis implemented in an IPAIPC, and can be swapped out (though swapping out\nisn't enabled yet, as we only have one IPC mechanism for now). For this\nreason, the proxy (and worker) is responsible for calling the\nde/serializer. On the other side, the proxy worker listens on the socket\n(in the case of IPAIPCUnixSocket), and does the switch-case on the cmd\nto call the appropriate function from the linked IPARPi.\n\nFor callbacks, since they're implemented as signals, we simply propagate\nthe signal emission (except when crossing the process boundary, which\nhas a send-receive).\n\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\n---\n .../internal/ipa_proxy_raspberrypi.h | 117 ++++++\n src/libcamera/proxy/ipa_proxy_raspberrypi.cpp | 381 ++++++++++++++++++\n .../worker/ipa_proxy_raspberrypi_worker.cpp | 355 ++++++++++++++++\n 3 files changed, 853 insertions(+)\n create mode 100644 include/libcamera/internal/ipa_proxy_raspberrypi.h\n create mode 100644 src/libcamera/proxy/ipa_proxy_raspberrypi.cpp\n create mode 100644 src/libcamera/proxy/worker/ipa_proxy_raspberrypi_worker.cpp", "diff": "diff --git a/include/libcamera/internal/ipa_proxy_raspberrypi.h b/include/libcamera/internal/ipa_proxy_raspberrypi.h\nnew file mode 100644\nindex 00000000..bc2001ad\n--- /dev/null\n+++ b/include/libcamera/internal/ipa_proxy_raspberrypi.h\n@@ -0,0 +1,117 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2020, Google Inc.\n+ *\n+ * ipa_proxy_raspberrypi.h - Image Processing Algorithm proxy for Raspberry pi\n+ */\n+#ifndef __LIBCAMERA_INTERNAL_IPA_PROXY_RASPBERRYPI_H__\n+#define __LIBCAMERA_INTERNAL_IPA_PROXY_RASPBERRYPI_H__\n+\n+// automatically generated by custom compiler\n+\n+// TODO move this to a proxy header directory\n+\n+#include <libcamera/ipa/ipa_interface.h>\n+#include <libcamera/ipa/raspberrypi.h>\n+#include <libcamera/ipa/raspberrypi_wrapper.h>\n+\n+#include \"libcamera/internal/ipa_ipc.h\"\n+#include \"libcamera/internal/ipa_proxy.h\"\n+#include \"libcamera/internal/ipc_unixsocket.h\"\n+#include \"libcamera/internal/thread.h\"\n+\n+namespace libcamera {\n+\n+class IPAProxyRPi : public IPAProxy, public IPARPiInterface\n+{\n+public:\n+\tIPAProxyRPi(IPAModule *ipam, bool isolate);\n+\t~IPAProxyRPi();\n+\n+\tint init(const IPASettings &settings) override;\n+\tint start() override;\n+\tvoid stop() override;\n+\tvoid configure(const CameraSensorInfo &sensorInfo,\n+\t\t const std::map<unsigned int, IPAStream> &streamConfig,\n+\t\t const std::map<unsigned int, const ControlInfoMap> &entityControls,\n+\t\t const RPiConfigureParams &ipaConfig,\n+\t\t RPiConfigureParams *result) override;\n+\tvoid mapBuffers(const std::vector<IPABuffer> &buffers) override;\n+\tvoid unmapBuffers(const std::vector<unsigned int> &ids) override;\n+\tvoid processEvent(const RPiEventParams &event) override;\n+\n+\tSignal<unsigned int, const RPiActionParams &> queueFrameAction;\n+\n+private:\n+\tvoid recvIPC(std::vector<uint8_t> &data, std::vector<int32_t> &fds);\n+\n+\tint initThread(const IPASettings &settings);\n+\tint startThread();\n+\tvoid stopThread();\n+\tvoid configureThread(const CameraSensorInfo &sensorInfo,\n+\t\t const std::map<unsigned int, IPAStream> &streamConfig,\n+\t\t const std::map<unsigned int, const ControlInfoMap> &entityControls,\n+\t\t const RPiConfigureParams &ipaConfig,\n+\t\t RPiConfigureParams *result);\n+\tvoid mapBuffersThread(const std::vector<IPABuffer> &buffers);\n+\tvoid unmapBuffersThread(const std::vector<unsigned int> &ids);\n+\tvoid processEventThread(const RPiEventParams &event);\n+\tvoid frameActionHandlerThread(unsigned int frame, const RPiActionParams &data);\n+\n+\tint initIPC(const IPASettings &settings);\n+\tint startIPC();\n+\tvoid stopIPC();\n+\tvoid configureIPC(const CameraSensorInfo &sensorInfo,\n+\t\t const std::map<unsigned int, IPAStream> &streamConfig,\n+\t\t const std::map<unsigned int, const ControlInfoMap> &entityControls,\n+\t\t const RPiConfigureParams &ipaConfig,\n+\t\t RPiConfigureParams *result);\n+\tvoid mapBuffersIPC(const std::vector<IPABuffer> &buffers);\n+\tvoid unmapBuffersIPC(const std::vector<unsigned int> &ids);\n+\tvoid processEventIPC(const RPiEventParams &event);\n+\tvoid frameActionHandlerIPC(std::vector<uint8_t> &data,\n+\t\t\t\t std::vector<int32_t> &fds);\n+\n+\t/* Helper class to invoke processEvent() in another thread. */\n+\tclass ThreadProxy : public Object\n+\t{\n+\tpublic:\n+\t\tvoid setIPA(IPARPiInterface *ipa)\n+\t\t{\n+\t\t\tipa_ = ipa;\n+\t\t}\n+\n+\t\tint start()\n+\t\t{\n+\t\t\treturn ipa_->start();\n+\t\t}\n+\n+\t\tvoid stop()\n+\t\t{\n+\t\t\tipa_->stop();\n+\t\t}\n+\n+\t\tvoid processEvent(const RPiEventParams &event)\n+\t\t{\n+\t\t\tipa_->processEvent(event);\n+\t\t}\n+\n+\tprivate:\n+\t\tIPARPiInterface *ipa_;\n+\t};\n+\n+\tbool running_;\n+\tThread thread_;\n+\tThreadProxy proxy_;\n+\tstd::unique_ptr<IPARPiInterface> ipa_;\n+\n+\tconst bool isolate_;\n+\n+\tstd::unique_ptr<IPAIPC> ipc_;\n+\n+\tControlSerializer controlSerializer_;\n+};\n+\n+} /* namespace libcamera */\n+\n+#endif /* __LIBCAMERA_INTERNAL_IPA_PROXY_RASPBERRYPI_H__ */\ndiff --git a/src/libcamera/proxy/ipa_proxy_raspberrypi.cpp b/src/libcamera/proxy/ipa_proxy_raspberrypi.cpp\nnew file mode 100644\nindex 00000000..6526fb1d\n--- /dev/null\n+++ b/src/libcamera/proxy/ipa_proxy_raspberrypi.cpp\n@@ -0,0 +1,381 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2020, Google Inc.\n+ *\n+ * ipa_proxy_raspberrypi.cpp - Image Processing Algorithm proxy for Raspberry pi\n+ */\n+\n+// automatically generated by custom compiler\n+\n+#include <vector>\n+\n+#include <libcamera/ipa/ipa_interface.h>\n+#include <libcamera/ipa/ipa_module_info.h>\n+#include <libcamera/ipa/raspberrypi.h>\n+#include <libcamera/ipa/raspberrypi_wrapper.h>\n+#include <libcamera/ipa/raspberrypi_serializer.h>\n+\n+#include \"libcamera/internal/control_serializer.h\"\n+#include \"libcamera/internal/ipa_ipc.h\"\n+#include \"libcamera/internal/ipa_data_serializer.h\"\n+#include \"libcamera/internal/ipa_module.h\"\n+#include \"libcamera/internal/ipa_proxy.h\"\n+#include \"libcamera/internal/ipc_unixsocket.h\"\n+#include \"libcamera/internal/log.h\"\n+#include \"libcamera/internal/process.h\"\n+#include \"libcamera/internal/thread.h\"\n+\n+#include \"libcamera/internal/ipa_proxy_raspberrypi.h\"\n+\n+// just inline this at code generation\n+#define CALL_THREAD_OR_IPC(isolate, thread_func, ipc_func, ...) \\\n+\t(isolate ? ipc_func(__VA_ARGS__) : thread_func(__VA_ARGS__))\n+\n+namespace libcamera {\n+\n+LOG_DECLARE_CATEGORY(IPAProxy)\n+\n+// TODO yeah we really need to move this into a header somewhere\n+void appendUInt32(std::vector<uint8_t> &vec, uint32_t val)\n+{\n+\tuint8_t arr[] = {static_cast<uint8_t>(val & 0xff),\n+\t\t\t static_cast<uint8_t>((val >> 8) & 0xff),\n+\t\t\t static_cast<uint8_t>((val >> 16) & 0xff),\n+\t\t\t static_cast<uint8_t>((val >> 24) & 0xff)};\n+\tvec.insert(vec.end(), arr, arr + 4);\n+}\n+\n+IPAProxyRPi::IPAProxyRPi(IPAModule *ipam, bool isolate)\n+\t: IPAProxy(ipam), running_(false),\n+\t isolate_(isolate)\n+{\n+\tLOG(IPAProxy, Debug)\n+\t\t<< \"initializing dummy proxy: loading IPA from \"\n+\t\t<< ipam->path();\n+\n+\tif (isolate_) {\n+\t\tconst std::string proxy_worker_path = resolvePath(\"ipa_proxy_raspberrypi\");\n+\t\tif (proxy_worker_path.empty()) {\n+\t\t\tLOG(IPAProxy, Error)\n+\t\t\t\t<< \"Failed to get proxy worker path\";\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tIPAIPCFactory *ipcf = nullptr;\n+\t\tfor (IPAIPCFactory *factory : IPAIPCFactory::factories()) {\n+\t\t\tif (!strcmp(factory->name().c_str(), \"IPAIPCUnixSocket\")) {\n+\t\t\t\tipcf = factory;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (!ipcf) {\n+\t\t\tLOG(IPAProxy, Error) << \"Failed to get IPAIPC factory\";\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tipc_ = ipcf->create(ipam->path().c_str(), proxy_worker_path.c_str());\n+\t\tif (!ipc_->isValid()) {\n+\t\t\tLOG(IPAProxy, Error) << \"Failed to create IPAIPC\";\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tipc_->recvIPC.connect(this, &IPAProxyRPi::recvIPC);\n+\n+\t\tvalid_ = true;\n+\t\treturn;\n+\t}\n+\n+\tif (!ipam->load())\n+\t\treturn;\n+\n+\tIPAInterface *ipai = ipam->createInterface();\n+\tif (!ipai) {\n+\t\tLOG(IPAProxy, Error)\n+\t\t\t<< \"Failed to create IPA context for \" << ipam->path();\n+\t\treturn;\n+\t}\n+\n+\tipa_ = std::unique_ptr<IPARPiInterface>(dynamic_cast<IPARPiInterface *>(ipai));\n+\tproxy_.setIPA(ipa_.get());\n+\n+\t/*\n+\t * Proxy the queueFrameAction signal to dispatch it in the caller's\n+\t * thread.\n+\t */\n+\tipa_->queueFrameAction.connect(this, &IPAProxyRPi::frameActionHandlerThread);\n+\n+\tvalid_ = true;\n+}\n+\n+IPAProxyRPi::~IPAProxyRPi()\n+{\n+\tif (isolate_)\n+\t\tipc_->sendAsync(CMD_EXIT, {}, {});\n+}\n+\n+void IPAProxyRPi::recvIPC(std::vector<uint8_t> &data, std::vector<int32_t> &fds)\n+{\n+\tuint32_t cmd = (data[0] & 0xff) |\n+\t\t ((data[1] & 0xff) << 8) |\n+\t\t ((data[2] & 0xff) << 16) |\n+\t\t ((data[3] & 0xff) << 24);\n+\n+\t/* Need to skip another 4 bytes for the sequence number. */\n+\tstd::vector<uint8_t> vec(data.begin() + 8, data.end());\n+\n+\tswitch (cmd) {\n+\tcase CMD_QUEUEFRAMEACTION: {\n+\t\tframeActionHandlerIPC(vec, fds);\n+\t\tbreak;\n+\t}\n+\t}\n+}\n+\n+int IPAProxyRPi::init(const IPASettings &settings)\n+{\n+\treturn CALL_THREAD_OR_IPC(isolate_, initThread, initIPC, settings);\n+}\n+\n+int IPAProxyRPi::start()\n+{\n+\treturn CALL_THREAD_OR_IPC(isolate_, startThread, startIPC);\n+}\n+\n+void IPAProxyRPi::stop()\n+{\n+\tCALL_THREAD_OR_IPC(isolate_, stopThread, stopIPC);\n+}\n+\n+void IPAProxyRPi::configure(const CameraSensorInfo &sensorInfo,\n+\t\t\t const std::map<unsigned int, IPAStream> &streamConfig,\n+\t\t\t const std::map<unsigned int, const ControlInfoMap> &entityControls,\n+\t\t\t const RPiConfigureParams &ipaConfig,\n+\t\t\t RPiConfigureParams *result)\n+{\n+\tCALL_THREAD_OR_IPC(isolate_, configureThread, configureIPC,\n+\t\t\t sensorInfo, streamConfig, entityControls, ipaConfig, result);\n+}\n+\n+void IPAProxyRPi::mapBuffers(const std::vector<IPABuffer> &buffers)\n+{\n+\tCALL_THREAD_OR_IPC(isolate_, mapBuffersThread, mapBuffersIPC, buffers);\n+}\n+\n+void IPAProxyRPi::unmapBuffers(const std::vector<unsigned int> &ids)\n+{\n+\tCALL_THREAD_OR_IPC(isolate_, unmapBuffersThread, unmapBuffersIPC, ids);\n+}\n+\n+void IPAProxyRPi::processEvent(const RPiEventParams &event)\n+{\n+\tCALL_THREAD_OR_IPC(isolate_, processEventThread, processEventIPC, event);\n+}\n+\n+/* Thread functions */\n+\n+int IPAProxyRPi::initThread(const IPASettings &settings)\n+{\n+\tint ret = ipa_->init(settings);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tproxy_.moveToThread(&thread_);\n+\n+\treturn 0;\n+}\n+\n+int IPAProxyRPi::startThread()\n+{\n+\trunning_ = true;\n+\tthread_.start();\n+\n+\treturn proxy_.invokeMethod(&ThreadProxy::start, ConnectionTypeBlocking);\n+}\n+\n+void IPAProxyRPi::stopThread()\n+{\n+\tif (!running_)\n+\t\treturn;\n+\n+\trunning_ = false;\n+\n+\tproxy_.invokeMethod(&ThreadProxy::stop, ConnectionTypeBlocking);\n+\n+\tthread_.exit();\n+\tthread_.wait();\n+}\n+\n+void IPAProxyRPi::configureThread(const CameraSensorInfo &sensorInfo,\n+\t\t\t const std::map<unsigned int, IPAStream> &streamConfig,\n+\t\t\t const std::map<unsigned int, const ControlInfoMap> &entityControls,\n+\t\t\t const RPiConfigureParams &ipaConfig,\n+\t\t\t RPiConfigureParams *result)\n+{\n+\tipa_->configure(sensorInfo, streamConfig, entityControls, ipaConfig,\n+\t\t\tresult);\n+}\n+\n+void IPAProxyRPi::mapBuffersThread(const std::vector<IPABuffer> &buffers)\n+{\n+\tipa_->mapBuffers(buffers);\n+}\n+\n+void IPAProxyRPi::unmapBuffersThread(const std::vector<unsigned int> &ids)\n+{\n+\tipa_->unmapBuffers(ids);\n+}\n+\n+void IPAProxyRPi::processEventThread(const RPiEventParams &event)\n+{\n+\tif (!running_)\n+\t\treturn;\n+\n+\t/* Dispatch the processEvent() call to the thread. */\n+\tproxy_.invokeMethod(&ThreadProxy::processEvent, ConnectionTypeQueued,\n+\t\t\t event);\n+}\n+\n+void IPAProxyRPi::frameActionHandlerThread(unsigned int frame, const RPiActionParams &data)\n+{\n+\tqueueFrameAction.emit(frame, data);\n+}\n+\n+/* IPC functions */\n+\n+int IPAProxyRPi::initIPC(const IPASettings &settings)\n+{\n+\tstd::vector<uint8_t> buf;\n+\tstd::tie(buf, std::ignore) = IPADataSerializer<IPASettings>::serialize(settings);\n+\tstd::vector<uint8_t> resultBuf;\n+\n+\tint ret = ipc_->sendSync(CMD_INIT, buf, {}, &resultBuf);\n+\tif (ret < 0) {\n+\t\tLOG(IPAProxy, Error) << \"Failed to call init\";\n+\t\treturn ret;\n+\t}\n+\tLOG(IPAProxy, Info) << \"Done calling init\";\n+\n+\t/* Maybe we should cast this properly? */\n+\treturn static_cast<int32_t>(resultBuf[0]);\n+}\n+\n+int IPAProxyRPi::startIPC()\n+{\n+\tstd::vector<uint8_t> resultBuf;\n+\n+\tint ret = ipc_->sendSync(CMD_START, {}, {}, &resultBuf);\n+\tif (ret < 0) {\n+\t\tLOG(IPAProxy, Error) << \"Failed to call start\";\n+\t\treturn ret;\n+\t}\n+\tLOG(IPAProxy, Info) << \"Done calling start\";\n+\n+\t/* Maybe we should cast this properly? */\n+\treturn static_cast<int32_t>(resultBuf[0]);\n+}\n+\n+void IPAProxyRPi::stopIPC()\n+{\n+\tint ret = ipc_->sendSync(CMD_STOP, {}, {});\n+\tif (ret < 0)\n+\t\tLOG(IPAProxy, Error) << \"Failed to call stop\";\n+\tLOG(IPAProxy, Info) << \"Done calling stop\";\n+}\n+\n+void IPAProxyRPi::configureIPC(const CameraSensorInfo &sensorInfo,\n+\t\t\t const std::map<unsigned int, IPAStream> &streamConfig,\n+\t\t\t const std::map<unsigned int, const ControlInfoMap> &entityControls,\n+\t\t\t const RPiConfigureParams &ipaConfig,\n+\t\t\t RPiConfigureParams *result)\n+{\n+\tstd::vector<uint8_t> sensorInfoBuf;\n+\tstd::tie(sensorInfoBuf, std::ignore) =\n+\t\tIPADataSerializer<CameraSensorInfo>::serialize(sensorInfo);\n+\tstd::vector<uint8_t> streamConfigBuf;\n+\tstd::tie(streamConfigBuf, std::ignore) =\n+\t\tIPADataSerializer<std::map<unsigned int, IPAStream>>::serialize(streamConfig);\n+\tstd::vector<uint8_t> entityControlsBuf;\n+\tstd::tie(entityControlsBuf, std::ignore) =\n+\t\tIPADataSerializer<std::map<unsigned int, const ControlInfoMap>>::serialize(entityControls, &controlSerializer_);\n+\tstd::vector<uint8_t> ipaConfigBuf;\n+\tstd::vector<int32_t> ipaConfigFds;\n+\tstd::tie(ipaConfigBuf, ipaConfigFds) =\n+\t\tIPADataSerializer<RPiConfigureParams>::serialize(ipaConfig, &controlSerializer_);\n+\n+\tstd::vector<uint8_t> resultBuf;\n+\tstd::vector<int32_t> resultFds;\n+\n+\tstd::vector<uint8_t> input;\n+\tappendUInt32(input, sensorInfoBuf.size());\n+\tappendUInt32(input, streamConfigBuf.size());\n+\tappendUInt32(input, entityControlsBuf.size());\n+\tappendUInt32(input, ipaConfigBuf.size());\n+\tappendUInt32(input, ipaConfigFds.size());\n+\tinput.insert(input.end(), sensorInfoBuf.begin(), sensorInfoBuf.end());\n+\tinput.insert(input.end(), streamConfigBuf.begin(), streamConfigBuf.end());\n+\tinput.insert(input.end(), entityControlsBuf.begin(), entityControlsBuf.end());\n+\tinput.insert(input.end(), ipaConfigBuf.begin(), ipaConfigBuf.end());\n+\n+\tstd::vector<int32_t> fds;\n+\tfds.insert(fds.end(), ipaConfigFds.begin(), ipaConfigFds.end());\n+\n+\tint ret = ipc_->sendSync(CMD_CONFIGURE, input, fds, &resultBuf, &resultFds);\n+\tif (ret < 0) {\n+\t\tLOG(IPAProxy, Error) << \"Failed to call configure\";\n+\t\treturn;\n+\t}\n+\tLOG(IPAProxy, Info) << \"Done calling configure\";\n+\n+\t*result = IPADataSerializer<RPiConfigureParams>::deserialize(resultBuf, resultFds, &controlSerializer_);\n+}\n+\n+void IPAProxyRPi::mapBuffersIPC(const std::vector<IPABuffer> &buffers)\n+{\n+\tstd::vector<uint8_t> buffersBuf;\n+\tstd::vector<int32_t> fdsBuf;\n+\tstd::tie(buffersBuf, fdsBuf) =\n+\t\tIPADataSerializer<std::vector<IPABuffer>>::serialize(buffers);\n+\n+\tint ret = ipc_->sendSync(CMD_MAPBUFFERS, buffersBuf, fdsBuf);\n+\tif (ret < 0)\n+\t\tLOG(IPAProxy, Error) << \"Failed to call mapBuffers\";\n+\tLOG(IPAProxy, Info) << \"Done calling mapBuffers\";\n+}\n+\n+void IPAProxyRPi::unmapBuffersIPC(const std::vector<unsigned int> &ids)\n+{\n+\tstd::vector<uint8_t> idsBuf;\n+\tstd::tie(idsBuf, std::ignore) =\n+\t\tIPADataSerializer<std::vector<unsigned int>>::serialize(ids);\n+\tint ret = ipc_->sendSync(CMD_UNMAPBUFFERS, idsBuf, {});\n+\tif (ret < 0)\n+\t\tLOG(IPAProxy, Error) << \"Failed to call unmapBuffers\";\n+\tLOG(IPAProxy, Info) << \"Done calling unmapBuffers\";\n+}\n+\n+void IPAProxyRPi::processEventIPC(const RPiEventParams &event)\n+{\n+\tstd::vector<uint8_t> eventBuf;\n+\tstd::tie(eventBuf, std::ignore) =\n+\t\tIPADataSerializer<RPiEventParams>::serialize(event, &controlSerializer_);\n+\tint ret = ipc_->sendAsync(CMD_PROCESSEVENT, eventBuf, {});\n+\tif (ret < 0)\n+\t\tLOG(IPAProxy, Error) << \"Failed to call processEvent\";\n+\tLOG(IPAProxy, Info) << \"Done calling processEvent\";\n+}\n+\n+void IPAProxyRPi::frameActionHandlerIPC(std::vector<uint8_t> &data,\n+\t\t\t\t\t[[maybe_unused]] std::vector<int32_t> &fds)\n+{ \n+\tuint32_t frame = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);\n+\tRPiActionParams params =\n+\t\tIPADataSerializer<RPiActionParams>::deserialize(data.begin() + 4, data.end(), &controlSerializer_);\n+\n+\tqueueFrameAction.emit(frame, params);\n+}\n+\n+\n+REGISTER_IPA_PROXY(IPAProxyRPi)\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/proxy/worker/ipa_proxy_raspberrypi_worker.cpp b/src/libcamera/proxy/worker/ipa_proxy_raspberrypi_worker.cpp\nnew file mode 100644\nindex 00000000..ad917756\n--- /dev/null\n+++ b/src/libcamera/proxy/worker/ipa_proxy_raspberrypi_worker.cpp\n@@ -0,0 +1,355 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2020, Google Inc.\n+ *\n+ * ipa_proxy_raspberrypi_worker.cpp - Image Processing Algorithm proxy worker for Raspberry pi\n+ */\n+\n+// automatically generated by custom compiler\n+\n+#include <algorithm>\n+#include <iostream>\n+#include <sys/types.h>\n+#include <tuple>\n+#include <unistd.h>\n+\n+#include <libcamera/event_dispatcher.h>\n+#include <libcamera/ipa/ipa_interface.h>\n+#include <libcamera/ipa/raspberrypi_wrapper.h>\n+#include <libcamera/ipa/raspberrypi_serializer.h>\n+#include <libcamera/logging.h>\n+\n+#include \"libcamera/internal/camera_sensor.h\"\n+#include \"libcamera/internal/control_serializer.h\"\n+#include \"libcamera/internal/ipa_data_serializer.h\"\n+#include \"libcamera/internal/ipa_module.h\"\n+#include \"libcamera/internal/ipc_unixsocket.h\"\n+#include \"libcamera/internal/log.h\"\n+#include \"libcamera/internal/thread.h\"\n+\n+using namespace libcamera;\n+\n+LOG_DEFINE_CATEGORY(IPAProxyRPiWorker)\n+\n+struct CallData {\n+\tIPCUnixSocket::Payload *response;\n+\tbool done;\n+};\n+\n+IPARPiInterface *ipa_;\n+IPCUnixSocket socket_;\n+std::map<uint32_t, struct CallData> callData_;\n+\n+ControlSerializer controlSerializer_;\n+\n+bool exit_ = false;\n+\n+void writeHeader(IPCUnixSocket::Payload &payload, uint32_t cmd, uint32_t seq)\n+{\n+\tuint8_t cmd_arr[] = {static_cast<uint8_t>(cmd & 0xff),\n+\t\t\t static_cast<uint8_t>((cmd >> 8 ) & 0xff),\n+\t\t\t static_cast<uint8_t>((cmd >> 16) & 0xff),\n+\t\t\t static_cast<uint8_t>((cmd >> 24) & 0xff)};\n+\tuint8_t seq_arr[] = {static_cast<uint8_t>(seq & 0xff),\n+\t\t\t static_cast<uint8_t>((seq >> 8 ) & 0xff),\n+\t\t\t static_cast<uint8_t>((seq >> 16) & 0xff),\n+\t\t\t static_cast<uint8_t>((seq >> 24) & 0xff)};\n+\tpayload.data.insert(payload.data.begin(), cmd_arr, cmd_arr+4);\n+\tpayload.data.insert(payload.data.begin() + 4, seq_arr, seq_arr+4);\n+}\n+\n+// cmd, seq\n+std::tuple<uint32_t, uint32_t> readHeader(IPCUnixSocket::Payload &payload)\n+{\n+\tuint32_t cmd = (payload.data[0] & 0xff) |\n+\t\t ((payload.data[1] & 0xff) << 8) |\n+\t\t ((payload.data[2] & 0xff) << 16) |\n+\t\t ((payload.data[3] & 0xff) << 24);\n+\tuint32_t seq = (payload.data[4] & 0xff) |\n+\t\t ((payload.data[5] & 0xff) << 8) |\n+\t\t ((payload.data[6] & 0xff) << 16) |\n+\t\t ((payload.data[7] & 0xff) << 24);\n+\n+\treturn {cmd, seq};\n+}\n+\n+void eraseHeader(IPCUnixSocket::Payload &payload)\n+{\n+\tpayload.data.erase(payload.data.begin(), payload.data.begin() + 8);\n+}\n+\n+void writeUInt32(IPCUnixSocket::Payload &payload, uint32_t val, uint32_t pos)\n+{\n+\tif (pos + 4 > payload.data.size())\n+\t\tpayload.data.resize(pos + 4);\n+\n+\tuint8_t arr[] = {static_cast<uint8_t>(val & 0xff),\n+\t\t\t static_cast<uint8_t>((val >> 8 ) & 0xff),\n+\t\t\t static_cast<uint8_t>((val >> 16) & 0xff),\n+\t\t\t static_cast<uint8_t>((val >> 24) & 0xff)};\n+\n+\tstd::copy(arr, arr + 4, payload.data.begin() + pos);\n+}\n+\n+uint32_t readUInt32(IPCUnixSocket::Payload &payload, uint32_t pos)\n+{\n+\tif (pos + 4 > payload.data.size())\n+\t\treturn 0;\n+\n+\treturn (payload.data[pos] & 0xff) |\n+\t ((payload.data[pos + 1] & 0xff) << 8) |\n+\t ((payload.data[pos + 2] & 0xff) << 16) |\n+\t ((payload.data[pos + 3] & 0xff) << 24);\n+}\n+\n+void readyRead(IPCUnixSocket *socket)\n+{\n+\tIPCUnixSocket::Payload message, response;\n+\tint ret = socket->receive(&message);\n+\tif (ret) {\n+\t\tLOG(IPAProxyRPiWorker, Error)\n+\t\t\t<< \"Receive message failed\" << ret;\n+\t\treturn;\n+\t}\n+\n+\tuint32_t cmd, seq;\n+\tstd::tie(cmd, seq) = readHeader(message);\n+\teraseHeader(message);\n+\n+\tswitch (cmd) {\n+\tcase CMD_INIT: {\n+\t\tIPASettings settings = IPADataSerializer<IPASettings>::deserialize(message.data);\n+\n+\t\tint ret = ipa_->init(settings);\n+\t\twriteHeader(response, cmd, seq);\n+\t\t/* Maybe we should do this cast properly? */\n+\t\tresponse.data.push_back(static_cast<uint8_t>(ret));\n+\n+\t\tret = socket_.send(response);\n+\t\tif (ret < 0) {\n+\t\t\tLOG(IPAProxyRPiWorker, Error)\n+\t\t\t\t<< \"Reply to init() failed\" << ret;\n+\t\t}\n+\n+\t\tLOG(IPAProxyRPiWorker, Info) << \"Done replying to init()\";\n+\t\tbreak;\n+\t}\n+\n+\tcase CMD_EXIT: {\n+\t\texit_ = true;\n+\t\tbreak;\n+\t}\n+\n+\tcase CMD_START: {\n+\t\tint ret = ipa_->start();\n+\t\twriteHeader(response, cmd, seq);\n+\t\t/* Maybe we should do this cast properly? */\n+\t\tresponse.data.push_back(static_cast<uint8_t>(ret));\n+\n+\t\tret = socket_.send(response);\n+\t\tif (ret < 0) {\n+\t\t\tLOG(IPAProxyRPiWorker, Error)\n+\t\t\t\t<< \"Reply to start() failed\" << ret;\n+\t\t}\n+\n+\t\tLOG(IPAProxyRPiWorker, Info) << \"Done replying to start()\";\n+\t\tbreak;\n+\t}\n+\n+\tcase CMD_STOP: {\n+\t\tipa_->stop();\n+\n+\t\twriteHeader(response, cmd, seq);\n+\t\tint ret = socket_.send(response);\n+\t\tif (ret < 0) {\n+\t\t\tLOG(IPAProxyRPiWorker, Error)\n+\t\t\t\t<< \"Reply to stop() failed\" << ret;\n+\t\t}\n+\n+\t\tLOG(IPAProxyRPiWorker, Info) << \"Done replying to stop()\";\n+\t\tbreak;\n+\t}\n+\n+\tcase CMD_CONFIGURE: {\n+\t\tLOG(IPAProxyRPiWorker, Info) << \"Starting configure()\";\n+\n+\t\tsize_t sensorInfoSize = readUInt32(message, 0);\n+\t\tsize_t streamConfigSize = readUInt32(message, 4);\n+\t\tsize_t entityControlsSize = readUInt32(message, 8);\n+\n+\t\t// TODO make this pattern more like the data ones\n+\t\t// - this will be fixed in the compiler\n+\t\t// remember to fix these offsets too\n+\t\tsize_t ipaConfigFdsSize = readUInt32(message, 16);\n+\n+\t\tsize_t sensorInfoStart = 20;\n+\t\tsize_t streamConfigStart = sensorInfoStart + sensorInfoSize;\n+\t\tsize_t entityControlsStart = streamConfigStart + streamConfigSize;\n+\t\tsize_t ipaConfigStart = entityControlsStart + entityControlsSize;\n+\n+\t\tLOG(IPAProxyRPiWorker, Info) << \" deserializing sensorInfo...\";\n+\t\tstruct CameraSensorInfo sensorInfo =\n+\t\t\tIPADataSerializer<struct CameraSensorInfo>::deserialize(\n+\t\t\t\tmessage.data.begin() + sensorInfoStart,\n+\t\t\t\tmessage.data.begin() + streamConfigStart);\n+\t\tLOG(IPAProxyRPiWorker, Info) << \" deserializing streamConfig...\";\n+\t\tstd::map<unsigned int, struct IPAStream> streamConfig =\n+\t\t\tIPADataSerializer<std::map<unsigned int, struct IPAStream>>::deserialize(\n+\t\t\t\tmessage.data.begin() + streamConfigStart,\n+\t\t\t\tmessage.data.begin() + entityControlsStart);\n+\t\tLOG(IPAProxyRPiWorker, Info) << \" deserializing entityControls...\";\n+\t\tconst std::map<unsigned int, const ControlInfoMap> entityControls =\n+\t\t\tIPADataSerializer<std::map<unsigned int, const ControlInfoMap>>::deserialize(\n+\t\t\t\tmessage.data.begin() + entityControlsStart,\n+\t\t\t\tmessage.data.begin() + ipaConfigStart,\n+\t\t\t\t&controlSerializer_);\n+\t\tLOG(IPAProxyRPiWorker, Info) << \" deserializing ipaConfig...\";\n+\t\tRPiConfigureParams ipaConfig =\n+\t\t\tIPADataSerializer<RPiConfigureParams>::deserialize(\n+\t\t\t\tmessage.data.begin() + ipaConfigStart,\n+\t\t\t\tmessage.data.end(),\n+\t\t\t\tmessage.fds.begin(),\n+\t\t\t\tmessage.fds.begin() + ipaConfigFdsSize,\n+\t\t\t\t&controlSerializer_);\n+\t\tRPiConfigureParams results;\n+\n+\t\tLOG(IPAProxyRPiWorker, Info) << \"Calling IPA's configure()\";\n+\t\tipa_->configure(sensorInfo, streamConfig, entityControls, ipaConfig, &results);\n+\t\tLOG(IPAProxyRPiWorker, Info) << \"Done calling IPA's configure()\";\n+\n+\t\tstd::vector<uint8_t> resultsSerialized;\n+\t\tstd::vector<int32_t> resultsFds;\n+\t\tstd::tie(resultsSerialized, resultsFds) =\n+\t\t\tIPADataSerializer<RPiConfigureParams>::serialize(results, &controlSerializer_);\n+\n+\t\twriteHeader(response, cmd, seq);\n+\t\tresponse.data.insert(response.data.end(),\n+\t\t\t\t resultsSerialized.begin(), resultsSerialized.end());\n+\t\tresponse.fds.insert(response.fds.end(),\n+\t\t\t\t resultsFds.begin(), resultsFds.end());\n+\t\tLOG(IPAProxyRPiWorker, Info) << \"Sending response to configure()\";\n+\t\tint ret = socket_.send(response);\n+\t\tif (ret < 0) {\n+\t\t\tLOG(IPAProxyRPiWorker, Error)\n+\t\t\t\t<< \"Reply to configure() failed\" << ret;\n+\t\t}\n+\n+\t\tLOG(IPAProxyRPiWorker, Info) << \"Done replying to configure()\";\n+\t\tbreak;\n+\t}\n+\n+\tcase CMD_MAPBUFFERS: {\n+\t\tstd::vector<IPABuffer> ipaBuffers =\n+\t\t\tIPADataSerializer<std::vector<IPABuffer>>::deserialize(message.data, message.fds);\n+\t\tipa_->mapBuffers(ipaBuffers);\n+\n+\t\twriteHeader(response, cmd, seq);\n+\t\tint ret = socket_.send(response);\n+\t\tif (ret < 0) {\n+\t\t\tLOG(IPAProxyRPiWorker, Error)\n+\t\t\t\t<< \"Reply to mapBuffers() failed\" << ret;\n+\t\t}\n+\n+\t\tLOG(IPAProxyRPiWorker, Info) << \"Done replying to mapBuffers()\";\n+\t\tbreak;\n+\t}\n+\n+\tcase CMD_UNMAPBUFFERS: {\n+\t\tstd::vector<unsigned int> ids =\n+\t\t\tIPADataSerializer<std::vector<unsigned int>>::deserialize(message.data);\n+\t\tipa_->unmapBuffers(ids);\n+\n+\t\twriteHeader(response, cmd, seq);\n+\t\tint ret = socket_.send(response);\n+\t\tif (ret < 0) {\n+\t\t\tLOG(IPAProxyRPiWorker, Error)\n+\t\t\t\t<< \"Reply to unmapBuffers() failed\" << ret;\n+\t\t}\n+\n+\t\tLOG(IPAProxyRPiWorker, Info) << \"Done replying to unmapBuffers()\";\n+\t\tbreak;\n+\t}\n+\n+\tcase CMD_PROCESSEVENT: {\n+\t\tRPiEventParams ev =\n+\t\t\tIPADataSerializer<RPiEventParams>::deserialize(message.data, &controlSerializer_);\n+\t\tipa_->processEvent(ev);\n+\n+\t\tLOG(IPAProxyRPiWorker, Info) << \"Done processEvent()ing\";\n+\t\tbreak;\n+\t}\n+\n+\t}\n+\n+\treturn;\n+}\n+\n+void queueFrameAction(unsigned int frame, const RPiActionParams &action)\n+{\n+\tLOG(IPAProxyRPiWorker, Info) << \"queueFrameAction triggered\";\n+\tIPCUnixSocket::Payload message;\n+\twriteUInt32(message, frame, 0);\n+\tstd::vector<uint8_t> actionVector;\n+\tstd::tie(actionVector, std::ignore) =\n+\t\tIPADataSerializer<RPiActionParams>::serialize(action, &controlSerializer_);\n+\n+\tmessage.data.insert(message.data.end(), actionVector.begin(), actionVector.end());\n+\twriteHeader(message, CMD_QUEUEFRAMEACTION, 0);\n+\n+\tsocket_.send(message);\n+\tLOG(IPAProxyRPiWorker, Info) << \"queueFrameAction done\";\n+\treturn;\n+}\n+\n+int main(int argc, char **argv)\n+{\n+\t/* Uncomment this for debugging. */\n+\tstd::string logPath = \"/tmp/libcamera.worker.\" +\n+\t\t\t std::to_string(getpid()) + \".log\";\n+\tlogSetFile(logPath.c_str());\n+\t//logSetTarget(LoggingTargetSyslog);\n+\n+\tif (argc < 3) {\n+\t\tLOG(IPAProxyRPiWorker, Debug)\n+\t\t\t<< \"Tried to start worker with no args\";\n+\t\treturn EXIT_FAILURE;\n+\t}\n+\n+\tint fd = std::stoi(argv[2]);\n+\tLOG(IPAProxyRPiWorker, Debug)\n+\t\t<< \"Starting worker for IPA module \" << argv[1]\n+\t\t<< \" with IPC fd = \" << fd;\n+\n+\tstd::unique_ptr<IPAModule> ipam = std::make_unique<IPAModule>(argv[1]);\n+\tif (!ipam->isValid() || !ipam->load()) {\n+\t\tLOG(IPAProxyRPiWorker, Error)\n+\t\t\t<< \"IPAModule \" << argv[1] << \" should be valid but isn't\";\n+\t\treturn EXIT_FAILURE;\n+\t}\n+\n+\tif (socket_.bind(fd) < 0) {\n+\t\tLOG(IPAProxyRPiWorker, Error) << \"IPC socket binding failed\";\n+\t\treturn EXIT_FAILURE;\n+\t}\n+\tsocket_.readyRead.connect(&readyRead);\n+\n+\tipa_ = dynamic_cast<IPARPiInterface *>(ipam->createInterface());\n+\tif (!ipa_) {\n+\t\tLOG(IPAProxyRPiWorker, Error) << \"Failed to create IPA context\";\n+\t\treturn EXIT_FAILURE;\n+\t}\n+\n+\tipa_->queueFrameAction.connect(&queueFrameAction);\n+\n+\tLOG(IPAProxyRPiWorker, Debug) << \"Proxy worker successfully started\";\n+\n+\t/* \\todo upgrade listening loop */\n+\tEventDispatcher *dispatcher = Thread::current()->eventDispatcher();\n+\twhile (!exit_)\n+\t\tdispatcher->processEvents();\n+\n+\tdelete ipa_;\n+\tsocket_.close();\n+\n+\treturn 0;\n+}\n", "prefixes": [ "libcamera-devel", "RFC", "05/17" ] }