diff options
| author | Jakub Kicinski <kuba@kernel.org> | 2023-01-20 09:50:36 -0800 |
|---|---|---|
| committer | Paolo Abeni <pabeni@redhat.com> | 2023-01-24 10:58:11 +0100 |
| commit | be5bea1cc0bf663aa0a45a2c0021dfcfb6cca976 (patch) | |
| tree | 6644133c9f111d233236925afbc342bac19f3935 /tools/net | |
| parent | e616c07ca51817ebdb1423807d52cac72d1c22fa (diff) | |
| download | linux-be5bea1cc0bf663aa0a45a2c0021dfcfb6cca976.tar.gz linux-be5bea1cc0bf663aa0a45a2c0021dfcfb6cca976.tar.bz2 linux-be5bea1cc0bf663aa0a45a2c0021dfcfb6cca976.zip | |
net: add basic C code generators for Netlink
Code generators to turn Netlink specs into C code.
I'm definitely not proud of it.
The main generator is in Python, there's a bash script
to regen all code-gen'ed files in tree after making
spec changes.
Acked-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Diffstat (limited to 'tools/net')
| -rwxr-xr-x | tools/net/ynl/ynl-gen-c.py | 2373 | ||||
| -rwxr-xr-x | tools/net/ynl/ynl-regen.sh | 30 |
2 files changed, 2403 insertions, 0 deletions
diff --git a/tools/net/ynl/ynl-gen-c.py b/tools/net/ynl/ynl-gen-c.py new file mode 100755 index 000000000000..0c0f18540b7f --- /dev/null +++ b/tools/net/ynl/ynl-gen-c.py @@ -0,0 +1,2373 @@ +#!/usr/bin/env python + +import argparse +import jsonschema +import os +import yaml + + +def c_upper(name): + return name.upper().replace('-', '_') + + +def c_lower(name): + return name.lower().replace('-', '_') + + +class BaseNlLib: + def get_family_id(self): + return 'ys->family_id' + + def parse_cb_run(self, cb, data, is_dump=False, indent=1): + ind = '\n\t\t' + '\t' * indent + ' ' + if is_dump: + return f"mnl_cb_run2(ys->rx_buf, len, 0, 0, {cb}, {data},{ind}ynl_cb_array, NLMSG_MIN_TYPE)" + else: + return f"mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid,{ind}{cb}, {data},{ind}" + \ + "ynl_cb_array, NLMSG_MIN_TYPE)" + + +class Type: + def __init__(self, family, attr_set, attr): + self.family = family + self.attr = attr + self.value = attr['value'] + self.name = c_lower(attr['name']) + self.type = attr['type'] + self.checks = attr.get('checks', {}) + + if 'len' in attr: + self.len = attr['len'] + if 'nested-attributes' in attr: + self.nested_attrs = attr['nested-attributes'] + if self.nested_attrs == family.name: + self.nested_render_name = f"{family.name}" + else: + self.nested_render_name = f"{family.name}_{c_lower(self.nested_attrs)}" + + self.enum_name = f"{attr_set.name_prefix}{self.name}" + self.enum_name = c_upper(self.enum_name) + self.c_name = c_lower(self.name) + if self.c_name in _C_KW: + self.c_name += '_' + + def __getitem__(self, key): + return self.attr[key] + + def __contains__(self, key): + return key in self.attr + + def is_multi_val(self): + return None + + def is_scalar(self): + return self.type in {'u8', 'u16', 'u32', 'u64', 's32', 's64'} + + def presence_type(self): + return 'bit' + + def presence_member(self, space, type_filter): + if self.presence_type() != type_filter: + return + + if self.presence_type() == 'bit': + pfx = '__' if space == 'user' else '' + return f"{pfx}u32 {self.c_name}:1;" + + if self.presence_type() == 'len': + pfx = '__' if space == 'user' else '' + return f"{pfx}u32 {self.c_name}_len;" + + def _complex_member_type(self, ri): + return None + + def free_needs_iter(self): + return False + + def free(self, ri, var, ref): + if self.is_multi_val() or self.presence_type() == 'len': + ri.cw.p(f'free({var}->{ref}{self.c_name});') + + def arg_member(self, ri): + member = self._complex_member_type(ri) + if member: + return [member + ' *' + self.c_name] + raise Exception(f"Struct member not implemented for class type {self.type}") + + def struct_member(self, ri): + if self.is_multi_val(): + ri.cw.p(f"unsigned int n_{self.c_name};") + member = self._complex_member_type(ri) + if member: + ptr = '*' if self.is_multi_val() else '' + ri.cw.p(f"{member} {ptr}{self.c_name};") + return + members = self.arg_member(ri) + for one in members: + ri.cw.p(one + ';') + + def _attr_policy(self, policy): + return '{ .type = ' + policy + ', }' + + def attr_policy(self, cw): + policy = c_upper('nla-' + self.attr['type']) + + spec = self._attr_policy(policy) + cw.p(f"\t[{self.enum_name}] = {spec},") + + def _attr_typol(self): + raise Exception(f"Type policy not implemented for class type {self.type}") + + def attr_typol(self, cw): + typol = self._attr_typol() + cw.p(f'[{self.enum_name}] = {"{"} .name = "{self.name}", {typol}{"}"},') + + def _attr_put_line(self, ri, var, line): + if self.presence_type() == 'bit': + ri.cw.p(f"if ({var}->_present.{self.c_name})") + elif self.presence_type() == 'len': + ri.cw.p(f"if ({var}->_present.{self.c_name}_len)") + ri.cw.p(f"{line};") + + def _attr_put_simple(self, ri, var, put_type): + line = f"mnl_attr_put_{put_type}(nlh, {self.enum_name}, {var}->{self.c_name})" + self._attr_put_line(ri, var, line) + + def attr_put(self, ri, var): + raise Exception(f"Put not implemented for class type {self.type}") + + def _attr_get(self, ri, var): + raise Exception(f"Attr get not implemented for class type {self.type}") + + def attr_get(self, ri, var, first): + lines, init_lines, local_vars = self._attr_get(ri, var) + if type(lines) is str: + lines = [lines] + if type(init_lines) is str: + init_lines = [init_lines] + + kw = 'if' if first else 'else if' + ri.cw.block_start(line=f"{kw} (mnl_attr_get_type(attr) == {self.enum_name})") + if local_vars: + for local in local_vars: + ri.cw.p(local) + ri.cw.nl() + + if not self.is_multi_val(): + ri.cw.p("if (ynl_attr_validate(yarg, attr))") + ri.cw.p("return MNL_CB_ERROR;") + if self.presence_type() == 'bit': + ri.cw.p(f"{var}->_present.{self.c_name} = 1;") + + if init_lines: + ri.cw.nl() + for line in init_lines: + ri.cw.p(line) + + for line in lines: + ri.cw.p(line) + ri.cw.block_end() + + def _setter_lines(self, ri, member, presence): + raise Exception(f"Setter not implemented for class type {self.type}") + + def setter(self, ri, space, direction, deref=False, ref=None): + ref = (ref if ref else []) + [self.c_name] + var = "req" + member = f"{var}->{'.'.join(ref)}" + + code = [] + presence = '' + for i in range(0, len(ref)): + presence = f"{var}->{'.'.join(ref[:i] + [''])}_present.{ref[i]}" + if self.presence_type() == 'bit': + code.append(presence + ' = 1;') + code += self._setter_lines(ri, member, presence) + + ri.cw.write_func('static inline void', + f"{op_prefix(ri, direction, deref=deref)}_set_{'_'.join(ref)}", + body=code, + args=[f'{type_name(ri, direction, deref=deref)} *{var}'] + self.arg_member(ri)) + + +class TypeUnused(Type): + def presence_type(self): + return '' + + def _attr_typol(self): + return '.type = YNL_PT_REJECT, ' + + def attr_policy(self, cw): + pass + + +class TypePad(Type): + def presence_type(self): + return '' + + def _attr_typol(self): + return '.type = YNL_PT_REJECT, ' + + def attr_policy(self, cw): + pass + + +class TypeScalar(Type): + def __init__(self, family, attr_set, attr): + super().__init__(family, attr_set, attr) + + self.is_bitfield = False + if 'enum' in self.attr: + self.is_bitfield = family.consts[self.attr['enum']]['type'] == 'flags' + if 'enum-as-flags' in self.attr and self.attr['enum-as-flags']: + self.is_bitfield = True + + if 'enum' in self.attr and not self.is_bitfield: + self.type_name = f"enum {family.name}_{c_lower(self.attr['enum'])}" + else: + self.type_name = '__' + self.type + + self.byte_order_comment = '' + if 'byte-order' in attr: + self.byte_order_comment = f" /* {attr['byte-order']} */" + + def _mnl_type(self): + t = self.type + # mnl does not have a helper for signed types + if t[0] == 's': + t = 'u' + t[1:] + return t + + def _attr_policy(self, policy): + if 'flags-mask' in self.checks or self.is_bitfield: + if self.is_bitfield: + mask = self.family.consts[self.attr['enum']].get_mask() + else: + flags = self.family.consts[self.checks['flags-mask']] + flag_cnt = len(flags['entries']) + mask = (1 << flag_cnt) - 1 + return f"NLA_POLICY_MASK({policy}, 0x{mask:x})" + elif 'min' in self.checks: + return f"NLA_POLICY_MIN({policy}, {self.checks['min']})" + elif 'enum' in self.attr: + enum = self.family.consts[self.attr['enum']] + cnt = len(enum['entries']) + return f"NLA_POLICY_MAX({policy}, {cnt - 1})" + return super()._attr_policy(policy) + + def _attr_typol(self): + return f'.type = YNL_PT_U{self.type[1:]}, ' + + def arg_member(self, ri): + return [f'{self.type_name} {self.c_name}{self.byte_order_comment}'] + + def attr_put(self, ri, var): + self._attr_put_simple(ri, var, self._mnl_type()) + + def _attr_get(self, ri, var): + return f"{var}->{self.c_name} = mnl_attr_get_{self._mnl_type()}(attr);", None, None + + def _setter_lines(self, ri, member, presence): + return [f"{member} = {self.c_name};"] + + +class TypeFlag(Type): + def arg_member(self, ri): + return [] + + def _attr_typol(self): + return '.type = YNL_PT_FLAG, ' + + def attr_put(self, ri, var): + self._attr_put_line(ri, var, f"mnl_attr_put(nlh, {self.enum_name}, 0, NULL)") + + def _attr_get(self, ri, var): + return [], None, None + + def _setter_lines(self, ri, member, presence): + return [] + + +class TypeString(Type): + def arg_member(self, ri): + return [f"const char *{self.c_name}"] + + def presence_type(self): + return 'len' + + def struct_member(self, ri): + ri.cw.p(f"char *{self.c_name};") + + def _attr_typol(self): + return f'.type = YNL_PT_NUL_STR, ' + + def _attr_policy(self, policy): + mem = '{ .type = ' + policy + if 'max-len' in self.checks: + mem += ', .len = ' + str(self.checks['max-len']) + mem += ', }' + return mem + + def attr_policy(self, cw): + if self.checks.get('unterminated-ok', False): + policy = 'NLA_STRING' + else: + policy = 'NLA_NUL_STRING' + + spec = self._attr_policy(policy) + cw.p(f"\t[{self.enum_name}] = {spec},") + + def attr_put(self, ri, var): + self._attr_put_simple(ri, var, 'strz') + + def _attr_get(self, ri, var): + len_mem = var + '->_present.' + self.c_name + '_len' + return [f"{len_mem} = len;", + f"{var}->{self.c_name} = malloc(len + 1);", + f"memcpy({var}->{self.c_name}, mnl_attr_get_str(attr), len);", + f"{var}->{self.c_name}[len] = 0;"], \ + ['len = strnlen(mnl_attr_get_str(attr), mnl_attr_get_payload_len(attr));'], \ + ['unsigned int len;'] + + def _setter_lines(self, ri, member, presence): + return [f"free({member});", + f"{presence}_len = strlen({self.c_name});", + f"{member} = malloc({presence}_len + 1);", + f'memcpy({member}, {self.c_name}, {presence}_len);', + f'{member}[{presence}_len] = 0;'] + + +class TypeBinary(Type): + def arg_member(self, ri): + return [f"const void *{self.c_name}", 'size_t len'] + + def presence_type(self): + return 'len' + + def struct_member(self, ri): + ri.cw.p(f"void *{self.c_name};") + + def _attr_typol(self): + return f'.type = YNL_PT_BINARY,' + + def _attr_policy(self, policy): + mem = '{ ' + if len(self.checks) == 1 and 'min-len' in self.checks: + mem += '.len = ' + str(self.checks['min-len']) + elif len(self.checks) == 0: + mem += '.type = NLA_BINARY' + else: + raise Exception('One or more of binary type checks not implemented, yet') + mem += ', }' + return mem + + def attr_put(self, ri, var): + self._attr_put_line(ri, var, f"mnl_attr_put(nlh, {self.enum_name}, " + + f"{var}->_present.{self.c_name}_len, {var}->{self.c_name})") + + def _attr_get(self, ri, var): + len_mem = var + '->_present.' + self.c_name + '_len' + return [f"{len_mem} = len;", + f"{var}->{self.c_name} = malloc(len);", + f"memcpy({var}->{self.c_name}, mnl_attr_get_payload(attr), len);"], \ + ['len = mnl_attr_get_payload_len(attr);'], \ + ['unsigned int len;'] + + def _setter_lines(self, ri, member, presence): + return [f"free({member});", + f"{member} = malloc({presence}_len);", + f'memcpy({member}, {self.c_name}, {presence}_len);'] + + +class TypeNest(Type): + def _complex_member_type(self, ri): + return f"struct {self.nested_render_name}" + + def free(self, ri, var, ref): + ri.cw.p(f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name});') + + def _attr_typol(self): + return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, ' + + def _attr_policy(self, policy): + return 'NLA_POLICY_NESTED(' + self.nested_render_name + '_nl_policy)' + + def attr_put(self, ri, var): + self._attr_put_line(ri, var, f"{self.nested_render_name}_put(nlh, " + + f"{self.enum_name}, &{var}->{self.c_name})") + + def _attr_get(self, ri, var): + get_lines = [f"{self.nested_render_name}_parse(&parg, attr);"] + init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;", + f"parg.data = &{var}->{self.c_name};"] + return get_lines, init_lines, None + + def setter(self, ri, space, direction, deref=False, ref=None): + ref = (ref if ref else []) + [self.c_name] + + for _, attr in ri.family.pure_nested_structs[self.nested_attrs].member_list(): + attr.setter(ri, self.nested_attrs, direction, deref=deref, ref=ref) + + +class TypeMultiAttr(Type): + def is_multi_val(self): + return True + + def presence_type(self): + return 'count' + + def _complex_member_type(self, ri): + if 'type' not in self.attr or self.attr['type'] == 'nest': + return f"struct {self.nested_render_name}" + elif self.attr['type'] in scalars: + scalar_pfx = '__' if ri.ku_space == 'user' else '' + return scalar_pfx + self.attr['type'] + else: + raise Exception(f"Sub-type {self.attr['type']} not supported yet") + + def free_needs_iter(self): + return 'type' not in self.attr or self.attr['type'] == 'nest' + + def free(self, ri, var, ref): + if 'type' not in self.attr or self.attr['type'] == 'nest': + ri.cw.p(f"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)") + ri.cw.p(f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);') + + def _attr_typol(self): + if 'type' not in self.attr or self.attr['type'] == 'nest': + return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, ' + elif self.attr['type'] in scalars: + return f".type = YNL_PT_U{self.attr['type'][1:]}, " + else: + raise Exception(f"Sub-type {self.attr['type']} not supported yet") + + def _attr_get(self, ri, var): + return f'{var}->n_{self.c_name}++;', None, None + + +class TypeArrayNest(Type): + def is_multi_val(self): + return True + + def presence_type(self): + return 'count' + + def _complex_member_type(self, ri): + if 'sub-type' not in self.attr or self.attr['sub-type'] == 'nest': + return f"struct {self.nested_render_name}" + elif self.attr['sub-type'] in scalars: + scalar_pfx = '__' if ri.ku_space == 'user' else '' + return scalar_pfx + self.attr['sub-type'] + else: + raise Exception(f"Sub-type {self.attr['sub-type']} not supported yet") + + def _attr_typol(self): + return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, ' + + def _attr_get(self, ri, var): + local_vars = ['const struct nlattr *attr2;'] + get_lines = [f'attr_{self.c_name} = attr;', + 'mnl_attr_for_each_nested(attr2, attr)', + f'\t{var}->n_{self.c_name}++;'] + return get_lines, None, local_vars + + +class TypeNestTypeValue(Type): + def _complex_member_type(self, ri): + return f"struct {self.nested_render_name}" + + def _attr_typol(self): + return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, ' + + def _attr_get(self, ri, var): + prev = 'attr' + tv_args = '' + get_lines = [] + local_vars = [] + init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;", + f"parg.data = &{var}->{self.c_name};"] + if 'type-value' in self.attr: + tv_names = [c_lower(x) for x in self.attr["type-value"]] + local_vars += [f'const struct nlattr *attr_{", *attr_".join(tv_names)};'] + local_vars += [f'__u32 {", ".join(tv_names)};'] + for level in self.attr["type-value"]: + level = c_lower(level) + get_lines += [f'attr_{level} = mnl_attr_get_payload({prev});'] + get_lines += [f'{level} = mnl_attr_get_type(attr_{level});'] + prev = 'attr_' + level + + tv_args = f", {', '.join(tv_names)}" + + get_lines += [f"{self.nested_render_name}_parse(&parg, {prev}{tv_args});"] + return get_lines, init_lines, local_vars + + +class Struct: + def __init__(self, family, space_name, type_list=None, inherited=None): + self.family = family + self.space_name = space_name + self.attr_set = family.attr_sets[space_name] + # Use list to catch comparisons with empty sets + self._inherited = inherited if inherited is not None else [] + self.inherited = [] + + self.nested = type_list is None + if family.name == c_lower(space_name): + self.render_name = f"{family.name}" + else: + self.render_name = f"{family.name}_{c_lower(space_name)}" + self.struct_name = 'struct ' + self.render_name + self.ptr_name = self.struct_name + ' *' + + self.request = False + self.reply = False + + self.attr_list = [] + self.attrs = dict() + if type_list: + for t in type_list: + self.attr_list.append((t, self.attr_set[t]),) + else: + for t in self.attr_set: + self.attr_list.append((t, self.attr_set[t]),) + + max_val = 0 + self.attr_max_val = None + for name, attr in self.attr_list: + if attr.value > max_val: + max_val = attr.value + self.attr_max_val = attr + self.attrs[name] = attr + + def __iter__(self): + yield from self.attrs + + def __getitem__(self, key): + return self.attrs[key] + + def member_list(self): + return self.attr_list + + def set_inherited(self, new_inherited): + if self._inherited != new_inherited: + raise Exception("Inheriting different members not supported") + self.inherited = [c_lower(x) for x in sorted(self._inherited)] + + +class EnumEntry: + def __init__(self, enum_set, yaml, prev, value_start): + if isinstance(yaml, str): + self.name = yaml + yaml = {} + self.doc = '' + else: + self.name = yaml['name'] + self.doc = yaml.get('doc', '') + + self.yaml = yaml + self.c_name = c_upper(enum_set.value_pfx + self.name) + + if 'value' in yaml: + self.value = yaml['value'] + if prev: + self.value_change = (self.value != prev.value + 1) + elif prev: + self.value = prev.value + 1 + else: + self.value = value_start + self.value_change = (self.value != 0) + + def __getitem__(self, key): + return self.yaml[key] + + def __contains__(self, key): + return key in self.yaml + + def has_doc(self): + return bool(self.doc) + + +class EnumSet: + def __init__(self, family, yaml): + self.yaml = yaml + self.family = family + + self.render_name = c_lower(family.name + '-' + yaml['name']) + self.enum_name = 'enum ' + self.render_name + + self.value_pfx = yaml.get('name-prefix', f"{family.name}-{yaml['name']}-") + + self.type = yaml['type'] + + prev_entry = None + value_start = self.yaml.get('value-start', 0) + self.entries = {} + self.entry_list = [] + for entry in self.yaml['entries']: + e = EnumEntry(self, entry, prev_entry, value_start) + self.entries[e.name] = e + self.entry_list.append(e) + prev_entry = e + + def __getitem__(self, key): + return self.yaml[key] + + def __contains__(self, key): + return key in self.yaml + + def has_doc(self): + if 'doc' in self.yaml: + return True + for entry in self.entry_list: + if entry.has_doc(): + return True + return False + + def get_mask(self): + mask = 0 + idx = self.yaml.get('value-start', 0) + for _ in self.entry_list: + mask |= 1 << idx + idx += 1 + return mask + + +class AttrSet: + def __init__(self, family, yaml): + self.yaml = yaml + + self.attrs = dict() + self.name = self.yaml['name'] + if 'subset-of' not in yaml: + self.subset_of = None + if 'name-prefix' in yaml: + pfx = yaml['name-prefix'] + elif self.name == family.name: + pfx = family.name + '-a-' + else: + pfx = f"{family.name}-a-{self.name}-" + self.name_prefix = c_upper(pfx) + self.max_name = c_upper(self.yaml.get('attr-max-name', f"{self.name_prefix}max")) + else: + self.subset_of = self.yaml['subset-of'] + self.name_prefix = family.attr_sets[self.subset_of].name_prefix + self.max_name = family.attr_sets[self.subset_of].max_name + + self.c_name = c_lower(self.name) + if self.c_name in _C_KW: + self.c_name += '_' + if self.c_name == family.c_name: + self.c_name = '' + + val = 0 + for elem in self.yaml['attributes']: + if 'value' in elem: + val = elem['value'] + else: + elem['value'] = val + val += 1 + + if 'multi-attr' in elem and elem['multi-attr']: + attr = TypeMultiAttr(family, self, elem) + elif elem['type'] in scalars: + attr = TypeScalar(family, self, elem) + elif elem['type'] == 'unused': + attr = TypeUnused(family, self, elem) + elif elem['type'] == 'pad': + attr = TypePad(family, self, elem) + elif elem['type'] == 'flag': + attr = TypeFlag(family, self, elem) + elif elem['type'] == 'string': + attr = TypeString(family, self, elem) + elif elem['type'] == 'binary': + attr = TypeBinary(family, self, elem) + elif elem['type'] == 'nest': + attr = TypeNest(family, self, elem) + elif elem['type'] == 'array-nest': + attr = TypeArrayNest(family, self, elem) + elif elem['type'] == 'nest-type-value': + attr = TypeNestTypeValue(family, self, elem) + else: + raise Exception(f"No typed class for type {elem['type']}") + + self.attrs[elem['name']] = attr + + def __getitem__(self, key): + return self.attrs[key] + + def __contains__(self, key): + return key in self.yaml + + def __iter__(self): + yield from self.attrs + + def items(self): + return self.attrs.items() + + +class Operation: + def __init__(self, family, yaml, value): + self.yaml = yaml + self.value = value + + self.name = self.yaml['name'] + self.render_name = family.name + '_' + c_lower(self.name) + self.is_async = 'notify' in yaml or 'event' in yaml + if not self.is_async: + self.enum_name = family.op_prefix + c_upper(self.name) + else: + self.enum_name = family.async_op_prefix + c_upper(self.name) + + self.dual_policy = ('do' in yaml and 'request' in yaml['do']) and \ + ('dump' in yaml and 'request' in yaml['dump']) + + def __getitem__(self, key): + return self.yaml[key] + + def __contains__(self, key): + return key in self.yaml + + def add_notification(self, op): + if 'notify' not in self.yaml: + self.yaml['notify'] = dict() + self.yaml['notify']['reply'] = self.yaml['do']['reply'] + self.yaml['notify']['cmds'] = [] + self.yaml['notify']['cmds'].append(op) + + +class Family: + def __init__(self, file_name): + with open(file_name, "r") as stream: + self.yaml = yaml.safe_load(stream) + + self.proto = self.yaml.get('protocol', 'genetlink') + + with open(os.path.dirname(os.path.dirname(file_name)) + + f'/{self.proto}.yaml', "r") as stream: + schema = yaml.safe_load(stream) + + jsonschema.validate(self.yaml, schema) + + if self.yaml.get('protocol', 'genetlink') not in {'genetlink', 'genetlink-c', 'genetlink-legacy'}: + raise Exception("Codegen only supported for genetlink") + + self.fam_key = c_upper(self.yaml.get('c-family-name', self.yaml["name"] + '_FAMILY_NAME')) + self.ver_key = c_upper(self.yaml.get('c-version-name', self.yaml["name"] + '_FAMILY_VERSION')) + + if 'definitions' not in self.yaml: + self.yaml['definitions'] = [] + + self.name = self.yaml['name'] + self.c_name = c_lower(self.name) + if 'uapi-header' in self.yaml: + self.uapi_header = self.yaml['uapi-header'] + else: + self.uapi_header = f"linux/{self.name}.h" + if 'name-prefix' in self.yaml['operations']: + self.op_prefix = c_upper(self.yaml['operations']['name-prefix']) + else: + self.op_prefix = c_upper(self.yaml['name'] + '-cmd-') + if 'async-prefix' in self.yaml['operations']: + self.async_op_prefix = c_upper(self.yaml['operations']['async-prefix']) + else: + self.async_op_prefix = self.op_prefix + + self.mcgrps = self.yaml.get('mcast-groups', {'list': []}) + + self.consts = dict() + self.ops = dict() + self.ops_list = [] + self.attr_sets = dict() + self.attr_sets_list = [] + + self.hooks = dict() + for when in ['pre', 'post']: + self.hooks[when] = dict() + for op_mode in ['do', 'dump']: + self.hooks[when][op_mode] = dict() + self.hooks[when][op_mode]['set'] = set() + self.hooks[when][op_mode]['list'] = [] + + # dict space-name -> 'request': set(attrs), 'reply': set(attrs) + self.root_sets = dict() + # dict space-name -> set('request', 'reply') + self.pure_nested_structs = dict() + self.all_notify = dict() + + self._mock_up_events() + + self._dictify() + self._load_root_sets() + self._load_nested_sets() + self._load_all_notify() + self._load_hooks() + + self.kernel_policy = self.yaml.get('kernel-policy', 'split') + if self.kernel_policy == 'global': + self._load_global_policy() + + def __getitem__(self, key): + return self.yaml[key] + + def get(self, key, default=None): + return self.yaml.get(key, default) + + # Fake a 'do' equivalent of all events, so that we can render their response parsing + def _mock_up_events(self): + for op in self.yaml['operations']['list']: + if 'event' in op: + op['do'] = { + 'reply': { + 'attributes': op['event']['attributes'] + } + } + + def _dictify(self): + for elem in self.yaml['definitions']: + if elem['type'] == 'enum': + self.consts[elem['name']] = EnumSet(self, elem) + else: + self.consts[elem['name']] = elem + + for elem in self.yaml['attribute-sets']: + attr_set = AttrSet(self, elem) + self.attr_sets[elem['name']] = attr_set + self.attr_sets_list.append((elem['name'], attr_set), ) + + ntf = [] + val = 0 + for elem in self.yaml['operations']['list']: + if 'value' in elem: + val = elem['value'] + + op = Operation(self, elem, val) + val += 1 + + self.ops_list.append((elem['name'], op),) + if 'notify' in elem: + ntf.append(op) + continue + if 'attribute-set' not in elem: + continue + self.ops[elem['name']] = op + for n in ntf: + self.ops[n['notify']].add_notification(n) + + def _load_root_sets(self): + for op_name, op in self.ops.items(): + if 'attribute-set' not in op: + continue + + req_attrs = set() + rsp_attrs = set() + for op_mode in ['do', 'dump']: + if op_mode in op and 'request' in op[op_mode]: + req_attrs.update(set(op[op_mode]['request']['attributes'])) + if op_mode in op and 'reply' in op[op_mode]: + rsp_attrs.update(set(op[op_mode]['reply']['attributes'])) + + if op['attribute-set'] not in self.root_sets: + self.root_sets[op['attribute-set']] = {'request': req_attrs, 'reply': rsp_attrs} + else: + self.root_sets[op['attribute-set']]['request'].update(req_attrs) + self.root_sets[op['attribute-set']]['reply'].update(rsp_attrs) + + def _load_nested_sets(self): + for root_set, rs_members in self.root_sets.items(): + for attr, spec in self.attr_sets[root_set].items(): + if 'nested-attributes' in spec: + inherit = set() + nested = spec['nested-attributes'] + if nested not in self.root_sets: + self.pure_nested_structs[nested] = Struct(self, nested, inherited=inherit) + if attr in rs_members['request']: + self.pure_nested_structs[nested].request = True + if attr in rs_members['reply']: + self.pure_nested_structs[nested].reply = True + + if 'type-value' in spec: + if nested in self.root_sets: + raise Exception("Inheriting members to a space used as root not supported") + inherit.update(set(spec['type-value'])) + elif spec['type'] == 'array-nest': + inherit.add('idx') + self.pure_nested_structs[nested].set_inherited(inherit) + + def _load_all_notify(self): + for op_name, op in self.ops.items(): + if not op: + continue + + if 'notify' in op: + self.all_notify[op_name] = op['notify']['cmds'] + + def _load_global_policy(self): + global_set = set() + attr_set_name = None + for op_name, op in self.ops.items(): + if not op: + continue + if 'attribute-set' not in op: + continue + + if attr_set_name is None: + attr_set_name = op['attribute-set'] + if attr_set_name != op['attribute-set']: + raise Exception('For a global policy all ops must use the same set') + + for op_mode in {'do', 'dump'}: + if op_mode in op: + global_set.update(op[op_mode].get('request', [])) + + self.global_policy = [] + self.global_policy_set = attr_set_name + for attr in self.attr_sets[attr_set_name]: + if attr in global_set: + self.global_policy.append(attr) + + def _load_hooks(self): + for op in self.ops.values(): + for op_mode in ['do', 'dump']: + if op_mode not in op: + continue + for when in ['pre', 'post']: + if when not in op[op_mode]: + continue + name = op[op_mode][when] + if name in self.hooks[when][op_mode]['set']: + continue + self.hooks[when][op_mode]['set'].add(name) + self.hooks[when][op_mode]['list'].append(name) + + +class RenderInfo: + def __init__(self, cw, family, ku_space, op, op_name, op_mode, attr_set=None): + self.family = family + self.nl = cw.nlib + self.ku_space = ku_space + self.op = op + self.op_name = op_name + self.op_mode = op_mode + + # 'do' and 'dump' response parsing is identical + if op_mode != 'do' and 'dump' in op and 'do' in op and 'reply' in op['do'] and \ + op["do"]["reply"] == op["dump"]["reply"]: + self.type_consistent = True + else: + self.type_consistent = op_mode == 'event' + + self.attr_set = attr_set + if not self.attr_set: + self.attr_set = op['attribute-set'] + + if op: + self.type_name = c_lower(op_name) + else: + self.type_name = c_lower(attr_set) + + self.cw = cw + + self.struct = dict() + for op_dir in ['request', 'reply']: + if op and op_dir in op[op_mode]: + self.struct[op_dir] = Struct(family, self.attr_set, + type_list=op[op_mode][op_dir]['attributes']) + if op_mode == 'event': + self.struct['reply'] = Struct(family, self.attr_set, type_list=op['event']['attributes']) + + +clas |
