From patchwork Tue Sep 22 13:35: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: 9719 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 5D3E3BF01C for ; Tue, 22 Sep 2020 13:37:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 27AA562FDA; Tue, 22 Sep 2020 15:37:41 +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="mkih9vp/"; 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 DA54460576 for ; Tue, 22 Sep 2020 15:37:39 +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 DA78C2D7; Tue, 22 Sep 2020 15:37:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1600781854; bh=Sl8/ekM00ZsZJyK1R41zw9qroT8C0+eQEFj2coOYeTQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mkih9vp/qgzl6MxYFc6cj96eDBTmwy/WWHojrkfGTCeh3/cdo85DSda2OcF9qoIdP XtuVNxFegvCGqsYM8370xcDOhck0d1A/VmKWRz8ABoVQBluUPAQKZlLvR/IGO2oKa5 YshMLuINfVeeeVTm70Xp9p/I1xQVhLH6lR6TknEk= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Tue, 22 Sep 2020 22:35:14 +0900 Message-Id: <20200922133537.258098-16-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20200922133537.258098-1-paul.elder@ideasonboard.com> References: <20200922133537.258098-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 15/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 v2: - specify in doxygen to skip generating documentation for IPAIPCUnixSocket --- Documentation/Doxyfile.in | 2 + .../libcamera/internal/ipa_ipc_unixsocket.h | 115 ++++++++++++ src/libcamera/ipa_ipc_unixsocket.cpp | 175 ++++++++++++++++++ src/libcamera/meson.build | 1 + 4 files changed, 293 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..de8af35b --- /dev/null +++ b/include/libcamera/internal/ipa_ipc_unixsocket.h @@ -0,0 +1,115 @@ +/* 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; + +inline 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); +} + +inline 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}; +} + +inline void eraseHeader(IPCUnixSocket::Payload &payload) +{ + payload.data.erase(payload.data.begin(), payload.data.begin() + 8); +} + +inline void writeUInt32(IPCUnixSocket::Payload &payload, uint32_t val, uint32_t pos) +{ + if (pos + 4 > payload.data.size()) + return; + + uint8_t arr[] = {static_cast(val & 0xff), + static_cast(val & (0xff << 8)), + static_cast(val & (0xff << 16)), + static_cast(val & (0xff << 24))}; + std::copy(arr, arr + 4, payload.data.begin() + pos); +} + +inline uint32_t readUInt32(IPCUnixSocket::Payload &payload, uint32_t pos) +{ + if (pos + 4 > payload.data.size()) + return 0; + + return payload.data[pos] & (payload.data[pos + 1] << 8) & + (payload.data[pos + 2] << 16) & (payload.data[pos + 3] << 24); +} + + +class IPAIPCUnixSocket : public IPAIPC +{ +public: + IPAIPCUnixSocket(const char *ipa_module_path, const char *ipa_proxy_worker_path); + ~IPAIPCUnixSocket(); + + bool isValid() const { return valid_; } + + 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; + +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..c1997e96 --- /dev/null +++ b/src/libcamera/ipa_ipc_unixsocket.cpp @@ -0,0 +1,175 @@ +/* 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::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 e3eade57..24f32815 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -25,6 +25,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',