From patchwork Wed Aug 26 11:09:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 9389 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 4E259BD87E for ; Wed, 26 Aug 2020 11:10:03 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1A773628F9; Wed, 26 Aug 2020 13:10: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="FCqb1Hz2"; 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 43794628E6 for ; Wed, 26 Aug 2020 13:10:01 +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 188719CE; Wed, 26 Aug 2020 13:09:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1598440201; bh=YjoOuE7v0XfTU6DmTt/8gVf6mg1AqOc4JKPOkzG9Ipg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FCqb1Hz2GSbLlSmRj7t1CoPVa0tWFmmaMpQoQduAhwOVsAtn2kSGRPgMzJhykn27J It3T3Mq3MVbPflv6xDNpShJdSC1Y+/U2Drsoh1cEqdWF0xyVJtWJ1P+A/v/s9+OsHS pZnGbXLa738DfHW7+cDFBsPlhpHcV+I1Kl902m2g= From: Paul Elder 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 IPAProxyRPiWorker 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" This patch implements IPAProxyRPi and the corresponding proxy worker. Both of these are meant to be generated based on some template proxy, and the RPi IPA interface mojo definition. The IPAProxyRPi implements an IPARPiInterface, and internally does a switch on if isolation is enabled or not, and does either a thread call into the IPARPi directly, or sends a message over IPC. The IPC mechanism is implemented in an IPAIPC, and can be swapped out (though swapping out isn't enabled yet, as we only have one IPC mechanism for now). For this reason, the proxy (and worker) is responsible for calling the de/serializer. On the other side, the proxy worker listens on the socket (in the case of IPAIPCUnixSocket), and does the switch-case on the cmd to call the appropriate function from the linked IPARPi. For callbacks, since they're implemented as signals, we simply propagate the signal emission (except when crossing the process boundary, which has a send-receive). Signed-off-by: Paul Elder --- .../internal/ipa_proxy_raspberrypi.h | 117 ++++++ src/libcamera/proxy/ipa_proxy_raspberrypi.cpp | 381 ++++++++++++++++++ .../worker/ipa_proxy_raspberrypi_worker.cpp | 355 ++++++++++++++++ 3 files changed, 853 insertions(+) create mode 100644 include/libcamera/internal/ipa_proxy_raspberrypi.h create mode 100644 src/libcamera/proxy/ipa_proxy_raspberrypi.cpp create mode 100644 src/libcamera/proxy/worker/ipa_proxy_raspberrypi_worker.cpp diff --git a/include/libcamera/internal/ipa_proxy_raspberrypi.h b/include/libcamera/internal/ipa_proxy_raspberrypi.h new file mode 100644 index 00000000..bc2001ad --- /dev/null +++ b/include/libcamera/internal/ipa_proxy_raspberrypi.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * ipa_proxy_raspberrypi.h - Image Processing Algorithm proxy for Raspberry pi + */ +#ifndef __LIBCAMERA_INTERNAL_IPA_PROXY_RASPBERRYPI_H__ +#define __LIBCAMERA_INTERNAL_IPA_PROXY_RASPBERRYPI_H__ + +// automatically generated by custom compiler + +// TODO move this to a proxy header directory + +#include +#include +#include + +#include "libcamera/internal/ipa_ipc.h" +#include "libcamera/internal/ipa_proxy.h" +#include "libcamera/internal/ipc_unixsocket.h" +#include "libcamera/internal/thread.h" + +namespace libcamera { + +class IPAProxyRPi : public IPAProxy, public IPARPiInterface +{ +public: + IPAProxyRPi(IPAModule *ipam, bool isolate); + ~IPAProxyRPi(); + + 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 RPiConfigureParams &ipaConfig, + RPiConfigureParams *result) override; + void mapBuffers(const std::vector &buffers) override; + void unmapBuffers(const std::vector &ids) override; + void processEvent(const RPiEventParams &event) override; + + Signal queueFrameAction; + +private: + void recvIPC(std::vector &data, std::vector &fds); + + int initThread(const IPASettings &settings); + int startThread(); + void stopThread(); + void configureThread(const CameraSensorInfo &sensorInfo, + const std::map &streamConfig, + const std::map &entityControls, + const RPiConfigureParams &ipaConfig, + RPiConfigureParams *result); + void mapBuffersThread(const std::vector &buffers); + void unmapBuffersThread(const std::vector &ids); + void processEventThread(const RPiEventParams &event); + void frameActionHandlerThread(unsigned int frame, const RPiActionParams &data); + + int initIPC(const IPASettings &settings); + int startIPC(); + void stopIPC(); + void configureIPC(const CameraSensorInfo &sensorInfo, + const std::map &streamConfig, + const std::map &entityControls, + const RPiConfigureParams &ipaConfig, + RPiConfigureParams *result); + void mapBuffersIPC(const std::vector &buffers); + void unmapBuffersIPC(const std::vector &ids); + void processEventIPC(const RPiEventParams &event); + void frameActionHandlerIPC(std::vector &data, + std::vector &fds); + + /* Helper class to invoke processEvent() in another thread. */ + class ThreadProxy : public Object + { + public: + void setIPA(IPARPiInterface *ipa) + { + ipa_ = ipa; + } + + int start() + { + return ipa_->start(); + } + + void stop() + { + ipa_->stop(); + } + + void processEvent(const RPiEventParams &event) + { + ipa_->processEvent(event); + } + + private: + IPARPiInterface *ipa_; + }; + + bool running_; + Thread thread_; + ThreadProxy proxy_; + std::unique_ptr ipa_; + + const bool isolate_; + + std::unique_ptr ipc_; + + ControlSerializer controlSerializer_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_INTERNAL_IPA_PROXY_RASPBERRYPI_H__ */ diff --git a/src/libcamera/proxy/ipa_proxy_raspberrypi.cpp b/src/libcamera/proxy/ipa_proxy_raspberrypi.cpp new file mode 100644 index 00000000..6526fb1d --- /dev/null +++ b/src/libcamera/proxy/ipa_proxy_raspberrypi.cpp @@ -0,0 +1,381 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * ipa_proxy_raspberrypi.cpp - Image Processing Algorithm proxy for Raspberry pi + */ + +// automatically generated by custom compiler + +#include + +#include +#include +#include +#include +#include + +#include "libcamera/internal/control_serializer.h" +#include "libcamera/internal/ipa_ipc.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 "libcamera/internal/ipa_proxy_raspberrypi.h" + +// just inline this at code generation +#define CALL_THREAD_OR_IPC(isolate, thread_func, ipc_func, ...) \ + (isolate ? ipc_func(__VA_ARGS__) : thread_func(__VA_ARGS__)) + +namespace libcamera { + +LOG_DECLARE_CATEGORY(IPAProxy) + +// TODO yeah we really need to move this into a header somewhere +void appendUInt32(std::vector &vec, uint32_t val) +{ + uint8_t arr[] = {static_cast(val & 0xff), + static_cast((val >> 8) & 0xff), + static_cast((val >> 16) & 0xff), + static_cast((val >> 24) & 0xff)}; + vec.insert(vec.end(), arr, arr + 4); +} + +IPAProxyRPi::IPAProxyRPi(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_raspberrypi"); + if (proxy_worker_path.empty()) { + LOG(IPAProxy, Error) + << "Failed to get proxy worker path"; + return; + } + + IPAIPCFactory *ipcf = nullptr; + for (IPAIPCFactory *factory : IPAIPCFactory::factories()) { + if (!strcmp(factory->name().c_str(), "IPAIPCUnixSocket")) { + ipcf = factory; + break; + } + } + + if (!ipcf) { + LOG(IPAProxy, Error) << "Failed to get IPAIPC factory"; + return; + } + + ipc_ = ipcf->create(ipam->path().c_str(), proxy_worker_path.c_str()); + if (!ipc_->isValid()) { + LOG(IPAProxy, Error) << "Failed to create IPAIPC"; + return; + } + + ipc_->recvIPC.connect(this, &IPAProxyRPi::recvIPC); + + 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(dynamic_cast(ipai)); + proxy_.setIPA(ipa_.get()); + + /* + * Proxy the queueFrameAction signal to dispatch it in the caller's + * thread. + */ + ipa_->queueFrameAction.connect(this, &IPAProxyRPi::frameActionHandlerThread); + + valid_ = true; +} + +IPAProxyRPi::~IPAProxyRPi() +{ + if (isolate_) + ipc_->sendAsync(CMD_EXIT, {}, {}); +} + +void IPAProxyRPi::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) { + case CMD_QUEUEFRAMEACTION: { + frameActionHandlerIPC(vec, fds); + break; + } + } +} + +int IPAProxyRPi::init(const IPASettings &settings) +{ + return CALL_THREAD_OR_IPC(isolate_, initThread, initIPC, settings); +} + +int IPAProxyRPi::start() +{ + return CALL_THREAD_OR_IPC(isolate_, startThread, startIPC); +} + +void IPAProxyRPi::stop() +{ + CALL_THREAD_OR_IPC(isolate_, stopThread, stopIPC); +} + +void IPAProxyRPi::configure(const CameraSensorInfo &sensorInfo, + const std::map &streamConfig, + const std::map &entityControls, + const RPiConfigureParams &ipaConfig, + RPiConfigureParams *result) +{ + CALL_THREAD_OR_IPC(isolate_, configureThread, configureIPC, + sensorInfo, streamConfig, entityControls, ipaConfig, result); +} + +void IPAProxyRPi::mapBuffers(const std::vector &buffers) +{ + CALL_THREAD_OR_IPC(isolate_, mapBuffersThread, mapBuffersIPC, buffers); +} + +void IPAProxyRPi::unmapBuffers(const std::vector &ids) +{ + CALL_THREAD_OR_IPC(isolate_, unmapBuffersThread, unmapBuffersIPC, ids); +} + +void IPAProxyRPi::processEvent(const RPiEventParams &event) +{ + CALL_THREAD_OR_IPC(isolate_, processEventThread, processEventIPC, event); +} + +/* Thread functions */ + +int IPAProxyRPi::initThread(const IPASettings &settings) +{ + int ret = ipa_->init(settings); + if (ret) + return ret; + + proxy_.moveToThread(&thread_); + + return 0; +} + +int IPAProxyRPi::startThread() +{ + running_ = true; + thread_.start(); + + return proxy_.invokeMethod(&ThreadProxy::start, ConnectionTypeBlocking); +} + +void IPAProxyRPi::stopThread() +{ + if (!running_) + return; + + running_ = false; + + proxy_.invokeMethod(&ThreadProxy::stop, ConnectionTypeBlocking); + + thread_.exit(); + thread_.wait(); +} + +void IPAProxyRPi::configureThread(const CameraSensorInfo &sensorInfo, + const std::map &streamConfig, + const std::map &entityControls, + const RPiConfigureParams &ipaConfig, + RPiConfigureParams *result) +{ + ipa_->configure(sensorInfo, streamConfig, entityControls, ipaConfig, + result); +} + +void IPAProxyRPi::mapBuffersThread(const std::vector &buffers) +{ + ipa_->mapBuffers(buffers); +} + +void IPAProxyRPi::unmapBuffersThread(const std::vector &ids) +{ + ipa_->unmapBuffers(ids); +} + +void IPAProxyRPi::processEventThread(const RPiEventParams &event) +{ + if (!running_) + return; + + /* Dispatch the processEvent() call to the thread. */ + proxy_.invokeMethod(&ThreadProxy::processEvent, ConnectionTypeQueued, + event); +} + +void IPAProxyRPi::frameActionHandlerThread(unsigned int frame, const RPiActionParams &data) +{ + queueFrameAction.emit(frame, data); +} + +/* IPC functions */ + +int IPAProxyRPi::initIPC(const IPASettings &settings) +{ + std::vector buf; + std::tie(buf, std::ignore) = IPADataSerializer::serialize(settings); + std::vector resultBuf; + + int ret = ipc_->sendSync(CMD_INIT, buf, {}, &resultBuf); + if (ret < 0) { + LOG(IPAProxy, Error) << "Failed to call init"; + return ret; + } + LOG(IPAProxy, Info) << "Done calling init"; + + /* Maybe we should cast this properly? */ + return static_cast(resultBuf[0]); +} + +int IPAProxyRPi::startIPC() +{ + std::vector resultBuf; + + int ret = ipc_->sendSync(CMD_START, {}, {}, &resultBuf); + if (ret < 0) { + LOG(IPAProxy, Error) << "Failed to call start"; + return ret; + } + LOG(IPAProxy, Info) << "Done calling start"; + + /* Maybe we should cast this properly? */ + return static_cast(resultBuf[0]); +} + +void IPAProxyRPi::stopIPC() +{ + int ret = ipc_->sendSync(CMD_STOP, {}, {}); + if (ret < 0) + LOG(IPAProxy, Error) << "Failed to call stop"; + LOG(IPAProxy, Info) << "Done calling stop"; +} + +void IPAProxyRPi::configureIPC(const CameraSensorInfo &sensorInfo, + const std::map &streamConfig, + const std::map &entityControls, + const RPiConfigureParams &ipaConfig, + RPiConfigureParams *result) +{ + std::vector sensorInfoBuf; + std::tie(sensorInfoBuf, std::ignore) = + IPADataSerializer::serialize(sensorInfo); + std::vector streamConfigBuf; + std::tie(streamConfigBuf, std::ignore) = + IPADataSerializer>::serialize(streamConfig); + std::vector entityControlsBuf; + std::tie(entityControlsBuf, std::ignore) = + IPADataSerializer>::serialize(entityControls, &controlSerializer_); + std::vector ipaConfigBuf; + std::vector ipaConfigFds; + std::tie(ipaConfigBuf, ipaConfigFds) = + IPADataSerializer::serialize(ipaConfig, &controlSerializer_); + + std::vector resultBuf; + std::vector resultFds; + + std::vector input; + appendUInt32(input, sensorInfoBuf.size()); + appendUInt32(input, streamConfigBuf.size()); + appendUInt32(input, entityControlsBuf.size()); + appendUInt32(input, ipaConfigBuf.size()); + appendUInt32(input, ipaConfigFds.size()); + input.insert(input.end(), sensorInfoBuf.begin(), sensorInfoBuf.end()); + input.insert(input.end(), streamConfigBuf.begin(), streamConfigBuf.end()); + input.insert(input.end(), entityControlsBuf.begin(), entityControlsBuf.end()); + input.insert(input.end(), ipaConfigBuf.begin(), ipaConfigBuf.end()); + + std::vector fds; + fds.insert(fds.end(), ipaConfigFds.begin(), ipaConfigFds.end()); + + int ret = ipc_->sendSync(CMD_CONFIGURE, input, fds, &resultBuf, &resultFds); + if (ret < 0) { + LOG(IPAProxy, Error) << "Failed to call configure"; + return; + } + LOG(IPAProxy, Info) << "Done calling configure"; + + *result = IPADataSerializer::deserialize(resultBuf, resultFds, &controlSerializer_); +} + +void IPAProxyRPi::mapBuffersIPC(const std::vector &buffers) +{ + std::vector buffersBuf; + std::vector fdsBuf; + std::tie(buffersBuf, fdsBuf) = + IPADataSerializer>::serialize(buffers); + + int ret = ipc_->sendSync(CMD_MAPBUFFERS, buffersBuf, fdsBuf); + if (ret < 0) + LOG(IPAProxy, Error) << "Failed to call mapBuffers"; + LOG(IPAProxy, Info) << "Done calling mapBuffers"; +} + +void IPAProxyRPi::unmapBuffersIPC(const std::vector &ids) +{ + std::vector idsBuf; + std::tie(idsBuf, std::ignore) = + IPADataSerializer>::serialize(ids); + int ret = ipc_->sendSync(CMD_UNMAPBUFFERS, idsBuf, {}); + if (ret < 0) + LOG(IPAProxy, Error) << "Failed to call unmapBuffers"; + LOG(IPAProxy, Info) << "Done calling unmapBuffers"; +} + +void IPAProxyRPi::processEventIPC(const RPiEventParams &event) +{ + std::vector eventBuf; + std::tie(eventBuf, std::ignore) = + IPADataSerializer::serialize(event, &controlSerializer_); + int ret = ipc_->sendAsync(CMD_PROCESSEVENT, eventBuf, {}); + if (ret < 0) + LOG(IPAProxy, Error) << "Failed to call processEvent"; + LOG(IPAProxy, Info) << "Done calling processEvent"; +} + +void IPAProxyRPi::frameActionHandlerIPC(std::vector &data, + [[maybe_unused]] std::vector &fds) +{ + uint32_t frame = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + RPiActionParams params = + IPADataSerializer::deserialize(data.begin() + 4, data.end(), &controlSerializer_); + + queueFrameAction.emit(frame, params); +} + + +REGISTER_IPA_PROXY(IPAProxyRPi) + +} /* namespace libcamera */ diff --git a/src/libcamera/proxy/worker/ipa_proxy_raspberrypi_worker.cpp b/src/libcamera/proxy/worker/ipa_proxy_raspberrypi_worker.cpp new file mode 100644 index 00000000..ad917756 --- /dev/null +++ b/src/libcamera/proxy/worker/ipa_proxy_raspberrypi_worker.cpp @@ -0,0 +1,355 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * ipa_proxy_raspberrypi_worker.cpp - Image Processing Algorithm proxy worker for Raspberry pi + */ + +// automatically generated by custom compiler + +#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_module.h" +#include "libcamera/internal/ipc_unixsocket.h" +#include "libcamera/internal/log.h" +#include "libcamera/internal/thread.h" + +using namespace libcamera; + +LOG_DEFINE_CATEGORY(IPAProxyRPiWorker) + +struct CallData { + IPCUnixSocket::Payload *response; + bool done; +}; + +IPARPiInterface *ipa_; +IPCUnixSocket socket_; +std::map callData_; + +ControlSerializer controlSerializer_; + +bool exit_ = false; + +void writeHeader(IPCUnixSocket::Payload &payload, uint32_t cmd, uint32_t seq) +{ + uint8_t cmd_arr[] = {static_cast(cmd & 0xff), + static_cast((cmd >> 8 ) & 0xff), + static_cast((cmd >> 16) & 0xff), + static_cast((cmd >> 24) & 0xff)}; + uint8_t seq_arr[] = {static_cast(seq & 0xff), + static_cast((seq >> 8 ) & 0xff), + static_cast((seq >> 16) & 0xff), + static_cast((seq >> 24) & 0xff)}; + payload.data.insert(payload.data.begin(), cmd_arr, cmd_arr+4); + payload.data.insert(payload.data.begin() + 4, seq_arr, seq_arr+4); +} + +// cmd, seq +std::tuple readHeader(IPCUnixSocket::Payload &payload) +{ + uint32_t cmd = (payload.data[0] & 0xff) | + ((payload.data[1] & 0xff) << 8) | + ((payload.data[2] & 0xff) << 16) | + ((payload.data[3] & 0xff) << 24); + uint32_t seq = (payload.data[4] & 0xff) | + ((payload.data[5] & 0xff) << 8) | + ((payload.data[6] & 0xff) << 16) | + ((payload.data[7] & 0xff) << 24); + + return {cmd, seq}; +} + +void eraseHeader(IPCUnixSocket::Payload &payload) +{ + payload.data.erase(payload.data.begin(), payload.data.begin() + 8); +} + +void writeUInt32(IPCUnixSocket::Payload &payload, uint32_t val, uint32_t pos) +{ + if (pos + 4 > payload.data.size()) + payload.data.resize(pos + 4); + + uint8_t arr[] = {static_cast(val & 0xff), + static_cast((val >> 8 ) & 0xff), + static_cast((val >> 16) & 0xff), + static_cast((val >> 24) & 0xff)}; + + std::copy(arr, arr + 4, payload.data.begin() + pos); +} + +uint32_t readUInt32(IPCUnixSocket::Payload &payload, uint32_t pos) +{ + if (pos + 4 > payload.data.size()) + return 0; + + return (payload.data[pos] & 0xff) | + ((payload.data[pos + 1] & 0xff) << 8) | + ((payload.data[pos + 2] & 0xff) << 16) | + ((payload.data[pos + 3] & 0xff) << 24); +} + +void readyRead(IPCUnixSocket *socket) +{ + IPCUnixSocket::Payload message, response; + int ret = socket->receive(&message); + if (ret) { + LOG(IPAProxyRPiWorker, Error) + << "Receive message failed" << ret; + return; + } + + uint32_t cmd, seq; + std::tie(cmd, seq) = readHeader(message); + eraseHeader(message); + + switch (cmd) { + case CMD_INIT: { + IPASettings settings = IPADataSerializer::deserialize(message.data); + + int ret = ipa_->init(settings); + writeHeader(response, cmd, seq); + /* Maybe we should do this cast properly? */ + response.data.push_back(static_cast(ret)); + + ret = socket_.send(response); + if (ret < 0) { + LOG(IPAProxyRPiWorker, Error) + << "Reply to init() failed" << ret; + } + + LOG(IPAProxyRPiWorker, Info) << "Done replying to init()"; + break; + } + + case CMD_EXIT: { + exit_ = true; + break; + } + + case CMD_START: { + int ret = ipa_->start(); + writeHeader(response, cmd, seq); + /* Maybe we should do this cast properly? */ + response.data.push_back(static_cast(ret)); + + ret = socket_.send(response); + if (ret < 0) { + LOG(IPAProxyRPiWorker, Error) + << "Reply to start() failed" << ret; + } + + LOG(IPAProxyRPiWorker, Info) << "Done replying to start()"; + break; + } + + case CMD_STOP: { + ipa_->stop(); + + writeHeader(response, cmd, seq); + int ret = socket_.send(response); + if (ret < 0) { + LOG(IPAProxyRPiWorker, Error) + << "Reply to stop() failed" << ret; + } + + LOG(IPAProxyRPiWorker, Info) << "Done replying to stop()"; + break; + } + + case CMD_CONFIGURE: { + LOG(IPAProxyRPiWorker, Info) << "Starting configure()"; + + size_t sensorInfoSize = readUInt32(message, 0); + size_t streamConfigSize = readUInt32(message, 4); + size_t entityControlsSize = readUInt32(message, 8); + + // TODO make this pattern more like the data ones + // - this will be fixed in the compiler + // remember to fix these offsets too + size_t ipaConfigFdsSize = readUInt32(message, 16); + + size_t sensorInfoStart = 20; + size_t streamConfigStart = sensorInfoStart + sensorInfoSize; + size_t entityControlsStart = streamConfigStart + streamConfigSize; + size_t ipaConfigStart = entityControlsStart + entityControlsSize; + + LOG(IPAProxyRPiWorker, Info) << " deserializing sensorInfo..."; + struct CameraSensorInfo sensorInfo = + IPADataSerializer::deserialize( + message.data.begin() + sensorInfoStart, + message.data.begin() + streamConfigStart); + LOG(IPAProxyRPiWorker, Info) << " deserializing streamConfig..."; + std::map streamConfig = + IPADataSerializer>::deserialize( + message.data.begin() + streamConfigStart, + message.data.begin() + entityControlsStart); + LOG(IPAProxyRPiWorker, Info) << " deserializing entityControls..."; + const std::map entityControls = + IPADataSerializer>::deserialize( + message.data.begin() + entityControlsStart, + message.data.begin() + ipaConfigStart, + &controlSerializer_); + LOG(IPAProxyRPiWorker, Info) << " deserializing ipaConfig..."; + RPiConfigureParams ipaConfig = + IPADataSerializer::deserialize( + message.data.begin() + ipaConfigStart, + message.data.end(), + message.fds.begin(), + message.fds.begin() + ipaConfigFdsSize, + &controlSerializer_); + RPiConfigureParams results; + + LOG(IPAProxyRPiWorker, Info) << "Calling IPA's configure()"; + ipa_->configure(sensorInfo, streamConfig, entityControls, ipaConfig, &results); + LOG(IPAProxyRPiWorker, Info) << "Done calling IPA's configure()"; + + std::vector resultsSerialized; + std::vector resultsFds; + std::tie(resultsSerialized, resultsFds) = + IPADataSerializer::serialize(results, &controlSerializer_); + + writeHeader(response, cmd, seq); + response.data.insert(response.data.end(), + resultsSerialized.begin(), resultsSerialized.end()); + response.fds.insert(response.fds.end(), + resultsFds.begin(), resultsFds.end()); + LOG(IPAProxyRPiWorker, Info) << "Sending response to configure()"; + int ret = socket_.send(response); + if (ret < 0) { + LOG(IPAProxyRPiWorker, Error) + << "Reply to configure() failed" << ret; + } + + LOG(IPAProxyRPiWorker, Info) << "Done replying to configure()"; + break; + } + + case CMD_MAPBUFFERS: { + std::vector ipaBuffers = + IPADataSerializer>::deserialize(message.data, message.fds); + ipa_->mapBuffers(ipaBuffers); + + writeHeader(response, cmd, seq); + int ret = socket_.send(response); + if (ret < 0) { + LOG(IPAProxyRPiWorker, Error) + << "Reply to mapBuffers() failed" << ret; + } + + LOG(IPAProxyRPiWorker, Info) << "Done replying to mapBuffers()"; + break; + } + + case CMD_UNMAPBUFFERS: { + std::vector ids = + IPADataSerializer>::deserialize(message.data); + ipa_->unmapBuffers(ids); + + writeHeader(response, cmd, seq); + int ret = socket_.send(response); + if (ret < 0) { + LOG(IPAProxyRPiWorker, Error) + << "Reply to unmapBuffers() failed" << ret; + } + + LOG(IPAProxyRPiWorker, Info) << "Done replying to unmapBuffers()"; + break; + } + + case CMD_PROCESSEVENT: { + RPiEventParams ev = + IPADataSerializer::deserialize(message.data, &controlSerializer_); + ipa_->processEvent(ev); + + LOG(IPAProxyRPiWorker, Info) << "Done processEvent()ing"; + break; + } + + } + + return; +} + +void queueFrameAction(unsigned int frame, const RPiActionParams &action) +{ + LOG(IPAProxyRPiWorker, Info) << "queueFrameAction triggered"; + IPCUnixSocket::Payload message; + writeUInt32(message, frame, 0); + std::vector actionVector; + std::tie(actionVector, std::ignore) = + IPADataSerializer::serialize(action, &controlSerializer_); + + message.data.insert(message.data.end(), actionVector.begin(), actionVector.end()); + writeHeader(message, CMD_QUEUEFRAMEACTION, 0); + + socket_.send(message); + LOG(IPAProxyRPiWorker, Info) << "queueFrameAction done"; + return; +} + +int main(int argc, char **argv) +{ + /* Uncomment this for debugging. */ + std::string logPath = "/tmp/libcamera.worker." + + std::to_string(getpid()) + ".log"; + logSetFile(logPath.c_str()); + //logSetTarget(LoggingTargetSyslog); + + if (argc < 3) { + LOG(IPAProxyRPiWorker, Debug) + << "Tried to start worker with no args"; + return EXIT_FAILURE; + } + + int fd = std::stoi(argv[2]); + LOG(IPAProxyRPiWorker, 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(IPAProxyRPiWorker, Error) + << "IPAModule " << argv[1] << " should be valid but isn't"; + return EXIT_FAILURE; + } + + if (socket_.bind(fd) < 0) { + LOG(IPAProxyRPiWorker, Error) << "IPC socket binding failed"; + return EXIT_FAILURE; + } + socket_.readyRead.connect(&readyRead); + + ipa_ = dynamic_cast(ipam->createInterface()); + if (!ipa_) { + LOG(IPAProxyRPiWorker, Error) << "Failed to create IPA context"; + return EXIT_FAILURE; + } + + ipa_->queueFrameAction.connect(&queueFrameAction); + + LOG(IPAProxyRPiWorker, Debug) << "Proxy worker successfully started"; + + /* \todo upgrade listening loop */ + EventDispatcher *dispatcher = Thread::current()->eventDispatcher(); + while (!exit_) + dispatcher->processEvents(); + + delete ipa_; + socket_.close(); + + return 0; +}