/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2020, Google Inc. * * ipc_pipe_unixsocket.cpp - Image Processing Algorithm IPC module using unix socket */ #include "libcamera/internal/ipc_pipe_unixsocket.h" #include #include #include #include #include #include "libcamera/internal/ipc_pipe.h" #include "libcamera/internal/ipc_unixsocket.h" #include "libcamera/internal/process.h" namespace libcamera { LOG_DECLARE_CATEGORY(IPCPipe) IPCPipeUnixSocket::IPCPipeUnixSocket(const char *ipaModulePath, const char *ipaProxyWorkerPath) : IPCPipe() { std::vector fds; std::vector args; args.push_back(ipaModulePath); socket_ = std::make_unique(); int fd = socket_->create(); if (fd < 0) { LOG(IPCPipe, Error) << "Failed to create socket"; return; } socket_->readyRead.connect(this, &IPCPipeUnixSocket::readyRead); args.push_back(std::to_string(fd)); fds.push_back(fd); proc_ = std::make_unique(); int ret = proc_->start(ipaProxyWorkerPath, args, fds); if (ret) { LOG(IPCPipe, Error) << "Failed to start proxy worker process"; return; } connected_ = true; } IPCPipeUnixSocket::~IPCPipeUnixSocket() { } int IPCPipeUnixSocket::sendSync(const IPCMessage &in, IPCMessage *out) { IPCUnixSocket::Payload response; int ret = call(in.payload(), &response, in.header().cookie); if (ret) { LOG(IPCPipe, Error) << "Failed to call sync"; return ret; } if (out) *out = IPCMessage(response); return 0; } int IPCPipeUnixSocket::sendAsync(const IPCMessage &data) { int ret = socket_->send(data.payload()); if (ret) { LOG(IPCPipe, Error) << "Failed to call async"; return ret; } return 0; } void IPCPipeUnixSocket::readyRead(IPCUnixSocket *socket) { IPCUnixSocket::Payload payload; int ret = socket->receive(&payload); if (ret) { LOG(IPCPipe, Error) << "Receive message failed" << ret; return; } /* \todo Use span to avoid the double copy when callData is found. */ if (payload.data.size() < sizeof(IPCMessage::Header)) { LOG(IPCPipe, Error) << "Not enough data received"; return; } IPCMessage ipcMessage(payload); auto callData = callData_.find(ipcMessage.header().cookie); if (callData != callData_.end()) { *callData->second.response = std::move(payload); callData->second.done = true; return; } /* Received unexpected data, this means it's a call from the IPA. */ recv.emit(ipcMessage); } int IPCPipeUnixSocket::call(const IPCUnixSocket::Payload &message, IPCUnixSocket::Payload *response, uint32_t cookie) { Timer timeout; int ret; const auto result = callData_.insert({ cookie, { response, false } }); const auto &iter = result.first; ret = socket_->send(message); if (ret) { callData_.erase(iter); return ret; } /* \todo Make this less dangerous, see IPCPipe::sendSync() */ timeout.start(2000); while (!iter->second.done) { if (!timeout.isRunning()) { LOG(IPCPipe, Error) << "Call timeout!"; callData_.erase(iter); return -ETIMEDOUT; } Thread::current()->eventDispatcher()->processEvents(); } callData_.erase(iter); return 0; } } /* namespace libcamera */