diff options
author | Armelle Laine <armellel@google.com> | 2022-05-27 07:39:42 +0000 |
---|---|---|
committer | Armelle Laine <armellel@google.com> | 2022-06-20 17:01:11 +0000 |
commit | b44d4c5aefea7f78422870c50a9e7cc1ce0f2a29 (patch) | |
tree | d57d16c41b3a74af0cfd1dd7415cc41197e14fb9 | |
parent | 3228ab6c5530343fde92e14e963135c890daae13 (diff) | |
download | aosp-for/refs/master.tar.gz |
Add a python tool generating a c wrapper on top of aidl clientsfor/refs/master
Support trusty_user and ql_tipc domains
Bug: 234584308
Change-Id: If136588fc7b8b408d2c2e480d009e8c68d9ba2e0
-rw-r--r-- | scripts/aidl/aidl_to_c.py | 186 | ||||
-rw-r--r-- | scripts/aidl/c_wrapper/__init__.py | 3 | ||||
-rw-r--r-- | scripts/aidl/c_wrapper/iface_h2c.py | 337 | ||||
-rw-r--r-- | scripts/aidl/c_wrapper/tpl/client_c.j2 | 69 | ||||
-rw-r--r-- | scripts/aidl/c_wrapper/tpl/client_h.j2 | 70 | ||||
-rw-r--r-- | scripts/aidl/c_wrapper/tpl/macros/c_connect_fn.j2 | 134 | ||||
-rw-r--r-- | scripts/aidl/c_wrapper/tpl/macros/c_method_fn.j2 | 198 |
7 files changed, 997 insertions, 0 deletions
diff --git a/scripts/aidl/aidl_to_c.py b/scripts/aidl/aidl_to_c.py new file mode 100644 index 0000000..99ace73 --- /dev/null +++ b/scripts/aidl/aidl_to_c.py @@ -0,0 +1,186 @@ +#!/bin/sh +"." "`dirname $0`/envsetup.sh" +"exec" "$PY3" "$0" "$@" +# +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +"""Generate from an aidl-generated interface header, its client c wrapper.""" + +from pathlib import Path +import argparse +import subprocess +import glob +from c_wrapper import iface_to_c + +script_dir = Path(__file__).parent +root_dir = script_dir / "../../../../../.." + + +def run_aidl(aidl_file, aidl_tool_str: str, out_cpp: str, out_hdr: str): + aidl_tool = Path(aidl_tool_str) + if not aidl_tool.exists(): + raise ( + "aidl tool cannot be found at {}, please build qemu-generic-64 target\n".format( + aidl_tool.resolve() + ) + ) + aidl_path = Path(aidl_file) + completed_process = subprocess.run( + " ".join( + [ + aidl_tool.as_posix(), + "--lang=trusty", + "-h", + out_hdr, + "-o", + out_cpp, + aidl_path.as_posix(), + ] + ), + shell=True, + text=True, + capture_output=True, + ) + if completed_process.returncode: + raise Exception(completed_process.stderr) + return Path(out_hdr) / "{}.h".format(aidl_path.stem) + + +def run_clang_format(d): + files = [] + for f in glob.glob("{}/*.cpp".format(d)): + files.append(f) + for f in glob.glob("{}/*.h".format(d)): + files.append(f) + for f in files: + completed_process = subprocess.run( + " ".join( + [ + "clang-format", + "-i", + f, + ] + ), + shell=True, + text=True, + capture_output=True, + ) + if completed_process.returncode: + raise Exception(completed_process.stderr) + + +def main(*cmd_line): + parser = argparse.ArgumentParser() + parser.add_argument( + "INPUT", + type=str, + help="An AIDL-generated interface header file or an AIDL file.", + ) + parser.add_argument( + "--domain", + type=str, + choices=["ql_tipc", "trusty_user"], + help="c client domain.", + ) + parser.add_argument( + "--bn", + action="store_true", + help="Bn (server) mode - do not generate Bp C wrapper.", + ) + parser.add_argument( + "--append-cpp", + action="store_true", + help="append generated C code (Bp C wrapper) into the interface's cpp.", + ) + parser.add_argument( + "--dbg", + action="store_true", + help="debug mode - stores ast and methods json files.", + ) + parser.add_argument( + "--out", type=str, help="base output directory for generated files" + ) + parser.add_argument( + "--header", + type=str, + help="header output directory for the generated header files.", + ) + parser.add_argument( + "--aidl-tool", + type=str, + help="aidl tool path.", + ) + if len(cmd_line) > 0: + args = parser.parse_args(cmd_line) + else: + args = parser.parse_args() + file_input = Path(args.INPUT) + iface_name = file_input.stem + if file_input.suffix == ".h": + iface_hdr = Path(args.header) / "{}.h".format(iface_name) + elif file_input.suffix == ".aidl": + iface_hdr = run_aidl( + file_input.as_posix(), args.aidl_tool, args.out, args.header + ) + else: + raise Exception( + "input file `{}` not supported header, please specify with a .h or .aidl file".format( + file_input.as_posix() + ) + ) + if not iface_hdr.exists(): + raise Exception( + "interface header `{}` not found, please run aidl tool".format( + iface_hdr.as_posix() + ) + ) + iface_to_c( + iface_hdr, + args.domain, + args.out, + args.header, + bn=args.bn, + append_cpp=args.append_cpp, + dbg=args.dbg, + ) + for d in [args.out, args.header]: + run_clang_format(d) + + +def example(): + # example command lines for generating trusty_user and ql_tipc flavors + for aidl_file in [ + root_dir / "trusty/user/base/interface/boot_done/IBootDone.aidl", + ]: + aidl_dir = aidl_file.parent + for domain in ["trusty_user", "ql_tipc"]: + generated = "generated" + if domain == "ql_tipc": + generated += "_ql_tipc" + main( + "--domain", + domain, + "--out", + (aidl_dir / generated).as_posix(), + "--header", + (aidl_dir / generated / "include").as_posix(), + "--aidl-tool", + (root_dir / "trusty/prebuilts/aosp/tidl/tidl").as_posix(), + aidl_file.as_posix(), + ) + + +if __name__ == "__main__": + main() diff --git a/scripts/aidl/c_wrapper/__init__.py b/scripts/aidl/c_wrapper/__init__.py new file mode 100644 index 0000000..6eb31c6 --- /dev/null +++ b/scripts/aidl/c_wrapper/__init__.py @@ -0,0 +1,3 @@ +from .iface_h2c import iface_to_c + +__all__ = [iface_to_c] diff --git a/scripts/aidl/c_wrapper/iface_h2c.py b/scripts/aidl/c_wrapper/iface_h2c.py new file mode 100644 index 0000000..d0b3a3b --- /dev/null +++ b/scripts/aidl/c_wrapper/iface_h2c.py @@ -0,0 +1,337 @@ +# +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +"""Generate a client (proxy) in c, as a wrapper on top of the aidl-generated +binder proxy cpp class. + +Requires the aidl-generated interface header as input +""" +import functools +from jinja2 import FileSystemLoader, Environment +import json +from pathlib import Path +import re +import subprocess +from typing import List + +script_dir = Path(__file__).parent +root_dir = script_dir / "../../../../../../.." +aidl_tool = root_dir / "trusty/prebuilts/aosp/tidl/tidl" + +jinja_template_loader = FileSystemLoader(searchpath=script_dir / "tpl") +jinja_template_env = Environment( + loader=jinja_template_loader, + trim_blocks=True, + lstrip_blocks=True, + keep_trailing_newline=True, + line_comment_prefix="###", +) + + +class Consts: + def __init__(self, const_list: List): + self.consts = const_list + + @functools.cached_property + def ordered_strings(self): + return sorted( + [s for s in self.consts if s["qualType"].find("const char [") == 0], + key=lambda s: s["name"], + ) + + @functools.cached_property + def ordered_positive_integers(self): + return sorted( + [ + s + for s in self.consts + if (s["qualType"].find("const int32_t") == 0) and (s["value"][0] != "-") + ], + key=lambda s: s["value"], + ) + + @functools.cached_property + def ordered_negative_integers(self): + return sorted( + [ + s + for s in self.consts + if (s["qualType"].find("const int32_t") == 0) and (s["value"][0] == "-") + ], + key=lambda s: s["value"], + ) + + @functools.cached_property + def ordered_error_integers(self): + return sorted( + [ + s + for s in self.consts + if (s["qualType"].find("const int32_t") == 0) + and (s["name"].find("ERR") > -1) + ], + key=lambda s: s["value"], + ) + + @staticmethod + def len(ll: List): + return len(ll) + + +def clang_ast(hdr_file: Path, filter_name: str, domain: str): + completed_process = subprocess.run( + " ".join( + [ + "clang++", + "-I{}".format(hdr_file.parent), + "-I{}".format( + root_dir / "trusty/user/base/experimental/lib/binder-paidl/include" + ), + " ".join( + [ + "-I{}".format( + root_dir / "external/trusty/bootloader/ql-tipc/include" + ), + "-I{}".format( + root_dir + / "external/trusty/bootloader/ql-tipc/include/trusty/sysdeps/aarch64" + ), + "-I{}".format(root_dir / "external/lk/include/uapi"), + "-I{}".format(root_dir / "external/lk/include/shared"), + "-I{}".format(root_dir / "external/lk/lib/libc/include"), + ] + ) + if domain == "ql_tipc" + else " ".join( + [ + "-I{}".format(root_dir / "external/lk/include/uapi"), + "-I{}".format(root_dir / "external/lk/include/shared"), + "-I{}".format(root_dir / "trusty/kernel/include/uapi"), + "-I{}".format(root_dir / "trusty/kernel/include/shared"), + "-I{}".format(root_dir / "trusty/user/base/lib/tipc/include"), + "-I{}".format(root_dir / "trusty/user/base/include/user"), + ] + ), + "-UANDROID", + "-D__TRUSTY__", + "-D__QL_TIPC__" if domain == "ql_tipc" else "", + "-std=c++17", + "-Xclang", + "-ast-dump=json", + "-Xclang", + "-ast-dump-filter", + "-Xclang", + filter_name, + "-fsyntax-only", + "-fno-color-diagnostics", + "-Wno-visibility", + hdr_file.as_posix(), + ] + ), + shell=True, + text=True, + capture_output=True, + ) + if completed_process.returncode: + print(completed_process.stderr) + + blobs = re.split("Dumping.*\n", completed_process.stdout) + ast = [] + for b in blobs: + if len(b) == 0: + continue + ast.append(json.loads(b)) + return ast + + +def ast_parse_arg(item): + def get_spec(arg): + m = re.match(r"std::array<([^,]*), ([^\>]*)>", arg["qualType"]) + if m is not None: + (type, size) = m.groups() + return dict(isInput=True, isArray=True, arrayType=type, arraySize=size) + + m = re.match(r"::trusty::aidl::FixedPayload<([^\>]*)>", arg["qualType"]) + if m is not None: + (size,) = m.groups() + return dict(isInput=True, isArray=True, arrayType="uint8_t", arraySize=size) + + m = re.match(r"::trusty::aidl::Payload.*\*", arg["qualType"]) + if m is not None: + return dict(isInput=False, isArray=False, isPayload=True) + + m = re.match(r"::trusty::aidl::Payload.*\&", arg["qualType"]) + if m is not None: + return dict(isInput=True, isArray=False, isPayload=True) + + if arg["qualType"][-1] == "*": + return dict(isInput=False, isArray=False, isPayload=False) + + return dict(isInput=True, isArray=False, isPayload=False) + + if item["kind"] != "ParmVarDecl": + return None + if "name" not in item: + return None + arg = dict(name=item["name"].replace("arg_", ""), qualType=item["type"]["qualType"]) + arg["spec"] = get_spec(arg) + return arg + + +def ast_parse_field(item): + if item["kind"] != "FieldDecl": + return None + if "name" not in item: + return None + field = dict(name=item["name"], qualType=item["type"]["qualType"]) + return field + + +def ast_parse_method(item): + return dict( + name=item["name"], + args=list(filter(None, [ast_parse_arg(arg) for arg in item["inner"]])) + if "inner" in item + else [], + ) + + +def ast_parse_struct(item): + return dict( + name=item["name"], + fields=list(filter(None, [ast_parse_field(field) for field in item["inner"]])) + if "inner" in item + else [], + ) + + +def ast_parse_const(item): + def get_literal_value(item): + if item["inner"][0]["kind"] in ["StringLiteral", "IntegerLiteral"]: + return item["inner"][0]["value"] + if item["inner"][0]["kind"] in ["UnaryOperator"]: + return item["inner"][0]["opcode"] + get_literal_value(item["inner"][0]) + raise Exception("Cannot parse item {}item".format(item)) + + try: + return dict( + name=item["name"], + value=get_literal_value(item), + qualType=item["type"]["qualType"], + ) + except Exception as e: + print(item) + + +def ast_cross_ref(iface_name, methods, structs): + for method in methods: + for arg in method["args"]: + m = re.match( + ".*{}::([^:\s]*)\s+[\*\&]+$".format(iface_name), arg["qualType"] + ) + if m is None: + continue + (struct,) = m.groups() + for s in structs: + if s["name"] == struct: + arg["xref"] = s + + +def c_client_wrapper(iface_ast, iface_name, impl_name, c_impl_name, domain): + methods = [] + structs = [] + consts = [] + for ast in iface_ast: + if ast["name"] != iface_name: + continue + if ast["kind"] != "CXXRecordDecl": + raise Exception( + "iface {} shall be defined as a CXXRecordDecl".format(iface_name) + ) + if ast["tagUsed"] != "class": + raise Exception("iface {} shall be defined as a class".format(iface_name)) + for item in ast["inner"]: + if item["kind"] == "CXXMethodDecl": + if "isImplicit" in item and item["isImplicit"]: + continue # C++ operators + if "pure" not in item or not item["pure"]: + continue # no need to implement in c the non-pure methods + # as they are inherited by the base class Binder + methods.append(ast_parse_method(item)) + if item["kind"] == "CXXRecordDecl" and item["tagUsed"] == "struct": + structs.append(ast_parse_struct(item)) + if item["kind"] == "VarDecl" and item["constexpr"] == True: + consts.append(ast_parse_const(item)) + ast_cross_ref(iface_name, methods, structs) + tm = jinja_template_env.get_template("client_c.j2") + tm_env = dict( + iface_name=iface_name, + impl_name=impl_name, + c_impl_name=c_impl_name, + methods=methods, + structs=structs, + consts=Consts(consts), + domain=domain, + ) + c = tm.render(**tm_env) + + tm = jinja_template_env.get_template("client_h.j2") + h = tm.render(**tm_env) + return dict(c=c, h=h, methods=methods, structs=structs) + + +def iface_to_c( + iface_hdr: Path, + domain: str, + out_cpp: str, + out_hdr: str, + bn=False, + append_cpp=False, + dbg=False, +): + if domain not in ["trusty_user", "ql_tipc"]: + raise Exception("domain `{}` not supported", domain) + iface_name = iface_hdr.stem + impl_name = iface_name[1:] + iface_ast = clang_ast(iface_hdr, iface_name, domain) + # bp_ast = clang_ast(args.bp_cpp) + out_cpp_path = Path(out_cpp) + out_cpp_path.mkdir(parents=True, exist_ok=True) + out_hdr_path = Path(out_hdr) + out_hdr_path.mkdir(parents=True, exist_ok=True) + if dbg: + with open(out_cpp_path / "{}_ast.json".format(iface_name), "w") as f: + json.dump(iface_ast, f, indent=2) + + # convert impl name from camelCase to snake_case + c_impl_name = re.sub(r"(?<!^)(?=[A-Z])", "_", impl_name).lower() + c_client = c_client_wrapper(iface_ast, iface_name, impl_name, c_impl_name, domain) + if dbg: + with open(out_cpp_path / "{}_methods.json".format(c_impl_name), "w") as f: + json.dump( + dict(methods=c_client["methods"], structs=c_client["structs"]), + f, + indent=2, + ) + if not bn: + if append_cpp: + with open(out_cpp_path / "{}.cpp".format(iface_name), "a") as f: + f.write(c_client["c"]) + else: + with open(out_cpp_path / "{}_CBp.cpp".format(iface_name), "w") as f: + f.write(c_client["c"]) + + with open(out_hdr_path / "{}.h".format(c_impl_name), "w") as f: + f.write(c_client["h"]) diff --git a/scripts/aidl/c_wrapper/tpl/client_c.j2 b/scripts/aidl/c_wrapper/tpl/client_c.j2 new file mode 100644 index 0000000..8251de9 --- /dev/null +++ b/scripts/aidl/c_wrapper/tpl/client_c.j2 @@ -0,0 +1,69 @@ +{% import 'macros/c_method_fn.j2' as method_fn %} +{% import 'macros/c_connect_fn.j2' as connect_fn %} +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define TLOG_TAG "{{c_impl_name}}_client" + +{% if domain == 'trusty_user' %} +#include <assert.h> +#include <lib/tipc/tipc.h> +#include <string.h> +#include <trusty_log.h> +#include <uapi/err.h> +{% elif domain == 'ql_tipc' %} +#include <trusty/sysdeps.h> +#include <trusty/trusty_log.h> +#include <uapi/err.h> +{% endif %} + +#include <Bp{{impl_name}}.h> +{% if consts.len(consts.ordered_error_integers) > 0 %} +#include <{{c_impl_name}}.h> +{% else %} +#include <lib/binder/lk_strerror.h> +{% endif %} + +{{connect_fn.connect_impl(consts, c_impl_name, impl_name, domain)}} +{{connect_fn.connect_teardown(c_impl_name, impl_name, domain)}} +{% for method in methods %} +{{method_fn.comment(c_impl_name, method)}} +{{method_fn.signature(c_impl_name, method, hdr=false)}} { +{{connect_fn.connect_invoke(c_impl_name, impl_name, domain)}} +{% for arg in method.args %} +{{ method_fn.arg_prolog(iface_name, domain, arg) }} +{%- endfor %} + rc = {{c_impl_name}}->{{method.name}}( +{% for arg in method.args %} +{{ method_fn.arg_invoke(arg) }} {{ "," if not loop.last else "" }} +{%- endfor %} + ); + if (rc != android::OK) { +{% if consts.len(consts.ordered_error_integers) > 0 %} + TLOGE("{{method.name}} failed - %s(%d).\n", {{c_impl_name}}_strerror(rc), rc); +{% else %} + TLOGE("{{method.name}} failed - %s(%d).\n", lk_strerror(rc), rc); +{% endif %} + return rc; + } +{% for arg in method.args %} +{{ method_fn.arg_epilog(arg) }} +{%- endfor %} + return rc; +} +{{''}} +{{''}} +{%- endfor %} diff --git a/scripts/aidl/c_wrapper/tpl/client_h.j2 b/scripts/aidl/c_wrapper/tpl/client_h.j2 new file mode 100644 index 0000000..e23e26c --- /dev/null +++ b/scripts/aidl/c_wrapper/tpl/client_h.j2 @@ -0,0 +1,70 @@ +{% import 'macros/c_connect_fn.j2' as connect_fn %} +{% import 'macros/c_method_fn.j2' as method_fn %} +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{% if domain == "trusty_user" %} +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +{% elif domain == "ql_tipc" %} +#include <trusty/sysdeps.h> +{% endif %} +{% if consts.len(consts.ordered_error_integers) > 0 %} +#include <lib/binder/lk_strerror.h> +{% endif %} + +#pragma once + +{% for s in consts.ordered_strings %} +#define {{c_impl_name.upper()}}_{{s["name"]}} {{s["value"]}} +{% endfor %} +{{'' if consts.len(consts.ordered_strings) > 0}} +{% for s in consts.ordered_positive_integers %} +#define {{s["name"]}} {{s["value"]}} /* 0x{{ '%08x' % s["value"] | int }} */ +{% endfor %} +{{'' if consts.len(consts.ordered_positive_integers) > 0}} +{% for s in consts.ordered_negative_integers %} +#define {{s["name"]}} {{s["value"]}} /* -0x{{ '%08x' % s["value"] | int | abs}} */ +{% endfor %} +{{'' if consts.len(consts.ordered_negative_integers) > 0}} + +__BEGIN_CDECLS + +{{connect_fn.connect_hdr(c_impl_name, impl_name, domain)}} +{% for method in methods %} +{{method_fn.comment(c_impl_name, method)}} +{{method_fn.signature(c_impl_name, method, hdr=true)}}; +{{''}} +{{''}} +{%- endfor %} + +{% if consts.len(consts.ordered_error_integers) > 0 %} +static __ALWAYS_INLINE char * {{c_impl_name}}_strerror(int errnum) { + switch (errnum) + { +{% for err in consts.ordered_error_integers %} + case {{err.name}}: + return (char *)"{{err.name}}"; +{% endfor %} + default: + return lk_strerror(errnum); + } +} +{% endif %} + +__END_CDECLS diff --git a/scripts/aidl/c_wrapper/tpl/macros/c_connect_fn.j2 b/scripts/aidl/c_wrapper/tpl/macros/c_connect_fn.j2 new file mode 100644 index 0000000..e5fe4c6 --- /dev/null +++ b/scripts/aidl/c_wrapper/tpl/macros/c_connect_fn.j2 @@ -0,0 +1,134 @@ +### ***************** +### Connect Header +### ***************** + +{% macro connect_hdr(c_impl_name, impl_name, domain) %} +{% if domain == "ql_tipc" %} +/** + * {{c_impl_name}}_connect() - connect to the interface port + * and initialize the global pointer {{c_impl_name}}. + * + * Requires invocation of {{c_impl_name}}_shutdown() + * for proper teardown. + * @Param port: port to which to connect to + * @return: 0 on success, or an error code < 0 on failure. + */ +int {{c_impl_name}}_connect(const char* port); + +/** + * {{c_impl_name}}_shutdown() - shutdown the interface client + * + * Destroys the global pointer of the interface client {{c_impl_name}}. + * + */ +void {{c_impl_name}}_shutdown(void); +{% endif %} +{% endmacro %} + +### ************************* +### Connect Implementation +### ************************* + +{% macro connect_impl(consts, c_impl_name, impl_name, domain) %} +{% if domain == "trusty_user" %} +/** + * {{c_impl_name}}_connect() + * @param {{c_impl_name}} reference to the binder proxy instance + * + * Return: 0 on success, or an error code < 0 on failure. + */ +int {{c_impl_name}}_connect(std::optional<aidl::Bp{{impl_name}}>& {{c_impl_name}}) { + int rc; + rc = aidl::Bp{{impl_name}}::connect( + {{c_impl_name}}, + aidl::I{{impl_name}}::PORT, + IPC_CONNECT_WAIT_FOR_PORT + ); + if (rc < 0) { + TLOGE("Failed to connect to %s: %d\n", aidl::I{{impl_name}}::PORT, rc); + return rc; + } + assert({{c_impl_name}}.has_value()); + return rc; +} +{% elif domain == "ql_tipc" %} + +aidl::Bp{{impl_name}}* {{c_impl_name}} = (aidl::Bp{{impl_name}}*)NULL; + +{% for s in consts.ordered_strings %} +constexpr char aidl::I{{impl_name}}::{{s["name"]}}[]; +{% endfor %} + +/** + * {{c_impl_name}}_connect() - connect to the interface port + * and initialize the global pointer {{c_impl_name}}. + * + * Requires invocation of {{c_impl_name}}_shutdown() + * for proper teardown. + * @param: port to which to connect to, + * if port is NULL, use the default API's port + * Return: 0 on success, or an error code < 0 on failure. + */ +extern "C" int {{c_impl_name}}_connect(const char* port) { + int rc; + if ({{c_impl_name}}) { + return NO_ERROR; + } + if (port) { + rc = aidl::Bp{{impl_name}}::connect({{c_impl_name}}, port, 0); + } else { + rc = aidl::Bp{{impl_name}}::connect( + {{c_impl_name}}, aidl::I{{impl_name}}::PORT, 0 + ); + } + if (rc < 0) { + TLOGE("Failed to connect to %s: %d\n", aidl::I{{impl_name}}::PORT, rc); + return rc; + } + assert({{c_impl_name}}); + return rc; +} +{% endif %} +{% endmacro %} + +### ****************************************** +### Connect teardown: only needed on ql_tipc +### ****************************************** + +{% macro connect_teardown(c_impl_name, impl_name, domain) %} +{% if domain == "ql_tipc" %} +/** + * {{c_impl_name}}_shutdown() - shutdown the interface client + * + * Destroy the global pointer of the interface client {{c_impl_name}}. + * + */ +extern "C" void {{c_impl_name}}_shutdown(void) { + if ({{c_impl_name}}) { + delete {{c_impl_name}}; + {{c_impl_name}} = (aidl::Bp{{impl_name}}*)NULL; + } +} +{% endif %} +{% endmacro %} + +### ***************** +### Connect invoke +### ***************** + +{% macro connect_invoke(c_impl_name, impl_name, domain) %} +{% if domain == "trusty_user" %} + int rc; + std::optional<aidl::Bp{{impl_name}}> {{c_impl_name}}; + rc = {{c_impl_name}}_connect({{c_impl_name}}); + if (rc < 0) { + return rc; + } +{% endif %} +{% if domain == "ql_tipc" %} + int rc; + if (!{{c_impl_name}}) { + return ERR_BAD_STATE; + } +{% endif %} +{%- endmacro %} diff --git a/scripts/aidl/c_wrapper/tpl/macros/c_method_fn.j2 b/scripts/aidl/c_wrapper/tpl/macros/c_method_fn.j2 new file mode 100644 index 0000000..13cd2b1 --- /dev/null +++ b/scripts/aidl/c_wrapper/tpl/macros/c_method_fn.j2 @@ -0,0 +1,198 @@ +### **************** +### Arg Comments +### **************** + +{% macro arg_to_comment(arg) %} +{% if arg.spec.isInput %} + {% if arg.spec.isArray %} + * @param {{arg.name}} + * @param {{arg.name}}_size: size of the input {{arg.name}} array, + * must be set to {{arg.spec.arraySize}} + {% elif arg.spec.isPayload %} + * @param {{arg.name}} + * @param {{arg.name}}_size: size of the input {{arg.name}} array + {% elif arg.xref %} + {% for field in arg.xref.fields %} + * @param {{field.name}} + {% endfor %} + {% else %} + * @param {{arg.name}} + {% endif %} +{%- else %} + {% if arg.spec.isPayload %} + * @param[out] {{arg.name}} + * @param[out] {{arg.name}}_buf_size: size of the output {{arg.name}} array + * @param[out] {{arg.name}}_size: number of bytes written into the output array + {% elif arg.xref %} + {% for field in arg.xref.fields %} + * @param[out] {{field.name}} + {% endfor %} + {% else %} + * @param[out] {{arg.name}} + {% endif %} +{%- endif %} +{%- endmacro %} + +### ******************* +### Method Comments +### ******************* + +{% macro comment(impl_name, method) %} +/** + * {{impl_name}}_{{method.name}}() + * +{% for arg in method.args %} +{{arg_to_comment(arg)}} +{%- endfor %} +{% if method.args|length > 0 %} + * +{% endif %} + * @return: 0 on success, or an error code < 0 on failure. + */ +{%- endmacro %} + +### **************** +### Arg Signature +### **************** + +{% macro arg_to_c(arg) %} +{% if arg.spec.isInput %} + {% if arg.spec.isArray %} + uint8_t* {{arg.name}}, + size_t {{arg.name}}_size + {% elif arg.spec.isPayload %} + uint8_t* {{arg.name}}, + size_t {{arg.name}}_size + {% elif arg.xref %} + {% for field in arg.xref.fields %} + {{field.qualType}} {{field.name}} {{ "," if not loop.last else "" }} + {% endfor %} + {% else %} + {{arg.qualType}} {{arg.name}} + {% endif %} +{% else %} + {% if arg.spec.isPayload %} + uint8_t* {{arg.name}}, + size_t {{arg.name}}_buf_size, + size_t* {{arg.name}}_size + {% elif arg.xref %} + {% for field in arg.xref.fields %} + {{field.qualType}}* {{field.name}} {{ "," if not loop.last else "" }} + {% endfor %} + {% else %} + {{arg.qualType}} {{arg.name}} + {% endif %} +{%- endif %} +{%- endmacro %} + +### ******************* +### Method Signature +### ******************* + +{% macro signature(impl_name, method, hdr) %} +{% if not hdr %} +extern "C"{{" "}} +{%- endif %} +{% if method.args|length == 0 %} +int {{impl_name}}_{{method.name}}(void) +{%- else %} +int {{impl_name}}_{{method.name}}( +{% for arg in method.args %} +{{arg_to_c(arg)}}{{ "," if not loop.last else "" }} +{%- endfor %} +) +{%- endif %} +{%- endmacro %} + + +### ******************************************************* +### Arg Prolog: Initialisation prior to method invocation +### ******************************************************* + +{% macro arg_prolog(iface_name, domain, arg) %} +{% if arg.spec.isInput %} + {% if arg.spec.isArray %} + if ({{arg.name}}_size != {{arg.spec.arraySize}}) { + TLOGE("{{arg.name}} is not its expected size {{arg.spec.arraySize}}.\n"); + return ERR_BAD_LEN; + } + + {{arg.qualType.replace("&", "")}} {{arg.name}}_array; + memcpy({{arg.name}}_array.data(), {{arg.name}}, {{arg.name}}_array.size()); + + {% elif arg.spec.isPayload %} + if ({{arg.name}}_size > aidl::{{iface_name}}::MAX_PAYLOAD_REQ) { + TLOGE("{{arg.name}} exceeds aidl::{{iface_name}}::MAX_PAYLOAD_REQ limit.\n"); + return ERR_BAD_LEN; + } + + const trusty::aidl::Payload {{arg.name}}_payload{const_cast<uint8_t*>({{arg.name}}), + static_cast<uint32_t>({{arg.name}}_size)}; + + {% elif arg.xref %} + const {{arg.qualType.replace(" &","")}} {{arg.name}} = { + {% for field in arg.xref.fields %} + .{{field.name}} = {{field.name}}{{ "," if not loop.last else "" }} + {% endfor %} + }; + {% endif %} +{% else %} + {% if arg.spec.isPayload %} + if ({{arg.name}}_size > aidl::{{iface_name}}::MAX_PAYLOAD_RESP) { + TLOGE("{{arg.name}} exceeds aidl::{{iface_name}}::MAX_PAYLOAD_RESP limit.\n"); + return ERR_BAD_LEN; + } + + trusty::aidl::Payload {{arg.name}}_payload{const_cast<uint8_t*>({{arg.name}}), + static_cast<uint32_t>({{arg.name}}_buf_size)}; + {% elif arg.xref %} + {% for field in arg.xref.fields %} + assert({{field.name}}); + {% endfor %} + {{arg.qualType.replace(" *","")}} {{arg.name}}; + {% endif %} +{%- endif %} +{%- endmacro %} + + +### ******************************************************* +### Arg Epilog: Update after method invocation +### ******************************************************* + +{% macro arg_epilog(arg) %} +{% if not arg.spec.isInput %} + {% if arg.spec.isPayload %} + *{{arg.name}}_size = {{arg.name}}_payload.size(); + {% elif arg.xref %} + {% for field in arg.xref.fields %} + *{{field.name}} = {{arg.name}}.{{field.name}}; + {% endfor %} + {% endif %} +{%- endif %} +{%- endmacro %} + +### **************** +### Arg Invoke +### **************** + +{% macro arg_invoke(arg) %} +{% if arg.spec.isInput %} + {% if arg.spec.isArray %} + {{arg.name}}_array + {% elif arg.spec.isPayload %} + {{arg.name}}_payload + {% elif arg.xref %} + {{arg_name}} + {% else %} + {{arg.name}} + {% endif %} +{% else %} + {% if arg.spec.isPayload %} + &{{arg.name}}_payload + {% elif arg.xref %} + &{{arg.name}} + {% else %} + {{arg.name}} + {% endif %} +{%- endif %} +{%- endmacro %} |