[libcamera-devel] [PATCH v4 04/37] utils: ipc: add templates for code generation for IPC mechanism
Jacopo Mondi
jacopo at jmondi.org
Tue Nov 17 15:55:40 CET 2020
Hi Paul,
On Fri, Nov 06, 2020 at 07:36:34PM +0900, Paul Elder wrote:
> Add templates to mojo to generate code for the IPC mechanism. These
> templates generate:
> - module header
> - module serializer
> - IPA proxy cpp, header, and worker
>
> Given an input data definition mojom file for a pipeline.
>
> Signed-off-by: Paul Elder <paul.elder at ideasonboard.com>
>
> ---
> Changes in v4:
> For the non-template files:
> - rename IPA{pipeline_name}CallbackInterface to
> IPA{pipeline_name}EventInterface
> - to avoid the notion of "callback" and emphasize that it's an event
> - add support for strings in custom structs
> - add validation, that async methods must not have return values
> - it throws exception and isn't very clear though...?
> - rename controls to libcamera::{pipeline_name}::controls (controls is
> now lowercase)
> - rename {pipeline_name}_generated.h to {pipeline_name}_ipa_interface.h,
> and {pipeline_name}_serializer.h to {pipeline_name}_ipa_serializer.h
> - same for their corresponding template files
> For the template files:
> - fix spacing (now it's all {{var}} instead of some {{ var }})
> - except if it's code, so code is still {{ code }}
> - move inclusion of corresponding header to first in the inclusion list
> - fix copy&paste errors
> - change snake_case to camelCase in the generated code
> - template code still uses snake_case
> - change the generated command enums to an enum class, and make it
> capitalized (instead of allcaps)
> - add length checks to recvIPC (in proxy)
> - fix some template spacing
> - don't use const for PODs in function/signal parameters
> - add the proper length checks to readPOD/appendPOD
> - the helper functions for reading and writing PODs to and from
> serialized data
> - rename readUInt/appendUInt to readPOD/appendPOD
> - add support for strings in custom structs
>
> Changes in v3:
> - add support for namespaces
> - fix enum assignment (used to have +1 for CMD applied to all enums)
> - use readHeader, writeHeader, and eraseHeader as static class functions
> of IPAIPCUnixSocket (in the proxy worker)
> - add requirement that base controls *must* be defined in
> libcamera::{pipeline_name}::Controls
>
> Changes in v2:
> - mandate the main and callback interfaces, and init(), start(), stop()
> and their parameters
> - fix returning single pod value from IPC-called function
> - add licenses
> - improve auto-generated message
> - other fixes related to serdes
> ---
> .../module_ipa_interface.h.tmpl | 113 ++++
> .../module_ipa_proxy.cpp.tmpl | 238 +++++++++
> .../module_ipa_proxy.h.tmpl | 118 +++++
> .../module_ipa_proxy_worker.cpp.tmpl | 187 +++++++
> .../module_ipa_serializer.h.tmpl | 44 ++
> .../libcamera_templates/proxy_functions.tmpl | 205 ++++++++
> .../libcamera_templates/serializer.tmpl | 280 ++++++++++
> .../generators/mojom_libcamera_generator.py | 488 ++++++++++++++++++
> 8 files changed, 1673 insertions(+)
> create mode 100644 utils/ipc/generators/libcamera_templates/module_ipa_interface.h.tmpl
> create mode 100644 utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
> create mode 100644 utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
> create mode 100644 utils/ipc/generators/libcamera_templates/module_ipa_proxy_worker.cpp.tmpl
> create mode 100644 utils/ipc/generators/libcamera_templates/module_ipa_serializer.h.tmpl
> create mode 100644 utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
> create mode 100644 utils/ipc/generators/libcamera_templates/serializer.tmpl
> create mode 100644 utils/ipc/generators/mojom_libcamera_generator.py
>
> diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_interface.h.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_interface.h.tmpl
> new file mode 100644
> index 00000000..a470b99e
> --- /dev/null
> +++ b/utils/ipc/generators/libcamera_templates/module_ipa_interface.h.tmpl
> @@ -0,0 +1,113 @@
> +{#- SPDX-License-Identifier: LGPL-2.1-or-later -#}
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) {{year}}, Google Inc.
Wondering if the copyright shouldn't be attribute to who writes the
template.
Apart from this, thanks for the monstrous patch, that's a template,
I'm not reviewing it in detail
> + *
> + * {{module_name}}_ipa_interface.h - Image Processing Algorithm interface for {{module_name}}
> + *
> + * This file is auto-generated. Do not edit.
> + */
> +
> +#ifndef __LIBCAMERA_IPA_INTERFACE_{{module_name|upper}}_GENERATED_H__
> +#define __LIBCAMERA_IPA_INTERFACE_{{module_name|upper}}_GENERATED_H__
> +
> +#include <libcamera/ipa/ipa_interface.h>
> +#include <libcamera/ipa/{{module_name}}.h>
> +
> +{% if has_map %}#include <map>{% endif %}
> +{% if has_array %}#include <vector>{% endif %}
> +
> +namespace libcamera {
> +{%- if has_namespace %}
> +{% for ns in namespace %}
> +namespace {{ns}} {
> +{% endfor %}
> +{%- endif %}
> +
> +enum class {{cmd_enum_name}} {
> + Exit = 0,
> +{%- for method in interface_main.methods %}
> + {{method.mojom_name|cap}} = {{loop.index}},
> +{%- endfor %}
> +};
> +
> +enum class {{cmd_event_enum_name}} {
> +{%- for method in interface_cb.methods %}
> + {{method.mojom_name|cap}} = {{loop.index}},
> +{%- endfor %}
> +};
> +
> +{% for enum in enums %}
> +enum {{enum.mojom_name}} {
> +{%- for field in enum.fields %}
> + {{field.mojom_name}} = {{field.numeric_value}},
> +{%- endfor %}
> +};
> +{% endfor %}
> +
> +{%- for struct in structs_nonempty %}
> +struct {{struct.mojom_name}}
> +{
> +public:
> + {{struct.mojom_name}}() {%- if struct|has_default_fields %}
> + :{% endif %}
> +{%- for field in struct.fields|with_default_values -%}
> +{{" " if loop.first}}{{field.mojom_name}}_({{field|default_value}}){{", " if not loop.last}}
> +{%- endfor %}
> + {
> + }
> +
> + ~{{struct.mojom_name}}() {}
> +
> + {{struct.mojom_name}}(
> +{%- for field in struct.fields -%}
> +{{field|name}} {{field.mojom_name}}{{", " if not loop.last}}
> +{%- endfor -%}
> +)
> + :
> +{%- for field in struct.fields -%}
> +{{" " if loop.first}}{{field.mojom_name}}_({{field.mojom_name}}){{", " if not loop.last}}
> +{%- endfor %}
> + {
> + }
> +{% for field in struct.fields %}
> + {{field|name}} {{field.mojom_name}}_;
> +{%- endfor %}
> +};
> +{% endfor %}
> +
> +{#-
> +Any consts or #defines should be moved to the mojom file when possible.
> +If anything needs to be #included, then {{module_name}}.h needs to have the
> +#include.
> +#}
> +class {{interface_name}} : public IPAInterface
> +{
> +public:
> + virtual ~{{interface_name}}() {}
> +{% for method in interface_main.methods %}
> + virtual {{method|method_return_value}} {{method.mojom_name}}(
> +{%- for param in method|method_parameters %}
> + {{param}}{{- "," if not loop.last}}
> +{%- endfor -%}
> +) = 0;
> +{% endfor %}
> +
> +{%- for method in interface_cb.methods %}
> + Signal<
> +{%- for param in method.parameters -%}
> + {{"const " if not param|is_pod}}{{param|name}}{{" &" if not param|is_pod}}
> + {{- ", " if not loop.last}}
> +{%- endfor -%}
> +> {{method.mojom_name}};
> +{% endfor -%}
> +};
> +
> +{%- if has_namespace %}
> +{% for ns in namespace|reverse %}
> +} /* {{ns}} */
> +{% endfor %}
> +{%- endif %}
> +} /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_IPA_INTERFACE_{{module_name|upper}}_GENERATED_H__ */
> diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
> new file mode 100644
> index 00000000..9328c7ca
> --- /dev/null
> +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl
> @@ -0,0 +1,238 @@
> +{#- SPDX-License-Identifier: LGPL-2.1-or-later -#}
> +{%- import "proxy_functions.tmpl" as proxy_funcs -%}
> +
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) {{year}}, Google Inc.
> + *
> + * ipa_proxy_{{module_name}}.cpp - Image Processing Algorithm proxy for {{module_name}}
> + *
> + * This file is auto-generated. Do not edit.
> + */
> +
> +#include <libcamera/ipa/ipa_proxy_{{module_name}}.h>
> +
> +#include <vector>
> +
> +#include <libcamera/ipa/ipa_module_info.h>
> +#include <libcamera/ipa/{{module_name}}.h>
> +#include <libcamera/ipa/{{module_name}}_ipa_interface.h>
> +#include <libcamera/ipa/{{module_name}}_ipa_serializer.h>
> +
> +#include "libcamera/internal/control_serializer.h"
> +#include "libcamera/internal/ipa_ipc.h"
> +#include "libcamera/internal/ipa_ipc_unixsocket.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"
> +
> +namespace libcamera {
> +
> +LOG_DECLARE_CATEGORY(IPAProxy)
> +
> +{%- if has_namespace %}
> +{% for ns in namespace %}
> +namespace {{ns}} {
> +{% endfor %}
> +{%- endif %}
> +
> +{{proxy_name}}::{{proxy_name}}(IPAModule *ipam, bool isolate)
> + : IPAProxy(ipam), running_(false),
> + isolate_(isolate)
> +{
> + LOG(IPAProxy, Debug)
> + << "initializing {{module_name}} proxy: loading IPA from "
> + << ipam->path();
> +
> + if (isolate_) {
> + const std::string proxyWorkerPath = resolvePath("ipa_proxy_{{module_name}}");
> + if (proxyWorkerPath.empty()) {
> + LOG(IPAProxy, Error)
> + << "Failed to get proxy worker path";
> + return;
> + }
> +
> + ipc_ = std::make_unique<IPAIPCUnixSocket>(ipam->path().c_str(), proxyWorkerPath.c_str());
> + if (!ipc_->isValid()) {
> + LOG(IPAProxy, Error) << "Failed to create IPAIPC";
> + return;
> + }
> +
> +{% if interface_cb.methods|length > 0 %}
> + ipc_->recvIPC.connect(this, &{{proxy_name}}::recvIPC);
> +{% endif %}
> +
> + 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<{{interface_name}}>(dynamic_cast<{{interface_name}} *>(ipai));
> + proxy_.setIPA(ipa_.get());
> +
> +{% for method in interface_cb.methods %}
> + ipa_->{{method.mojom_name}}.connect(this, &{{proxy_name}}::{{method.mojom_name}}Thread);
> +{%- endfor %}
> +
> + valid_ = true;
> +}
> +
> +{{proxy_name}}::~{{proxy_name}}()
> +{
> + if (isolate_)
> + ipc_->sendAsync(static_cast<uint32_t>({{cmd_enum_name}}::Exit), {}, {});
> +}
> +
> +{% if interface_cb.methods|length > 0 %}
> +void {{proxy_name}}::recvIPC(std::vector<uint8_t> &data, std::vector<int32_t> &fds)
> +{
> + if (data.size() < 8) {
> + LOG(IPAProxy, Error)
> + << "Didn't receive enough bytes to parse event";
> + return;
> + }
> +
> + {{cmd_event_enum_name}} cmd = static_cast<{{cmd_event_enum_name}}>((
> + data[0]) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24));
> +
> + /* Need to skip another 4 bytes for the sequence number. */
> + std::vector<uint8_t>::iterator vec = data.begin() + 8;
> + size_t dataSize = data.size() - 8;
> + switch (cmd) {
> +{%- for method in interface_cb.methods %}
> + case {{cmd_event_enum_name}}::{{method.mojom_name|cap}}: {
> + {{method.mojom_name}}IPC(vec, dataSize, fds);
> + break;
> + }
> +{%- endfor %}
> + default:
> + LOG(IPAProxy, Error) << "Unknown command " << static_cast<uint32_t>(cmd);
> + }
> +}
> +{%- endif %}
> +
> +{% for method in interface_main.methods %}
> +{{proxy_funcs.func_sig(proxy_name, method)}}
> +{
> + if (isolate_)
> + {{"return " if method|method_return_value != "void"}}{{method.mojom_name}}IPC(
> +{%- for param in method|method_param_names -%}
> + {{param}}{{- ", " if not loop.last}}
> +{%- endfor -%}
> +);
> + else
> + {{"return " if method|method_return_value != "void"}}{{method.mojom_name}}Thread(
> +{%- for param in method|method_param_names -%}
> + {{param}}{{- ", " if not loop.last}}
> +{%- endfor -%}
> +);
> +}
> +
> +{{proxy_funcs.func_sig(proxy_name, method, "Thread")}}
> +{
> +{%- if method.mojom_name == "init" %}
> + {{proxy_funcs.init_thread_body()}}
> +{%- elif method.mojom_name == "start" %}
> + {{proxy_funcs.start_thread_body()}}
> +{%- elif method.mojom_name == "stop" %}
> + {{proxy_funcs.stop_thread_body()}}
> +{%- elif not method|is_async %}
> + ipa_->{{method.mojom_name}}(
> + {%- for param in method|method_param_names -%}
> + {{param}}{{- ", " if not loop.last}}
> + {%- endfor -%}
> +);
> +{% elif method|is_async %}
> + proxy_.invokeMethod(&ThreadProxy::{{method.mojom_name}}, ConnectionTypeQueued,
> + {%- for param in method|method_param_names -%}
> + {{param}}{{- ", " if not loop.last}}
> + {%- endfor -%}
> +);
> +{%- endif %}
> +}
> +
> +{{proxy_funcs.func_sig(proxy_name, method, "IPC")}}
> +{
> +{%- set has_input = true if method|method_param_inputs|length > 0 %}
> +{%- set has_output = true if method|method_param_outputs|length > 0 or method|method_return_value != "void" %}
> +{%- if has_input %}
> + std::vector<uint8_t> _ipcInputBuf;
> +{%- if method|method_input_has_fd %}
> + std::vector<int32_t> _ipcInputFds;
> +{%- endif %}
> +{%- endif %}
> +{%- if has_output %}
> + std::vector<uint8_t> _ipcOutputBuf;
> +{%- if method|method_output_has_fd %}
> + std::vector<int32_t> _ipcOutputFds;
> +{%- endif %}
> +{%- endif %}
> +
> +{{proxy_funcs.serialize_call(method|method_param_inputs, '_ipcInputBuf', '_ipcInputFds', base_controls)}}
> +
> +{%- set input_buf = "_ipcInputBuf" if has_input else "{}" %}
> +{%- set fds_buf = "_ipcInputFds" if method|method_input_has_fd else "{}" %}
> +{%- set cmd = cmd_enum_name + "::" + method.mojom_name|cap %}
> +{% if method|is_async %}
> + int ret = ipc_->sendAsync(static_cast<uint32_t>({{cmd}}), {{input_buf}}, {{fds_buf}});
> +{%- else %}
> + int ret = ipc_->sendSync(static_cast<uint32_t>({{cmd}}), {{input_buf}}, {{fds_buf}}
> +{{- ", &_ipcOutputBuf" if has_output -}}
> +{{- ", &_ipcOutputFds" if has_output and method|method_output_has_fd -}}
> +);
> +{%- endif %}
> + if (ret < 0) {
> + LOG(IPAProxy, Error) << "Failed to call {{method.mojom_name}}";
> +{%- if method|method_return_value != "void" %}
> + return static_cast<{{method|method_return_value}}>(ret);
> +{%- else %}
> + return;
> +{%- endif %}
> + }
> +{% if method|method_return_value != "void" %}
> + return IPADataSerializer<{{method.response_parameters|first|name}}>::deserialize(_ipcOutputBuf, 0);
> +{% elif method|method_param_outputs|length > 0 %}
> +{{proxy_funcs.deserialize_call(method|method_param_outputs, '_ipcOutputBuf', '_ipcOutputFds')}}
> +{% endif -%}
> +}
> +
> +{% endfor %}
> +
> +{% for method in interface_cb.methods %}
> +{{proxy_funcs.func_sig(proxy_name, method, "Thread")}}
> +{
> + {{method.mojom_name}}.emit({{method.parameters|params_comma_sep}});
> +}
> +
> +void {{proxy_name}}::{{method.mojom_name}}IPC(
> + std::vector<uint8_t>::iterator data,
> + size_t dataSize,
> + [[maybe_unused]] std::vector<int32_t> &fds)
> +{
> +{%- for param in method.parameters %}
> + {{param|name}} {{param.mojom_name}};
> +{%- endfor %}
> +{{proxy_funcs.deserialize_call(method.parameters, 'data', 'fds', false, false, true, 'dataSize')}}
> + {{method.mojom_name}}.emit({{method.parameters|params_comma_sep}});
> +}
> +{% endfor %}
> +
> +{%- if has_namespace %}
> +{% for ns in namespace|reverse %}
> +} /* {{ns}} */
> +{% endfor %}
> +{%- endif %}
> +} /* namespace libcamera */
> diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
> new file mode 100644
> index 00000000..3fb7192f
> --- /dev/null
> +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl
> @@ -0,0 +1,118 @@
> +{#- SPDX-License-Identifier: LGPL-2.1-or-later -#}
> +{%- import "proxy_functions.tmpl" as proxy_funcs -%}
> +
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) {{year}}, Google Inc.
> + *
> + * ipa_proxy_{{module_name}}.h - Image Processing Algorithm proxy for {{module_name}}
> + *
> + * This file is auto-generated. Do not edit.
> + */
> +
> +#ifndef __LIBCAMERA_INTERNAL_IPA_PROXY_{{module_name|upper}}_H__
> +#define __LIBCAMERA_INTERNAL_IPA_PROXY_{{module_name|upper}}_H__
> +
> +#include <libcamera/ipa/ipa_interface.h>
> +#include <libcamera/ipa/{{module_name}}.h>
> +#include <libcamera/ipa/{{module_name}}_ipa_interface.h>
> +
> +#include "libcamera/internal/control_serializer.h"
> +#include "libcamera/internal/ipa_ipc.h"
> +#include "libcamera/internal/ipa_ipc_unixsocket.h"
> +#include "libcamera/internal/ipa_proxy.h"
> +#include "libcamera/internal/ipc_unixsocket.h"
> +#include "libcamera/internal/thread.h"
> +
> +namespace libcamera {
> +{%- if has_namespace %}
> +{% for ns in namespace %}
> +namespace {{ns}} {
> +{% endfor %}
> +{%- endif %}
> +
> +class {{proxy_name}} : public IPAProxy, public {{interface_name}}, public Object
> +{
> +public:
> + {{proxy_name}}(IPAModule *ipam, bool isolate);
> + ~{{proxy_name}}();
> +
> +{% for method in interface_main.methods %}
> +{{proxy_funcs.func_sig(proxy_name, method, "", false, true)|indent(8, true)}};
> +{% endfor %}
> +
> +{%- for method in interface_cb.methods %}
> + Signal<
> +{%- for param in method.parameters -%}
> + {{"const " if not param|is_pod}}{{param|name}}{{" &" if not param|is_pod}}
> + {{- ", " if not loop.last}}
> +{%- endfor -%}
> +> {{method.mojom_name}};
> +{% endfor %}
> +
> +private:
> + void recvIPC(std::vector<uint8_t> &data, std::vector<int32_t> &fds);
> +
> +{% for method in interface_main.methods %}
> +{{proxy_funcs.func_sig(proxy_name, method, "Thread", false)|indent(8, true)}};
> +{{proxy_funcs.func_sig(proxy_name, method, "IPC", false)|indent(8, true)}};
> +{% endfor %}
> +{% for method in interface_cb.methods %}
> +{{proxy_funcs.func_sig(proxy_name, method, "Thread", false)|indent(8, true)}};
> + void {{method.mojom_name}}IPC(
> + std::vector<uint8_t>::iterator data,
> + size_t dataSize,
> + std::vector<int32_t> &fds);
> +{% endfor %}
> +
> + /* Helper class to invoke async functions in another thread. */
> + class ThreadProxy : public Object
> + {
> + public:
> + void setIPA({{interface_name}} *ipa)
> + {
> + ipa_ = ipa;
> + }
> +
> + int start()
> + {
> + return ipa_->start();
> + }
> +
> + void stop()
> + {
> + ipa_->stop();
> + }
> +{% for method in interface_main.methods %}
> +{%- if method|is_async %}
> + {{proxy_funcs.func_sig(proxy_name, method, "", false)|indent(16)}}
> + {
> + ipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}});
> + }
> +{%- endif %}
> +{%- endfor %}
> +
> + private:
> + {{interface_name}} *ipa_;
> + };
> +
> + bool running_;
> + Thread thread_;
> + ThreadProxy proxy_;
> + std::unique_ptr<{{interface_name}}> ipa_;
> +
> + const bool isolate_;
> +
> + std::unique_ptr<IPAIPCUnixSocket> ipc_;
> +
> + ControlSerializer controlSerializer_;
> +};
> +
> +{%- if has_namespace %}
> +{% for ns in namespace|reverse %}
> +} /* {{ns}} */
> +{% endfor %}
> +{%- endif %}
> +} /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_INTERNAL_IPA_PROXY_{{module_name|upper}}_H__ */
> diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy_worker.cpp.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy_worker.cpp.tmpl
> new file mode 100644
> index 00000000..dca4f99d
> --- /dev/null
> +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy_worker.cpp.tmpl
> @@ -0,0 +1,187 @@
> +{#- SPDX-License-Identifier: LGPL-2.1-or-later -#}
> +{%- import "proxy_functions.tmpl" as proxy_funcs -%}
> +
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) {{year}}, Google Inc.
> + *
> + * ipa_proxy_{{module_name}}_worker.cpp - Image Processing Algorithm proxy worker for {{module_name}}
> + *
> + * This file is auto-generated. Do not edit.
> + */
> +
> +#include <algorithm>
> +#include <iostream>
> +#include <sys/types.h>
> +#include <tuple>
> +#include <unistd.h>
> +
> +#include <libcamera/event_dispatcher.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/internal/camera_sensor.h"
> +#include "libcamera/internal/control_serializer.h"
> +#include "libcamera/internal/ipa_data_serializer.h"
> +#include "libcamera/internal/ipa_ipc_unixsocket.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/thread.h"
> +
> +using namespace libcamera;
> +
> +LOG_DEFINE_CATEGORY({{proxy_name}}Worker)
> +
> +{%- if has_namespace %}
> +{% for ns in namespace -%}
> +using namespace {{ns}};
> +{% endfor %}
> +{%- endif %}
> +
> +struct CallData {
> + IPCUnixSocket::Payload *response;
> + bool done;
> +};
> +
> +{{interface_name}} *ipa_;
> +IPCUnixSocket socket_;
> +
> +ControlSerializer controlSerializer_;
> +
> +bool exit_ = false;
> +
> +void readyRead(IPCUnixSocket *socket)
> +{
> + IPCUnixSocket::Payload _message, _response;
> + int _retRecv = socket->receive(&_message);
> + if (_retRecv) {
> + LOG({{proxy_name}}Worker, Error)
> + << "Receive message failed" << _retRecv;
> + return;
> + }
> +
> + uint32_t _cmdUint, _seq;
> + std::tie(_cmdUint, _seq) = IPAIPCUnixSocket::readHeader(_message);
> + IPAIPCUnixSocket::eraseHeader(_message);
> +
> + {{cmd_enum_name}} _cmd = static_cast<{{cmd_enum_name}}>(_cmdUint);
> +
> + 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, '_message.data', '_message.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 = ipa_->{{method.mojom_name}}({{method.parameters|params_comma_sep}});
> +{%- else %}
> + 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 -%}
> +);
> +{%- endif %}
> +{% if not method|is_async %}
> + IPAIPCUnixSocket::writeHeader(_response, _cmdUint, _seq);
> +{%- 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.begin(), _callRetBuf.end());
> +{%- else %}
> + {{proxy_funcs.serialize_call(method|method_param_outputs, "_response.data", "_response.fds", base_controls)|indent(8, true)}}
> +{%- endif %}
> + int _ret = socket_.send(_response);
> + if (_ret < 0) {
> + LOG({{proxy_name}}Worker, Error)
> + << "Reply to {{method.mojom_name}}() failed" << _ret;
> + }
> + LOG({{proxy_name}}Worker, Debug) << "Done replying to {{method.mojom_name}}()";
> +{%- endif %}
> + break;
> + }
> +{% endfor %}
> + default:
> + LOG({{proxy_name}}Worker, Error) << "Unknown command " << _cmdUint;
> + }
> +}
> +
> +{% for method in interface_cb.methods %}
> +{{proxy_funcs.func_sig(proxy_name, method, "", false)}}
> +{
> + IPCUnixSocket::Payload _message;
> +
> + IPAIPCUnixSocket::writeHeader(_message, static_cast<uint32_t>({{cmd_event_enum_name}}::{{method.mojom_name|cap}}), 0);
> + {{proxy_funcs.serialize_call(method|method_param_inputs, "_message.data", "_message.fds", base_controls)}}
> +
> + socket_.send(_message);
> +
> + LOG({{proxy_name}}Worker, Debug) << "{{method.mojom_name}} done";
> +}
> +{% endfor %}
> +
> +int main(int argc, char **argv)
> +{
> + /* 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_name}}Worker, Error)
> + << "Tried to start worker with no args";
> + return EXIT_FAILURE;
> + }
> +
> + int fd = std::stoi(argv[2]);
> + LOG({{proxy_name}}Worker, 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_name}}Worker, Error)
> + << "IPAModule " << argv[1] << " isn't valid";
> + return EXIT_FAILURE;
> + }
> +
> + if (socket_.bind(fd) < 0) {
> + LOG({{proxy_name}}Worker, Error) << "IPC socket binding failed";
> + return EXIT_FAILURE;
> + }
> + socket_.readyRead.connect(&readyRead);
> +
> + ipa_ = dynamic_cast<{{interface_name}} *>(ipam->createInterface());
> + if (!ipa_) {
> + LOG({{proxy_name}}Worker, Error) << "Failed to create IPA interface instance";
> + return EXIT_FAILURE;
> + }
> +{% for method in interface_cb.methods %}
> + ipa_->{{method.mojom_name}}.connect(&{{method.mojom_name}});
> +{%- endfor %}
> +
> + LOG({{proxy_name}}Worker, Debug) << "Proxy worker successfully started";
> +
> + /* \todo upgrade listening loop */
> + EventDispatcher *dispatcher = Thread::current()->eventDispatcher();
> + while (!exit_)
> + dispatcher->processEvents();
> +
> + delete ipa_;
> + socket_.close();
> +
> + return 0;
> +}
> diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_serializer.h.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_serializer.h.tmpl
> new file mode 100644
> index 00000000..675f9adf
> --- /dev/null
> +++ b/utils/ipc/generators/libcamera_templates/module_ipa_serializer.h.tmpl
> @@ -0,0 +1,44 @@
> +{#- SPDX-License-Identifier: LGPL-2.1-or-later -#}
> +{%- import "serializer.tmpl" as serializer -%}
> +
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) {{year}}, Google Inc.
> + *
> + * {{module_name}}_serializer.h - Image Processing Algorithm data serializer for {{module_name}}
> + *
> + * This file is auto-generated. Do not edit.
> + */
> +
> +#ifndef __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_{{module_name|upper}}_H__
> +#define __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_{{module_name|upper}}_H__
> +
> +#include <libcamera/ipa/{{module_name}}.h>
> +#include <libcamera/ipa/{{module_name}}_ipa_interface.h>
> +
> +#include "libcamera/internal/control_serializer.h"
> +#include "libcamera/internal/ipa_data_serializer.h"
> +
> +#include <tuple>
> +#include <vector>
> +
> +namespace libcamera {
> +
> +LOG_DECLARE_CATEGORY(IPADataSerializer)
> +{% for struct in structs_nonempty %}
> +template<>
> +class IPADataSerializer<{{struct|name_full(namespace_str)}}>
> +{
> +public:
> +{{- serializer.serializer(struct, base_controls, namespace_str)}}
> +{%- if struct|has_fd %}
> +{{serializer.deserializer_fd(struct, namespace_str)}}
> +{%- else %}
> +{{serializer.deserializer_no_fd(struct, namespace_str)}}
> +{%- endif %}
> +};
> +{% endfor %}
> +
> +} /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_INTERNAL_IPA_DATA_SERIALIZER_{{module_name|upper}}_H__ */
> diff --git a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
> new file mode 100644
> index 00000000..f6836034
> --- /dev/null
> +++ b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl
> @@ -0,0 +1,205 @@
> +{#- SPDX-License-Identifier: LGPL-2.1-or-later -#}
> +{#
> + # \brief Generate fuction prototype
> + #
> + # \param class Class name
> + # \param method mojom Method object
> + # \param suffix Suffix to append to \a method function name
> + # \param need_class_name True to generate class name with function
> + # \param override True to generate override tag after the function prototype
> + #}
> +{%- macro func_sig(class, method, suffix, need_class_name = true, override = false) -%}
> +{{method|method_return_value}} {{class + "::" if need_class_name}}{{method.mojom_name}}{{suffix}}(
> +{%- for param in method|method_parameters %}
> + {{param}}{{- "," if not loop.last}}
> +{%- endfor -%}
> +){{" override" if override}}
> +{%- endmacro -%}
> +
> +{#
> + # \brief Generate function body for IPA init() function for thread
> + #}
> +{%- macro init_thread_body() -%}
> + int ret = ipa_->init(settings);
> + if (ret)
> + return ret;
> +
> + proxy_.moveToThread(&thread_);
> +
> + return 0;
> +{%- endmacro -%}
> +
> +{#
> + # \brief Generate function body for IPA start() function for thread
> + #}
> +{%- macro start_thread_body() -%}
> + running_ = true;
> + thread_.start();
> +
> + return proxy_.invokeMethod(&ThreadProxy::start, ConnectionTypeBlocking);
> +{%- endmacro -%}
> +
> +{#
> + # \brief Generate function body for IPA stop() function for thread
> + #}
> +{%- macro stop_thread_body() -%}
> + if (!running_)
> + return;
> +
> + running_ = false;
> +
> + proxy_.invokeMethod(&ThreadProxy::stop, ConnectionTypeBlocking);
> +
> + thread_.exit();
> + thread_.wait();
> +{%- endmacro -%}
> +
> +
> +{#
> + # \brief Serialize multiple objects into data buffer and fd vector
> + #
> + # Generate code to serialize multiple objects, as specified in \a params
> + # (which are the parameters to some function), into \a buf data buffer and
> + # \a fds fd vector.
> + # This code is meant to be used by the proxy, for serializing prior to IPC calls.
> + #}
> +{%- macro serialize_call(params, buf, fds, base_controls) %}
> +{%- for param in params %}
> + std::vector<uint8_t> {{param.mojom_name}}Buf;
> +{%- if param|has_fd %}
> + std::vector<int32_t> {{param.mojom_name}}Fds;
> + std::tie({{param.mojom_name}}Buf, {{param.mojom_name}}Fds) =
> +{%- else %}
> + std::tie({{param.mojom_name}}Buf, std::ignore) =
> +{%- endif %}
> +{%- if param|is_controls %}
> + IPADataSerializer<{{param|name}}>::serialize({{param.mojom_name}}, {{param.mojom_name}}.infoMap() ? *{{param.mojom_name}}.infoMap() : {{base_controls}}
> +{%- else %}
> + IPADataSerializer<{{param|name}}>::serialize({{param.mojom_name}}
> +{%- endif %}
> +{{- ", &controlSerializer_" if param|needs_control_serializer -}}
> +);
> +{%- endfor %}
> +
> +{%- if params|length > 1 %}
> +{%- for param in params %}
> + appendPOD<uint32_t>({{buf}}, {{param.mojom_name}}Buf.size());
> +{%- if param|has_fd %}
> + appendPOD<uint32_t>({{buf}}, {{param.mojom_name}}Fds.size());
> +{%- endif %}
> +{%- endfor %}
> +{%- endif %}
> +
> +{%- for param in params %}
> + {{buf}}.insert({{buf}}.end(), {{param.mojom_name}}Buf.begin(), {{param.mojom_name}}Buf.end());
> +{%- endfor %}
> +
> +{%- for param in params %}
> +{%- if param|has_fd %}
> + {{fds}}.insert({{fds}}.end(), {{param.mojom_name}}Fds.begin(), {{param.mojom_name}}Fds.end());
> +{%- endif %}
> +{%- endfor %}
> +{%- endmacro -%}
> +
> +
> +{#
> + # \brief Deserialize a single object from data buffer and fd vector
> + #
> + # \param pointer True deserializes the object into a dereferenced pointer
> + # \param iter True treats \a buf as an iterator instead of a vector
> + # \param data_size Variable that holds the size of the vector referenced by \a buf
> + #
> + # Generate code to deserialize a single object, as specified in \a param,
> + # from \a buf data buffer and \a fds fd vector.
> + # This code is meant to be used by macro deserialize_call.
> + #}
> +{%- macro deserialize_param(param, pointer, loop, buf, fds, iter, data_size) -%}
> +{{"*" if pointer}}{{param.mojom_name}} = IPADataSerializer<{{param|name}}>::deserialize(
> +{%- if not iter %}
> + {{buf}}.begin() + {{param.mojom_name}}Start,
> +{%- else %}
> + {{buf}} + {{param.mojom_name}}Start,
> +{%- endif %}
> +{%- if loop.last and not iter %}
> + {{buf}}.end()
> +{%- elif not iter %}
> + {{buf}}.begin() + {{param.mojom_name}}Start + {{param.mojom_name}}BufSize
> +{%- elif iter and loop.length == 1 %}
> + {{buf}} + {{data_size}}
> +{%- else %}
> + {{buf}} + {{param.mojom_name}}Start + {{param.mojom_name}}BufSize
> +{%- endif -%}
> +{{- "," if param|has_fd}}
> +{%- if param|has_fd %}
> + {{fds}}.begin() + {{param.mojom_name}}FdStart,
> +{%- if loop.last %}
> + {{fds}}.end()
> +{%- else %}
> + {{fds}}.begin() + {{param.mojom_name}}FdStart + {{param.mojom_name}}FdsSize
> +{%- endif -%}
> +{%- endif -%}
> +{{- "," if param|needs_control_serializer}}
> +{%- if param|needs_control_serializer %}
> + &controlSerializer_
> +{%- endif -%}
> +);
> +{%- endmacro -%}
> +
> +
> +{#
> + # \brief Deserialize multiple objects from data buffer and fd vector
> + #
> + # \param pointer True deserializes objects into pointers, and adds a null check.
> + # \param declare True declares the objects in addition to deserialization.
> + # \param iter True treats \a buf as an iterator instead of a vector
> + # \param data_size Variable that holds the size of the vector referenced by \a buf
> + #
> + # Generate code to deserialize multiple objects, as specified in \a params
> + # (which are the parameters to some function), from \a buf data buffer and
> + # \a fds fd vector.
> + # This code is meant to be used by the proxy, for deserializing after IPC calls.
> + #}
> +{%- macro deserialize_call(params, buf, fds, pointer = true, declare = false, iter = false, data_size = '') -%}
> +{% set ns = namespace(size_offset = 0) %}
> +{%- if params|length > 1 %}
> +{%- for param in params %}
> + [[maybe_unused]] size_t {{param.mojom_name}}BufSize = readPOD<uint32_t>({{buf}}, {{ns.size_offset}}
> +{%- if iter -%}
> +, {{buf}} + {{data_size}}
> +{%- endif -%}
> +);
> + {%- set ns.size_offset = ns.size_offset + 4 %}
> +{%- if param|has_fd %}
> + [[maybe_unused]] size_t {{param.mojom_name}}FdsSize = readPOD<uint32_t>({{buf}}, {{ns.size_offset}}
> +{%- if iter -%}
> +, {{buf}} + {{data_size}}
> +{%- endif -%}
> +);
> + {%- set ns.size_offset = ns.size_offset + 4 %}
> +{%- endif %}
> +{%- endfor %}
> +{%- endif %}
> +{% for param in params %}
> +{%- if loop.first %}
> + size_t {{param.mojom_name}}Start = {{ns.size_offset}};
> +{%- else %}
> + size_t {{param.mojom_name}}Start = {{loop.previtem.mojom_name}}Start + {{loop.previtem.mojom_name}}BufSize;
> +{%- endif %}
> +{%- endfor %}
> +{% for param in params|with_fds %}
> +{%- if loop.first %}
> + size_t {{param.mojom_name}}FdStart = 0;
> +{%- elif not loop.last %}
> + size_t {{param.mojom_name}}FdStart = {{loop.previtem.mojom_name}}FdStart + {{loop.previtem.mojom_name}}FdsSize;
> +{%- endif %}
> +{%- endfor %}
> +{% for param in params %}
> + {%- if pointer %}
> + if ({{param.mojom_name}}) {
> +{{deserialize_param(param, pointer, loop, buf, fds, iter, data_size)|indent(16, True)}}
> + }
> + {%- else %}
> + {{param|name + " " if declare}}{{deserialize_param(param, pointer, loop, buf, fds, iter, data_size)|indent(8)}}
> + {%- endif %}
> +{% endfor %}
> +{%- endmacro -%}
> diff --git a/utils/ipc/generators/libcamera_templates/serializer.tmpl b/utils/ipc/generators/libcamera_templates/serializer.tmpl
> new file mode 100644
> index 00000000..51dbeb0e
> --- /dev/null
> +++ b/utils/ipc/generators/libcamera_templates/serializer.tmpl
> @@ -0,0 +1,280 @@
> +{#- SPDX-License-Identifier: LGPL-2.1-or-later -#}
> +{# Turn this into a C macro? #}
> +{#
> + # \brief Verify that there is enough bytes to deserialize
> + #
> + # Generate code that verifies that \a size is not greater than \a dataSize.
> + # Otherwise log an error with \a name and \a typename.
> + #}
> +{%- macro check_data_size(size, dataSize, name, typename) %}
> + if ({{size}} > {{dataSize}}) {
> + LOG(IPADataSerializer, Error)
> + << "Failed to deserialize {{name}}: not enough {{typename}}, expected "
> + << ({{size}}) << ", got " << ({{dataSize}});
> + return ret;
> + }
> +{%- endmacro %}
> +
> +
> +{#
> + # \brief Serialize some field into return vector
> + #
> + # Generate code to serialize \a field into retData, including size of the
> + # field and fds (where appropriate). \a base_controls indicates the
> + # default ControlInfoMap in the event that the ControlList does not have one.
> + # This code is meant to be used by the IPADataSerializer specialization.
> + #}
> +{%- macro serializer_field(field, base_controls, namespace, loop) %}
> +{%- if field|is_pod or field|is_enum %}
> + std::vector<uint8_t> {{field.mojom_name}};
> + std::tie({{field.mojom_name}}, std::ignore) =
> + {%- if field|is_pod %}
> + IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}}_);
> + {%- elif field|is_enum %}
> + IPADataSerializer<uint{{field|bit_width}}_t>::serialize(data.{{field.mojom_name}}_);
> + {%- endif %}
> + retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
> +{%- elif field|is_fd %}
> + std::vector<uint8_t> {{field.mojom_name}};
> + std::vector<int32_t> {{field.mojom_name}}Fds;
> + std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
> + IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}}_);
> + retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
> + retFds.insert(retFds.end(), {{field.mojom_name}}Fds.begin(), {{field.mojom_name}}Fds.end());
> +{%- elif field|is_controls %}
> + if (data.{{field.mojom_name}}_.size() > 0) {
> + std::vector<uint8_t> {{field.mojom_name}};
> + std::tie({{field.mojom_name}}, std::ignore) =
> + IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}}_,
> + data.{{field.mojom_name}}_.infoMap() ? *data.{{field.mojom_name}}_.infoMap() : {{base_controls}},
> + cs);
> + appendPOD<uint32_t>(retData, {{field.mojom_name}}.size());
> + retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
> + } else {
> + appendPOD<uint32_t>(retData, 0);
> + }
> +{%- elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}
> + std::vector<uint8_t> {{field.mojom_name}};
> + {%- if field|has_fd %}
> + std::vector<int32_t> {{field.mojom_name}}Fds;
> + std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =
> + {%- else %}
> + std::tie({{field.mojom_name}}, std::ignore) =
> + {%- endif %}
> + {%- if field|is_array or field|is_map %}
> + IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}}_, cs);
> + {%- elif field|is_str %}
> + IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}}_);
> + {%- else %}
> + IPADataSerializer<{{field|name_full(namespace)}}>::serialize(data.{{field.mojom_name}}_, cs);
> + {%- endif %}
> + appendPOD<uint32_t>(retData, {{field.mojom_name}}.size());
> + {%- if field|has_fd %}
> + appendPOD<uint32_t>(retData, {{field.mojom_name}}Fds.size());
> + {%- endif %}
> + retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());
> + {%- if field|has_fd %}
> + retFds.insert(retFds.end(), {{field.mojom_name}}Fds.begin(), {{field.mojom_name}}Fds.end());
> + {%- endif %}
> +{%- else %}
> + /* Unknown serialization for {{field.mojom_name}}. */
> +{%- endif %}
> +{%- endmacro %}
> +
> +
> +{#
> + # \brief Deserialize some field into return struct
> + #
> + # Generate code to deserialize \a field into object ret.
> + # This code is meant to be used by the IPADataSerializer specialization.
> + #}
> +{%- macro deserializer_field(field, namespace, loop) %}
> +{% if field|is_pod or field|is_enum %}
> + {%- set field_size = (field|bit_width|int / 8)|int %}
> + {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data')}}
> + {%- if field|is_pod %}
> + ret.{{field.mojom_name}}_ = static_cast<{{field|name}}>(IPADataSerializer<{{field|name}}>::deserialize(m, m + {{field_size}}));
> + {%- else %}
> + ret.{{field.mojom_name}}_ = static_cast<{{field|name}}>(IPADataSerializer<uint{{field|bit_width}}_t>::deserialize(m, m + {{field_size}}));
> + {%- endif %}
> + {%- if not loop.last %}
> + m += {{field_size}};
> + dataSize -= {{field_size}};
> + {%- endif %}
> +{% elif field|is_fd %}
> + {%- set field_size = 1 %}
> + {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data')}}
> + ret.{{field.mojom_name}}_ = IPADataSerializer<{{field|name}}>::deserialize(m, m + 1, n, n + 1, cs);
> + {%- if not loop.last %}
> + m += {{field_size}};
> + dataSize -= {{field_size}};
> + n += ret.{{field.mojom_name}}_.isValid() ? 1 : 0;
> + fdsSize -= ret.{{field.mojom_name}}_.isValid() ? 1 : 0;
> + {%- endif %}
> +{% elif field|is_controls %}
> + {%- set field_size = 4 %}
> + {{- check_data_size(field_size, 'dataSize', field.mojom_name + 'Size', 'data')}}
> + size_t {{field.mojom_name}}Size = readPOD<uint32_t>(m, 0, data.end());
> + {%- set field_size = '4 + ' + field.mojom_name + 'Size' -%}
> + {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data')}}
> + if ({{field.mojom_name}}Size > 0)
> + ret.{{field.mojom_name}}_ =
> + IPADataSerializer<{{field|name}}>::deserialize(m + 4, m + 4 + {{field.mojom_name}}Size, cs);
> + {%- if not loop.last %}
> + m += {{field_size}};
> + dataSize -= {{field_size}};
> + {%- endif %}
> +{% elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}
> + {%- set field_size = 4 %}
> + {{- check_data_size(field_size, 'dataSize', field.mojom_name + 'Size', 'data')}}
> + size_t {{field.mojom_name}}Size = readPOD<uint32_t>(m, 0, data.end());
> + {%- if field|has_fd %}
> + {%- set field_size = 8 %}
> + {{- check_data_size(field_size, 'dataSize', field.mojom_name + 'FdsSize', 'data')}}
> + size_t {{field.mojom_name}}FdsSize = readPOD<uint32_t>(m, 4, data.end());
> + {{- check_data_size(field.mojom_name + 'FdsSize', 'fdsSize', field.mojom_name, 'fds')}}
> + {%- endif %}
> + {%- set field_size = field|has_fd|choose('8 + ', '4 + ') + field.mojom_name + 'Size' -%}
> + {{- check_data_size(field_size, 'dataSize', field.mojom_name, 'data')}}
> + ret.{{field.mojom_name}}_ =
> + {%- if field|is_str %}
> + IPADataSerializer<{{field|name}}>::deserialize(m + 4, m + 4 + {{field.mojom_name}}Size);
> + {%- elif field|has_fd and (field|is_array or field|is_map) %}
> + IPADataSerializer<{{field|name}}>::deserialize(m + 8, m + 8 + {{field.mojom_name}}Size, n, n + {{field.mojom_name}}FdsSize, cs);
> + {%- elif field|has_fd and (not (field|is_array or field|is_map)) %}
> + IPADataSerializer<{{field|name_full(namespace)}}>::deserialize(m + 8, m + 8 + {{field.mojom_name}}Size, n, n + {{field.mojom_name}}FdsSize, cs);
> + {%- elif (not field|has_fd) and (field|is_array or field|is_map) %}
> + IPADataSerializer<{{field|name}}>::deserialize(m + 4, m + 4 + {{field.mojom_name}}Size, cs);
> + {%- else %}
> + IPADataSerializer<{{field|name_full(namespace)}}>::deserialize(m + 4, m + 4 + {{field.mojom_name}}Size, cs);
> + {%- endif %}
> + {%- if not loop.last %}
> + m += {{field_size}};
> + dataSize -= {{field_size}};
> + {%- if field|has_fd %}
> + n += {{field.mojom_name}}FdsSize;
> + fdsSize -= {{field.mojom_name}}FdsSize;
> + {%- endif %}
> + {%- endif %}
> +{% else %}
> + /* Unknown deserialization for {{field.mojom_name}}. */
> +{%- endif %}
> +{%- endmacro %}
> +
> +
> +{#
> + # \brief Serialize a struct
> + #
> + # Generate code for IPADataSerializer specialization, for serializing
> + # \a struct. \a base_controls indicates the default ControlInfoMap
> + # in the event that the ControlList does not have one.
> + #}
> +{%- macro serializer(struct, base_controls, namespace) %}
> + static std::tuple<std::vector<uint8_t>, std::vector<int32_t>>
> + serialize(const {{struct|name_full(namespace)}} &data,
> +{%- if struct|needs_control_serializer %}
> + ControlSerializer *cs)
> +{%- else %}
> + [[maybe_unused]] ControlSerializer *cs = nullptr)
> +{%- endif %}
> + {
> + std::vector<uint8_t> retData;
> +{%- if struct|has_fd %}
> + std::vector<int32_t> retFds;
> +{%- endif %}
> +{%- for field in struct.fields %}
> +{{serializer_field(field, base_controls, namespace, loop)}}
> +{%- endfor %}
> +{% if struct|has_fd %}
> + return {retData, retFds};
> +{%- else %}
> + return {retData, {}};
> +{%- endif %}
> + }
> +{%- endmacro %}
> +
> +
> +{#
> + # \brief Deserialize a struct that has fds
> + #
> + # Generate code for IPADataSerializer specialization, for deserializing
> + # \a struct, in the case that \a struct has file descriptors.
> + #}
> +{%- macro deserializer_fd(struct, namespace) %}
> + static {{struct|name_full(namespace)}}
> + deserialize(std::vector<uint8_t> &data,
> + std::vector<int32_t> &fds,
> +{%- if struct|needs_control_serializer %}
> + ControlSerializer *cs)
> +{%- else %}
> + [[maybe_unused]] ControlSerializer *cs = nullptr)
> +{%- endif %}
> + {
> + {{struct|name_full(namespace)}} ret;
> + std::vector<uint8_t>::iterator m = data.begin();
> + std::vector<int32_t>::iterator n = fds.begin();
> +
> + size_t dataSize = data.size();
> + size_t fdsSize = fds.size();
> +{%- for field in struct.fields -%}
> +{{deserializer_field(field, namespace, loop)}}
> +{%- endfor %}
> + return ret;
> + }
> +
> + static {{struct|name_full(namespace)}}
> + deserialize(std::vector<uint8_t>::iterator dataBegin,
> + std::vector<uint8_t>::iterator dataEnd,
> + std::vector<int32_t>::iterator fdsBegin,
> + std::vector<int32_t>::iterator fdsEnd,
> +{%- if struct|needs_control_serializer %}
> + ControlSerializer *cs)
> +{%- else %}
> + [[maybe_unused]] ControlSerializer *cs = nullptr)
> +{%- endif %}
> + {
> + std::vector<uint8_t> data(dataBegin, dataEnd);
> + std::vector<int32_t> fds(fdsBegin, fdsEnd);
> + return IPADataSerializer<{{struct|name_full(namespace)}}>::deserialize(data, fds, cs);
> + }
> +{%- endmacro %}
> +
> +
> +{#
> + # \brief Deserialize a struct that has no fds
> + #
> + # Generate code for IPADataSerializer specialization, for deserializing
> + # \a struct, in the case that \a struct does not have file descriptors.
> + #}
> +{%- macro deserializer_no_fd(struct, namespace) %}
> + static {{struct|name_full(namespace)}}
> + deserialize(std::vector<uint8_t> &data,
> +{%- if struct|needs_control_serializer %}
> + ControlSerializer *cs)
> +{%- else %}
> + [[maybe_unused]] ControlSerializer *cs = nullptr)
> +{%- endif %}
> + {
> + {{struct|name_full(namespace)}} ret;
> + std::vector<uint8_t>::iterator m = data.begin();
> +
> + size_t dataSize = data.size();
> +{%- for field in struct.fields -%}
> +{{deserializer_field(field, namespace, loop)}}
> +{%- endfor %}
> + return ret;
> + }
> +
> + static {{struct|name_full(namespace)}}
> + deserialize(std::vector<uint8_t>::iterator dataBegin,
> + std::vector<uint8_t>::iterator dataEnd,
> +{%- if struct|needs_control_serializer %}
> + ControlSerializer *cs)
> +{%- else %}
> + [[maybe_unused]] ControlSerializer *cs = nullptr)
> +{%- endif %}
> + {
> + std::vector<uint8_t> data(dataBegin, dataEnd);
> + return IPADataSerializer<{{struct|name_full(namespace)}}>::deserialize(data, cs);
> + }
> +{%- endmacro %}
> diff --git a/utils/ipc/generators/mojom_libcamera_generator.py b/utils/ipc/generators/mojom_libcamera_generator.py
> new file mode 100644
> index 00000000..c4fbf9b3
> --- /dev/null
> +++ b/utils/ipc/generators/mojom_libcamera_generator.py
> @@ -0,0 +1,488 @@
> +'''Generates libcamera files from a mojom.Module.'''
> +
> +import argparse
> +import ast
> +import datetime
> +import contextlib
> +import os
> +import re
> +import shutil
> +import sys
> +import tempfile
> +
> +from jinja2 import contextfilter
> +
> +import mojom.fileutil as fileutil
> +import mojom.generate.generator as generator
> +import mojom.generate.module as mojom
> +from mojom.generate.template_expander import UseJinja
> +
> +
> +GENERATOR_PREFIX = 'libcamera'
> +
> +_kind_to_cpp_type = {
> + mojom.BOOL: 'bool',
> + mojom.INT8: 'int8_t',
> + mojom.UINT8: 'uint8_t',
> + mojom.INT16: 'int16_t',
> + mojom.UINT16: 'uint16_t',
> + mojom.INT32: 'int32_t',
> + mojom.UINT32: 'uint32_t',
> + mojom.FLOAT: 'float',
> + mojom.INT64: 'int64_t',
> + mojom.UINT64: 'uint64_t',
> + mojom.DOUBLE: 'double',
> +}
> +
> +_bit_widths = {
> + mojom.BOOL: '8',
> + mojom.INT8: '8',
> + mojom.UINT8: '8',
> + mojom.INT16: '16',
> + mojom.UINT16: '16',
> + mojom.INT32: '32',
> + mojom.UINT32: '32',
> + mojom.FLOAT: '32',
> + mojom.INT64: '64',
> + mojom.UINT64: '64',
> + mojom.DOUBLE: '64',
> +}
> +
> +def ModuleName(path):
> + return path.split('/')[-1].split('.')[0]
> +
> +def ModuleClassName(module):
> + s = re.sub(r'^IPA', '', module.interfaces[0].mojom_name)
> + return re.sub(r'Interface$', '', s)
> +
> +def UpperCamelCase(name):
> + return ''.join([x.capitalize() for x in generator.SplitCamelCase(name)])
> +
> +def CamelCase(name):
> + uccc = UpperCamelCase(name)
> + return uccc[0].lower() + uccc[1:]
> +
> +def Capitalize(name):
> + return name[0].upper() + name[1:]
> +
> +def ConstantStyle(name):
> + return generator.ToUpperSnakeCase(name)
> +
> +def Choose(cond, t, f):
> + return t if cond else f
> +
> +def CommaSep(l):
> + return ', '.join([m for m in l])
> +
> +def ParamsCommaSep(l):
> + return ', '.join([m.mojom_name for m in l])
> +
> +def GetDefaultValue(element):
> + if element.default is not None:
> + return element.default
> + if type(element.kind) == mojom.Kind:
> + return '0'
> + if mojom.IsEnumKind(element.kind):
> + return f'static_cast<{element.kind.mojom_name}>(0)'
> + if (isinstance(element.kind, mojom.Struct) and
> + element.kind.mojom_name == 'FileDescriptor'):
> + return '-1'
> + return ''
> +
> +def WithDefaultValues(element):
> + return [x for x in element if HasDefaultValue(x)]
> +
> +def WithFds(element):
> + return [x for x in element if HasFd(x)]
> +
> +def HasDefaultValue(element):
> + return GetDefaultValue(element) != ''
> +
> +def HasDefaultFields(element):
> + return True in [HasDefaultValue(x) for x in element.fields]
> +
> +def GetAllTypes(element):
> + if mojom.IsArrayKind(element):
> + return GetAllTypes(element.kind)
> + if mojom.IsMapKind(element):
> + return GetAllTypes(element.key_kind) + GetAllTypes(element.value_kind)
> + if isinstance(element, mojom.Parameter):
> + return GetAllTypes(element.kind)
> + if mojom.IsEnumKind(element):
> + return [element.mojom_name]
> + if not mojom.IsStructKind(element):
> + return [element.spec]
> + if len(element.fields) == 0:
> + return [element.mojom_name]
> + ret = [GetAllTypes(x.kind) for x in element.fields]
> + ret = [x for sublist in ret for x in sublist]
> + return list(set(ret))
> +
> +def GetAllAttrs(element):
> + if mojom.IsArrayKind(element):
> + return GetAllAttrs(element.kind)
> + if mojom.IsMapKind(element):
> + return {**GetAllAttrs(element.key_kind), **GetAllAttrs(element.value_kind)}
> + if isinstance(element, mojom.Parameter):
> + return GetAllAttrs(element.kind)
> + if mojom.IsEnumKind(element):
> + return element.attributes if element.attributes is not None else {}
> + if mojom.IsStructKind(element) and len(element.fields) == 0:
> + return element.attributes if element.attributes is not None else {}
> + if not mojom.IsStructKind(element):
> + return {}
> + attrs = [(x.attributes) for x in element.fields]
> + ret = {}
> + for d in attrs:
> + if d is not None:
> + ret = {**ret, **d}
> + return ret
> +
> +def NeedsControlSerializer(element):
> + types = GetAllTypes(element)
> + return "ControlList" in types or "ControlInfoMap" in types
> +
> +def HasFd(element):
> + attrs = GetAllAttrs(element)
> + if isinstance(element, mojom.Kind):
> + types = GetAllTypes(element)
> + else:
> + types = GetAllTypes(element.kind)
> + return "FileDescriptor" in types or (attrs is not None and "hasFd" in attrs)
> +
> +def MethodInputHasFd(method):
> + if len([x for x in method.parameters if HasFd(x)]) > 0:
> + return True
> + return False
> +
> +def MethodOutputHasFd(method):
> + if (MethodReturnValue(method) != 'void' or
> + method.response_parameters is None):
> + return False
> + if len([x for x in method.parameters if HasFd(x)]) > 0:
> + return True
> + return False
> +
> +def MethodParamInputs(method):
> + return method.parameters
> +
> +def MethodParamOutputs(method):
> + if (MethodReturnValue(method) != 'void' or
> + method.response_parameters is None):
> + return []
> + return method.response_parameters
> +
> +def MethodParamNames(method):
> + params = []
> + for param in method.parameters:
> + params.append(param.mojom_name)
> + if MethodReturnValue(method) == 'void':
> + if method.response_parameters is None:
> + return params
> + for param in method.response_parameters:
> + params.append(param.mojom_name)
> + return params
> +
> +def MethodParameters(method):
> + params = []
> + for param in method.parameters:
> + params.append('const %s %s%s' % (GetNameForElement(param),
> + ('&' if not IsPod(param) else ''),
> + param.mojom_name))
> + if MethodReturnValue(method) == 'void':
> + if method.response_parameters is None:
> + return params
> + for param in method.response_parameters:
> + params.append(f'{GetNameForElement(param)} *{param.mojom_name}')
> + return params
> +
> +def MethodReturnValue(method):
> + if method.response_parameters is None:
> + return 'void'
> + if len(method.response_parameters) == 1 and IsPod(method.response_parameters[0]):
> + return GetNameForElement(method.response_parameters[0])
> + return 'void'
> +
> +def IsAsync(method):
> + # callbacks are always async
> + if re.match("^IPA.*EventInterface$", method.interface.mojom_name):
> + return True
> + elif re.match("^IPA.*Interface$", method.interface.mojom_name):
> + if method.attributes is None:
> + return False
> + elif 'async' in method.attributes and method.attributes['async']:
> + return True
> + return False
> +
> +def IsArray(element):
> + return mojom.IsArrayKind(element.kind)
> +
> +def IsControls(element):
> + return mojom.IsStructKind(element.kind) and (element.kind.mojom_name == "ControlList" or
> + element.kind.mojom_name == "ControlInfoMap")
> +
> +def IsEnum(element):
> + return mojom.IsEnumKind(element.kind)
> +
> +def IsFd(element):
> + return mojom.IsStructKind(element.kind) and element.kind.mojom_name == "FileDescriptor"
> +
> +def IsMap(element):
> + return mojom.IsMapKind(element.kind)
> +
> +def IsPlainStruct(element):
> + return mojom.IsStructKind(element.kind) and not IsControls(element) and not IsFd(element)
> +
> +def IsPod(element):
> + return element.kind in _kind_to_cpp_type
> +
> +def IsStr(element):
> + return element.kind.spec == 's'
> +
> +def BitWidth(element):
> + if element.kind in _bit_widths:
> + return _bit_widths[element.kind]
> + if mojom.IsEnumKind(element.kind):
> + return '32'
> + return ''
> +
> +def GetNameForElement(element):
> + if (mojom.IsEnumKind(element) or
> + mojom.IsInterfaceKind(element) or
> + mojom.IsStructKind(element)):
> + return element.mojom_name
> + if (mojom.IsArrayKind(element)):
> + elem_name = GetNameForElement(element.kind)
> + return f'std::vector<{elem_name}>'
> + if (mojom.IsMapKind(element)):
> + key_name = GetNameForElement(element.key_kind)
> + value_name = GetNameForElement(element.value_kind)
> + return f'std::map<{key_name}, {value_name}>'
> + if isinstance(element, (mojom.Field, mojom.Method, mojom.Parameter)):
> + if (mojom.IsArrayKind(element.kind) or mojom.IsMapKind(element.kind)):
> + return GetNameForElement(element.kind)
> + if (mojom.IsReferenceKind(element.kind) and element.kind.spec == 's'):
> + return 'std::string'
> + if (hasattr(element, 'kind') and element.kind in _kind_to_cpp_type):
> + return _kind_to_cpp_type[element.kind]
> + return element.kind.mojom_name
> + if isinstance(element, mojom.EnumValue):
> + return (GetNameForElement(element.enum) + '.' +
> + ConstantStyle(element.name))
> + if isinstance(element, (mojom.NamedValue,
> + mojom.Constant,
> + mojom.EnumField)):
> + return ConstantStyle(element.name)
> + if (hasattr(element, '__hash__') and element in _kind_to_cpp_type):
> + return _kind_to_cpp_type[element]
> + if (hasattr(element, 'kind') and element.kind in _kind_to_cpp_type):
> + return _kind_to_cpp_type[element.kind]
> + if (hasattr(element, 'spec')):
> + if (element.spec == 's'):
> + return 'std::string'
> + return element.spec.split(':')[-1]
> + if (mojom.IsInterfaceRequestKind(element) or
> + mojom.IsAssociatedKind(element) or
> + mojom.IsPendingRemoteKind(element) or
> + mojom.IsPendingReceiverKind(element) or
> + mojom.IsUnionKind(element)):
> + raise Exception('Unsupported element: %s' % element)
> + raise Exception('Unexpected element: %s' % element)
> +
> +def GetFullNameForElement(element, namespace_str):
> + name = GetNameForElement(element)
> + if namespace_str == '':
> + return name
> + return f'{namespace_str}::{name}'
> +
> +def ValidateZeroLength(l, s, cap=True):
> + if l is None:
> + return
> + if len(l) > 0:
> + raise Exception(f'{s.capitalize() if cap else s} should be empty')
> +
> +def ValidateSingleLength(l, s, cap=True):
> + if len(l) > 1:
> + raise Exception(f'Only one {s} allowed')
> + if len(l) < 1:
> + raise Exception(f'{s.capitalize() if cap else s} is required')
> +
> +def ValidateInterfaces(interfaces):
> + # Validate presence of main interface
> + intf = [x for x in interfaces
> + if re.match("^IPA.*Interface", x.mojom_name) and
> + not re.match("^IPA.*EventInterface", x.mojom_name)]
> + ValidateSingleLength(intf, 'main interface')
> + intf = intf[0]
> +
> + # Validate presence of callback interface
> + cb = [x for x in interfaces if re.match("^IPA.*EventInterface", x.mojom_name)]
> + ValidateSingleLength(cb, 'event interface')
> + cb = cb[0]
> +
> + # Validate required main interface functions
> + f_init = [x for x in intf.methods if x.mojom_name == 'init']
> + f_start = [x for x in intf.methods if x.mojom_name == 'start']
> + f_stop = [x for x in intf.methods if x.mojom_name == 'stop']
> +
> + ValidateSingleLength(f_init, 'init()', False)
> + ValidateSingleLength(f_start, 'start()', False)
> + ValidateSingleLength(f_stop, 'stop()', False)
> +
> + f_init = f_init[0]
> + f_start = f_start[0]
> + f_stop = f_stop[0]
> +
> + # Validate parameters to init()
> + ValidateSingleLength(f_init.parameters, 'input parameter to init()')
> + ValidateSingleLength(f_init.response_parameters, 'output parameter from init()')
> + if f_init.parameters[0].kind.mojom_name != 'IPASettings':
> + raise Exception('init() must have single IPASettings input parameter')
> + if f_init.response_parameters[0].kind.spec != 'i32':
> + raise Exception('init() must have single int32 output parameter')
> +
> + # Validate parameters to start()
> + ValidateZeroLength(f_start.parameters, 'input parameter to start()')
> + ValidateSingleLength(f_start.response_parameters, 'output parameter from start()')
> + if f_start.response_parameters[0].kind.spec != 'i32':
> + raise Exception('start() must have single int32 output parameter')
> +
> + # Validate parameters to stop()
> + ValidateZeroLength(f_stop.parameters, 'input parameter to stop()')
> + ValidateZeroLength(f_stop.parameters, 'output parameter from stop()')
> +
> + # Validate that all async methods don't have return values
> + intf_methods_async = [x for x in intf.methods if IsAsync(x)]
> + for method in intf_methods_async:
> + ValidateZeroLength(method.response_parameters,
> + f'{method.mojom_name} response parameters', False)
> +
> + cb_methods_async = [x for x in cb.methods if IsAsync(x)]
> + for method in cb_methods_async:
> + ValidateZeroLength(method.response_parameters,
> + f'{method.mojom_name} response parameters', False)
> +
> +class Generator(generator.Generator):
> +
> + @staticmethod
> + def GetTemplatePrefix():
> + return 'libcamera_templates'
> +
> + def GetFilters(self):
> + libcamera_filters = {
> + 'all_types': GetAllTypes,
> + 'bit_width': BitWidth,
> + 'cap': Capitalize,
> + 'choose': Choose,
> + 'comma_sep': CommaSep,
> + 'default_value': GetDefaultValue,
> + 'has_default_fields': HasDefaultFields,
> + 'has_fd': HasFd,
> + 'is_async': IsAsync,
> + 'is_array': IsArray,
> + 'is_controls': IsControls,
> + 'is_enum': IsEnum,
> + 'is_fd': IsFd,
> + 'is_map': IsMap,
> + 'is_plain_struct': IsPlainStruct,
> + 'is_pod': IsPod,
> + 'is_str': IsStr,
> + 'method_input_has_fd': MethodInputHasFd,
> + 'method_output_has_fd': MethodOutputHasFd,
> + 'method_param_names': MethodParamNames,
> + 'method_param_inputs': MethodParamInputs,
> + 'method_param_outputs': MethodParamOutputs,
> + 'method_parameters': MethodParameters,
> + 'method_return_value': MethodReturnValue,
> + 'name': GetNameForElement,
> + 'name_full': GetFullNameForElement,
> + 'needs_control_serializer': NeedsControlSerializer,
> + 'params_comma_sep': ParamsCommaSep,
> + 'with_default_values': WithDefaultValues,
> + 'with_fds': WithFds,
> + }
> + return libcamera_filters
> +
> + def _GetJinjaExports(self):
> + return {
> + 'base_controls': '%s::controls' % ModuleClassName(self.module),
> + 'cmd_enum_name': '_%sCmd' % ModuleClassName(self.module),
> + 'cmd_event_enum_name': '_%sEventCmd' % ModuleClassName(self.module),
> + 'enums': self.module.enums,
> + 'has_array': len([x for x in self.module.kinds.keys() if x[0] == 'a']) > 0,
> + 'has_map': len([x for x in self.module.kinds.keys() if x[0] == 'm']) > 0,
> + 'has_namespace': self.module.mojom_namespace != '',
> + 'imports': self.module.imports,
> + 'interface_cb': self.module.interfaces[1],
> + 'interface_main': self.module.interfaces[0],
> + 'interface_name': 'IPA%sInterface' % ModuleClassName(self.module),
> + 'ipc_name': 'IPAIPCUnixSocket',
> + 'kinds': self.module.kinds,
> + 'module': self.module,
> + 'module_name': ModuleName(self.module.path),
> + 'module_class_name': ModuleClassName(self.module),
> + 'namespace': self.module.mojom_namespace.split('.'),
> + 'namespace_str': self.module.mojom_namespace.replace('.', '::') if
> + self.module.mojom_namespace is not None else '',
> + 'proxy_name': 'IPAProxy%s' % ModuleClassName(self.module),
> + 'proxy_worker_name': 'IPAProxy%sWorker' % ModuleClassName(self.module),
> + 'structs_nonempty': [x for x in self.module.structs if len(x.fields) > 0],
> + 'year': datetime.datetime.now().year,
> + }
> +
> +
> + @UseJinja('module_ipa_interface.h.tmpl')
> + def _GenerateDataHeader(self):
> + return self._GetJinjaExports()
> +
> + @UseJinja('module_ipa_serializer.h.tmpl')
> + def _GenerateSerializer(self):
> + return self._GetJinjaExports()
> +
> + @UseJinja('module_ipa_proxy.cpp.tmpl')
> + def _GenerateProxyCpp(self):
> + return self._GetJinjaExports()
> +
> + @UseJinja('module_ipa_proxy.h.tmpl')
> + def _GenerateProxyHeader(self):
> + return self._GetJinjaExports()
> +
> + @UseJinja('module_ipa_proxy_worker.cpp.tmpl')
> + def _GenerateProxyWorker(self):
> + return self._GetJinjaExports()
> +
> + def GenerateFiles(self, unparsed_args):
> + parser = argparse.ArgumentParser()
> + parser.add_argument('--libcamera_generate_header', action='store_true')
> + parser.add_argument('--libcamera_generate_serializer', action='store_true')
> + parser.add_argument('--libcamera_generate_proxy_cpp', action='store_true')
> + parser.add_argument('--libcamera_generate_proxy_h', action='store_true')
> + parser.add_argument('--libcamera_generate_proxy_worker', action='store_true')
> + parser.add_argument('--libcamera_output_path')
> + args = parser.parse_args(unparsed_args)
> +
> + ValidateInterfaces(self.module.interfaces)
> +
> + fileutil.EnsureDirectoryExists(os.path.dirname(args.libcamera_output_path))
> +
> + module_name = ModuleName(self.module.path)
> +
> + if args.libcamera_generate_header:
> + self.Write(self._GenerateDataHeader(),
> + args.libcamera_output_path)
> +
> + if args.libcamera_generate_serializer:
> + self.Write(self._GenerateSerializer(),
> + args.libcamera_output_path)
> +
> + if args.libcamera_generate_proxy_cpp:
> + self.Write(self._GenerateProxyCpp(),
> + args.libcamera_output_path)
> +
> + if args.libcamera_generate_proxy_h:
> + self.Write(self._GenerateProxyHeader(),
> + args.libcamera_output_path)
> +
> + if args.libcamera_generate_proxy_worker:
> + self.Write(self._GenerateProxyWorker(),
> + args.libcamera_output_path)
A very cursory look:
Acked-by: Jacopo Mondi <jacopo at jmondi.org>
Thanks
j
> --
> 2.27.0
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel at lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
More information about the libcamera-devel
mailing list