blob: 8a88bd467da76593f78f074a456906b33d1f62b1 [file] [log] [blame]
{#-
# SPDX-License-Identifier: LGPL-2.1-or-later
# Copyright (C) 2020, Google Inc.
-#}
{%- import "proxy_functions.tmpl" as proxy_funcs -%}
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2020, Google Inc.
*
* {{module_name}}_ipa_proxy_worker.cpp - Image Processing Algorithm proxy worker for {{module_name}}
*
* This file is auto-generated. Do not edit.
*/
{#- \todo Split proxy worker into IPC worker and proxy worker. #}
#include <algorithm>
#include <iostream>
#include <sys/types.h>
#include <tuple>
#include <unistd.h>
#include <libcamera/ipa/ipa_interface.h>
#include <libcamera/ipa/{{module_name}}_ipa_interface.h>
#include <libcamera/ipa/{{module_name}}_ipa_serializer.h>
#include <libcamera/logging.h>
#include <libcamera/base/event_dispatcher.h>
#include <libcamera/base/thread.h>
#include <libcamera/base/log.h>
#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/ipa_proxy.h"
#include "libcamera/internal/ipc_pipe.h"
#include "libcamera/internal/ipc_pipe_unixsocket.h"
#include "libcamera/internal/ipc_unixsocket.h"
using namespace libcamera;
LOG_DEFINE_CATEGORY({{proxy_worker_name}})
{%- if has_namespace %}
{% for ns in namespace -%}
using namespace {{ns}};
{% endfor %}
{%- endif %}
class {{proxy_worker_name}}
{
public:
{{proxy_worker_name}}()
: ipa_(nullptr), exit_(false) {}
~{{proxy_worker_name}}() {}
void readyRead(IPCUnixSocket *socket)
{
IPCUnixSocket::Payload _message;
int _retRecv = socket->receive(&_message);
if (_retRecv) {
LOG({{proxy_worker_name}}, Error)
<< "Receive message failed: " << _retRecv;
return;
}
IPCMessage _ipcMessage(_message);
{{cmd_enum_name}} _cmd = static_cast<{{cmd_enum_name}}>(_ipcMessage.header().cmd);
switch (_cmd) {
case {{cmd_enum_name}}::Exit: {
exit_ = true;
break;
}
{% for method in interface_main.methods %}
case {{cmd_enum_name}}::{{method.mojom_name|cap}}: {
{{proxy_funcs.deserialize_call(method|method_param_inputs, '_ipcMessage.data()', '_ipcMessage.fds()', false, true)|indent(8, true)}}
{% for param in method|method_param_outputs %}
{{param|name}} {{param.mojom_name}};
{% endfor %}
{%- if method|method_return_value != "void" %}
{{method|method_return_value}} _callRet =
{%- endif -%}
ipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}}
{{- ", " if method|method_param_outputs|params_comma_sep -}}
{%- for param in method|method_param_outputs -%}
&{{param.mojom_name}}{{", " if not loop.last}}
{%- endfor -%}
);
{% if not method|is_async %}
IPCMessage::Header header = { _ipcMessage.header().cmd, _ipcMessage.header().cookie };
IPCMessage _response(header);
{%- if method|method_return_value != "void" %}
std::vector<uint8_t> _callRetBuf;
std::tie(_callRetBuf, std::ignore) =
IPADataSerializer<{{method|method_return_value}}>::serialize(_callRet);
_response.data().insert(_response.data().end(), _callRetBuf.cbegin(), _callRetBuf.cend());
{%- endif %}
{{proxy_funcs.serialize_call(method|method_param_outputs, "_response.data()", "_response.fds()")|indent(16, true)}}
int _ret = socket_.send(_response.payload());
if (_ret < 0) {
LOG({{proxy_worker_name}}, Error)
<< "Reply to {{method.mojom_name}}() failed: " << _ret;
}
LOG({{proxy_worker_name}}, Debug) << "Done replying to {{method.mojom_name}}()";
{%- endif %}
break;
}
{% endfor %}
default:
LOG({{proxy_worker_name}}, Error) << "Unknown command " << _ipcMessage.header().cmd;
}
}
int init(std::unique_ptr<IPAModule> &ipam, int socketfd)
{
if (socket_.bind(socketfd) < 0) {
LOG({{proxy_worker_name}}, Error)
<< "IPC socket binding failed";
return EXIT_FAILURE;
}
socket_.readyRead.connect(this, &{{proxy_worker_name}}::readyRead);
ipa_ = dynamic_cast<{{interface_name}} *>(ipam->createInterface());
if (!ipa_) {
LOG({{proxy_worker_name}}, Error)
<< "Failed to create IPA interface instance";
return EXIT_FAILURE;
}
{% for method in interface_event.methods %}
ipa_->{{method.mojom_name}}.connect(this, &{{proxy_worker_name}}::{{method.mojom_name}});
{%- endfor %}
return 0;
}
void run()
{
EventDispatcher *dispatcher = Thread::current()->eventDispatcher();
while (!exit_)
dispatcher->processEvents();
}
void cleanup()
{
delete ipa_;
socket_.close();
}
private:
{% for method in interface_event.methods %}
{{proxy_funcs.func_sig(proxy_name, method, "", false)|indent(8, true)}}
{
IPCMessage::Header header = {
static_cast<uint32_t>({{cmd_event_enum_name}}::{{method.mojom_name|cap}}),
0
};
IPCMessage _message(header);
{{proxy_funcs.serialize_call(method|method_param_inputs, "_message.data()", "_message.fds()")}}
int _ret = socket_.send(_message.payload());
if (_ret < 0)
LOG({{proxy_worker_name}}, Error)
<< "Sending event {{method.mojom_name}}() failed: " << _ret;
LOG({{proxy_worker_name}}, Debug) << "{{method.mojom_name}} done";
}
{% endfor %}
{{interface_name}} *ipa_;
IPCUnixSocket socket_;
ControlSerializer controlSerializer_;
bool exit_;
};
int main(int argc, char **argv)
{
{#- \todo Handle enabling debugging more dynamically. #}
/* Uncomment this for debugging. */
#if 0
std::string logPath = "/tmp/libcamera.worker." +
std::to_string(getpid()) + ".log";
logSetFile(logPath.c_str());
#endif
if (argc < 3) {
LOG({{proxy_worker_name}}, Error)
<< "Tried to start worker with no args: "
<< "expected <path to IPA so> <fd to bind unix socket>";
return EXIT_FAILURE;
}
int fd = std::stoi(argv[2]);
LOG({{proxy_worker_name}}, Info)
<< "Starting worker for IPA module " << argv[1]
<< " with IPC fd = " << fd;
std::unique_ptr<IPAModule> ipam = std::make_unique<IPAModule>(argv[1]);
if (!ipam->isValid() || !ipam->load()) {
LOG({{proxy_worker_name}}, Error)
<< "IPAModule " << argv[1] << " isn't valid";
return EXIT_FAILURE;
}
/*
* Shutdown of proxy worker can be pre-empted by events like
* SIGINT/SIGTERM, even before the pipeline handler can request
* shutdown. Hence, assign a new gid to prevent signals on the
* application being delivered to the proxy.
*/
if (setpgid(0, 0) < 0) {
int err = errno;
LOG({{proxy_worker_name}}, Warning)
<< "Failed to set new gid: " << strerror(err);
}
{{proxy_worker_name}} proxyWorker;
int ret = proxyWorker.init(ipam, fd);
if (ret < 0) {
LOG({{proxy_worker_name}}, Error)
<< "Failed to initialize proxy worker";
return ret;
}
LOG({{proxy_worker_name}}, Debug) << "Proxy worker successfully initialized";
proxyWorker.run();
proxyWorker.cleanup();
return 0;
}