xref: /openbmc/linux/tools/net/ynl/ynl-gen-c.py (revision 86aa961bb4619a68077ebeba21c52e9ba0eab43d)
1981cbcb0SJakub Kicinski#!/usr/bin/env python3
24e16b6a7SJakub Kicinski# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
3be5bea1cSJakub Kicinski
4be5bea1cSJakub Kicinskiimport argparse
53a43ded0SJakub Kicinskiimport collections
6be5bea1cSJakub Kicinskiimport os
7008bcd68SJakub Kicinskiimport re
8f65f305aSJiri Pirkoimport shutil
9f65f305aSJiri Pirkoimport tempfile
10be5bea1cSJakub Kicinskiimport yaml
11be5bea1cSJakub Kicinski
126517a60bSJakub Kicinskifrom lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, SpecEnumEntry
1330a5c6c8SJakub Kicinski
14be5bea1cSJakub Kicinski
15be5bea1cSJakub Kicinskidef c_upper(name):
16be5bea1cSJakub Kicinski    return name.upper().replace('-', '_')
17be5bea1cSJakub Kicinski
18be5bea1cSJakub Kicinski
19be5bea1cSJakub Kicinskidef c_lower(name):
20be5bea1cSJakub Kicinski    return name.lower().replace('-', '_')
21be5bea1cSJakub Kicinski
22be5bea1cSJakub Kicinski
23be5bea1cSJakub Kicinskiclass BaseNlLib:
24be5bea1cSJakub Kicinski    def get_family_id(self):
25be5bea1cSJakub Kicinski        return 'ys->family_id'
26be5bea1cSJakub Kicinski
27be5bea1cSJakub Kicinski    def parse_cb_run(self, cb, data, is_dump=False, indent=1):
28be5bea1cSJakub Kicinski        ind = '\n\t\t' + '\t' * indent + ' '
29be5bea1cSJakub Kicinski        if is_dump:
30be5bea1cSJakub Kicinski            return f"mnl_cb_run2(ys->rx_buf, len, 0, 0, {cb}, {data},{ind}ynl_cb_array, NLMSG_MIN_TYPE)"
31be5bea1cSJakub Kicinski        else:
32be5bea1cSJakub Kicinski            return f"mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid,{ind}{cb}, {data},{ind}" + \
33be5bea1cSJakub Kicinski                   "ynl_cb_array, NLMSG_MIN_TYPE)"
34be5bea1cSJakub Kicinski
35be5bea1cSJakub Kicinski
3630a5c6c8SJakub Kicinskiclass Type(SpecAttr):
3730a5c6c8SJakub Kicinski    def __init__(self, family, attr_set, attr, value):
3830a5c6c8SJakub Kicinski        super().__init__(family, attr_set, attr, value)
3930a5c6c8SJakub Kicinski
40be5bea1cSJakub Kicinski        self.attr = attr
4130a5c6c8SJakub Kicinski        self.attr_set = attr_set
42be5bea1cSJakub Kicinski        self.type = attr['type']
43be5bea1cSJakub Kicinski        self.checks = attr.get('checks', {})
44be5bea1cSJakub Kicinski
45be5bea1cSJakub Kicinski        if 'len' in attr:
46be5bea1cSJakub Kicinski            self.len = attr['len']
47be5bea1cSJakub Kicinski        if 'nested-attributes' in attr:
48be5bea1cSJakub Kicinski            self.nested_attrs = attr['nested-attributes']
49be5bea1cSJakub Kicinski            if self.nested_attrs == family.name:
50be5bea1cSJakub Kicinski                self.nested_render_name = f"{family.name}"
51be5bea1cSJakub Kicinski            else:
52be5bea1cSJakub Kicinski                self.nested_render_name = f"{family.name}_{c_lower(self.nested_attrs)}"
53be5bea1cSJakub Kicinski
542c9d47a0SJakub Kicinski            if self.nested_attrs in self.family.consts:
552c9d47a0SJakub Kicinski                self.nested_struct_type = 'struct ' + self.nested_render_name + '_'
562c9d47a0SJakub Kicinski            else:
572c9d47a0SJakub Kicinski                self.nested_struct_type = 'struct ' + self.nested_render_name
582c9d47a0SJakub Kicinski
59be5bea1cSJakub Kicinski        self.c_name = c_lower(self.name)
60be5bea1cSJakub Kicinski        if self.c_name in _C_KW:
61be5bea1cSJakub Kicinski            self.c_name += '_'
62be5bea1cSJakub Kicinski
6330a5c6c8SJakub Kicinski        # Added by resolve():
6430a5c6c8SJakub Kicinski        self.enum_name = None
6530a5c6c8SJakub Kicinski        delattr(self, "enum_name")
66be5bea1cSJakub Kicinski
6730a5c6c8SJakub Kicinski    def resolve(self):
68ed2042ccSJakub Kicinski        if 'name-prefix' in self.attr:
69ed2042ccSJakub Kicinski            enum_name = f"{self.attr['name-prefix']}{self.name}"
70ed2042ccSJakub Kicinski        else:
71ed2042ccSJakub Kicinski            enum_name = f"{self.attr_set.name_prefix}{self.name}"
72ed2042ccSJakub Kicinski        self.enum_name = c_upper(enum_name)
73be5bea1cSJakub Kicinski
74be5bea1cSJakub Kicinski    def is_multi_val(self):
75be5bea1cSJakub Kicinski        return None
76be5bea1cSJakub Kicinski
77be5bea1cSJakub Kicinski    def is_scalar(self):
78be5bea1cSJakub Kicinski        return self.type in {'u8', 'u16', 'u32', 'u64', 's32', 's64'}
79be5bea1cSJakub Kicinski
80be5bea1cSJakub Kicinski    def presence_type(self):
81be5bea1cSJakub Kicinski        return 'bit'
82be5bea1cSJakub Kicinski
83be5bea1cSJakub Kicinski    def presence_member(self, space, type_filter):
84be5bea1cSJakub Kicinski        if self.presence_type() != type_filter:
85be5bea1cSJakub Kicinski            return
86be5bea1cSJakub Kicinski
87be5bea1cSJakub Kicinski        if self.presence_type() == 'bit':
88be5bea1cSJakub Kicinski            pfx = '__' if space == 'user' else ''
89be5bea1cSJakub Kicinski            return f"{pfx}u32 {self.c_name}:1;"
90be5bea1cSJakub Kicinski
91be5bea1cSJakub Kicinski        if self.presence_type() == 'len':
92be5bea1cSJakub Kicinski            pfx = '__' if space == 'user' else ''
93be5bea1cSJakub Kicinski            return f"{pfx}u32 {self.c_name}_len;"
94be5bea1cSJakub Kicinski
95be5bea1cSJakub Kicinski    def _complex_member_type(self, ri):
96be5bea1cSJakub Kicinski        return None
97be5bea1cSJakub Kicinski
98be5bea1cSJakub Kicinski    def free_needs_iter(self):
99be5bea1cSJakub Kicinski        return False
100be5bea1cSJakub Kicinski
101be5bea1cSJakub Kicinski    def free(self, ri, var, ref):
102be5bea1cSJakub Kicinski        if self.is_multi_val() or self.presence_type() == 'len':
103be5bea1cSJakub Kicinski            ri.cw.p(f'free({var}->{ref}{self.c_name});')
104be5bea1cSJakub Kicinski
105be5bea1cSJakub Kicinski    def arg_member(self, ri):
106be5bea1cSJakub Kicinski        member = self._complex_member_type(ri)
107be5bea1cSJakub Kicinski        if member:
1082cc9671aSJakub Kicinski            arg = [member + ' *' + self.c_name]
1092cc9671aSJakub Kicinski            if self.presence_type() == 'count':
1102cc9671aSJakub Kicinski                arg += ['unsigned int n_' + self.c_name]
1112cc9671aSJakub Kicinski            return arg
112be5bea1cSJakub Kicinski        raise Exception(f"Struct member not implemented for class type {self.type}")
113be5bea1cSJakub Kicinski
114be5bea1cSJakub Kicinski    def struct_member(self, ri):
115be5bea1cSJakub Kicinski        if self.is_multi_val():
116be5bea1cSJakub Kicinski            ri.cw.p(f"unsigned int n_{self.c_name};")
117be5bea1cSJakub Kicinski        member = self._complex_member_type(ri)
118be5bea1cSJakub Kicinski        if member:
119be5bea1cSJakub Kicinski            ptr = '*' if self.is_multi_val() else ''
120be5bea1cSJakub Kicinski            ri.cw.p(f"{member} {ptr}{self.c_name};")
121be5bea1cSJakub Kicinski            return
122be5bea1cSJakub Kicinski        members = self.arg_member(ri)
123be5bea1cSJakub Kicinski        for one in members:
124be5bea1cSJakub Kicinski            ri.cw.p(one + ';')
125be5bea1cSJakub Kicinski
126be5bea1cSJakub Kicinski    def _attr_policy(self, policy):
127be5bea1cSJakub Kicinski        return '{ .type = ' + policy + ', }'
128be5bea1cSJakub Kicinski
129be5bea1cSJakub Kicinski    def attr_policy(self, cw):
130be5bea1cSJakub Kicinski        policy = c_upper('nla-' + self.attr['type'])
131be5bea1cSJakub Kicinski
132be5bea1cSJakub Kicinski        spec = self._attr_policy(policy)
133be5bea1cSJakub Kicinski        cw.p(f"\t[{self.enum_name}] = {spec},")
134be5bea1cSJakub Kicinski
135be5bea1cSJakub Kicinski    def _attr_typol(self):
136be5bea1cSJakub Kicinski        raise Exception(f"Type policy not implemented for class type {self.type}")
137be5bea1cSJakub Kicinski
138be5bea1cSJakub Kicinski    def attr_typol(self, cw):
139be5bea1cSJakub Kicinski        typol = self._attr_typol()
140be5bea1cSJakub Kicinski        cw.p(f'[{self.enum_name}] = {"{"} .name = "{self.name}", {typol}{"}"},')
141be5bea1cSJakub Kicinski
142be5bea1cSJakub Kicinski    def _attr_put_line(self, ri, var, line):
143be5bea1cSJakub Kicinski        if self.presence_type() == 'bit':
144be5bea1cSJakub Kicinski            ri.cw.p(f"if ({var}->_present.{self.c_name})")
145be5bea1cSJakub Kicinski        elif self.presence_type() == 'len':
146be5bea1cSJakub Kicinski            ri.cw.p(f"if ({var}->_present.{self.c_name}_len)")
147be5bea1cSJakub Kicinski        ri.cw.p(f"{line};")
148be5bea1cSJakub Kicinski
149be5bea1cSJakub Kicinski    def _attr_put_simple(self, ri, var, put_type):
150be5bea1cSJakub Kicinski        line = f"mnl_attr_put_{put_type}(nlh, {self.enum_name}, {var}->{self.c_name})"
151be5bea1cSJakub Kicinski        self._attr_put_line(ri, var, line)
152be5bea1cSJakub Kicinski
153be5bea1cSJakub Kicinski    def attr_put(self, ri, var):
154be5bea1cSJakub Kicinski        raise Exception(f"Put not implemented for class type {self.type}")
155be5bea1cSJakub Kicinski
156be5bea1cSJakub Kicinski    def _attr_get(self, ri, var):
157be5bea1cSJakub Kicinski        raise Exception(f"Attr get not implemented for class type {self.type}")
158be5bea1cSJakub Kicinski
159be5bea1cSJakub Kicinski    def attr_get(self, ri, var, first):
160be5bea1cSJakub Kicinski        lines, init_lines, local_vars = self._attr_get(ri, var)
161be5bea1cSJakub Kicinski        if type(lines) is str:
162be5bea1cSJakub Kicinski            lines = [lines]
163be5bea1cSJakub Kicinski        if type(init_lines) is str:
164be5bea1cSJakub Kicinski            init_lines = [init_lines]
165be5bea1cSJakub Kicinski
166be5bea1cSJakub Kicinski        kw = 'if' if first else 'else if'
167e4ea3cc6SJakub Kicinski        ri.cw.block_start(line=f"{kw} (type == {self.enum_name})")
168be5bea1cSJakub Kicinski        if local_vars:
169be5bea1cSJakub Kicinski            for local in local_vars:
170be5bea1cSJakub Kicinski                ri.cw.p(local)
171be5bea1cSJakub Kicinski            ri.cw.nl()
172be5bea1cSJakub Kicinski
173be5bea1cSJakub Kicinski        if not self.is_multi_val():
174be5bea1cSJakub Kicinski            ri.cw.p("if (ynl_attr_validate(yarg, attr))")
175be5bea1cSJakub Kicinski            ri.cw.p("return MNL_CB_ERROR;")
176be5bea1cSJakub Kicinski            if self.presence_type() == 'bit':
177be5bea1cSJakub Kicinski                ri.cw.p(f"{var}->_present.{self.c_name} = 1;")
178be5bea1cSJakub Kicinski
179be5bea1cSJakub Kicinski        if init_lines:
180be5bea1cSJakub Kicinski            ri.cw.nl()
181be5bea1cSJakub Kicinski            for line in init_lines:
182be5bea1cSJakub Kicinski                ri.cw.p(line)
183be5bea1cSJakub Kicinski
184be5bea1cSJakub Kicinski        for line in lines:
185be5bea1cSJakub Kicinski            ri.cw.p(line)
186be5bea1cSJakub Kicinski        ri.cw.block_end()
1876ad49839SJakub Kicinski        return True
188be5bea1cSJakub Kicinski
189be5bea1cSJakub Kicinski    def _setter_lines(self, ri, member, presence):
190be5bea1cSJakub Kicinski        raise Exception(f"Setter not implemented for class type {self.type}")
191be5bea1cSJakub Kicinski
192be5bea1cSJakub Kicinski    def setter(self, ri, space, direction, deref=False, ref=None):
193be5bea1cSJakub Kicinski        ref = (ref if ref else []) + [self.c_name]
194be5bea1cSJakub Kicinski        var = "req"
195be5bea1cSJakub Kicinski        member = f"{var}->{'.'.join(ref)}"
196be5bea1cSJakub Kicinski
197be5bea1cSJakub Kicinski        code = []
198be5bea1cSJakub Kicinski        presence = ''
199be5bea1cSJakub Kicinski        for i in range(0, len(ref)):
200be5bea1cSJakub Kicinski            presence = f"{var}->{'.'.join(ref[:i] + [''])}_present.{ref[i]}"
201*5c05bdd9SJakub Kicinski            # Every layer below last is a nest, so we know it uses bit presence
202*5c05bdd9SJakub Kicinski            # last layer is "self" and may be a complex type
203*5c05bdd9SJakub Kicinski            if i == len(ref) - 1 and self.presence_type() != 'bit':
204*5c05bdd9SJakub Kicinski                continue
205be5bea1cSJakub Kicinski            code.append(presence + ' = 1;')
206be5bea1cSJakub Kicinski        code += self._setter_lines(ri, member, presence)
207be5bea1cSJakub Kicinski
2082cc9671aSJakub Kicinski        func_name = f"{op_prefix(ri, direction, deref=deref)}_set_{'_'.join(ref)}"
2092cc9671aSJakub Kicinski        free = bool([x for x in code if 'free(' in x])
2102cc9671aSJakub Kicinski        alloc = bool([x for x in code if 'alloc(' in x])
2112cc9671aSJakub Kicinski        if free and not alloc:
2122cc9671aSJakub Kicinski            func_name = '__' + func_name
2132cc9671aSJakub Kicinski        ri.cw.write_func('static inline void', func_name, body=code,
214be5bea1cSJakub Kicinski                         args=[f'{type_name(ri, direction, deref=deref)} *{var}'] + self.arg_member(ri))
215be5bea1cSJakub Kicinski
216be5bea1cSJakub Kicinski
217be5bea1cSJakub Kicinskiclass TypeUnused(Type):
218be5bea1cSJakub Kicinski    def presence_type(self):
219be5bea1cSJakub Kicinski        return ''
220be5bea1cSJakub Kicinski
2216ad49839SJakub Kicinski    def arg_member(self, ri):
2226ad49839SJakub Kicinski        return []
2236ad49839SJakub Kicinski
2246ad49839SJakub Kicinski    def _attr_get(self, ri, var):
2256ad49839SJakub Kicinski        return ['return MNL_CB_ERROR;'], None, None
2266ad49839SJakub Kicinski
227be5bea1cSJakub Kicinski    def _attr_typol(self):
228be5bea1cSJakub Kicinski        return '.type = YNL_PT_REJECT, '
229be5bea1cSJakub Kicinski
230be5bea1cSJakub Kicinski    def attr_policy(self, cw):
231be5bea1cSJakub Kicinski        pass
232be5bea1cSJakub Kicinski
233be5bea1cSJakub Kicinski
234be5bea1cSJakub Kicinskiclass TypePad(Type):
235be5bea1cSJakub Kicinski    def presence_type(self):
236be5bea1cSJakub Kicinski        return ''
237be5bea1cSJakub Kicinski
2386ad49839SJakub Kicinski    def arg_member(self, ri):
2396ad49839SJakub Kicinski        return []
2406ad49839SJakub Kicinski
241be5bea1cSJakub Kicinski    def _attr_typol(self):
2426ad49839SJakub Kicinski        return '.type = YNL_PT_IGNORE, '
2436ad49839SJakub Kicinski
24476abff37SJakub Kicinski    def attr_put(self, ri, var):
24576abff37SJakub Kicinski        pass
24676abff37SJakub Kicinski
2476ad49839SJakub Kicinski    def attr_get(self, ri, var, first):
2486ad49839SJakub Kicinski        pass
249be5bea1cSJakub Kicinski
250be5bea1cSJakub Kicinski    def attr_policy(self, cw):
251be5bea1cSJakub Kicinski        pass
252be5bea1cSJakub Kicinski
25376abff37SJakub Kicinski    def setter(self, ri, space, direction, deref=False, ref=None):
25476abff37SJakub Kicinski        pass
25576abff37SJakub Kicinski
256be5bea1cSJakub Kicinski
257be5bea1cSJakub Kicinskiclass TypeScalar(Type):
25830a5c6c8SJakub Kicinski    def __init__(self, family, attr_set, attr, value):
25930a5c6c8SJakub Kicinski        super().__init__(family, attr_set, attr, value)
260be5bea1cSJakub Kicinski
261be5bea1cSJakub Kicinski        self.byte_order_comment = ''
262be5bea1cSJakub Kicinski        if 'byte-order' in attr:
263be5bea1cSJakub Kicinski            self.byte_order_comment = f" /* {attr['byte-order']} */"
264be5bea1cSJakub Kicinski
26530a5c6c8SJakub Kicinski        # Added by resolve():
26630a5c6c8SJakub Kicinski        self.is_bitfield = None
26730a5c6c8SJakub Kicinski        delattr(self, "is_bitfield")
26830a5c6c8SJakub Kicinski        self.type_name = None
26930a5c6c8SJakub Kicinski        delattr(self, "type_name")
27030a5c6c8SJakub Kicinski
27130a5c6c8SJakub Kicinski    def resolve(self):
27230a5c6c8SJakub Kicinski        self.resolve_up(super())
27330a5c6c8SJakub Kicinski
27430a5c6c8SJakub Kicinski        if 'enum-as-flags' in self.attr and self.attr['enum-as-flags']:
27530a5c6c8SJakub Kicinski            self.is_bitfield = True
27630a5c6c8SJakub Kicinski        elif 'enum' in self.attr:
27730a5c6c8SJakub Kicinski            self.is_bitfield = self.family.consts[self.attr['enum']]['type'] == 'flags'
27830a5c6c8SJakub Kicinski        else:
27930a5c6c8SJakub Kicinski            self.is_bitfield = False
28030a5c6c8SJakub Kicinski
281dddc9f53SJakub Kicinski        maybe_enum = not self.is_bitfield and 'enum' in self.attr
282dddc9f53SJakub Kicinski        if maybe_enum and self.family.consts[self.attr['enum']].enum_name:
28330a5c6c8SJakub Kicinski            self.type_name = f"enum {self.family.name}_{c_lower(self.attr['enum'])}"
28430a5c6c8SJakub Kicinski        else:
28530a5c6c8SJakub Kicinski            self.type_name = '__' + self.type
28630a5c6c8SJakub Kicinski
287be5bea1cSJakub Kicinski    def _mnl_type(self):
288be5bea1cSJakub Kicinski        t = self.type
289be5bea1cSJakub Kicinski        # mnl does not have a helper for signed types
290be5bea1cSJakub Kicinski        if t[0] == 's':
291be5bea1cSJakub Kicinski            t = 'u' + t[1:]
292be5bea1cSJakub Kicinski        return t
293be5bea1cSJakub Kicinski
294be5bea1cSJakub Kicinski    def _attr_policy(self, policy):
295be5bea1cSJakub Kicinski        if 'flags-mask' in self.checks or self.is_bitfield:
296be5bea1cSJakub Kicinski            if self.is_bitfield:
2974c6170d1SJakub Kicinski                enum = self.family.consts[self.attr['enum']]
2984c6170d1SJakub Kicinski                mask = enum.get_mask(as_flags=True)
299be5bea1cSJakub Kicinski            else:
300be5bea1cSJakub Kicinski                flags = self.family.consts[self.checks['flags-mask']]
301be5bea1cSJakub Kicinski                flag_cnt = len(flags['entries'])
302be5bea1cSJakub Kicinski                mask = (1 << flag_cnt) - 1
303be5bea1cSJakub Kicinski            return f"NLA_POLICY_MASK({policy}, 0x{mask:x})"
304be5bea1cSJakub Kicinski        elif 'min' in self.checks:
305be5bea1cSJakub Kicinski            return f"NLA_POLICY_MIN({policy}, {self.checks['min']})"
306be5bea1cSJakub Kicinski        elif 'enum' in self.attr:
307be5bea1cSJakub Kicinski            enum = self.family.consts[self.attr['enum']]
30810c4d2a7SJakub Kicinski            low, high = enum.value_range()
30910c4d2a7SJakub Kicinski            if low == 0:
31010c4d2a7SJakub Kicinski                return f"NLA_POLICY_MAX({policy}, {high})"
31110c4d2a7SJakub Kicinski            return f"NLA_POLICY_RANGE({policy}, {low}, {high})"
312be5bea1cSJakub Kicinski        return super()._attr_policy(policy)
313be5bea1cSJakub Kicinski
314be5bea1cSJakub Kicinski    def _attr_typol(self):
315be5bea1cSJakub Kicinski        return f'.type = YNL_PT_U{self.type[1:]}, '
316be5bea1cSJakub Kicinski
317be5bea1cSJakub Kicinski    def arg_member(self, ri):
318be5bea1cSJakub Kicinski        return [f'{self.type_name} {self.c_name}{self.byte_order_comment}']
319be5bea1cSJakub Kicinski
320be5bea1cSJakub Kicinski    def attr_put(self, ri, var):
321be5bea1cSJakub Kicinski        self._attr_put_simple(ri, var, self._mnl_type())
322be5bea1cSJakub Kicinski
323be5bea1cSJakub Kicinski    def _attr_get(self, ri, var):
324be5bea1cSJakub Kicinski        return f"{var}->{self.c_name} = mnl_attr_get_{self._mnl_type()}(attr);", None, None
325be5bea1cSJakub Kicinski
326be5bea1cSJakub Kicinski    def _setter_lines(self, ri, member, presence):
327be5bea1cSJakub Kicinski        return [f"{member} = {self.c_name};"]
328be5bea1cSJakub Kicinski
329be5bea1cSJakub Kicinski
330be5bea1cSJakub Kicinskiclass TypeFlag(Type):
331be5bea1cSJakub Kicinski    def arg_member(self, ri):
332be5bea1cSJakub Kicinski        return []
333be5bea1cSJakub Kicinski
334be5bea1cSJakub Kicinski    def _attr_typol(self):
335be5bea1cSJakub Kicinski        return '.type = YNL_PT_FLAG, '
336be5bea1cSJakub Kicinski
337be5bea1cSJakub Kicinski    def attr_put(self, ri, var):
338be5bea1cSJakub Kicinski        self._attr_put_line(ri, var, f"mnl_attr_put(nlh, {self.enum_name}, 0, NULL)")
339be5bea1cSJakub Kicinski
340be5bea1cSJakub Kicinski    def _attr_get(self, ri, var):
341be5bea1cSJakub Kicinski        return [], None, None
342be5bea1cSJakub Kicinski
343be5bea1cSJakub Kicinski    def _setter_lines(self, ri, member, presence):
344be5bea1cSJakub Kicinski        return []
345be5bea1cSJakub Kicinski
346be5bea1cSJakub Kicinski
347be5bea1cSJakub Kicinskiclass TypeString(Type):
348be5bea1cSJakub Kicinski    def arg_member(self, ri):
349be5bea1cSJakub Kicinski        return [f"const char *{self.c_name}"]
350be5bea1cSJakub Kicinski
351be5bea1cSJakub Kicinski    def presence_type(self):
352be5bea1cSJakub Kicinski        return 'len'
353be5bea1cSJakub Kicinski
354be5bea1cSJakub Kicinski    def struct_member(self, ri):
355be5bea1cSJakub Kicinski        ri.cw.p(f"char *{self.c_name};")
356be5bea1cSJakub Kicinski
357be5bea1cSJakub Kicinski    def _attr_typol(self):
358be5bea1cSJakub Kicinski        return f'.type = YNL_PT_NUL_STR, '
359be5bea1cSJakub Kicinski
360be5bea1cSJakub Kicinski    def _attr_policy(self, policy):
361be5bea1cSJakub Kicinski        mem = '{ .type = ' + policy
362be5bea1cSJakub Kicinski        if 'max-len' in self.checks:
363be5bea1cSJakub Kicinski            mem += ', .len = ' + str(self.checks['max-len'])
364be5bea1cSJakub Kicinski        mem += ', }'
365be5bea1cSJakub Kicinski        return mem
366be5bea1cSJakub Kicinski
367be5bea1cSJakub Kicinski    def attr_policy(self, cw):
368be5bea1cSJakub Kicinski        if self.checks.get('unterminated-ok', False):
369be5bea1cSJakub Kicinski            policy = 'NLA_STRING'
370be5bea1cSJakub Kicinski        else:
371be5bea1cSJakub Kicinski            policy = 'NLA_NUL_STRING'
372be5bea1cSJakub Kicinski
373be5bea1cSJakub Kicinski        spec = self._attr_policy(policy)
374be5bea1cSJakub Kicinski        cw.p(f"\t[{self.enum_name}] = {spec},")
375be5bea1cSJakub Kicinski
376be5bea1cSJakub Kicinski    def attr_put(self, ri, var):
377be5bea1cSJakub Kicinski        self._attr_put_simple(ri, var, 'strz')
378be5bea1cSJakub Kicinski
379be5bea1cSJakub Kicinski    def _attr_get(self, ri, var):
380be5bea1cSJakub Kicinski        len_mem = var + '->_present.' + self.c_name + '_len'
381be5bea1cSJakub Kicinski        return [f"{len_mem} = len;",
382be5bea1cSJakub Kicinski                f"{var}->{self.c_name} = malloc(len + 1);",
383be5bea1cSJakub Kicinski                f"memcpy({var}->{self.c_name}, mnl_attr_get_str(attr), len);",
384be5bea1cSJakub Kicinski                f"{var}->{self.c_name}[len] = 0;"], \
385be5bea1cSJakub Kicinski               ['len = strnlen(mnl_attr_get_str(attr), mnl_attr_get_payload_len(attr));'], \
386be5bea1cSJakub Kicinski               ['unsigned int len;']
387be5bea1cSJakub Kicinski
388be5bea1cSJakub Kicinski    def _setter_lines(self, ri, member, presence):
389be5bea1cSJakub Kicinski        return [f"free({member});",
390be5bea1cSJakub Kicinski                f"{presence}_len = strlen({self.c_name});",
391be5bea1cSJakub Kicinski                f"{member} = malloc({presence}_len + 1);",
392be5bea1cSJakub Kicinski                f'memcpy({member}, {self.c_name}, {presence}_len);',
393be5bea1cSJakub Kicinski                f'{member}[{presence}_len] = 0;']
394be5bea1cSJakub Kicinski
395be5bea1cSJakub Kicinski
396be5bea1cSJakub Kicinskiclass TypeBinary(Type):
397be5bea1cSJakub Kicinski    def arg_member(self, ri):
398be5bea1cSJakub Kicinski        return [f"const void *{self.c_name}", 'size_t len']
399be5bea1cSJakub Kicinski
400be5bea1cSJakub Kicinski    def presence_type(self):
401be5bea1cSJakub Kicinski        return 'len'
402be5bea1cSJakub Kicinski
403be5bea1cSJakub Kicinski    def struct_member(self, ri):
404be5bea1cSJakub Kicinski        ri.cw.p(f"void *{self.c_name};")
405be5bea1cSJakub Kicinski
406be5bea1cSJakub Kicinski    def _attr_typol(self):
407be5bea1cSJakub Kicinski        return f'.type = YNL_PT_BINARY,'
408be5bea1cSJakub Kicinski
409be5bea1cSJakub Kicinski    def _attr_policy(self, policy):
410be5bea1cSJakub Kicinski        mem = '{ '
411be5bea1cSJakub Kicinski        if len(self.checks) == 1 and 'min-len' in self.checks:
412be5bea1cSJakub Kicinski            mem += '.len = ' + str(self.checks['min-len'])
413be5bea1cSJakub Kicinski        elif len(self.checks) == 0:
414be5bea1cSJakub Kicinski            mem += '.type = NLA_BINARY'
415be5bea1cSJakub Kicinski        else:
416be5bea1cSJakub Kicinski            raise Exception('One or more of binary type checks not implemented, yet')
417be5bea1cSJakub Kicinski        mem += ', }'
418be5bea1cSJakub Kicinski        return mem
419be5bea1cSJakub Kicinski
420be5bea1cSJakub Kicinski    def attr_put(self, ri, var):
421be5bea1cSJakub Kicinski        self._attr_put_line(ri, var, f"mnl_attr_put(nlh, {self.enum_name}, " +
422be5bea1cSJakub Kicinski                            f"{var}->_present.{self.c_name}_len, {var}->{self.c_name})")
423be5bea1cSJakub Kicinski
424be5bea1cSJakub Kicinski    def _attr_get(self, ri, var):
425be5bea1cSJakub Kicinski        len_mem = var + '->_present.' + self.c_name + '_len'
426be5bea1cSJakub Kicinski        return [f"{len_mem} = len;",
427be5bea1cSJakub Kicinski                f"{var}->{self.c_name} = malloc(len);",
428be5bea1cSJakub Kicinski                f"memcpy({var}->{self.c_name}, mnl_attr_get_payload(attr), len);"], \
429be5bea1cSJakub Kicinski               ['len = mnl_attr_get_payload_len(attr);'], \
430be5bea1cSJakub Kicinski               ['unsigned int len;']
431be5bea1cSJakub Kicinski
432be5bea1cSJakub Kicinski    def _setter_lines(self, ri, member, presence):
433be5bea1cSJakub Kicinski        return [f"free({member});",
434a149a3a1SJakub Kicinski                f"{presence}_len = len;",
435be5bea1cSJakub Kicinski                f"{member} = malloc({presence}_len);",
436be5bea1cSJakub Kicinski                f'memcpy({member}, {self.c_name}, {presence}_len);']
437be5bea1cSJakub Kicinski
438be5bea1cSJakub Kicinski
439be5bea1cSJakub Kicinskiclass TypeNest(Type):
440be5bea1cSJakub Kicinski    def _complex_member_type(self, ri):
4412c9d47a0SJakub Kicinski        return self.nested_struct_type
442be5bea1cSJakub Kicinski
443be5bea1cSJakub Kicinski    def free(self, ri, var, ref):
444be5bea1cSJakub Kicinski        ri.cw.p(f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name});')
445be5bea1cSJakub Kicinski
446be5bea1cSJakub Kicinski    def _attr_typol(self):
447be5bea1cSJakub Kicinski        return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
448be5bea1cSJakub Kicinski
449be5bea1cSJakub Kicinski    def _attr_policy(self, policy):
450be5bea1cSJakub Kicinski        return 'NLA_POLICY_NESTED(' + self.nested_render_name + '_nl_policy)'
451be5bea1cSJakub Kicinski
452be5bea1cSJakub Kicinski    def attr_put(self, ri, var):
453be5bea1cSJakub Kicinski        self._attr_put_line(ri, var, f"{self.nested_render_name}_put(nlh, " +
454be5bea1cSJakub Kicinski                            f"{self.enum_name}, &{var}->{self.c_name})")
455be5bea1cSJakub Kicinski
456be5bea1cSJakub Kicinski    def _attr_get(self, ri, var):
457eef9b794SJakub Kicinski        get_lines = [f"if ({self.nested_render_name}_parse(&parg, attr))",
458eef9b794SJakub Kicinski                     "return MNL_CB_ERROR;"]
459be5bea1cSJakub Kicinski        init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;",
460be5bea1cSJakub Kicinski                      f"parg.data = &{var}->{self.c_name};"]
461be5bea1cSJakub Kicinski        return get_lines, init_lines, None
462be5bea1cSJakub Kicinski
463be5bea1cSJakub Kicinski    def setter(self, ri, space, direction, deref=False, ref=None):
464be5bea1cSJakub Kicinski        ref = (ref if ref else []) + [self.c_name]
465be5bea1cSJakub Kicinski
466be5bea1cSJakub Kicinski        for _, attr in ri.family.pure_nested_structs[self.nested_attrs].member_list():
467be5bea1cSJakub Kicinski            attr.setter(ri, self.nested_attrs, direction, deref=deref, ref=ref)
468be5bea1cSJakub Kicinski
469be5bea1cSJakub Kicinski
470be5bea1cSJakub Kicinskiclass TypeMultiAttr(Type):
471be093a80SJakub Kicinski    def __init__(self, family, attr_set, attr, value, base_type):
472be093a80SJakub Kicinski        super().__init__(family, attr_set, attr, value)
473be093a80SJakub Kicinski
474be093a80SJakub Kicinski        self.base_type = base_type
475be093a80SJakub Kicinski
476be5bea1cSJakub Kicinski    def is_multi_val(self):
477be5bea1cSJakub Kicinski        return True
478be5bea1cSJakub Kicinski
479be5bea1cSJakub Kicinski    def presence_type(self):
480be5bea1cSJakub Kicinski        return 'count'
481be5bea1cSJakub Kicinski
4822cc9671aSJakub Kicinski    def _mnl_type(self):
4832cc9671aSJakub Kicinski        t = self.type
4842cc9671aSJakub Kicinski        # mnl does not have a helper for signed types
4852cc9671aSJakub Kicinski        if t[0] == 's':
4862cc9671aSJakub Kicinski            t = 'u' + t[1:]
4872cc9671aSJakub Kicinski        return t
4882cc9671aSJakub Kicinski
489be5bea1cSJakub Kicinski    def _complex_member_type(self, ri):
490be5bea1cSJakub Kicinski        if 'type' not in self.attr or self.attr['type'] == 'nest':
4912c9d47a0SJakub Kicinski            return self.nested_struct_type
492be5bea1cSJakub Kicinski        elif self.attr['type'] in scalars:
493be5bea1cSJakub Kicinski            scalar_pfx = '__' if ri.ku_space == 'user' else ''
494be5bea1cSJakub Kicinski            return scalar_pfx + self.attr['type']
495be5bea1cSJakub Kicinski        else:
496be5bea1cSJakub Kicinski            raise Exception(f"Sub-type {self.attr['type']} not supported yet")
497be5bea1cSJakub Kicinski
498be5bea1cSJakub Kicinski    def free_needs_iter(self):
499be5bea1cSJakub Kicinski        return 'type' not in self.attr or self.attr['type'] == 'nest'
500be5bea1cSJakub Kicinski
501be5bea1cSJakub Kicinski    def free(self, ri, var, ref):
5022cc9671aSJakub Kicinski        if self.attr['type'] in scalars:
5032cc9671aSJakub Kicinski            ri.cw.p(f"free({var}->{ref}{self.c_name});")
5042cc9671aSJakub Kicinski        elif 'type' not in self.attr or self.attr['type'] == 'nest':
505be5bea1cSJakub Kicinski            ri.cw.p(f"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)")
506be5bea1cSJakub Kicinski            ri.cw.p(f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);')
5072cc9671aSJakub Kicinski            ri.cw.p(f"free({var}->{ref}{self.c_name});")
5082cc9671aSJakub Kicinski        else:
5092cc9671aSJakub Kicinski            raise Exception(f"Free of MultiAttr sub-type {self.attr['type']} not supported yet")
510be5bea1cSJakub Kicinski
511be093a80SJakub Kicinski    def _attr_policy(self, policy):
512be093a80SJakub Kicinski        return self.base_type._attr_policy(policy)
513be093a80SJakub Kicinski
514be5bea1cSJakub Kicinski    def _attr_typol(self):
515be093a80SJakub Kicinski        return self.base_type._attr_typol()
516be5bea1cSJakub Kicinski
517be5bea1cSJakub Kicinski    def _attr_get(self, ri, var):
51858da455bSJakub Kicinski        return f'n_{self.c_name}++;', None, None
519be5bea1cSJakub Kicinski
5202cc9671aSJakub Kicinski    def attr_put(self, ri, var):
5212cc9671aSJakub Kicinski        if self.attr['type'] in scalars:
5222cc9671aSJakub Kicinski            put_type = self._mnl_type()
5232cc9671aSJakub Kicinski            ri.cw.p(f"for (unsigned int i = 0; i < {var}->n_{self.c_name}; i++)")
5242cc9671aSJakub Kicinski            ri.cw.p(f"mnl_attr_put_{put_type}(nlh, {self.enum_name}, {var}->{self.c_name}[i]);")
5252cc9671aSJakub Kicinski        elif 'type' not in self.attr or self.attr['type'] == 'nest':
5262cc9671aSJakub Kicinski            ri.cw.p(f"for (unsigned int i = 0; i < {var}->n_{self.c_name}; i++)")
5272cc9671aSJakub Kicinski            self._attr_put_line(ri, var, f"{self.nested_render_name}_put(nlh, " +
5282cc9671aSJakub Kicinski                                f"{self.enum_name}, &{var}->{self.c_name}[i])")
5292cc9671aSJakub Kicinski        else:
5302cc9671aSJakub Kicinski            raise Exception(f"Put of MultiAttr sub-type {self.attr['type']} not supported yet")
5312cc9671aSJakub Kicinski
5322cc9671aSJakub Kicinski    def _setter_lines(self, ri, member, presence):
5332cc9671aSJakub Kicinski        # For multi-attr we have a count, not presence, hack up the presence
5342cc9671aSJakub Kicinski        presence = presence[:-(len('_present.') + len(self.c_name))] + "n_" + self.c_name
5352cc9671aSJakub Kicinski        return [f"free({member});",
5362cc9671aSJakub Kicinski                f"{member} = {self.c_name};",
5372cc9671aSJakub Kicinski                f"{presence} = n_{self.c_name};"]
5382cc9671aSJakub Kicinski
539be5bea1cSJakub Kicinski
540be5bea1cSJakub Kicinskiclass TypeArrayNest(Type):
541be5bea1cSJakub Kicinski    def is_multi_val(self):
542be5bea1cSJakub Kicinski        return True
543be5bea1cSJakub Kicinski
544be5bea1cSJakub Kicinski    def presence_type(self):
545be5bea1cSJakub Kicinski        return 'count'
546be5bea1cSJakub Kicinski
547be5bea1cSJakub Kicinski    def _complex_member_type(self, ri):
548be5bea1cSJakub Kicinski        if 'sub-type' not in self.attr or self.attr['sub-type'] == 'nest':
5492c9d47a0SJakub Kicinski            return self.nested_struct_type
550be5bea1cSJakub Kicinski        elif self.attr['sub-type'] in scalars:
551be5bea1cSJakub Kicinski            scalar_pfx = '__' if ri.ku_space == 'user' else ''
552be5bea1cSJakub Kicinski            return scalar_pfx + self.attr['sub-type']
553be5bea1cSJakub Kicinski        else:
554be5bea1cSJakub Kicinski            raise Exception(f"Sub-type {self.attr['sub-type']} not supported yet")
555be5bea1cSJakub Kicinski
556be5bea1cSJakub Kicinski    def _attr_typol(self):
557be5bea1cSJakub Kicinski        return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
558be5bea1cSJakub Kicinski
559be5bea1cSJakub Kicinski    def _attr_get(self, ri, var):
560be5bea1cSJakub Kicinski        local_vars = ['const struct nlattr *attr2;']
561be5bea1cSJakub Kicinski        get_lines = [f'attr_{self.c_name} = attr;',
562be5bea1cSJakub Kicinski                     'mnl_attr_for_each_nested(attr2, attr)',
563be5bea1cSJakub Kicinski                     f'\t{var}->n_{self.c_name}++;']
564be5bea1cSJakub Kicinski        return get_lines, None, local_vars
565be5bea1cSJakub Kicinski
566be5bea1cSJakub Kicinski
567be5bea1cSJakub Kicinskiclass TypeNestTypeValue(Type):
568be5bea1cSJakub Kicinski    def _complex_member_type(self, ri):
5692c9d47a0SJakub Kicinski        return self.nested_struct_type
570be5bea1cSJakub Kicinski
571be5bea1cSJakub Kicinski    def _attr_typol(self):
572be5bea1cSJakub Kicinski        return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
573be5bea1cSJakub Kicinski
574be5bea1cSJakub Kicinski    def _attr_get(self, ri, var):
575be5bea1cSJakub Kicinski        prev = 'attr'
576be5bea1cSJakub Kicinski        tv_args = ''
577be5bea1cSJakub Kicinski        get_lines = []
578be5bea1cSJakub Kicinski        local_vars = []
579be5bea1cSJakub Kicinski        init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;",
580be5bea1cSJakub Kicinski                      f"parg.data = &{var}->{self.c_name};"]
581be5bea1cSJakub Kicinski        if 'type-value' in self.attr:
582be5bea1cSJakub Kicinski            tv_names = [c_lower(x) for x in self.attr["type-value"]]
583be5bea1cSJakub Kicinski            local_vars += [f'const struct nlattr *attr_{", *attr_".join(tv_names)};']
584be5bea1cSJakub Kicinski            local_vars += [f'__u32 {", ".join(tv_names)};']
585be5bea1cSJakub Kicinski            for level in self.attr["type-value"]:
586be5bea1cSJakub Kicinski                level = c_lower(level)
587be5bea1cSJakub Kicinski                get_lines += [f'attr_{level} = mnl_attr_get_payload({prev});']
588be5bea1cSJakub Kicinski                get_lines += [f'{level} = mnl_attr_get_type(attr_{level});']
589be5bea1cSJakub Kicinski                prev = 'attr_' + level
590be5bea1cSJakub Kicinski
591be5bea1cSJakub Kicinski            tv_args = f", {', '.join(tv_names)}"
592be5bea1cSJakub Kicinski
593be5bea1cSJakub Kicinski        get_lines += [f"{self.nested_render_name}_parse(&parg, {prev}{tv_args});"]
594be5bea1cSJakub Kicinski        return get_lines, init_lines, local_vars
595be5bea1cSJakub Kicinski
596be5bea1cSJakub Kicinski
597be5bea1cSJakub Kicinskiclass Struct:
598be5bea1cSJakub Kicinski    def __init__(self, family, space_name, type_list=None, inherited=None):
599be5bea1cSJakub Kicinski        self.family = family
600be5bea1cSJakub Kicinski        self.space_name = space_name
601be5bea1cSJakub Kicinski        self.attr_set = family.attr_sets[space_name]
602be5bea1cSJakub Kicinski        # Use list to catch comparisons with empty sets
603be5bea1cSJakub Kicinski        self._inherited = inherited if inherited is not None else []
604be5bea1cSJakub Kicinski        self.inherited = []
605be5bea1cSJakub Kicinski
606be5bea1cSJakub Kicinski        self.nested = type_list is None
607be5bea1cSJakub Kicinski        if family.name == c_lower(space_name):
608be5bea1cSJakub Kicinski            self.render_name = f"{family.name}"
609be5bea1cSJakub Kicinski        else:
610be5bea1cSJakub Kicinski            self.render_name = f"{family.name}_{c_lower(space_name)}"
611be5bea1cSJakub Kicinski        self.struct_name = 'struct ' + self.render_name
6122c9d47a0SJakub Kicinski        if self.nested and space_name in family.consts:
6132c9d47a0SJakub Kicinski            self.struct_name += '_'
614be5bea1cSJakub Kicinski        self.ptr_name = self.struct_name + ' *'
615be5bea1cSJakub Kicinski
616be5bea1cSJakub Kicinski        self.request = False
617be5bea1cSJakub Kicinski        self.reply = False
618be5bea1cSJakub Kicinski
619be5bea1cSJakub Kicinski        self.attr_list = []
620be5bea1cSJakub Kicinski        self.attrs = dict()
6214c8c24e8SJakub Kicinski        if type_list is not None:
622be5bea1cSJakub Kicinski            for t in type_list:
623be5bea1cSJakub Kicinski                self.attr_list.append((t, self.attr_set[t]),)
624be5bea1cSJakub Kicinski        else:
625be5bea1cSJakub Kicinski            for t in self.attr_set:
626be5bea1cSJakub Kicinski                self.attr_list.append((t, self.attr_set[t]),)
627be5bea1cSJakub Kicinski
628be5bea1cSJakub Kicinski        max_val = 0
629be5bea1cSJakub Kicinski        self.attr_max_val = None
630be5bea1cSJakub Kicinski        for name, attr in self.attr_list:
631f7cf6447SJakub Kicinski            if attr.value >= max_val:
632be5bea1cSJakub Kicinski                max_val = attr.value
633be5bea1cSJakub Kicinski                self.attr_max_val = attr
634be5bea1cSJakub Kicinski            self.attrs[name] = attr
635be5bea1cSJakub Kicinski
636be5bea1cSJakub Kicinski    def __iter__(self):
637be5bea1cSJakub Kicinski        yield from self.attrs
638be5bea1cSJakub Kicinski
639be5bea1cSJakub Kicinski    def __getitem__(self, key):
640be5bea1cSJakub Kicinski        return self.attrs[key]
641be5bea1cSJakub Kicinski
642be5bea1cSJakub Kicinski    def member_list(self):
643be5bea1cSJakub Kicinski        return self.attr_list
644be5bea1cSJakub Kicinski
645be5bea1cSJakub Kicinski    def set_inherited(self, new_inherited):
646be5bea1cSJakub Kicinski        if self._inherited != new_inherited:
647be5bea1cSJakub Kicinski            raise Exception("Inheriting different members not supported")
648be5bea1cSJakub Kicinski        self.inherited = [c_lower(x) for x in sorted(self._inherited)]
649be5bea1cSJakub Kicinski
650be5bea1cSJakub Kicinski
6516517a60bSJakub Kicinskiclass EnumEntry(SpecEnumEntry):
652be5bea1cSJakub Kicinski    def __init__(self, enum_set, yaml, prev, value_start):
6536517a60bSJakub Kicinski        super().__init__(enum_set, yaml, prev, value_start)
654be5bea1cSJakub Kicinski
655be5bea1cSJakub Kicinski        if prev:
656be5bea1cSJakub Kicinski            self.value_change = (self.value != prev.value + 1)
657be5bea1cSJakub Kicinski        else:
658be5bea1cSJakub Kicinski            self.value_change = (self.value != 0)
65966fa34b9SJakub Kicinski        self.value_change = self.value_change or self.enum_set['type'] == 'flags'
66066fa34b9SJakub Kicinski
6616517a60bSJakub Kicinski        # Added by resolve:
6626517a60bSJakub Kicinski        self.c_name = None
6636517a60bSJakub Kicinski        delattr(self, "c_name")
664be5bea1cSJakub Kicinski
6656517a60bSJakub Kicinski    def resolve(self):
6666517a60bSJakub Kicinski        self.resolve_up(super())
667be5bea1cSJakub Kicinski
6686517a60bSJakub Kicinski        self.c_name = c_upper(self.enum_set.value_pfx + self.name)
66966fa34b9SJakub Kicinski
670be5bea1cSJakub Kicinski
6716517a60bSJakub Kicinskiclass EnumSet(SpecEnumSet):
672be5bea1cSJakub Kicinski    def __init__(self, family, yaml):
673be5bea1cSJakub Kicinski        self.render_name = c_lower(family.name + '-' + yaml['name'])
674dddc9f53SJakub Kicinski
675dddc9f53SJakub Kicinski        if 'enum-name' in yaml:
676dddc9f53SJakub Kicinski            if yaml['enum-name']:
677dddc9f53SJakub Kicinski                self.enum_name = 'enum ' + c_lower(yaml['enum-name'])
678dddc9f53SJakub Kicinski            else:
679dddc9f53SJakub Kicinski                self.enum_name = None
680dddc9f53SJakub Kicinski        else:
681be5bea1cSJakub Kicinski            self.enum_name = 'enum ' + self.render_name
682be5bea1cSJakub Kicinski
683be5bea1cSJakub Kicinski        self.value_pfx = yaml.get('name-prefix', f"{family.name}-{yaml['name']}-")
684be5bea1cSJakub Kicinski
6856517a60bSJakub Kicinski        super().__init__(family, yaml)
686be5bea1cSJakub Kicinski
6876517a60bSJakub Kicinski    def new_entry(self, entry, prev_entry, value_start):
6886517a60bSJakub Kicinski        return EnumEntry(self, entry, prev_entry, value_start)
689be5bea1cSJakub Kicinski
69010c4d2a7SJakub Kicinski    def value_range(self):
69110c4d2a7SJakub Kicinski        low = min([x.value for x in self.entries.values()])
69210c4d2a7SJakub Kicinski        high = max([x.value for x in self.entries.values()])
69310c4d2a7SJakub Kicinski
69410c4d2a7SJakub Kicinski        if high - low + 1 != len(self.entries):
69510c4d2a7SJakub Kicinski            raise Exception("Can't get value range for a noncontiguous enum")
69610c4d2a7SJakub Kicinski
69710c4d2a7SJakub Kicinski        return low, high
69810c4d2a7SJakub Kicinski
699be5bea1cSJakub Kicinski
70030a5c6c8SJakub Kicinskiclass AttrSet(SpecAttrSet):
701be5bea1cSJakub Kicinski    def __init__(self, family, yaml):
70230a5c6c8SJakub Kicinski        super().__init__(family, yaml)
703be5bea1cSJakub Kicinski
70430a5c6c8SJakub Kicinski        if self.subset_of is None:
705be5bea1cSJakub Kicinski            if 'name-prefix' in yaml:
706be5bea1cSJakub Kicinski                pfx = yaml['name-prefix']
707be5bea1cSJakub Kicinski            elif self.name == family.name:
708be5bea1cSJakub Kicinski                pfx = family.name + '-a-'
709be5bea1cSJakub Kicinski            else:
710be5bea1cSJakub Kicinski                pfx = f"{family.name}-a-{self.name}-"
711be5bea1cSJakub Kicinski            self.name_prefix = c_upper(pfx)
712be5bea1cSJakub Kicinski            self.max_name = c_upper(self.yaml.get('attr-max-name', f"{self.name_prefix}max"))
713be5bea1cSJakub Kicinski        else:
714be5bea1cSJakub Kicinski            self.name_prefix = family.attr_sets[self.subset_of].name_prefix
715be5bea1cSJakub Kicinski            self.max_name = family.attr_sets[self.subset_of].max_name
716be5bea1cSJakub Kicinski
71730a5c6c8SJakub Kicinski        # Added by resolve:
71830a5c6c8SJakub Kicinski        self.c_name = None
71930a5c6c8SJakub Kicinski        delattr(self, "c_name")
72030a5c6c8SJakub Kicinski
72130a5c6c8SJakub Kicinski    def resolve(self):
722be5bea1cSJakub Kicinski        self.c_name = c_lower(self.name)
723be5bea1cSJakub Kicinski        if self.c_name in _C_KW:
724be5bea1cSJakub Kicinski            self.c_name += '_'
72530a5c6c8SJakub Kicinski        if self.c_name == self.family.c_name:
726be5bea1cSJakub Kicinski            self.c_name = ''
727be5bea1cSJakub Kicinski
72830a5c6c8SJakub Kicinski    def new_attr(self, elem, value):
729be093a80SJakub Kicinski        if elem['type'] in scalars:
730be093a80SJakub Kicinski            t = TypeScalar(self.family, self, elem, value)
731be5bea1cSJakub Kicinski        elif elem['type'] == 'unused':
732be093a80SJakub Kicinski            t = TypeUnused(self.family, self, elem, value)
733be5bea1cSJakub Kicinski        elif elem['type'] == 'pad':
734be093a80SJakub Kicinski            t = TypePad(self.family, self, elem, value)
735be5bea1cSJakub Kicinski        elif elem['type'] == 'flag':
736be093a80SJakub Kicinski            t = TypeFlag(self.family, self, elem, value)
737be5bea1cSJakub Kicinski        elif elem['type'] == 'string':
738be093a80SJakub Kicinski            t = TypeString(self.family, self, elem, value)
739be5bea1cSJakub Kicinski        elif elem['type'] == 'binary':
740be093a80SJakub Kicinski            t = TypeBinary(self.family, self, elem, value)
741be5bea1cSJakub Kicinski        elif elem['type'] == 'nest':
742be093a80SJakub Kicinski            t = TypeNest(self.family, self, elem, value)
743be5bea1cSJakub Kicinski        elif elem['type'] == 'array-nest':
744be093a80SJakub Kicinski            t = TypeArrayNest(self.family, self, elem, value)
745be5bea1cSJakub Kicinski        elif elem['type'] == 'nest-type-value':
746be093a80SJakub Kicinski            t = TypeNestTypeValue(self.family, self, elem, value)
747be5bea1cSJakub Kicinski        else:
748be5bea1cSJakub Kicinski            raise Exception(f"No typed class for type {elem['type']}")
749be5bea1cSJakub Kicinski
750be093a80SJakub Kicinski        if 'multi-attr' in elem and elem['multi-attr']:
751be093a80SJakub Kicinski            t = TypeMultiAttr(self.family, self, elem, value, t)
752be093a80SJakub Kicinski
753be093a80SJakub Kicinski        return t
754be093a80SJakub Kicinski
755be5bea1cSJakub Kicinski
75630a5c6c8SJakub Kicinskiclass Operation(SpecOperation):
75730a5c6c8SJakub Kicinski    def __init__(self, family, yaml, req_value, rsp_value):
75830a5c6c8SJakub Kicinski        super().__init__(family, yaml, req_value, rsp_value)
759be5bea1cSJakub Kicinski
760be5bea1cSJakub Kicinski        self.render_name = family.name + '_' + c_lower(self.name)
761be5bea1cSJakub Kicinski
762be5bea1cSJakub Kicinski        self.dual_policy = ('do' in yaml and 'request' in yaml['do']) and \
763be5bea1cSJakub Kicinski                         ('dump' in yaml and 'request' in yaml['dump'])
764be5bea1cSJakub Kicinski
765ced15688SJakub Kicinski        self.has_ntf = False
766ced15688SJakub Kicinski
76730a5c6c8SJakub Kicinski        # Added by resolve:
76830a5c6c8SJakub Kicinski        self.enum_name = None
76930a5c6c8SJakub Kicinski        delattr(self, "enum_name")
770be5bea1cSJakub Kicinski
77130a5c6c8SJakub Kicinski    def resolve(self):
77230a5c6c8SJakub Kicinski        self.resolve_up(super())
77330a5c6c8SJakub Kicinski
77430a5c6c8SJakub Kicinski        if not self.is_async:
77530a5c6c8SJakub Kicinski            self.enum_name = self.family.op_prefix + c_upper(self.name)
77630a5c6c8SJakub Kicinski        else:
77730a5c6c8SJakub Kicinski            self.enum_name = self.family.async_op_prefix + c_upper(self.name)
778be5bea1cSJakub Kicinski
779ced15688SJakub Kicinski    def mark_has_ntf(self):
780ced15688SJakub Kicinski        self.has_ntf = True
781be5bea1cSJakub Kicinski
782be5bea1cSJakub Kicinski
78330a5c6c8SJakub Kicinskiclass Family(SpecFamily):
784008bcd68SJakub Kicinski    def __init__(self, file_name, exclude_ops):
78530a5c6c8SJakub Kicinski        # Added by resolve:
78630a5c6c8SJakub Kicinski        self.c_name = None
78730a5c6c8SJakub Kicinski        delattr(self, "c_name")
78830a5c6c8SJakub Kicinski        self.op_prefix = None
78930a5c6c8SJakub Kicinski        delattr(self, "op_prefix")
79030a5c6c8SJakub Kicinski        self.async_op_prefix = None
79130a5c6c8SJakub Kicinski        delattr(self, "async_op_prefix")
79230a5c6c8SJakub Kicinski        self.mcgrps = None
79330a5c6c8SJakub Kicinski        delattr(self, "mcgrps")
79430a5c6c8SJakub Kicinski        self.consts = None
79530a5c6c8SJakub Kicinski        delattr(self, "consts")
79630a5c6c8SJakub Kicinski        self.hooks = None
79730a5c6c8SJakub Kicinski        delattr(self, "hooks")
798be5bea1cSJakub Kicinski
799008bcd68SJakub Kicinski        super().__init__(file_name, exclude_ops=exclude_ops)
800be5bea1cSJakub Kicinski
801be5bea1cSJakub Kicinski        self.fam_key = c_upper(self.yaml.get('c-family-name', self.yaml["name"] + '_FAMILY_NAME'))
802be5bea1cSJakub Kicinski        self.ver_key = c_upper(self.yaml.get('c-version-name', self.yaml["name"] + '_FAMILY_VERSION'))
803be5bea1cSJakub Kicinski
804be5bea1cSJakub Kicinski        if 'definitions' not in self.yaml:
805be5bea1cSJakub Kicinski            self.yaml['definitions'] = []
806be5bea1cSJakub Kicinski
807be5bea1cSJakub Kicinski        if 'uapi-header' in self.yaml:
808be5bea1cSJakub Kicinski            self.uapi_header = self.yaml['uapi-header']
809be5bea1cSJakub Kicinski        else:
810be5bea1cSJakub Kicinski            self.uapi_header = f"linux/{self.name}.h"
81130a5c6c8SJakub Kicinski
81230a5c6c8SJakub Kicinski    def resolve(self):
81330a5c6c8SJakub Kicinski        self.resolve_up(super())
81430a5c6c8SJakub Kicinski
81530a5c6c8SJakub Kicinski        if self.yaml.get('protocol', 'genetlink') not in {'genetlink', 'genetlink-c', 'genetlink-legacy'}:
81630a5c6c8SJakub Kicinski            raise Exception("Codegen only supported for genetlink")
81730a5c6c8SJakub Kicinski
81830a5c6c8SJakub Kicinski        self.c_name = c_lower(self.name)
819be5bea1cSJakub Kicinski        if 'name-prefix' in self.yaml['operations']:
820be5bea1cSJakub Kicinski            self.op_prefix = c_upper(self.yaml['operations']['name-prefix'])
821be5bea1cSJakub Kicinski        else:
822be5bea1cSJakub Kicinski            self.op_prefix = c_upper(self.yaml['name'] + '-cmd-')
823be5bea1cSJakub Kicinski        if 'async-prefix' in self.yaml['operations']:
824be5bea1cSJakub Kicinski            self.async_op_prefix = c_upper(self.yaml['operations']['async-prefix'])
825be5bea1cSJakub Kicinski        else:
826be5bea1cSJakub Kicinski            self.async_op_prefix = self.op_prefix
827be5bea1cSJakub Kicinski
828be5bea1cSJakub Kicinski        self.mcgrps = self.yaml.get('mcast-groups', {'list': []})
829be5bea1cSJakub Kicinski
830be5bea1cSJakub Kicinski        self.hooks = dict()
831be5bea1cSJakub Kicinski        for when in ['pre', 'post']:
832be5bea1cSJakub Kicinski            self.hooks[when] = dict()
833be5bea1cSJakub Kicinski            for op_mode in ['do', 'dump']:
834be5bea1cSJakub Kicinski                self.hooks[when][op_mode] = dict()
835be5bea1cSJakub Kicinski                self.hooks[when][op_mode]['set'] = set()
836be5bea1cSJakub Kicinski                self.hooks[when][op_mode]['list'] = []
837be5bea1cSJakub Kicinski
838be5bea1cSJakub Kicinski        # dict space-name -> 'request': set(attrs), 'reply': set(attrs)
839be5bea1cSJakub Kicinski        self.root_sets = dict()
840be5bea1cSJakub Kicinski        # dict space-name -> set('request', 'reply')
841be5bea1cSJakub Kicinski        self.pure_nested_structs = dict()
842be5bea1cSJakub Kicinski
843ced15688SJakub Kicinski        self._mark_notify()
844be5bea1cSJakub Kicinski        self._mock_up_events()
845be5bea1cSJakub Kicinski
846be5bea1cSJakub Kicinski        self._load_root_sets()
847be5bea1cSJakub Kicinski        self._load_nested_sets()
848be5bea1cSJakub Kicinski        self._load_hooks()
849be5bea1cSJakub Kicinski
850be5bea1cSJakub Kicinski        self.kernel_policy = self.yaml.get('kernel-policy', 'split')
851be5bea1cSJakub Kicinski        if self.kernel_policy == 'global':
852be5bea1cSJakub Kicinski            self._load_global_policy()
853be5bea1cSJakub Kicinski
8546517a60bSJakub Kicinski    def new_enum(self, elem):
8556517a60bSJakub Kicinski        return EnumSet(self, elem)
8566517a60bSJakub Kicinski
85730a5c6c8SJakub Kicinski    def new_attr_set(self, elem):
85830a5c6c8SJakub Kicinski        return AttrSet(self, elem)
859be5bea1cSJakub Kicinski
86030a5c6c8SJakub Kicinski    def new_operation(self, elem, req_value, rsp_value):
86130a5c6c8SJakub Kicinski        return Operation(self, elem, req_value, rsp_value)
862be5bea1cSJakub Kicinski
863ced15688SJakub Kicinski    def _mark_notify(self):
864ced15688SJakub Kicinski        for op in self.msgs.values():
865ced15688SJakub Kicinski            if 'notify' in op:
866ced15688SJakub Kicinski                self.ops[op['notify']].mark_has_ntf()
867ced15688SJakub Kicinski
868be5bea1cSJakub Kicinski    # Fake a 'do' equivalent of all events, so that we can render their response parsing
869be5bea1cSJakub Kicinski    def _mock_up_events(self):
870be5bea1cSJakub Kicinski        for op in self.yaml['operations']['list']:
871be5bea1cSJakub Kicinski            if 'event' in op:
872be5bea1cSJakub Kicinski                op['do'] = {
873be5bea1cSJakub Kicinski                    'reply': {
874be5bea1cSJakub Kicinski                        'attributes': op['event']['attributes']
875be5bea1cSJakub Kicinski                    }
876be5bea1cSJakub Kicinski                }
877be5bea1cSJakub Kicinski
878be5bea1cSJakub Kicinski    def _load_root_sets(self):
8796da3424fSJakub Kicinski        for op_name, op in self.msgs.items():
880be5bea1cSJakub Kicinski            if 'attribute-set' not in op:
881be5bea1cSJakub Kicinski                continue
882be5bea1cSJakub Kicinski
883be5bea1cSJakub Kicinski            req_attrs = set()
884be5bea1cSJakub Kicinski            rsp_attrs = set()
885be5bea1cSJakub Kicinski            for op_mode in ['do', 'dump']:
886be5bea1cSJakub Kicinski                if op_mode in op and 'request' in op[op_mode]:
887be5bea1cSJakub Kicinski                    req_attrs.update(set(op[op_mode]['request']['attributes']))
888be5bea1cSJakub Kicinski                if op_mode in op and 'reply' in op[op_mode]:
889be5bea1cSJakub Kicinski                    rsp_attrs.update(set(op[op_mode]['reply']['attributes']))
8906da3424fSJakub Kicinski            if 'event' in op:
8916da3424fSJakub Kicinski                rsp_attrs.update(set(op['event']['attributes']))
892be5bea1cSJakub Kicinski
893be5bea1cSJakub Kicinski            if op['attribute-set'] not in self.root_sets:
894be5bea1cSJakub Kicinski                self.root_sets[op['attribute-set']] = {'request': req_attrs, 'reply': rsp_attrs}
895be5bea1cSJakub Kicinski            else:
896be5bea1cSJakub Kicinski                self.root_sets[op['attribute-set']]['request'].update(req_attrs)
897be5bea1cSJakub Kicinski                self.root_sets[op['attribute-set']]['reply'].update(rsp_attrs)
898be5bea1cSJakub Kicinski
899be5bea1cSJakub Kicinski    def _load_nested_sets(self):
900eae7af21SJakub Kicinski        attr_set_queue = list(self.root_sets.keys())
901eae7af21SJakub Kicinski        attr_set_seen = set(self.root_sets.keys())
902eae7af21SJakub Kicinski
903eae7af21SJakub Kicinski        while len(attr_set_queue):
904eae7af21SJakub Kicinski            a_set = attr_set_queue.pop(0)
905eae7af21SJakub Kicinski            for attr, spec in self.attr_sets[a_set].items():
906eae7af21SJakub Kicinski                if 'nested-attributes' not in spec:
907eae7af21SJakub Kicinski                    continue
908eae7af21SJakub Kicinski
909be5bea1cSJakub Kicinski                nested = spec['nested-attributes']
910eae7af21SJakub Kicinski                if nested not in attr_set_seen:
911eae7af21SJakub Kicinski                    attr_set_queue.append(nested)
912eae7af21SJakub Kicinski                    attr_set_seen.add(nested)
913eae7af21SJakub Kicinski
914eae7af21SJakub Kicinski                inherit = set()
915be5bea1cSJakub Kicinski                if nested not in self.root_sets:
91667c65ce7SJakub Kicinski                    if nested not in self.pure_nested_structs:
917be5bea1cSJakub Kicinski                        self.pure_nested_structs[nested] = Struct(self, nested, inherited=inherit)
918eae7af21SJakub Kicinski                else:
919eae7af21SJakub Kicinski                    raise Exception(f'Using attr set as root and nested not supported - {nested}')
920be5bea1cSJakub Kicinski
921be5bea1cSJakub Kicinski                if 'type-value' in spec:
922be5bea1cSJakub Kicinski                    if nested in self.root_sets:
923be5bea1cSJakub Kicinski                        raise Exception("Inheriting members to a space used as root not supported")
924be5bea1cSJakub Kicinski                    inherit.update(set(spec['type-value']))
925be5bea1cSJakub Kicinski                elif spec['type'] == 'array-nest':
926be5bea1cSJakub Kicinski                    inherit.add('idx')
927be5bea1cSJakub Kicinski                self.pure_nested_structs[nested].set_inherited(inherit)
928be5bea1cSJakub Kicinski
929eae7af21SJakub Kicinski        for root_set, rs_members in self.root_sets.items():
930eae7af21SJakub Kicinski            for attr, spec in self.attr_sets[root_set].items():
931eae7af21SJakub Kicinski                if 'nested-attributes' in spec:
932eae7af21SJakub Kicinski                    nested = spec['nested-attributes']
933eae7af21SJakub Kicinski                    if attr in rs_members['request']:
934eae7af21SJakub Kicinski                        self.pure_nested_structs[nested].request = True
935eae7af21SJakub Kicinski                    if attr in rs_members['reply']:
936eae7af21SJakub Kicinski                        self.pure_nested_structs[nested].reply = True
937eae7af21SJakub Kicinski
9386afaa0efSJakub Kicinski        # Try to reorder according to dependencies
9396afaa0efSJakub Kicinski        pns_key_list = list(self.pure_nested_structs.keys())
9406afaa0efSJakub Kicinski        pns_key_seen = set()
9416afaa0efSJakub Kicinski        rounds = len(pns_key_list)**2  # it's basically bubble sort
9426afaa0efSJakub Kicinski        for _ in range(rounds):
9436afaa0efSJakub Kicinski            if len(pns_key_list) == 0:
9446afaa0efSJakub Kicinski                break
9456afaa0efSJakub Kicinski            name = pns_key_list.pop(0)
9466afaa0efSJakub Kicinski            finished = True
9476afaa0efSJakub Kicinski            for _, spec in self.attr_sets[name].items():
9486afaa0efSJakub Kicinski                if 'nested-attributes' in spec:
9496afaa0efSJakub Kicinski                    if spec['nested-attributes'] not in pns_key_seen:
9506afaa0efSJakub Kicinski                        # Dicts are sorted, this will make struct last
9516afaa0efSJakub Kicinski                        struct = self.pure_nested_structs.pop(name)
9526afaa0efSJakub Kicinski                        self.pure_nested_structs[name] = struct
9536afaa0efSJakub Kicinski                        finished = False
9546afaa0efSJakub Kicinski                        break
9556afaa0efSJakub Kicinski            if finished:
9566afaa0efSJakub Kicinski                pns_key_seen.add(name)
9576afaa0efSJakub Kicinski            else:
9586afaa0efSJakub Kicinski                pns_key_list.append(name)
95937487f93SJakub Kicinski        # Propagate the request / reply
96037487f93SJakub Kicinski        for attr_set, struct in reversed(self.pure_nested_structs.items()):
96137487f93SJakub Kicinski            for _, spec in self.attr_sets[attr_set].items():
96237487f93SJakub Kicinski                if 'nested-attributes' in spec:
96337487f93SJakub Kicinski                    child = self.pure_nested_structs.get(spec['nested-attributes'])
96437487f93SJakub Kicinski                    if child:
96537487f93SJakub Kicinski                        child.request |= struct.request
96637487f93SJakub Kicinski                        child.reply |= struct.reply
9676afaa0efSJakub Kicinski
968be5bea1cSJakub Kicinski    def _load_global_policy(self):
969be5bea1cSJakub Kicinski        global_set = set()
970be5bea1cSJakub Kicinski        attr_set_name = None
971be5bea1cSJakub Kicinski        for op_name, op in self.ops.items():
972be5bea1cSJakub Kicinski            if not op:
973be5bea1cSJakub Kicinski                continue
974be5bea1cSJakub Kicinski            if 'attribute-set' not in op:
975be5bea1cSJakub Kicinski                continue
976be5bea1cSJakub Kicinski
977be5bea1cSJakub Kicinski            if attr_set_name is None:
978be5bea1cSJakub Kicinski                attr_set_name = op['attribute-set']
979be5bea1cSJakub Kicinski            if attr_set_name != op['attribute-set']:
980be5bea1cSJakub Kicinski                raise Exception('For a global policy all ops must use the same set')
981be5bea1cSJakub Kicinski
982eaf317e7SJakub Kicinski            for op_mode in ['do', 'dump']:
983be5bea1cSJakub Kicinski                if op_mode in op:
984dc2ef94dSJakub Kicinski                    req = op[op_mode].get('request')
985dc2ef94dSJakub Kicinski                    if req:
986dc2ef94dSJakub Kicinski                        global_set.update(req.get('attributes', []))
987be5bea1cSJakub Kicinski
988be5bea1cSJakub Kicinski        self.global_policy = []
989be5bea1cSJakub Kicinski        self.global_policy_set = attr_set_name
990be5bea1cSJakub Kicinski        for attr in self.attr_sets[attr_set_name]:
991be5bea1cSJakub Kicinski            if attr in global_set:
992be5bea1cSJakub Kicinski                self.global_policy.append(attr)
993be5bea1cSJakub Kicinski
994be5bea1cSJakub Kicinski    def _load_hooks(self):
995be5bea1cSJakub Kicinski        for op in self.ops.values():
996be5bea1cSJakub Kicinski            for op_mode in ['do', 'dump']:
997be5bea1cSJakub Kicinski                if op_mode not in op:
998be5bea1cSJakub Kicinski                    continue
999be5bea1cSJakub Kicinski                for when in ['pre', 'post']:
1000be5bea1cSJakub Kicinski                    if when not in op[op_mode]:
1001be5bea1cSJakub Kicinski                        continue
1002be5bea1cSJakub Kicinski                    name = op[op_mode][when]
1003be5bea1cSJakub Kicinski                    if name in self.hooks[when][op_mode]['set']:
1004be5bea1cSJakub Kicinski                        continue
1005be5bea1cSJakub Kicinski                    self.hooks[when][op_mode]['set'].add(name)
1006be5bea1cSJakub Kicinski                    self.hooks[when][op_mode]['list'].append(name)
1007be5bea1cSJakub Kicinski
1008be5bea1cSJakub Kicinski
1009be5bea1cSJakub Kicinskiclass RenderInfo:
10106f96ec73SJakub Kicinski    def __init__(self, cw, family, ku_space, op, op_mode, attr_set=None):
1011be5bea1cSJakub Kicinski        self.family = family
1012be5bea1cSJakub Kicinski        self.nl = cw.nlib
1013be5bea1cSJakub Kicinski        self.ku_space = ku_space
1014ced15688SJakub Kicinski        self.op_mode = op_mode
1015be5bea1cSJakub Kicinski        self.op = op
1016be5bea1cSJakub Kicinski
1017be5bea1cSJakub Kicinski        # 'do' and 'dump' response parsing is identical
1018be5bea1cSJakub Kicinski        self.type_consistent = True
10195605f102SJakub Kicinski        if op_mode != 'do' and 'dump' in op and 'do' in op:
10205605f102SJakub Kicinski            if ('reply' in op['do']) != ('reply' in op["dump"]):
10215605f102SJakub Kicinski                self.type_consistent = False
10225605f102SJakub Kicinski            elif 'reply' in op['do'] and op["do"]["reply"] != op["dump"]["reply"]:
10235605f102SJakub Kicinski                self.type_consistent = False
1024be5bea1cSJakub Kicinski
1025be5bea1cSJakub Kicinski        self.attr_set = attr_set
1026be5bea1cSJakub Kicinski        if not self.attr_set:
1027be5bea1cSJakub Kicinski            self.attr_set = op['attribute-set']
1028be5bea1cSJakub Kicinski
10292c9d47a0SJakub Kicinski        self.type_name_conflict = False
1030be5bea1cSJakub Kicinski        if op:
10316f96ec73SJakub Kicinski            self.type_name = c_lower(op.name)
1032be5bea1cSJakub Kicinski        else:
1033be5bea1cSJakub Kicinski            self.type_name = c_lower(attr_set)
10342c9d47a0SJakub Kicinski            if attr_set in family.consts:
10352c9d47a0SJakub Kicinski                self.type_name_conflict = True
1036be5bea1cSJakub Kicinski
1037be5bea1cSJakub Kicinski        self.cw = cw
1038be5bea1cSJakub Kicinski
1039be5bea1cSJakub Kicinski        self.struct = dict()
1040ced15688SJakub Kicinski        if op_mode == 'notify':
1041ced15688SJakub Kicinski            op_mode = 'do'
1042be5bea1cSJakub Kicinski        for op_dir in ['request', 'reply']:
1043be5bea1cSJakub Kicinski            if op and op_dir in op[op_mode]:
1044be5bea1cSJakub Kicinski                self.struct[op_dir] = Struct(family, self.attr_set,
1045be5bea1cSJakub Kicinski                                             type_list=op[op_mode][op_dir]['attributes'])
1046be5bea1cSJakub Kicinski        if op_mode == 'event':
1047be5bea1cSJakub Kicinski            self.struct['reply'] = Struct(family, self.attr_set, type_list=op['event']['attributes'])
1048be5bea1cSJakub Kicinski
1049be5bea1cSJakub Kicinski
1050be5bea1cSJakub Kicinskiclass CodeWriter:
1051a02430c0SJakub Kicinski    def __init__(self, nlib, out_file=None):
1052be5bea1cSJakub Kicinski        self.nlib = nlib
1053be5bea1cSJakub Kicinski
1054be5bea1cSJakub Kicinski        self._nl = False
10552c0f1466SJakub Kicinski        self._block_end = False
1056be5bea1cSJakub Kicinski        self._silent_block = False
1057be5bea1cSJakub Kicinski        self._ind = 0
1058a02430c0SJakub Kicinski        if out_file is None:
1059a02430c0SJakub Kicinski            self._out = os.sys.stdout
1060a02430c0SJakub Kicinski        else:
1061a02430c0SJakub Kicinski            self._out = tempfile.TemporaryFile('w+')
1062a02430c0SJakub Kicinski            self._out_file = out_file
1063a02430c0SJakub Kicinski
1064a02430c0SJakub Kicinski    def __del__(self):
1065a02430c0SJakub Kicinski        self.close_out_file()
1066a02430c0SJakub Kicinski
1067a02430c0SJakub Kicinski    def close_out_file(self):
1068a02430c0SJakub Kicinski        if self._out == os.sys.stdout:
1069a02430c0SJakub Kicinski            return
1070a02430c0SJakub Kicinski        with open(self._out_file, 'w+') as out_file:
1071a02430c0SJakub Kicinski            self._out.seek(0)
1072a02430c0SJakub Kicinski            shutil.copyfileobj(self._out, out_file)
1073a02430c0SJakub Kicinski            self._out.close()
1074a02430c0SJakub Kicinski        self._out = os.sys.stdout
1075be5bea1cSJakub Kicinski
1076be5bea1cSJakub Kicinski    @classmethod
1077be5bea1cSJakub Kicinski    def _is_cond(cls, line):
1078be5bea1cSJakub Kicinski        return line.startswith('if') or line.startswith('while') or line.startswith('for')
1079be5bea1cSJakub Kicinski
10802c0f1466SJakub Kicinski    def p(self, line, add_ind=0):
10812c0f1466SJakub Kicinski        if self._block_end:
10822c0f1466SJakub Kicinski            self._block_end = False
10832c0f1466SJakub Kicinski            if line.startswith('else'):
10842c0f1466SJakub Kicinski                line = '} ' + line
10852c0f1466SJakub Kicinski            else:
10862c0f1466SJakub Kicinski                self._out.write('\t' * self._ind + '}\n')
10872c0f1466SJakub Kicinski
1088be5bea1cSJakub Kicinski        if self._nl:
1089be5bea1cSJakub Kicinski            self._out.write('\n')
1090be5bea1cSJakub Kicinski            self._nl = False
10912c0f1466SJakub Kicinski
1092be5bea1cSJakub Kicinski        ind = self._ind
1093be5bea1cSJakub Kicinski        if line[-1] == ':':
1094be5bea1cSJakub Kicinski            ind -= 1
1095be5bea1cSJakub Kicinski        if self._silent_block:
1096be5bea1cSJakub Kicinski            ind += 1
1097be5bea1cSJakub Kicinski        self._silent_block = line.endswith(')') and CodeWriter._is_cond(line)
1098be5bea1cSJakub Kicinski        if add_ind:
1099be5bea1cSJakub Kicinski            ind += add_ind
1100be5bea1cSJakub Kicinski        self._out.write('\t' * ind + line + '\n')
1101be5bea1cSJakub Kicinski
1102be5bea1cSJakub Kicinski    def nl(self):
1103be5bea1cSJakub Kicinski        self._nl = True
1104be5bea1cSJakub Kicinski
1105be5bea1cSJakub Kicinski    def block_start(self, line=''):
1106be5bea1cSJakub Kicinski        if line:
1107be5bea1cSJakub Kicinski            line = line + ' '
1108be5bea1cSJakub Kicinski        self.p(line + '{')
1109be5bea1cSJakub Kicinski        self._ind += 1
1110be5bea1cSJakub Kicinski
1111be5bea1cSJakub Kicinski    def block_end(self, line=''):
1112be5bea1cSJakub Kicinski        if line and line[0] not in {';', ','}:
1113be5bea1cSJakub Kicinski            line = ' ' + line
1114be5bea1cSJakub Kicinski        self._ind -= 1
11152c0f1466SJakub Kicinski        self._nl = False
11162c0f1466SJakub Kicinski        if not line:
11172c0f1466SJakub Kicinski            # Delay printing closing bracket in case "else" comes next
11182c0f1466SJakub Kicinski            if self._block_end:
11192c0f1466SJakub Kicinski                self._out.write('\t' * (self._ind + 1) + '}\n')
11202c0f1466SJakub Kicinski            self._block_end = True
11212c0f1466SJakub Kicinski        else:
11222c0f1466SJakub Kicinski            self.p('}' + line)
1123be5bea1cSJakub Kicinski
1124be5bea1cSJakub Kicinski    def write_doc_line(self, doc, indent=True):
1125be5bea1cSJakub Kicinski        words = doc.split()
1126be5bea1cSJakub Kicinski        line = ' *'
1127be5bea1cSJakub Kicinski        for word in words:
1128be5bea1cSJakub Kicinski            if len(line) + len(word) >= 79:
1129be5bea1cSJakub Kicinski                self.p(line)
1130be5bea1cSJakub Kicinski                line = ' *'
1131be5bea1cSJakub Kicinski                if indent:
1132be5bea1cSJakub Kicinski                    line += '  '
1133be5bea1cSJakub Kicinski            line += ' ' + word
1134be5bea1cSJakub Kicinski        self.p(line)
1135be5bea1cSJakub Kicinski
1136be5bea1cSJakub Kicinski    def write_func_prot(self, qual_ret, name, args=None, doc=None, suffix=''):
1137be5bea1cSJakub Kicinski        if not args:
1138be5bea1cSJakub Kicinski            args = ['void']
1139be5bea1cSJakub Kicinski
1140be5bea1cSJakub Kicinski        if doc:
1141be5bea1cSJakub Kicinski            self.p('/*')
1142be5bea1cSJakub Kicinski            self.p(' * ' + doc)
1143be5bea1cSJakub Kicinski            self.p(' */')
1144be5bea1cSJakub Kicinski
1145be5bea1cSJakub Kicinski        oneline = qual_ret
1146be5bea1cSJakub Kicinski        if qual_ret[-1] != '*':
1147be5bea1cSJakub Kicinski            oneline += ' '
1148be5bea1cSJakub Kicinski        oneline += f"{name}({', '.join(args)}){suffix}"
1149be5bea1cSJakub Kicinski
1150be5bea1cSJakub Kicinski        if len(oneline) < 80:
1151be5bea1cSJakub Kicinski            self.p(oneline)
1152be5bea1cSJakub Kicinski            return
1153be5bea1cSJakub Kicinski
1154be5bea1cSJakub Kicinski        v = qual_ret
1155be5bea1cSJakub Kicinski        if len(v) > 3:
1156be5bea1cSJakub Kicinski            self.p(v)
1157be5bea1cSJakub Kicinski            v = ''
1158be5bea1cSJakub Kicinski        elif qual_ret[-1] != '*':
1159be5bea1cSJakub Kicinski            v += ' '
1160be5bea1cSJakub Kicinski        v += name + '('
1161be5bea1cSJakub Kicinski        ind = '\t' * (len(v) // 8) + ' ' * (len(v) % 8)
1162be5bea1cSJakub Kicinski        delta_ind = len(v) - len(ind)
1163be5bea1cSJakub Kicinski        v += args[0]
1164be5bea1cSJakub Kicinski        i = 1
1165be5bea1cSJakub Kicinski        while i < len(args):
1166be5bea1cSJakub Kicinski            next_len = len(v) + len(args[i])
1167be5bea1cSJakub Kicinski            if v[0] == '\t':
1168be5bea1cSJakub Kicinski                next_len += delta_ind
1169be5bea1cSJakub Kicinski            if next_len > 76:
1170be5bea1cSJakub Kicinski                self.p(v + ',')
1171be5bea1cSJakub Kicinski                v = ind
1172be5bea1cSJakub Kicinski            else:
1173be5bea1cSJakub Kicinski                v += ', '
1174be5bea1cSJakub Kicinski            v += args[i]
1175be5bea1cSJakub Kicinski            i += 1
1176be5bea1cSJakub Kicinski        self.p(v + ')' + suffix)
1177be5bea1cSJakub Kicinski
1178be5bea1cSJakub Kicinski    def write_func_lvar(self, local_vars):
1179be5bea1cSJakub Kicinski        if not local_vars:
1180be5bea1cSJakub Kicinski            return
1181be5bea1cSJakub Kicinski
1182be5bea1cSJakub Kicinski        if type(local_vars) is str:
1183be5bea1cSJakub Kicinski            local_vars = [local_vars]
1184be5bea1cSJakub Kicinski
1185be5bea1cSJakub Kicinski        local_vars.sort(key=len, reverse=True)
1186be5bea1cSJakub Kicinski        for var in local_vars:
1187be5bea1cSJakub Kicinski            self.p(var)
1188be5bea1cSJakub Kicinski        self.nl()
1189be5bea1cSJakub Kicinski
1190be5bea1cSJakub Kicinski    def write_func(self, qual_ret, name, body, args=None, local_vars=None):
1191be5bea1cSJakub Kicinski        self.write_func_prot(qual_ret=qual_ret, name=name, args=args)
1192be5bea1cSJakub Kicinski        self.write_func_lvar(local_vars=local_vars)
1193be5bea1cSJakub Kicinski
1194be5bea1cSJakub Kicinski        self.block_start()
1195be5bea1cSJakub Kicinski        for line in body:
1196be5bea1cSJakub Kicinski            self.p(line)
1197be5bea1cSJakub Kicinski        self.block_end()
1198be5bea1cSJakub Kicinski
1199be5bea1cSJakub Kicinski    def writes_defines(self, defines):
1200be5bea1cSJakub Kicinski        longest = 0
1201be5bea1cSJakub Kicinski        for define in defines:
1202be5bea1cSJakub Kicinski            if len(define[0]) > longest:
1203be5bea1cSJakub Kicinski                longest = len(define[0])
1204be5bea1cSJakub Kicinski        longest = ((longest + 8) // 8) * 8
1205be5bea1cSJakub Kicinski        for define in defines:
1206be5bea1cSJakub Kicinski            line = '#define ' + define[0]
1207be5bea1cSJakub Kicinski            line += '\t' * ((longest - len(define[0]) + 7) // 8)
1208be5bea1cSJakub Kicinski            if type(define[1]) is int:
1209be5bea1cSJakub Kicinski                line += str(define[1])
1210be5bea1cSJakub Kicinski            elif type(define[1]) is str:
1211be5bea1cSJakub Kicinski                line += '"' + define[1] + '"'
1212be5bea1cSJakub Kicinski            self.p(line)
1213be5bea1cSJakub Kicinski
1214be5bea1cSJakub Kicinski    def write_struct_init(self, members):
1215be5bea1cSJakub Kicinski        longest = max([len(x[0]) for x in members])
1216be5bea1cSJakub Kicinski        longest += 1  # because we prepend a .
1217be5bea1cSJakub Kicinski        longest = ((longest + 8) // 8) * 8
1218be5bea1cSJakub Kicinski        for one in members:
1219be5bea1cSJakub Kicinski            line = '.' + one[0]
1220be5bea1cSJakub Kicinski            line += '\t' * ((longest - len(one[0]) - 1 + 7) // 8)
1221be5bea1cSJakub Kicinski            line += '= ' + one[1] + ','
1222be5bea1cSJakub Kicinski            self.p(line)
1223be5bea1cSJakub Kicinski
1224be5bea1cSJakub Kicinski
1225be5bea1cSJakub Kicinskiscalars = {'u8', 'u16', 'u32', 'u64', 's32', 's64'}
1226be5bea1cSJakub Kicinski
1227be5bea1cSJakub Kicinskidirection_to_suffix = {
1228be5bea1cSJakub Kicinski    'reply': '_rsp',
1229be5bea1cSJakub Kicinski    'request': '_req',
1230be5bea1cSJakub Kicinski    '': ''
1231be5bea1cSJakub Kicinski}
1232be5bea1cSJakub Kicinski
1233be5bea1cSJakub Kicinskiop_mode_to_wrapper = {
1234be5bea1cSJakub Kicinski    'do': '',
1235be5bea1cSJakub Kicinski    'dump': '_list',
1236be5bea1cSJakub Kicinski    'notify': '_ntf',
1237be5bea1cSJakub Kicinski    'event': '',
1238be5bea1cSJakub Kicinski}
1239be5bea1cSJakub Kicinski
1240be5bea1cSJakub Kicinski_C_KW = {
1241820343ccSJakub Kicinski    'auto',
1242820343ccSJakub Kicinski    'bool',
1243820343ccSJakub Kicinski    'break',
1244820343ccSJakub Kicinski    'case',
1245820343ccSJakub Kicinski    'char',
1246820343ccSJakub Kicinski    'const',
1247820343ccSJakub Kicinski    'continue',
1248820343ccSJakub Kicinski    'default',
1249820343ccSJakub Kicinski    'do',
1250820343ccSJakub Kicinski    'double',
1251820343ccSJakub Kicinski    'else',
1252820343ccSJakub Kicinski    'enum',
1253820343ccSJakub Kicinski    'extern',
1254820343ccSJakub Kicinski    'float',
1255820343ccSJakub Kicinski    'for',
1256820343ccSJakub Kicinski    'goto',
1257820343ccSJakub Kicinski    'if',
1258820343ccSJakub Kicinski    'inline',
1259820343ccSJakub Kicinski    'int',
1260820343ccSJakub Kicinski    'long',
1261820343ccSJakub Kicinski    'register',
1262820343ccSJakub Kicinski    'return',
1263820343ccSJakub Kicinski    'short',
1264820343ccSJakub Kicinski    'signed',
1265820343ccSJakub Kicinski    'sizeof',
1266820343ccSJakub Kicinski    'static',
1267820343ccSJakub Kicinski    'struct',
1268820343ccSJakub Kicinski    'switch',
1269820343ccSJakub Kicinski    'typedef',
1270820343ccSJakub Kicinski    'union',
1271820343ccSJakub Kicinski    'unsigned',
1272820343ccSJakub Kicinski    'void',
1273820343ccSJakub Kicinski    'volatile',
1274820343ccSJakub Kicinski    'while'
1275be5bea1cSJakub Kicinski}
1276be5bea1cSJakub Kicinski
1277be5bea1cSJakub Kicinski
1278be5bea1cSJakub Kicinskidef rdir(direction):
1279be5bea1cSJakub Kicinski    if direction == 'reply':
1280be5bea1cSJakub Kicinski        return 'request'
1281be5bea1cSJakub Kicinski    if direction == 'request':
1282be5bea1cSJakub Kicinski        return 'reply'
1283be5bea1cSJakub Kicinski    return direction
1284be5bea1cSJakub Kicinski
1285be5bea1cSJakub Kicinski
1286be5bea1cSJakub Kicinskidef op_prefix(ri, direction, deref=False):
1287be5bea1cSJakub Kicinski    suffix = f"_{ri.type_name}"
1288be5bea1cSJakub Kicinski
1289be5bea1cSJakub Kicinski    if not ri.op_mode or ri.op_mode == 'do':
1290be5bea1cSJakub Kicinski        suffix += f"{direction_to_suffix[direction]}"
1291be5bea1cSJakub Kicinski    else:
1292be5bea1cSJakub Kicinski        if direction == 'request':
1293be5bea1cSJakub Kicinski            suffix += '_req_dump'
1294be5bea1cSJakub Kicinski        else:
1295be5bea1cSJakub Kicinski            if ri.type_consistent:
1296be5bea1cSJakub Kicinski                if deref:
1297be5bea1cSJakub Kicinski                    suffix += f"{direction_to_suffix[direction]}"
1298be5bea1cSJakub Kicinski                else:
1299be5bea1cSJakub Kicinski                    suffix += op_mode_to_wrapper[ri.op_mode]
1300be5bea1cSJakub Kicinski            else:
1301be5bea1cSJakub Kicinski                suffix += '_rsp'
1302be5bea1cSJakub Kicinski                suffix += '_dump' if deref else '_list'
1303be5bea1cSJakub Kicinski
1304be5bea1cSJakub Kicinski    return f"{ri.family['name']}{suffix}"
1305be5bea1cSJakub Kicinski
1306be5bea1cSJakub Kicinski
1307be5bea1cSJakub Kicinskidef type_name(ri, direction, deref=False):
1308be5bea1cSJakub Kicinski    return f"struct {op_prefix(ri, direction, deref=deref)}"
1309be5bea1cSJakub Kicinski
1310be5bea1cSJakub Kicinski
1311be5bea1cSJakub Kicinskidef print_prototype(ri, direction, terminate=True, doc=None):
1312be5bea1cSJakub Kicinski    suffix = ';' if terminate else ''
1313be5bea1cSJakub Kicinski
1314be5bea1cSJakub Kicinski    fname = ri.op.render_name
1315be5bea1cSJakub Kicinski    if ri.op_mode == 'dump':
1316be5bea1cSJakub Kicinski        fname += '_dump'
1317be5bea1cSJakub Kicinski
1318be5bea1cSJakub Kicinski    args = ['struct ynl_sock *ys']
1319be5bea1cSJakub Kicinski    if 'request' in ri.op[ri.op_mode]:
1320be5bea1cSJakub Kicinski        args.append(f"{type_name(ri, direction)} *" + f"{direction_to_suffix[direction][1:]}")
1321be5bea1cSJakub Kicinski
1322be5bea1cSJakub Kicinski    ret = 'int'
1323be5bea1cSJakub Kicinski    if 'reply' in ri.op[ri.op_mode]:
1324be5bea1cSJakub Kicinski        ret = f"{type_name(ri, rdir(direction))} *"
1325be5bea1cSJakub Kicinski
1326be5bea1cSJakub Kicinski    ri.cw.write_func_prot(ret, fname, args, doc=doc, suffix=suffix)
1327be5bea1cSJakub Kicinski
1328be5bea1cSJakub Kicinski
1329be5bea1cSJakub Kicinskidef print_req_prototype(ri):
1330be5bea1cSJakub Kicinski    print_prototype(ri, "request", doc=ri.op['doc'])
1331be5bea1cSJakub Kicinski
1332be5bea1cSJakub Kicinski
1333be5bea1cSJakub Kicinskidef print_dump_prototype(ri):
1334be5bea1cSJakub Kicinski    print_prototype(ri, "request")
1335be5bea1cSJakub Kicinski
1336be5bea1cSJakub Kicinski
1337be5bea1cSJakub Kicinskidef put_typol(cw, struct):
1338be5bea1cSJakub Kicinski    type_max = struct.attr_set.max_name
1339be5bea1cSJakub Kicinski    cw.block_start(line=f'struct ynl_policy_attr {struct.render_name}_policy[{type_max} + 1] =')
1340be5bea1cSJakub Kicinski
1341be5bea1cSJakub Kicinski    for _, arg in struct.member_list():
1342be5bea1cSJakub Kicinski        arg.attr_typol(cw)
1343be5bea1cSJakub Kicinski
1344be5bea1cSJakub Kicinski    cw.block_end(line=';')
1345be5bea1cSJakub Kicinski    cw.nl()
1346be5bea1cSJakub Kicinski
1347be5bea1cSJakub Kicinski    cw.block_start(line=f'struct ynl_policy_nest {struct.render_name}_nest =')
1348be5bea1cSJakub Kicinski    cw.p(f'.max_attr = {type_max},')
1349be5bea1cSJakub Kicinski    cw.p(f'.table = {struct.render_name}_policy,')
1350be5bea1cSJakub Kicinski    cw.block_end(line=';')
1351be5bea1cSJakub Kicinski    cw.nl()
1352be5bea1cSJakub Kicinski
1353be5bea1cSJakub Kicinski
13546f115d45SJakub Kicinskidef _put_enum_to_str_helper(cw, render_name, map_name, arg_name, enum=None):
13556f115d45SJakub Kicinski    args = [f'int {arg_name}']
13566f115d45SJakub Kicinski    if enum and not ('enum-name' in enum and not enum['enum-name']):
13576f115d45SJakub Kicinski        args = [f'enum {render_name} {arg_name}']
13586f115d45SJakub Kicinski    cw.write_func_prot('const char *', f'{render_name}_str', args)
13596f115d45SJakub Kicinski    cw.block_start()
13606f115d45SJakub Kicinski    if enum and enum.type == 'flags':
13616f115d45SJakub Kicinski        cw.p(f'{arg_name} = ffs({arg_name}) - 1;')
13626f115d45SJakub Kicinski    cw.p(f'if ({arg_name} < 0 || {arg_name} >= (int)MNL_ARRAY_SIZE({map_name}))')
13636f115d45SJakub Kicinski    cw.p('return NULL;')
13646f115d45SJakub Kicinski    cw.p(f'return {map_name}[{arg_name}];')
13656f115d45SJakub Kicinski    cw.block_end()
13666f115d45SJakub Kicinski    cw.nl()
13676f115d45SJakub Kicinski
13686f115d45SJakub Kicinski
136921b6e302SJakub Kicinskidef put_op_name_fwd(family, cw):
137021b6e302SJakub Kicinski    cw.write_func_prot('const char *', f'{family.name}_op_str', ['int op'], suffix=';')
137121b6e302SJakub Kicinski
137221b6e302SJakub Kicinski
137321b6e302SJakub Kicinskidef put_op_name(family, cw):
137421b6e302SJakub Kicinski    map_name = f'{family.name}_op_strmap'
137521b6e302SJakub Kicinski    cw.block_start(line=f"static const char * const {map_name}[] =")
137621b6e302SJakub Kicinski    for op_name, op in family.msgs.items():
13779858bfc2SJakub Kicinski        if op.rsp_value:
13789858bfc2SJakub Kicinski            if op.req_value == op.rsp_value:
137921b6e302SJakub Kicinski                cw.p(f'[{op.enum_name}] = "{op_name}",')
13809858bfc2SJakub Kicinski            else:
13819858bfc2SJakub Kicinski                cw.p(f'[{op.rsp_value}] = "{op_name}",')
138221b6e302SJakub Kicinski    cw.block_end(line=';')
138321b6e302SJakub Kicinski    cw.nl()
138421b6e302SJakub Kicinski
13856f115d45SJakub Kicinski    _put_enum_to_str_helper(cw, family.name + '_op', map_name, 'op')
138621b6e302SJakub Kicinski
138721b6e302SJakub Kicinski
138821b6e302SJakub Kicinskidef put_enum_to_str_fwd(family, cw, enum):
138921b6e302SJakub Kicinski    args = [f'enum {enum.render_name} value']
139021b6e302SJakub Kicinski    if 'enum-name' in enum and not enum['enum-name']:
139121b6e302SJakub Kicinski        args = ['int value']
139221b6e302SJakub Kicinski    cw.write_func_prot('const char *', f'{enum.render_name}_str', args, suffix=';')
139321b6e302SJakub Kicinski
139421b6e302SJakub Kicinski
139521b6e302SJakub Kicinskidef put_enum_to_str(family, cw, enum):
139621b6e302SJakub Kicinski    map_name = f'{enum.render_name}_strmap'
139721b6e302SJakub Kicinski    cw.block_start(line=f"static const char * const {map_name}[] =")
139821b6e302SJakub Kicinski    for entry in enum.entries.values():
139921b6e302SJakub Kicinski        cw.p(f'[{entry.value}] = "{entry.name}",')
140021b6e302SJakub Kicinski    cw.block_end(line=';')
140121b6e302SJakub Kicinski    cw.nl()
140221b6e302SJakub Kicinski
14036f115d45SJakub Kicinski    _put_enum_to_str_helper(cw, enum.render_name, map_name, 'value', enum=enum)
140421b6e302SJakub Kicinski
140521b6e302SJakub Kicinski
1406be5bea1cSJakub Kicinskidef put_req_nested(ri, struct):
1407be5bea1cSJakub Kicinski    func_args = ['struct nlmsghdr *nlh',
1408be5bea1cSJakub Kicinski                 'unsigned int attr_type',
1409be5bea1cSJakub Kicinski                 f'{struct.ptr_name}obj']
1410be5bea1cSJakub Kicinski
1411be5bea1cSJakub Kicinski    ri.cw.write_func_prot('int', f'{struct.render_name}_put', func_args)
1412be5bea1cSJakub Kicinski    ri.cw.block_start()
1413be5bea1cSJakub Kicinski    ri.cw.write_func_lvar('struct nlattr *nest;')
1414be5bea1cSJakub Kicinski
1415be5bea1cSJakub Kicinski    ri.cw.p("nest = mnl_attr_nest_start(nlh, attr_type);")
1416be5bea1cSJakub Kicinski
1417be5bea1cSJakub Kicinski    for _, arg in struct.member_list():
1418be5bea1cSJakub Kicinski        arg.attr_put(ri, "obj")
1419be5bea1cSJakub Kicinski
1420be5bea1cSJakub Kicinski    ri.cw.p("mnl_attr_nest_end(nlh, nest);")
1421be5bea1cSJakub Kicinski
1422be5bea1cSJakub Kicinski    ri.cw.nl()
1423be5bea1cSJakub Kicinski    ri.cw.p('return 0;')
1424be5bea1cSJakub Kicinski    ri.cw.block_end()
1425be5bea1cSJakub Kicinski    ri.cw.nl()
1426be5bea1cSJakub Kicinski
1427be5bea1cSJakub Kicinski
1428be5bea1cSJakub Kicinskidef _multi_parse(ri, struct, init_lines, local_vars):
1429be5bea1cSJakub Kicinski    if struct.nested:
1430be5bea1cSJakub Kicinski        iter_line = "mnl_attr_for_each_nested(attr, nested)"
1431be5bea1cSJakub Kicinski    else:
1432be5bea1cSJakub Kicinski        iter_line = "mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr))"
1433be5bea1cSJakub Kicinski
1434be5bea1cSJakub Kicinski    array_nests = set()
1435be5bea1cSJakub Kicinski    multi_attrs = set()
1436be5bea1cSJakub Kicinski    needs_parg = False
1437be5bea1cSJakub Kicinski    for arg, aspec in struct.member_list():
1438be5bea1cSJakub Kicinski        if aspec['type'] == 'array-nest':
1439be5bea1cSJakub Kicinski            local_vars.append(f'const struct nlattr *attr_{aspec.c_name};')
1440be5bea1cSJakub Kicinski            array_nests.add(arg)
1441be5bea1cSJakub Kicinski        if 'multi-attr' in aspec:
1442be5bea1cSJakub Kicinski            multi_attrs.add(arg)
1443be5bea1cSJakub Kicinski        needs_parg |= 'nested-attributes' in aspec
1444be5bea1cSJakub Kicinski    if array_nests or multi_attrs:
1445be5bea1cSJakub Kicinski        local_vars.append('int i;')
1446be5bea1cSJakub Kicinski    if needs_parg:
1447be5bea1cSJakub Kicinski        local_vars.append('struct ynl_parse_arg parg;')
1448be5bea1cSJakub Kicinski        init_lines.append('parg.ys = yarg->ys;')
1449be5bea1cSJakub Kicinski
145058da455bSJakub Kicinski    all_multi = array_nests | multi_attrs
145158da455bSJakub Kicinski
145258da455bSJakub Kicinski    for anest in sorted(all_multi):
145358da455bSJakub Kicinski        local_vars.append(f"unsigned int n_{struct[anest].c_name} = 0;")
145458da455bSJakub Kicinski
1455be5bea1cSJakub Kicinski    ri.cw.block_start()
1456be5bea1cSJakub Kicinski    ri.cw.write_func_lvar(local_vars)
1457be5bea1cSJakub Kicinski
1458be5bea1cSJakub Kicinski    for line in init_lines:
1459be5bea1cSJakub Kicinski        ri.cw.p(line)
1460be5bea1cSJakub Kicinski    ri.cw.nl()
1461be5bea1cSJakub Kicinski
1462be5bea1cSJakub Kicinski    for arg in struct.inherited:
1463be5bea1cSJakub Kicinski        ri.cw.p(f'dst->{arg} = {arg};')
1464be5bea1cSJakub Kicinski
146558da455bSJakub Kicinski    for anest in sorted(all_multi):
146658da455bSJakub Kicinski        aspec = struct[anest]
146758da455bSJakub Kicinski        ri.cw.p(f"if (dst->{aspec.c_name})")
146858da455bSJakub Kicinski        ri.cw.p(f'return ynl_error_parse(yarg, "attribute already present ({struct.attr_set.name}.{aspec.name})");')
146958da455bSJakub Kicinski
1470be5bea1cSJakub Kicinski    ri.cw.nl()
1471be5bea1cSJakub Kicinski    ri.cw.block_start(line=iter_line)
1472e4ea3cc6SJakub Kicinski    ri.cw.p('unsigned int type = mnl_attr_get_type(attr);')
1473e4ea3cc6SJakub Kicinski    ri.cw.nl()
1474be5bea1cSJakub Kicinski
1475be5bea1cSJakub Kicinski    first = True
1476be5bea1cSJakub Kicinski    for _, arg in struct.member_list():
14776ad49839SJakub Kicinski        good = arg.attr_get(ri, 'dst', first=first)
14786ad49839SJakub Kicinski        # First may be 'unused' or 'pad', ignore those
14796ad49839SJakub Kicinski        first &= not good
1480be5bea1cSJakub Kicinski
1481be5bea1cSJakub Kicinski    ri.cw.block_end()
1482be5bea1cSJakub Kicinski    ri.cw.nl()
1483be5bea1cSJakub Kicinski
1484be5bea1cSJakub Kicinski    for anest in sorted(array_nests):
1485be5bea1cSJakub Kicinski        aspec = struct[anest]
1486be5bea1cSJakub Kicinski
148758da455bSJakub Kicinski        ri.cw.block_start(line=f"if (n_{aspec.c_name})")
148858da455bSJakub Kicinski        ri.cw.p(f"dst->{aspec.c_name} = calloc({aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
148958da455bSJakub Kicinski        ri.cw.p(f"dst->n_{aspec.c_name} = n_{aspec.c_name};")
1490be5bea1cSJakub Kicinski        ri.cw.p('i = 0;')
1491be5bea1cSJakub Kicinski        ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
1492be5bea1cSJakub Kicinski        ri.cw.block_start(line=f"mnl_attr_for_each_nested(attr, attr_{aspec.c_name})")
1493be5bea1cSJakub Kicinski        ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];")
1494be5bea1cSJakub Kicinski        ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr, mnl_attr_get_type(attr)))")
1495be5bea1cSJakub Kicinski        ri.cw.p('return MNL_CB_ERROR;')
1496be5bea1cSJakub Kicinski        ri.cw.p('i++;')
1497be5bea1cSJakub Kicinski        ri.cw.block_end()
1498be5bea1cSJakub Kicinski        ri.cw.block_end()
1499be5bea1cSJakub Kicinski    ri.cw.nl()
1500be5bea1cSJakub Kicinski
1501be5bea1cSJakub Kicinski    for anest in sorted(multi_attrs):
1502be5bea1cSJakub Kicinski        aspec = struct[anest]
150358da455bSJakub Kicinski        ri.cw.block_start(line=f"if (n_{aspec.c_name})")
150458da455bSJakub Kicinski        ri.cw.p(f"dst->{aspec.c_name} = calloc(n_{aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
150558da455bSJakub Kicinski        ri.cw.p(f"dst->n_{aspec.c_name} = n_{aspec.c_name};")
1506be5bea1cSJakub Kicinski        ri.cw.p('i = 0;')
1507be5bea1cSJakub Kicinski        if 'nested-attributes' in aspec:
1508be5bea1cSJakub Kicinski            ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
1509be5bea1cSJakub Kicinski        ri.cw.block_start(line=iter_line)
1510be5bea1cSJakub Kicinski        ri.cw.block_start(line=f"if (mnl_attr_get_type(attr) == {aspec.enum_name})")
1511be5bea1cSJakub Kicinski        if 'nested-attributes' in aspec:
1512be5bea1cSJakub Kicinski            ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];")
1513be5bea1cSJakub Kicinski            ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr))")
1514be5bea1cSJakub Kicinski            ri.cw.p('return MNL_CB_ERROR;')
1515be5bea1cSJakub Kicinski        elif aspec['type'] in scalars:
1516be5bea1cSJakub Kicinski            t = aspec['type']
1517be5bea1cSJakub Kicinski            if t[0] == 's':
1518be5bea1cSJakub Kicinski                t = 'u' + t[1:]
1519be5bea1cSJakub Kicinski            ri.cw.p(f"dst->{aspec.c_name}[i] = mnl_attr_get_{t}(attr);")
1520be5bea1cSJakub Kicinski        else:
1521be5bea1cSJakub Kicinski            raise Exception('Nest parsing type not supported yet')
1522be5bea1cSJakub Kicinski        ri.cw.p('i++;')
1523be5bea1cSJakub Kicinski        ri.cw.block_end()
1524be5bea1cSJakub Kicinski        ri.cw.block_end()
1525be5bea1cSJakub Kicinski        ri.cw.block_end()
1526be5bea1cSJakub Kicinski    ri.cw.nl()
1527be5bea1cSJakub Kicinski
1528be5bea1cSJakub Kicinski    if struct.nested:
1529be5bea1cSJakub Kicinski        ri.cw.p('return 0;')
1530be5bea1cSJakub Kicinski    else:
1531be5bea1cSJakub Kicinski        ri.cw.p('return MNL_CB_OK;')
1532be5bea1cSJakub Kicinski    ri.cw.block_end()
1533be5bea1cSJakub Kicinski    ri.cw.nl()
1534be5bea1cSJakub Kicinski
1535be5bea1cSJakub Kicinski
1536be5bea1cSJakub Kicinskidef parse_rsp_nested(ri, struct):
1537be5bea1cSJakub Kicinski    func_args = ['struct ynl_parse_arg *yarg',
1538be5bea1cSJakub Kicinski                 'const struct nlattr *nested']
1539be5bea1cSJakub Kicinski    for arg in struct.inherited:
1540be5bea1cSJakub Kicinski        func_args.append('__u32 ' + arg)
1541be5bea1cSJakub Kicinski
1542be5bea1cSJakub Kicinski    local_vars = ['const struct nlattr *attr;',
1543be5bea1cSJakub Kicinski                  f'{struct.ptr_name}dst = yarg->data;']
1544be5bea1cSJakub Kicinski    init_lines = []
1545be5bea1cSJakub Kicinski
1546be5bea1cSJakub Kicinski    ri.cw.write_func_prot('int', f'{struct.render_name}_parse', func_args)
1547be5bea1cSJakub Kicinski
1548be5bea1cSJakub Kicinski    _multi_parse(ri, struct, init_lines, local_vars)
1549be5bea1cSJakub Kicinski
1550be5bea1cSJakub Kicinski
1551be5bea1cSJakub Kicinskidef parse_rsp_msg(ri, deref=False):
1552be5bea1cSJakub Kicinski    if 'reply' not in ri.op[ri.op_mode] and ri.op_mode != 'event':
1553be5bea1cSJakub Kicinski        return
1554be5bea1cSJakub Kicinski
1555be5bea1cSJakub Kicinski    func_args = ['const struct nlmsghdr *nlh',
1556be5bea1cSJakub Kicinski                 'void *data']
1557be5bea1cSJakub Kicinski
1558be5bea1cSJakub Kicinski    local_vars = [f'{type_name(ri, "reply", deref=deref)} *dst;',
1559be5bea1cSJakub Kicinski                  'struct ynl_parse_arg *yarg = data;',
1560be5bea1cSJakub Kicinski                  'const struct nlattr *attr;']
1561be5bea1cSJakub Kicinski    init_lines = ['dst = yarg->data;']
1562be5bea1cSJakub Kicinski
1563be5bea1cSJakub Kicinski    ri.cw.write_func_prot('int', f'{op_prefix(ri, "reply", deref=deref)}_parse', func_args)
1564be5bea1cSJakub Kicinski
15654c8c24e8SJakub Kicinski    if ri.struct["reply"].member_list():
1566be5bea1cSJakub Kicinski        _multi_parse(ri, ri.struct["reply"], init_lines, local_vars)
15674c8c24e8SJakub Kicinski    else:
15684c8c24e8SJakub Kicinski        # Empty reply
15694c8c24e8SJakub Kicinski        ri.cw.block_start()
15704c8c24e8SJakub Kicinski        ri.cw.p('return MNL_CB_OK;')
15714c8c24e8SJakub Kicinski        ri.cw.block_end()
15724c8c24e8SJakub Kicinski        ri.cw.nl()
1573be5bea1cSJakub Kicinski
1574be5bea1cSJakub Kicinski
1575be5bea1cSJakub Kicinskidef print_req(ri):
1576be5bea1cSJakub Kicinski    ret_ok = '0'
1577be5bea1cSJakub Kicinski    ret_err = '-1'
1578be5bea1cSJakub Kicinski    direction = "request"
1579be5bea1cSJakub Kicinski    local_vars = ['struct nlmsghdr *nlh;',
1580dc0956c9SJakub Kicinski                  'int err;']
1581be5bea1cSJakub Kicinski
1582be5bea1cSJakub Kicinski    if 'reply' in ri.op[ri.op_mode]:
1583be5bea1cSJakub Kicinski        ret_ok = 'rsp'
1584be5bea1cSJakub Kicinski        ret_err = 'NULL'
1585be5bea1cSJakub Kicinski        local_vars += [f'{type_name(ri, rdir(direction))} *rsp;',
1586dc0956c9SJakub Kicinski                       'struct ynl_req_state yrs = { .yarg = { .ys = ys, }, };']
1587be5bea1cSJakub Kicinski
1588be5bea1cSJakub Kicinski    print_prototype(ri, direction, terminate=False)
1589be5bea1cSJakub Kicinski    ri.cw.block_start()
1590be5bea1cSJakub Kicinski    ri.cw.write_func_lvar(local_vars)
1591be5bea1cSJakub Kicinski
1592be5bea1cSJakub Kicinski    ri.cw.p(f"nlh = ynl_gemsg_start_req(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
1593be5bea1cSJakub Kicinski
1594be5bea1cSJakub Kicinski    ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
1595be5bea1cSJakub Kicinski    if 'reply' in ri.op[ri.op_mode]:
1596dc0956c9SJakub Kicinski        ri.cw.p(f"yrs.yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
1597be5bea1cSJakub Kicinski    ri.cw.nl()
1598be5bea1cSJakub Kicinski    for _, attr in ri.struct["request"].member_list():
1599be5bea1cSJakub Kicinski        attr.attr_put(ri, "req")
1600be5bea1cSJakub Kicinski    ri.cw.nl()
1601be5bea1cSJakub Kicinski
1602dc0956c9SJakub Kicinski    parse_arg = "NULL"
1603be5bea1cSJakub Kicinski    if 'reply' in ri.op[ri.op_mode]:
1604be5bea1cSJakub Kicinski        ri.cw.p('rsp = calloc(1, sizeof(*rsp));')
1605dc0956c9SJakub Kicinski        ri.cw.p('yrs.yarg.data = rsp;')
1606dc0956c9SJakub Kicinski        ri.cw.p(f"yrs.cb = {op_prefix(ri, 'reply')}_parse;")
1607dc0956c9SJakub Kicinski        if ri.op.value is not None:
1608dc0956c9SJakub Kicinski            ri.cw.p(f'yrs.rsp_cmd = {ri.op.enum_name};')
1609dc0956c9SJakub Kicinski        else:
1610dc0956c9SJakub Kicinski            ri.cw.p(f'yrs.rsp_cmd = {ri.op.rsp_value};')
1611be5bea1cSJakub Kicinski        ri.cw.nl()
1612dc0956c9SJakub Kicinski        parse_arg = '&yrs'
1613dc0956c9SJakub Kicinski    ri.cw.p(f"err = ynl_exec(ys, nlh, {parse_arg});")
1614be5bea1cSJakub Kicinski    ri.cw.p('if (err < 0)')
1615dc0956c9SJakub Kicinski    if 'reply' in ri.op[ri.op_mode]:
1616be5bea1cSJakub Kicinski        ri.cw.p('goto err_free;')
1617dc0956c9SJakub Kicinski    else:
1618dc0956c9SJakub Kicinski        ri.cw.p('return -1;')
1619be5bea1cSJakub Kicinski    ri.cw.nl()
1620be5bea1cSJakub Kicinski
1621be5bea1cSJakub Kicinski    ri.cw.p(f"return {ret_ok};")
1622be5bea1cSJakub Kicinski    ri.cw.nl()
1623be5bea1cSJakub Kicinski
1624be5bea1cSJakub Kicinski    if 'reply' in ri.op[ri.op_mode]:
1625dc0956c9SJakub Kicinski        ri.cw.p('err_free:')
1626be5bea1cSJakub Kicinski        ri.cw.p(f"{call_free(ri, rdir(direction), 'rsp')}")
1627be5bea1cSJakub Kicinski        ri.cw.p(f"return {ret_err};")
1628dc0956c9SJakub Kicinski
1629be5bea1cSJakub Kicinski    ri.cw.block_end()
1630be5bea1cSJakub Kicinski
1631be5bea1cSJakub Kicinski
1632be5bea1cSJakub Kicinskidef print_dump(ri):
1633be5bea1cSJakub Kicinski    direction = "request"
1634be5bea1cSJakub Kicinski    print_prototype(ri, direction, terminate=False)
1635be5bea1cSJakub Kicinski    ri.cw.block_start()
1636be5bea1cSJakub Kicinski    local_vars = ['struct ynl_dump_state yds = {};',
1637be5bea1cSJakub Kicinski                  'struct nlmsghdr *nlh;',
1638dc0956c9SJakub Kicinski                  'int err;']
1639be5bea1cSJakub Kicinski
1640be5bea1cSJakub Kicinski    for var in local_vars:
1641be5bea1cSJakub Kicinski        ri.cw.p(f'{var}')
1642be5bea1cSJakub Kicinski    ri.cw.nl()
1643be5bea1cSJakub Kicinski
1644be5bea1cSJakub Kicinski    ri.cw.p('yds.ys = ys;')
1645be5bea1cSJakub Kicinski    ri.cw.p(f"yds.alloc_sz = sizeof({type_name(ri, rdir(direction))});")
1646be5bea1cSJakub Kicinski    ri.cw.p(f"yds.cb = {op_prefix(ri, 'reply', deref=True)}_parse;")
1647dc0956c9SJakub Kicinski    if ri.op.value is not None:
1648dc0956c9SJakub Kicinski        ri.cw.p(f'yds.rsp_cmd = {ri.op.enum_name};')
1649dc0956c9SJakub Kicinski    else:
1650dc0956c9SJakub Kicinski        ri.cw.p(f'yds.rsp_cmd = {ri.op.rsp_value};')
1651be5bea1cSJakub Kicinski    ri.cw.p(f"yds.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
1652be5bea1cSJakub Kicinski    ri.cw.nl()
1653be5bea1cSJakub Kicinski    ri.cw.p(f"nlh = ynl_gemsg_start_dump(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
1654be5bea1cSJakub Kicinski
1655be5bea1cSJakub Kicinski    if "request" in ri.op[ri.op_mode]:
1656be5bea1cSJakub Kicinski        ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
1657be5bea1cSJakub Kicinski        ri.cw.nl()
1658be5bea1cSJakub Kicinski        for _, attr in ri.struct["request"].member_list():
1659be5bea1cSJakub Kicinski            attr.attr_put(ri, "req")
1660be5bea1cSJakub Kicinski    ri.cw.nl()
1661be5bea1cSJakub Kicinski
1662dc0956c9SJakub Kicinski    ri.cw.p('err = ynl_exec_dump(ys, nlh, &yds);')
1663be5bea1cSJakub Kicinski    ri.cw.p('if (err < 0)')
1664be5bea1cSJakub Kicinski    ri.cw.p('goto free_list;')
1665be5bea1cSJakub Kicinski    ri.cw.nl()
1666be5bea1cSJakub Kicinski
1667be5bea1cSJakub Kicinski    ri.cw.p('return yds.first;')
1668be5bea1cSJakub Kicinski    ri.cw.nl()
1669be5bea1cSJakub Kicinski    ri.cw.p('free_list:')
1670be5bea1cSJakub Kicinski    ri.cw.p(call_free(ri, rdir(direction), 'yds.first'))
1671be5bea1cSJakub Kicinski    ri.cw.p('return NULL;')
1672be5bea1cSJakub Kicinski    ri.cw.block_end()
1673be5bea1cSJakub Kicinski
1674be5bea1cSJakub Kicinski
1675be5bea1cSJakub Kicinskidef call_free(ri, direction, var):
1676be5bea1cSJakub Kicinski    return f"{op_prefix(ri, direction)}_free({var});"
1677be5bea1cSJakub Kicinski
1678be5bea1cSJakub Kicinski
1679be5bea1cSJakub Kicinskidef free_arg_name(direction):
1680be5bea1cSJakub Kicinski    if direction:
1681be5bea1cSJakub Kicinski        return direction_to_suffix[direction][1:]
1682be5bea1cSJakub Kicinski    return 'obj'
1683be5bea1cSJakub Kicinski
1684be5bea1cSJakub Kicinski
16855d58f911SJakub Kicinskidef print_alloc_wrapper(ri, direction):
16865d58f911SJakub Kicinski    name = op_prefix(ri, direction)
16875d58f911SJakub Kicinski    ri.cw.write_func_prot(f'static inline struct {name} *', f"{name}_alloc", [f"void"])
16885d58f911SJakub Kicinski    ri.cw.block_start()
16895d58f911SJakub Kicinski    ri.cw.p(f'return calloc(1, sizeof(struct {name}));')
16905d58f911SJakub Kicinski    ri.cw.block_end()
16915d58f911SJakub Kicinski
16925d58f911SJakub Kicinski
1693be5bea1cSJakub Kicinskidef print_free_prototype(ri, direction, suffix=';'):
1694be5bea1cSJakub Kicinski    name = op_prefix(ri, direction)
16952c9d47a0SJakub Kicinski    struct_name = name
16962c9d47a0SJakub Kicinski    if ri.type_name_conflict:
16972c9d47a0SJakub Kicinski        struct_name += '_'
1698be5bea1cSJakub Kicinski    arg = free_arg_name(direction)
16992c9d47a0SJakub Kicinski    ri.cw.write_func_prot('void', f"{name}_free", [f"struct {struct_name} *{arg}"], suffix=suffix)
1700be5bea1cSJakub Kicinski
1701be5bea1cSJakub Kicinski
1702be5bea1cSJakub Kicinskidef _print_type(ri, direction, struct):
1703be5bea1cSJakub Kicinski    suffix = f'_{ri.type_name}{direction_to_suffix[direction]}'
17042c9d47a0SJakub Kicinski    if not direction and ri.type_name_conflict:
17052c9d47a0SJakub Kicinski        suffix += '_'
1706be5bea1cSJakub Kicinski
1707be5bea1cSJakub Kicinski    if ri.op_mode == 'dump':
1708be5bea1cSJakub Kicinski        suffix += '_dump'
1709be5bea1cSJakub Kicinski
1710be5bea1cSJakub Kicinski    ri.cw.block_start(line=f"struct {ri.family['name']}{suffix}")
1711be5bea1cSJakub Kicinski
1712be5bea1cSJakub Kicinski    meta_started = False
1713be5bea1cSJakub Kicinski    for _, attr in struct.member_list():
1714be5bea1cSJakub Kicinski        for type_filter in ['len', 'bit']:
1715be5bea1cSJakub Kicinski            line = attr.presence_member(ri.ku_space, type_filter)
1716be5bea1cSJakub Kicinski            if line:
1717be5bea1cSJakub Kicinski                if not meta_started:
1718be5bea1cSJakub Kicinski                    ri.cw.block_start(line=f"struct")
1719be5bea1cSJakub Kicinski                    meta_started = True
1720be5bea1cSJakub Kicinski                ri.cw.p(line)
1721be5bea1cSJakub Kicinski    if meta_started:
1722be5bea1cSJakub Kicinski        ri.cw.block_end(line='_present;')
1723be5bea1cSJakub Kicinski        ri.cw.nl()
1724be5bea1cSJakub Kicinski
1725be5bea1cSJakub Kicinski    for arg in struct.inherited:
1726be5bea1cSJakub Kicinski        ri.cw.p(f"__u32 {arg};")
1727be5bea1cSJakub Kicinski
1728be5bea1cSJakub Kicinski    for _, attr in struct.member_list():
1729be5bea1cSJakub Kicinski        attr.struct_member(ri)
1730be5bea1cSJakub Kicinski
1731be5bea1cSJakub Kicinski    ri.cw.block_end(line=';')
1732be5bea1cSJakub Kicinski    ri.cw.nl()
1733be5bea1cSJakub Kicinski
1734be5bea1cSJakub Kicinski
1735be5bea1cSJakub Kicinskidef print_type(ri, direction):
1736be5bea1cSJakub Kicinski    _print_type(ri, direction, ri.struct[direction])
1737be5bea1cSJakub Kicinski
1738be5bea1cSJakub Kicinski
1739be5bea1cSJakub Kicinskidef print_type_full(ri, struct):
1740be5bea1cSJakub Kicinski    _print_type(ri, "", struct)
1741be5bea1cSJakub Kicinski
1742be5bea1cSJakub Kicinski
1743be5bea1cSJakub Kicinskidef print_type_helpers(ri, direction, deref=False):
1744be5bea1cSJakub Kicinski    print_free_prototype(ri, direction)
17455d58f911SJakub Kicinski    ri.cw.nl()
1746be5bea1cSJakub Kicinski
1747be5bea1cSJakub Kicinski    if ri.ku_space == 'user' and direction == 'request':
1748be5bea1cSJakub Kicinski        for _, attr in ri.struct[direction].member_list():
1749be5bea1cSJakub Kicinski            attr.setter(ri, ri.attr_set, direction, deref=deref)
1750be5bea1cSJakub Kicinski    ri.cw.nl()
1751be5bea1cSJakub Kicinski
1752be5bea1cSJakub Kicinski
1753be5bea1cSJakub Kicinskidef print_req_type_helpers(ri):
17545d58f911SJakub Kicinski    print_alloc_wrapper(ri, "request")
1755be5bea1cSJakub Kicinski    print_type_helpers(ri, "request")
1756be5bea1cSJakub Kicinski
1757be5bea1cSJakub Kicinski
1758be5bea1cSJakub Kicinskidef print_rsp_type_helpers(ri):
1759be5bea1cSJakub Kicinski    if 'reply' not in ri.op[ri.op_mode]:
1760be5bea1cSJakub Kicinski        return
1761be5bea1cSJakub Kicinski    print_type_helpers(ri, "reply")
1762be5bea1cSJakub Kicinski
1763be5bea1cSJakub Kicinski
1764be5bea1cSJakub Kicinskidef print_parse_prototype(ri, direction, terminate=True):
1765be5bea1cSJakub Kicinski    suffix = "_rsp" if direction == "reply" else "_req"
1766be5bea1cSJakub Kicinski    term = ';' if terminate else ''
1767be5bea1cSJakub Kicinski
1768be5bea1cSJakub Kicinski    ri.cw.write_func_prot('void', f"{ri.op.render_name}{suffix}_parse",
1769be5bea1cSJakub Kicinski                          ['const struct nlattr **tb',
1770be5bea1cSJakub Kicinski                           f"struct {ri.op.render_name}{suffix} *req"],
1771be5bea1cSJakub Kicinski                          suffix=term)
1772be5bea1cSJakub Kicinski
1773be5bea1cSJakub Kicinski
1774be5bea1cSJakub Kicinskidef print_req_type(ri):
1775be5bea1cSJakub Kicinski    print_type(ri, "request")
1776be5bea1cSJakub Kicinski
1777be5bea1cSJakub Kicinski
17785d58f911SJakub Kicinskidef print_req_free(ri):
17795d58f911SJakub Kicinski    if 'request' not in ri.op[ri.op_mode]:
17805d58f911SJakub Kicinski        return
17815d58f911SJakub Kicinski    _free_type(ri, 'request', ri.struct['request'])
17825d58f911SJakub Kicinski
17835d58f911SJakub Kicinski
1784be5bea1cSJakub Kicinskidef print_rsp_type(ri):
1785be5bea1cSJakub Kicinski    if (ri.op_mode == 'do' or ri.op_mode == 'dump') and 'reply' in ri.op[ri.op_mode]:
1786be5bea1cSJakub Kicinski        direction = 'reply'
1787be5bea1cSJakub Kicinski    elif ri.op_mode == 'event':
1788be5bea1cSJakub Kicinski        direction = 'reply'
1789be5bea1cSJakub Kicinski    else:
1790be5bea1cSJakub Kicinski        return
1791be5bea1cSJakub Kicinski    print_type(ri, direction)
1792be5bea1cSJakub Kicinski
1793be5bea1cSJakub Kicinski
1794be5bea1cSJakub Kicinskidef print_wrapped_type(ri):
1795be5bea1cSJakub Kicinski    ri.cw.block_start(line=f"{type_name(ri, 'reply')}")
1796be5bea1cSJakub Kicinski    if ri.op_mode == 'dump':
1797be5bea1cSJakub Kicinski        ri.cw.p(f"{type_name(ri, 'reply')} *next;")
1798be5bea1cSJakub Kicinski    elif ri.op_mode == 'notify' or ri.op_mode == 'event':
1799be5bea1cSJakub Kicinski        ri.cw.p('__u16 family;')
1800be5bea1cSJakub Kicinski        ri.cw.p('__u8 cmd;')
180159d814f0SJakub Kicinski        ri.cw.p('struct ynl_ntf_base_type *next;')
1802be5bea1cSJakub Kicinski        ri.cw.p(f"void (*free)({type_name(ri, 'reply')} *ntf);")
1803be5bea1cSJakub Kicinski    ri.cw.p(f"{type_name(ri, 'reply', deref=True)} obj __attribute__ ((aligned (8)));")
1804be5bea1cSJakub Kicinski    ri.cw.block_end(line=';')
1805be5bea1cSJakub Kicinski    ri.cw.nl()
1806be5bea1cSJakub Kicinski    print_free_prototype(ri, 'reply')
1807be5bea1cSJakub Kicinski    ri.cw.nl()
1808be5bea1cSJakub Kicinski
1809be5bea1cSJakub Kicinski
1810be5bea1cSJakub Kicinskidef _free_type_members_iter(ri, struct):
1811be5bea1cSJakub Kicinski    for _, attr in struct.member_list():
1812be5bea1cSJakub Kicinski        if attr.free_needs_iter():
1813be5bea1cSJakub Kicinski            ri.cw.p('unsigned int i;')
1814be5bea1cSJakub Kicinski            ri.cw.nl()
1815be5bea1cSJakub Kicinski            break
1816be5bea1cSJakub Kicinski
1817be5bea1cSJakub Kicinski
1818be5bea1cSJakub Kicinskidef _free_type_members(ri, var, struct, ref=''):
1819be5bea1cSJakub Kicinski    for _, attr in struct.member_list():
1820be5bea1cSJakub Kicinski        attr.free(ri, var, ref)
1821be5bea1cSJakub Kicinski
1822be5bea1cSJakub Kicinski
1823be5bea1cSJakub Kicinskidef _free_type(ri, direction, struct):
1824be5bea1cSJakub Kicinski    var = free_arg_name(direction)
1825be5bea1cSJakub Kicinski
1826be5bea1cSJakub Kicinski    print_free_prototype(ri, direction, suffix='')
1827be5bea1cSJakub Kicinski    ri.cw.block_start()
1828be5bea1cSJakub Kicinski    _free_type_members_iter(ri, struct)
1829be5bea1cSJakub Kicinski    _free_type_members(ri, var, struct)
1830be5bea1cSJakub Kicinski    if direction:
1831be5bea1cSJakub Kicinski        ri.cw.p(f'free({var});')
1832be5bea1cSJakub Kicinski    ri.cw.block_end()
1833be5bea1cSJakub Kicinski    ri.cw.nl()
1834be5bea1cSJakub Kicinski
1835be5bea1cSJakub Kicinski
1836be5bea1cSJakub Kicinskidef free_rsp_nested(ri, struct):
1837be5bea1cSJakub Kicinski    _free_type(ri, "", struct)
1838be5bea1cSJakub Kicinski
1839be5bea1cSJakub Kicinski
1840be5bea1cSJakub Kicinskidef print_rsp_free(ri):
1841be5bea1cSJakub Kicinski    if 'reply' not in ri.op[ri.op_mode]:
1842be5bea1cSJakub Kicinski        return
1843be5bea1cSJakub Kicinski    _free_type(ri, 'reply', ri.struct['reply'])
1844be5bea1cSJakub Kicinski
1845be5bea1cSJakub Kicinski
1846be5bea1cSJakub Kicinskidef print_dump_type_free(ri):
1847be5bea1cSJakub Kicinski    sub_type = type_name(ri, 'reply')
1848be5bea1cSJakub Kicinski
1849be5bea1cSJakub Kicinski    print_free_prototype(ri, 'reply', suffix='')
1850be5bea1cSJakub Kicinski    ri.cw.block_start()
1851be5bea1cSJakub Kicinski    ri.cw.p(f"{sub_type} *next = rsp;")
1852be5bea1cSJakub Kicinski    ri.cw.nl()
1853dc0956c9SJakub Kicinski    ri.cw.block_start(line='while ((void *)next != YNL_LIST_END)')
1854be5bea1cSJakub Kicinski    _free_type_members_iter(ri, ri.struct['reply'])
1855be5bea1cSJakub Kicinski    ri.cw.p('rsp = next;')
1856be5bea1cSJakub Kicinski    ri.cw.p('next = rsp->next;')
1857be5bea1cSJakub Kicinski    ri.cw.nl()
1858be5bea1cSJakub Kicinski
1859be5bea1cSJakub Kicinski    _free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.')
1860be5bea1cSJakub Kicinski    ri.cw.p(f'free(rsp);')
1861be5bea1cSJakub Kicinski    ri.cw.block_end()
1862be5bea1cSJakub Kicinski    ri.cw.block_end()
1863be5bea1cSJakub Kicinski    ri.cw.nl()
1864be5bea1cSJakub Kicinski
1865be5bea1cSJakub Kicinski
1866be5bea1cSJakub Kicinskidef print_ntf_type_free(ri):
1867be5bea1cSJakub Kicinski    print_free_prototype(ri, 'reply', suffix='')
1868be5bea1cSJakub Kicinski    ri.cw.block_start()
1869be5bea1cSJakub Kicinski    _free_type_members_iter(ri, ri.struct['reply'])
1870be5bea1cSJakub Kicinski    _free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.')
1871be5bea1cSJakub Kicinski    ri.cw.p(f'free(rsp);')
1872be5bea1cSJakub Kicinski    ri.cw.block_end()
1873be5bea1cSJakub Kicinski    ri.cw.nl()
1874be5bea1cSJakub Kicinski
1875be5bea1cSJakub Kicinski
1876be5bea1cSJakub Kicinskidef print_req_policy_fwd(cw, struct, ri=None, terminate=True):
1877fa8ba350SJiri Pirko    if terminate and ri and policy_should_be_static(struct.family):
1878be5bea1cSJakub Kicinski        return
1879be5bea1cSJakub Kicinski
1880be5bea1cSJakub Kicinski    if terminate:
1881be5bea1cSJakub Kicinski        prefix = 'extern '
1882be5bea1cSJakub Kicinski    else:
1883fa8ba350SJiri Pirko        if ri and policy_should_be_static(struct.family):
1884be5bea1cSJakub Kicinski            prefix = 'static '
1885be5bea1cSJakub Kicinski        else:
1886be5bea1cSJakub Kicinski            prefix = ''
1887be5bea1cSJakub Kicinski
1888be5bea1cSJakub Kicinski    suffix = ';' if terminate else ' = {'
1889be5bea1cSJakub Kicinski
1890be5bea1cSJakub Kicinski    max_attr = struct.attr_max_val
1891be5bea1cSJakub Kicinski    if ri:
1892be5bea1cSJakub Kicinski        name = ri.op.render_name
1893be5bea1cSJakub Kicinski        if ri.op.dual_policy:
1894be5bea1cSJakub Kicinski            name += '_' + ri.op_mode
1895be5bea1cSJakub Kicinski    else:
1896be5bea1cSJakub Kicinski        name = struct.render_name
1897be5bea1cSJakub Kicinski    cw.p(f"{prefix}const struct nla_policy {name}_nl_policy[{max_attr.enum_name} + 1]{suffix}")
1898be5bea1cSJakub Kicinski
1899be5bea1cSJakub Kicinski
1900be5bea1cSJakub Kicinskidef print_req_policy(cw, struct, ri=None):
1901be5bea1cSJakub Kicinski    print_req_policy_fwd(cw, struct, ri=ri, terminate=False)
1902be5bea1cSJakub Kicinski    for _, arg in struct.member_list():
1903be5bea1cSJakub Kicinski        arg.attr_policy(cw)
1904be5bea1cSJakub Kicinski    cw.p("};")
1905cd3112ebSJakub Kicinski    cw.nl()
1906be5bea1cSJakub Kicinski
1907be5bea1cSJakub Kicinski
1908be5bea1cSJakub Kicinskidef kernel_can_gen_family_struct(family):
1909be5bea1cSJakub Kicinski    return family.proto == 'genetlink'
1910be5bea1cSJakub Kicinski
1911be5bea1cSJakub Kicinski
1912fa8ba350SJiri Pirkodef policy_should_be_static(family):
1913fa8ba350SJiri Pirko    return family.kernel_policy == 'split' or kernel_can_gen_family_struct(family)
1914fa8ba350SJiri Pirko
1915fa8ba350SJiri Pirko
1916be5bea1cSJakub Kicinskidef print_kernel_op_table_fwd(family, cw, terminate):
1917be5bea1cSJakub Kicinski    exported = not kernel_can_gen_family_struct(family)
1918be5bea1cSJakub Kicinski
1919be5bea1cSJakub Kicinski    if not terminate or exported:
1920be5bea1cSJakub Kicinski        cw.p(f"/* Ops table for {family.name} */")
1921be5bea1cSJakub Kicinski
1922be5bea1cSJakub Kicinski        pol_to_struct = {'global': 'genl_small_ops',
1923be5bea1cSJakub Kicinski                         'per-op': 'genl_ops',
1924be5bea1cSJakub Kicinski                         'split': 'genl_split_ops'}
1925be5bea1cSJakub Kicinski        struct_type = pol_to_struct[family.kernel_policy]
1926be5bea1cSJakub Kicinski
192756c874f7SJakub Kicinski        if not exported:
192856c874f7SJakub Kicinski            cnt = ""
192956c874f7SJakub Kicinski        elif family.kernel_policy == 'split':
1930be5bea1cSJakub Kicinski            cnt = 0
1931be5bea1cSJakub Kicinski            for op in family.ops.values():
1932be5bea1cSJakub Kicinski                if 'do' in op:
1933be5bea1cSJakub Kicinski                    cnt += 1
1934be5bea1cSJakub Kicinski                if 'dump' in op:
1935be5bea1cSJakub Kicinski                    cnt += 1
1936be5bea1cSJakub Kicinski        else:
1937be5bea1cSJakub Kicinski            cnt = len(family.ops)
1938be5bea1cSJakub Kicinski
1939be5bea1cSJakub Kicinski        qual = 'static const' if not exported else 'const'
1940be5bea1cSJakub Kicinski        line = f"{qual} struct {struct_type} {family.name}_nl_ops[{cnt}]"
1941be5bea1cSJakub Kicinski        if terminate:
1942be5bea1cSJakub Kicinski            cw.p(f"extern {line};")
1943be5bea1cSJakub Kicinski        else:
1944be5bea1cSJakub Kicinski            cw.block_start(line=line + ' =')
1945be5bea1cSJakub Kicinski
1946be5bea1cSJakub Kicinski    if not terminate:
1947be5bea1cSJakub Kicinski        return
1948be5bea1cSJakub Kicinski
1949be5bea1cSJakub Kicinski    cw.nl()
1950be5bea1cSJakub Kicinski    for name in family.hooks['pre']['do']['list']:
1951be5bea1cSJakub Kicinski        cw.write_func_prot('int', c_lower(name),
1952be5bea1cSJakub Kicinski                           ['const struct genl_split_ops *ops',
1953be5bea1cSJakub Kicinski                            'struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
1954be5bea1cSJakub Kicinski    for name in family.hooks['post']['do']['list']:
1955be5bea1cSJakub Kicinski        cw.write_func_prot('void', c_lower(name),
1956be5bea1cSJakub Kicinski                           ['const struct genl_split_ops *ops',
1957be5bea1cSJakub Kicinski                            'struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
1958be5bea1cSJakub Kicinski    for name in family.hooks['pre']['dump']['list']:
1959be5bea1cSJakub Kicinski        cw.write_func_prot('int', c_lower(name),
1960be5bea1cSJakub Kicinski                           ['struct netlink_callback *cb'], suffix=';')
1961be5bea1cSJakub Kicinski    for name in family.hooks['post']['dump']['list']:
1962be5bea1cSJakub Kicinski        cw.write_func_prot('int', c_lower(name),
1963be5bea1cSJakub Kicinski                           ['struct netlink_callback *cb'], suffix=';')
1964be5bea1cSJakub Kicinski
1965be5bea1cSJakub Kicinski    cw.nl()
1966be5bea1cSJakub Kicinski
1967be5bea1cSJakub Kicinski    for op_name, op in family.ops.items():
1968be5bea1cSJakub Kicinski        if op.is_async:
1969be5bea1cSJakub Kicinski            continue
1970be5bea1cSJakub Kicinski
1971be5bea1cSJakub Kicinski        if 'do' in op:
1972be5bea1cSJakub Kicinski            name = c_lower(f"{family.name}-nl-{op_name}-doit")
1973be5bea1cSJakub Kicinski            cw.write_func_prot('int', name,
1974be5bea1cSJakub Kicinski                               ['struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
1975be5bea1cSJakub Kicinski
1976be5bea1cSJakub Kicinski        if 'dump' in op:
1977be5bea1cSJakub Kicinski            name = c_lower(f"{family.name}-nl-{op_name}-dumpit")
1978be5bea1cSJakub Kicinski            cw.write_func_prot('int', name,
1979be5bea1cSJakub Kicinski                               ['struct sk_buff *skb', 'struct netlink_callback *cb'], suffix=';')
1980be5bea1cSJakub Kicinski    cw.nl()
1981be5bea1cSJakub Kicinski
1982be5bea1cSJakub Kicinski
1983be5bea1cSJakub Kicinskidef print_kernel_op_table_hdr(family, cw):
1984be5bea1cSJakub Kicinski    print_kernel_op_table_fwd(family, cw, terminate=True)
1985be5bea1cSJakub Kicinski
1986be5bea1cSJakub Kicinski
1987be5bea1cSJakub Kicinskidef print_kernel_op_table(family, cw):
1988be5bea1cSJakub Kicinski    print_kernel_op_table_fwd(family, cw, terminate=False)
1989be5bea1cSJakub Kicinski    if family.kernel_policy == 'global' or family.kernel_policy == 'per-op':
1990be5bea1cSJakub Kicinski        for op_name, op in family.ops.items():
1991be5bea1cSJakub Kicinski            if op.is_async:
1992be5bea1cSJakub Kicinski                continue
1993be5bea1cSJakub Kicinski
1994be5bea1cSJakub Kicinski            cw.block_start()
1995be5bea1cSJakub Kicinski            members = [('cmd', op.enum_name)]
1996be5bea1cSJakub Kicinski            if 'dont-validate' in op:
1997be5bea1cSJakub Kicinski                members.append(('validate',
1998be5bea1cSJakub Kicinski                                ' | '.join([c_upper('genl-dont-validate-' + x)
1999be5bea1cSJakub Kicinski                                            for x in op['dont-validate']])), )
2000be5bea1cSJakub Kicinski            for op_mode in ['do', 'dump']:
2001be5bea1cSJakub Kicinski                if op_mode in op:
2002be5bea1cSJakub Kicinski                    name = c_lower(f"{family.name}-nl-{op_name}-{op_mode}it")
2003be5bea1cSJakub Kicinski                    members.append((op_mode + 'it', name))
2004be5bea1cSJakub Kicinski            if family.kernel_policy == 'per-op':
2005be5bea1cSJakub Kicinski                struct = Struct(family, op['attribute-set'],
2006be5bea1cSJakub Kicinski                                type_list=op['do']['request']['attributes'])
2007be5bea1cSJakub Kicinski
2008be5bea1cSJakub Kicinski                name = c_lower(f"{family.name}-{op_name}-nl-policy")
2009be5bea1cSJakub Kicinski                members.append(('policy', name))
2010be5bea1cSJakub Kicinski                members.append(('maxattr', struct.attr_max_val.enum_name))
2011be5bea1cSJakub Kicinski            if 'flags' in op:
2012be5bea1cSJakub Kicinski                members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in op['flags']])))
2013be5bea1cSJakub Kicinski            cw.write_struct_init(members)
2014be5bea1cSJakub Kicinski            cw.block_end(line=',')
2015be5bea1cSJakub Kicinski    elif family.kernel_policy == 'split':
2016be5bea1cSJakub Kicinski        cb_names = {'do':   {'pre': 'pre_doit', 'post': 'post_doit'},
2017be5bea1cSJakub Kicinski                    'dump': {'pre': 'start', 'post': 'done'}}
2018be5bea1cSJakub Kicinski
2019be5bea1cSJakub Kicinski        for op_name, op in family.ops.items():
2020be5bea1cSJakub Kicinski            for op_mode in ['do', 'dump']:
2021be5bea1cSJakub Kicinski                if op.is_async or op_mode not in op:
2022be5bea1cSJakub Kicinski                    continue
2023be5bea1cSJakub Kicinski
2024be5bea1cSJakub Kicinski                cw.block_start()
2025be5bea1cSJakub Kicinski                members = [('cmd', op.enum_name)]
2026be5bea1cSJakub Kicinski                if 'dont-validate' in op:
2027dc7b81a8SJiri Pirko                    dont_validate = []
2028dc7b81a8SJiri Pirko                    for x in op['dont-validate']:
2029dc7b81a8SJiri Pirko                        if op_mode == 'do' and x in ['dump', 'dump-strict']:
2030dc7b81a8SJiri Pirko                            continue
2031dc7b81a8SJiri Pirko                        if op_mode == "dump" and x == 'strict':
2032dc7b81a8SJiri Pirko                            continue
2033dc7b81a8SJiri Pirko                        dont_validate.append(x)
2034dc7b81a8SJiri Pirko
20352c0e9f38SJiri Pirko                    if dont_validate:
2036be5bea1cSJakub Kicinski                        members.append(('validate',
2037be5bea1cSJakub Kicinski                                        ' | '.join([c_upper('genl-dont-validate-' + x)
2038dc7b81a8SJiri Pirko                                                    for x in dont_validate])), )
2039be5bea1cSJakub Kicinski                name = c_lower(f"{family.name}-nl-{op_name}-{op_mode}it")
2040be5bea1cSJakub Kicinski                if 'pre' in op[op_mode]:
2041be5bea1cSJakub Kicinski                    members.append((cb_names[op_mode]['pre'], c_lower(op[op_mode]['pre'])))
2042be5bea1cSJakub Kicinski                members.append((op_mode + 'it', name))
2043be5bea1cSJakub Kicinski                if 'post' in op[op_mode]:
2044be5bea1cSJakub Kicinski                    members.append((cb_names[op_mode]['post'], c_lower(op[op_mode]['post'])))
2045be5bea1cSJakub Kicinski                if 'request' in op[op_mode]:
2046be5bea1cSJakub Kicinski                    struct = Struct(family, op['attribute-set'],
2047be5bea1cSJakub Kicinski                                    type_list=op[op_mode]['request']['attributes'])
2048be5bea1cSJakub Kicinski
2049be5bea1cSJakub Kicinski                    if op.dual_policy:
2050be5bea1cSJakub Kicinski                        name = c_lower(f"{family.name}-{op_name}-{op_mode}-nl-policy")
2051be5bea1cSJakub Kicinski                    else:
2052be5bea1cSJakub Kicinski                        name = c_lower(f"{family.name}-{op_name}-nl-policy")
2053be5bea1cSJakub Kicinski                    members.append(('policy', name))
2054be5bea1cSJakub Kicinski                    members.append(('maxattr', struct.attr_max_val.enum_name))
2055be5bea1cSJakub Kicinski                flags = (op['flags'] if 'flags' in op else []) + ['cmd-cap-' + op_mode]
2056be5bea1cSJakub Kicinski                members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in flags])))
2057be5bea1cSJakub Kicinski                cw.write_struct_init(members)
2058be5bea1cSJakub Kicinski                cw.block_end(line=',')
2059be5bea1cSJakub Kicinski
2060be5bea1cSJakub Kicinski    cw.block_end(line=';')
2061be5bea1cSJakub Kicinski    cw.nl()
2062be5bea1cSJakub Kicinski
2063be5bea1cSJakub Kicinski
2064be5bea1cSJakub Kicinskidef print_kernel_mcgrp_hdr(family, cw):
2065be5bea1cSJakub Kicinski    if not family.mcgrps['list']:
2066be5bea1cSJakub Kicinski        return
2067be5bea1cSJakub Kicinski
2068be5bea1cSJakub Kicinski    cw.block_start('enum')
2069be5bea1cSJakub Kicinski    for grp in family.mcgrps['list']:
2070be5bea1cSJakub Kicinski        grp_id = c_upper(f"{family.name}-nlgrp-{grp['name']},")
2071be5bea1cSJakub Kicinski        cw.p(grp_id)
2072be5bea1cSJakub Kicinski    cw.block_end(';')
2073be5bea1cSJakub Kicinski    cw.nl()
2074be5bea1cSJakub Kicinski
2075be5bea1cSJakub Kicinski
2076be5bea1cSJakub Kicinskidef print_kernel_mcgrp_src(family, cw):
2077be5bea1cSJakub Kicinski    if not family.mcgrps['list']:
2078be5bea1cSJakub Kicinski        return
2079be5bea1cSJakub Kicinski
2080be5bea1cSJakub Kicinski    cw.block_start('static const struct genl_multicast_group ' + family.name + '_nl_mcgrps[] =')
2081be5bea1cSJakub Kicinski    for grp in family.mcgrps['list']:
2082be5bea1cSJakub Kicinski        name = grp['name']
2083be5bea1cSJakub Kicinski        grp_id = c_upper(f"{family.name}-nlgrp-{name}")
2084be5bea1cSJakub Kicinski        cw.p('[' + grp_id + '] = { "' + name + '", },')
2085be5bea1cSJakub Kicinski    cw.block_end(';')
2086be5bea1cSJakub Kicinski    cw.nl()
2087be5bea1cSJakub Kicinski
2088be5bea1cSJakub Kicinski
2089be5bea1cSJakub Kicinskidef print_kernel_family_struct_hdr(family, cw):
2090be5bea1cSJakub Kicinski    if not kernel_can_gen_family_struct(family):
2091be5bea1cSJakub Kicinski        return
2092be5bea1cSJakub Kicinski
2093be5bea1cSJakub Kicinski    cw.p(f"extern struct genl_family {family.name}_nl_family;")
2094be5bea1cSJakub Kicinski    cw.nl()
2095be5bea1cSJakub Kicinski
2096be5bea1cSJakub Kicinski
2097be5bea1cSJakub Kicinskidef print_kernel_family_struct_src(family, cw):
2098be5bea1cSJakub Kicinski    if not kernel_can_gen_family_struct(family):
2099be5bea1cSJakub Kicinski        return
2100be5bea1cSJakub Kicinski
2101be5bea1cSJakub Kicinski    cw.block_start(f"struct genl_family {family.name}_nl_family __ro_after_init =")
2102be5bea1cSJakub Kicinski    cw.p('.name\t\t= ' + family.fam_key + ',')
2103be5bea1cSJakub Kicinski    cw.p('.version\t= ' + family.ver_key + ',')
2104be5bea1cSJakub Kicinski    cw.p('.netnsok\t= true,')
2105be5bea1cSJakub Kicinski    cw.p('.parallel_ops\t= true,')
2106be5bea1cSJakub Kicinski    cw.p('.module\t\t= THIS_MODULE,')
2107be5bea1cSJakub Kicinski    if family.kernel_policy == 'per-op':
2108be5bea1cSJakub Kicinski        cw.p(f'.ops\t\t= {family.name}_nl_ops,')
2109be5bea1cSJakub Kicinski        cw.p(f'.n_ops\t\t= ARRAY_SIZE({family.name}_nl_ops),')
2110be5bea1cSJakub Kicinski    elif family.kernel_policy == 'split':
2111be5bea1cSJakub Kicinski        cw.p(f'.split_ops\t= {family.name}_nl_ops,')
2112be5bea1cSJakub Kicinski        cw.p(f'.n_split_ops\t= ARRAY_SIZE({family.name}_nl_ops),')
2113be5bea1cSJakub Kicinski    if family.mcgrps['list']:
2114be5bea1cSJakub Kicinski        cw.p(f'.mcgrps\t\t= {family.name}_nl_mcgrps,')
2115be5bea1cSJakub Kicinski        cw.p(f'.n_mcgrps\t= ARRAY_SIZE({family.name}_nl_mcgrps),')
2116be5bea1cSJakub Kicinski    cw.block_end(';')
2117be5bea1cSJakub Kicinski
2118be5bea1cSJakub Kicinski
2119be5bea1cSJakub Kicinskidef uapi_enum_start(family, cw, obj, ckey='', enum_name='enum-name'):
2120be5bea1cSJakub Kicinski    start_line = 'enum'
2121be5bea1cSJakub Kicinski    if enum_name in obj:
2122be5bea1cSJakub Kicinski        if obj[enum_name]:
2123be5bea1cSJakub Kicinski            start_line = 'enum ' + c_lower(obj[enum_name])
2124be5bea1cSJakub Kicinski    elif ckey and ckey in obj:
2125be5bea1cSJakub Kicinski        start_line = 'enum ' + family.name + '_' + c_lower(obj[ckey])
2126be5bea1cSJakub Kicinski    cw.block_start(line=start_line)
2127be5bea1cSJakub Kicinski
2128be5bea1cSJakub Kicinski
2129be5bea1cSJakub Kicinskidef render_uapi(family, cw):
2130be5bea1cSJakub Kicinski    hdr_prot = f"_UAPI_LINUX_{family.name.upper()}_H"
2131be5bea1cSJakub Kicinski    cw.p('#ifndef ' + hdr_prot)
2132be5bea1cSJakub Kicinski    cw.p('#define ' + hdr_prot)
2133be5bea1cSJakub Kicinski    cw.nl()
2134be5bea1cSJakub Kicinski
2135be5bea1cSJakub Kicinski    defines = [(family.fam_key, family["name"]),
2136be5bea1cSJakub Kicinski               (family.ver_key, family.get('version', 1))]
2137be5bea1cSJakub Kicinski    cw.writes_defines(defines)
2138be5bea1cSJakub Kicinski    cw.nl()
2139be5bea1cSJakub Kicinski
2140be5bea1cSJakub Kicinski    defines = []
2141be5bea1cSJakub Kicinski    for const in family['definitions']:
2142be5bea1cSJakub Kicinski        if const['type'] != 'const':
2143be5bea1cSJakub Kicinski            cw.writes_defines(defines)
2144be5bea1cSJakub Kicinski            defines = []
2145be5bea1cSJakub Kicinski            cw.nl()
2146be5bea1cSJakub Kicinski
214766fa34b9SJakub Kicinski        # Write kdoc for enum and flags (one day maybe also structs)
214866fa34b9SJakub Kicinski        if const['type'] == 'enum' or const['type'] == 'flags':
2149be5bea1cSJakub Kicinski            enum = family.consts[const['name']]
2150be5bea1cSJakub Kicinski
2151be5bea1cSJakub Kicinski            if enum.has_doc():
2152be5bea1cSJakub Kicinski                cw.p('/**')
2153be5bea1cSJakub Kicinski                doc = ''
2154be5bea1cSJakub Kicinski                if 'doc' in enum:
2155be5bea1cSJakub Kicinski                    doc = ' - ' + enum['doc']
2156be5bea1cSJakub Kicinski                cw.write_doc_line(enum.enum_name + doc)
21576517a60bSJakub Kicinski                for entry in enum.entries.values():
2158be5bea1cSJakub Kicinski                    if entry.has_doc():
2159be5bea1cSJakub Kicinski                        doc = '@' + entry.c_name + ': ' + entry['doc']
2160be5bea1cSJakub Kicinski                        cw.write_doc_line(doc)
2161be5bea1cSJakub Kicinski                cw.p(' */')
2162be5bea1cSJakub Kicinski
2163be5bea1cSJakub Kicinski            uapi_enum_start(family, cw, const, 'name')
2164be5bea1cSJakub Kicinski            name_pfx = const.get('name-prefix', f"{family.name}-{const['name']}-")
21656517a60bSJakub Kicinski            for entry in enum.entries.values():
2166be5bea1cSJakub Kicinski                suffix = ','
216766fa34b9SJakub Kicinski                if entry.value_change:
216866fa34b9SJakub Kicinski                    suffix = f" = {entry.user_value()}" + suffix
2169be5bea1cSJakub Kicinski                cw.p(entry.c_name + suffix)
2170be5bea1cSJakub Kicinski
2171be5bea1cSJakub Kicinski            if const.get('render-max', False):
2172be5bea1cSJakub Kicinski                cw.nl()
217337844828SStanislav Fomichev                cw.p('/* private: */')
21748f76a4f8SLorenzo Bianconi                if const['type'] == 'flags':
21758f76a4f8SLorenzo Bianconi                    max_name = c_upper(name_pfx + 'mask')
21768f76a4f8SLorenzo Bianconi                    max_val = f' = {enum.get_mask()},'
21778f76a4f8SLorenzo Bianconi                    cw.p(max_name + max_val)
21788f76a4f8SLorenzo Bianconi                else:
2179be5bea1cSJakub Kicinski                    max_name = c_upper(name_pfx + 'max')
2180be5bea1cSJakub Kicinski                    cw.p('__' + max_name + ',')
2181be5bea1cSJakub Kicinski                    cw.p(max_name + ' = (__' + max_name + ' - 1)')
2182be5bea1cSJakub Kicinski            cw.block_end(line=';')
2183be5bea1cSJakub Kicinski            cw.nl()
2184be5bea1cSJakub Kicinski        elif const['type'] == 'const':
2185be5bea1cSJakub Kicinski            defines.append([c_upper(family.get('c-define-name',
2186be5bea1cSJakub Kicinski                                               f"{family.name}-{const['name']}")),
2187be5bea1cSJakub Kicinski                            const['value']])
2188be5bea1cSJakub Kicinski
2189be5bea1cSJakub Kicinski    if defines:
2190be5bea1cSJakub Kicinski        cw.writes_defines(defines)
2191be5bea1cSJakub Kicinski        cw.nl()
2192be5bea1cSJakub Kicinski
2193be5bea1cSJakub Kicinski    max_by_define = family.get('max-by-define', False)
2194be5bea1cSJakub Kicinski
219530a5c6c8SJakub Kicinski    for _, attr_set in family.attr_sets.items():
2196be5bea1cSJakub Kicinski        if attr_set.subset_of:
2197be5bea1cSJakub Kicinski            continue
2198be5bea1cSJakub Kicinski
2199be5bea1cSJakub Kicinski        cnt_name = c_upper(family.get('attr-cnt-name', f"__{attr_set.name_prefix}MAX"))
2200be5bea1cSJakub Kicinski        max_value = f"({cnt_name} - 1)"
2201be5bea1cSJakub Kicinski
2202be5bea1cSJakub Kicinski        val = 0
2203be5bea1cSJakub Kicinski        uapi_enum_start(family, cw, attr_set.yaml, 'enum-name')
2204be5bea1cSJakub Kicinski        for _, attr in attr_set.items():
2205be5bea1cSJakub Kicinski            suffix = ','
220630a5c6c8SJakub Kicinski            if attr.value != val:
220730a5c6c8SJakub Kicinski                suffix = f" = {attr.value},"
220830a5c6c8SJakub Kicinski                val = attr.value
2209be5bea1cSJakub Kicinski            val += 1
2210be5bea1cSJakub Kicinski            cw.p(attr.enum_name + suffix)
2211be5bea1cSJakub Kicinski        cw.nl()
2212be5bea1cSJakub Kicinski        cw.p(cnt_name + ('' if max_by_define else ','))
2213be5bea1cSJakub Kicinski        if not max_by_define:
2214be5bea1cSJakub Kicinski            cw.p(f"{attr_set.max_name} = {max_value}")
2215be5bea1cSJakub Kicinski        cw.block_end(line=';')
2216be5bea1cSJakub Kicinski        if max_by_define:
2217be5bea1cSJakub Kicinski            cw.p(f"#define {attr_set.max_name} {max_value}")
2218be5bea1cSJakub Kicinski        cw.nl()
2219be5bea1cSJakub Kicinski
2220be5bea1cSJakub Kicinski    # Commands
2221be5bea1cSJakub Kicinski    separate_ntf = 'async-prefix' in family['operations']
2222be5bea1cSJakub Kicinski
2223be5bea1cSJakub Kicinski    max_name = c_upper(family.get('cmd-max-name', f"{family.op_prefix}MAX"))
2224be5bea1cSJakub Kicinski    cnt_name = c_upper(family.get('cmd-cnt-name', f"__{family.op_prefix}MAX"))
2225be5bea1cSJakub Kicinski    max_value = f"({cnt_name} - 1)"
2226be5bea1cSJakub Kicinski
2227be5bea1cSJakub Kicinski    uapi_enum_start(family, cw, family['operations'], 'enum-name')
2228ad4fafcdSJakub Kicinski    val = 0
222930a5c6c8SJakub Kicinski    for op in family.msgs.values():
2230be5bea1cSJakub Kicinski        if separate_ntf and ('notify' in op or 'event' in op):
2231be5bea1cSJakub Kicinski            continue
2232be5bea1cSJakub Kicinski
2233be5bea1cSJakub Kicinski        suffix = ','
2234ad4fafcdSJakub Kicinski        if op.value != val:
2235ad4fafcdSJakub Kicinski            suffix = f" = {op.value},"
2236ad4fafcdSJakub Kicinski            val = op.value
2237be5bea1cSJakub Kicinski        cw.p(op.enum_name + suffix)
2238ad4fafcdSJakub Kicinski        val += 1
2239be5bea1cSJakub Kicinski    cw.nl()
2240be5bea1cSJakub Kicinski    cw.p(cnt_name + ('' if max_by_define else ','))
2241be5bea1cSJakub Kicinski    if not max_by_define:
2242be5bea1cSJakub Kicinski        cw.p(f"{max_name} = {max_value}")
2243be5bea1cSJakub Kicinski    cw.block_end(line=';')
2244be5bea1cSJakub Kicinski    if max_by_define:
2245be5bea1cSJakub Kicinski        cw.p(f"#define {max_name} {max_value}")
2246be5bea1cSJakub Kicinski    cw.nl()
2247be5bea1cSJakub Kicinski
2248be5bea1cSJakub Kicinski    if separate_ntf:
2249be5bea1cSJakub Kicinski        uapi_enum_start(family, cw, family['operations'], enum_name='async-enum')
225030a5c6c8SJakub Kicinski        for op in family.msgs.values():
2251be5bea1cSJakub Kicinski            if separate_ntf and not ('notify' in op or 'event' in op):
2252be5bea1cSJakub Kicinski                continue
2253be5bea1cSJakub Kicinski
2254be5bea1cSJakub Kicinski            suffix = ','
2255be5bea1cSJakub Kicinski            if 'value' in op:
2256be5bea1cSJakub Kicinski                suffix = f" = {op['value']},"
2257be5bea1cSJakub Kicinski            cw.p(op.enum_name + suffix)
2258be5bea1cSJakub Kicinski        cw.block_end(line=';')
2259be5bea1cSJakub Kicinski        cw.nl()
2260be5bea1cSJakub Kicinski
2261be5bea1cSJakub Kicinski    # Multicast
2262be5bea1cSJakub Kicinski    defines = []
2263be5bea1cSJakub Kicinski    for grp in family.mcgrps['list']:
2264be5bea1cSJakub Kicinski        name = grp['name']
2265be5bea1cSJakub Kicinski        defines.append([c_upper(grp.get('c-define-name', f"{family.name}-mcgrp-{name}")),
2266be5bea1cSJakub Kicinski                        f'{name}'])
2267be5bea1cSJakub Kicinski    cw.nl()
2268be5bea1cSJakub Kicinski    if defines:
2269be5bea1cSJakub Kicinski        cw.writes_defines(defines)
2270be5bea1cSJakub Kicinski        cw.nl()
2271be5bea1cSJakub Kicinski
2272be5bea1cSJakub Kicinski    cw.p(f'#endif /* {hdr_prot} */')
2273be5bea1cSJakub Kicinski
2274be5bea1cSJakub Kicinski
227559d814f0SJakub Kicinskidef _render_user_ntf_entry(ri, op):
227659d814f0SJakub Kicinski    ri.cw.block_start(line=f"[{op.enum_name}] = ")
227759d814f0SJakub Kicinski    ri.cw.p(f".alloc_sz\t= sizeof({type_name(ri, 'event')}),")
227859d814f0SJakub Kicinski    ri.cw.p(f".cb\t\t= {op_prefix(ri, 'reply', deref=True)}_parse,")
227959d814f0SJakub Kicinski    ri.cw.p(f".policy\t\t= &{ri.struct['reply'].render_name}_nest,")
228059d814f0SJakub Kicinski    ri.cw.p(f".free\t\t= (void *){op_prefix(ri, 'notify')}_free,")
228159d814f0SJakub Kicinski    ri.cw.block_end(line=',')
228259d814f0SJakub Kicinski
228359d814f0SJakub Kicinski
22848cb6afb3SJakub Kicinskidef render_user_family(family, cw, prototype):
22858cb6afb3SJakub Kicinski    symbol = f'const struct ynl_family ynl_{family.c_name}_family'
22868cb6afb3SJakub Kicinski    if prototype:
22878cb6afb3SJakub Kicinski        cw.p(f'extern {symbol};')
228859d814f0SJakub Kicinski        return
228959d814f0SJakub Kicinski
2290ced15688SJakub Kicinski    if family.ntfs:
229159d814f0SJakub Kicinski        cw.block_start(line=f"static const struct ynl_ntf_info {family['name']}_ntf_info[] = ")
2292ced15688SJakub Kicinski        for ntf_op_name, ntf_op in family.ntfs.items():
22936da3424fSJakub Kicinski            if 'notify' in ntf_op:
2294ced15688SJakub Kicinski                op = family.ops[ntf_op['notify']]
22956f96ec73SJakub Kicinski                ri = RenderInfo(cw, family, "user", op, "notify")
22966da3424fSJakub Kicinski            elif 'event' in ntf_op:
22976f96ec73SJakub Kicinski                ri = RenderInfo(cw, family, "user", ntf_op, "event")
22986da3424fSJakub Kicinski            else:
22996da3424fSJakub Kicinski                raise Exception('Invalid notification ' + ntf_op_name)
2300ced15688SJakub Kicinski            _render_user_ntf_entry(ri, ntf_op)
230159d814f0SJakub Kicinski        for op_name, op in family.ops.items():
230259d814f0SJakub Kicinski            if 'event' not in op:
230359d814f0SJakub Kicinski                continue
23046f96ec73SJakub Kicinski            ri = RenderInfo(cw, family, "user", op, "event")
230559d814f0SJakub Kicinski            _render_user_ntf_entry(ri, op)
230659d814f0SJakub Kicinski        cw.block_end(line=";")
230759d814f0SJakub Kicinski        cw.nl()
230859d814f0SJakub Kicinski
23098cb6afb3SJakub Kicinski    cw.block_start(f'{symbol} = ')
231059d814f0SJakub Kicinski    cw.p(f'.name\t\t= "{family.name}",')
2311ced15688SJakub Kicinski    if family.ntfs:
231259d814f0SJakub Kicinski        cw.p(f".ntf_info\t= {family['name']}_ntf_info,")
231359d814f0SJakub Kicinski        cw.p(f".ntf_info_size\t= MNL_ARRAY_SIZE({family['name']}_ntf_info),")
23148cb6afb3SJakub Kicinski    cw.block_end(line=';')
23158cb6afb3SJakub Kicinski
23168cb6afb3SJakub Kicinski
2317be5bea1cSJakub Kicinskidef find_kernel_root(full_path):
2318be5bea1cSJakub Kicinski    sub_path = ''
2319be5bea1cSJakub Kicinski    while True:
2320be5bea1cSJakub Kicinski        sub_path = os.path.join(os.path.basename(full_path), sub_path)
2321be5bea1cSJakub Kicinski        full_path = os.path.dirname(full_path)
2322be5bea1cSJakub Kicinski        maintainers = os.path.join(full_path, "MAINTAINERS")
2323be5bea1cSJakub Kicinski        if os.path.exists(maintainers):
2324be5bea1cSJakub Kicinski            return full_path, sub_path[:-1]
2325be5bea1cSJakub Kicinski
2326be5bea1cSJakub Kicinski
2327be5bea1cSJakub Kicinskidef main():
2328be5bea1cSJakub Kicinski    parser = argparse.ArgumentParser(description='Netlink simple parsing generator')
2329be5bea1cSJakub Kicinski    parser.add_argument('--mode', dest='mode', type=str, required=True)
2330be5bea1cSJakub Kicinski    parser.add_argument('--spec', dest='spec', type=str, required=True)
2331be5bea1cSJakub Kicinski    parser.add_argument('--header', dest='header', action='store_true', default=None)
2332be5bea1cSJakub Kicinski    parser.add_argument('--source', dest='header', action='store_false')
2333be5bea1cSJakub Kicinski    parser.add_argument('--user-header', nargs='+', default=[])
2334008bcd68SJakub Kicinski    parser.add_argument('--exclude-op', action='append', default=[])
2335a02430c0SJakub Kicinski    parser.add_argument('-o', dest='out_file', type=str, default=None)
2336be5bea1cSJakub Kicinski    args = parser.parse_args()
2337be5bea1cSJakub Kicinski
2338be5bea1cSJakub Kicinski    if args.header is None:
2339be5bea1cSJakub Kicinski        parser.error("--header or --source is required")
2340be5bea1cSJakub Kicinski
2341008bcd68SJakub Kicinski    exclude_ops = [re.compile(expr) for expr in args.exclude_op]
2342008bcd68SJakub Kicinski
2343be5bea1cSJakub Kicinski    try:
2344008bcd68SJakub Kicinski        parsed = Family(args.spec, exclude_ops)
2345cfab77c0SJakub Kicinski        if parsed.license != '((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)':
2346cfab77c0SJakub Kicinski            print('Spec license:', parsed.license)
2347cfab77c0SJakub Kicinski            print('License must be: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)')
2348cfab77c0SJakub Kicinski            os.sys.exit(1)
2349be5bea1cSJakub Kicinski    except yaml.YAMLError as exc:
2350be5bea1cSJakub Kicinski        print(exc)
2351be5bea1cSJakub Kicinski        os.sys.exit(1)
2352be5bea1cSJakub Kicinski        return
2353be5bea1cSJakub Kicinski
2354ff6db4b5SJakub Kicinski    supported_models = ['unified']
2355eab7be68SJiri Pirko    if args.mode in ['user', 'kernel']:
2356ff6db4b5SJakub Kicinski        supported_models += ['directional']
2357ff6db4b5SJakub Kicinski    if parsed.msg_id_model not in supported_models:
2358ff6db4b5SJakub Kicinski        print(f'Message enum-model {parsed.msg_id_model} not supported for {args.mode} generation')
2359ff6db4b5SJakub Kicinski        os.sys.exit(1)
2360ff6db4b5SJakub Kicinski
2361a02430c0SJakub Kicinski    cw = CodeWriter(BaseNlLib(), args.out_file)
2362be5bea1cSJakub Kicinski
2363be5bea1cSJakub Kicinski    _, spec_kernel = find_kernel_root(args.spec)
2364cfab77c0SJakub Kicinski    if args.mode == 'uapi' or args.header:
2365cfab77c0SJakub Kicinski        cw.p(f'/* SPDX-License-Identifier: {parsed.license} */')
2366be5bea1cSJakub Kicinski    else:
2367cfab77c0SJakub Kicinski        cw.p(f'// SPDX-License-Identifier: {parsed.license}')
2368be5bea1cSJakub Kicinski    cw.p("/* Do not edit directly, auto-generated from: */")
2369be5bea1cSJakub Kicinski    cw.p(f"/*\t{spec_kernel} */")
2370be5bea1cSJakub Kicinski    cw.p(f"/* YNL-GEN {args.mode} {'header' if args.header else 'source'} */")
237133eedb00SJakub Kicinski    if args.exclude_op or args.user_header:
237233eedb00SJakub Kicinski        line = ''
237333eedb00SJakub Kicinski        line += ' --user-header '.join([''] + args.user_header)
237433eedb00SJakub Kicinski        line += ' --exclude-op '.join([''] + args.exclude_op)
237533eedb00SJakub Kicinski        cw.p(f'/* YNL-ARG{line} */')
2376be5bea1cSJakub Kicinski    cw.nl()
2377be5bea1cSJakub Kicinski
2378be5bea1cSJakub Kicinski    if args.mode == 'uapi':
2379be5bea1cSJakub Kicinski        render_uapi(parsed, cw)
2380be5bea1cSJakub Kicinski        return
2381be5bea1cSJakub Kicinski
2382be5bea1cSJakub Kicinski    hdr_prot = f"_LINUX_{parsed.name.upper()}_GEN_H"
2383be5bea1cSJakub Kicinski    if args.header:
2384be5bea1cSJakub Kicinski        cw.p('#ifndef ' + hdr_prot)
2385be5bea1cSJakub Kicinski        cw.p('#define ' + hdr_prot)
2386be5bea1cSJakub Kicinski        cw.nl()
2387be5bea1cSJakub Kicinski
2388be5bea1cSJakub Kicinski    if args.mode == 'kernel':
2389be5bea1cSJakub Kicinski        cw.p('#include <net/netlink.h>')
2390be5bea1cSJakub Kicinski        cw.p('#include <net/genetlink.h>')
2391be5bea1cSJakub Kicinski        cw.nl()
2392be5bea1cSJakub Kicinski        if not args.header:
2393be5bea1cSJakub Kicinski            if args.out_file:
2394be5bea1cSJakub Kicinski                cw.p(f'#include "{os.path.basename(args.out_file[:-2])}.h"')
2395be5bea1cSJakub Kicinski            cw.nl()
23969b66ee06SJakub Kicinski        headers = ['uapi/' + parsed.uapi_header]
23979b66ee06SJakub Kicinski    else:
239891dfaef2SJakub Kicinski        cw.p('#include <stdlib.h>')
239991dfaef2SJakub Kicinski        cw.p('#include <string.h>')
240030b5c720SJakub Kicinski        if args.header:
240191dfaef2SJakub Kicinski            cw.p('#include <linux/types.h>')
240291dfaef2SJakub Kicinski        else:
240391dfaef2SJakub Kicinski            cw.p(f'#include "{parsed.name}-user.h"')
240491dfaef2SJakub Kicinski            cw.p('#include "ynl.h"')
2405be5bea1cSJakub Kicinski        headers = [parsed.uapi_header]
2406be5bea1cSJakub Kicinski    for definition in parsed['definitions']:
2407be5bea1cSJakub Kicinski        if 'header' in definition:
2408be5bea1cSJakub Kicinski            headers.append(definition['header'])
2409be5bea1cSJakub Kicinski    for one in headers:
2410be5bea1cSJakub Kicinski        cw.p(f"#include <{one}>")
2411be5bea1cSJakub Kicinski    cw.nl()
2412be5bea1cSJakub Kicinski
2413be5bea1cSJakub Kicinski    if args.mode == "user":
2414be5bea1cSJakub Kicinski        if not args.header:
2415be5bea1cSJakub Kicinski            cw.p("#include <libmnl/libmnl.h>")
2416be5bea1cSJakub Kicinski            cw.p("#include <linux/genetlink.h>")
2417be5bea1cSJakub Kicinski            cw.nl()
2418be5bea1cSJakub Kicinski            for one in args.user_header:
2419be5bea1cSJakub Kicinski                cw.p(f'#include "{one}"')
2420be5bea1cSJakub Kicinski        else:
2421be5bea1cSJakub Kicinski            cw.p('struct ynl_sock;')
2422be5bea1cSJakub Kicinski            cw.nl()
24238cb6afb3SJakub Kicinski            render_user_family(parsed, cw, True)
24248cb6afb3SJakub Kicinski        cw.nl()
2425be5bea1cSJakub Kicinski
2426be5bea1cSJakub Kicinski    if args.mode == "kernel":
2427be5bea1cSJakub Kicinski        if args.header:
2428be5bea1cSJakub Kicinski            for _, struct in sorted(parsed.pure_nested_structs.items()):
2429be5bea1cSJakub Kicinski                if struct.request:
2430be5bea1cSJakub Kicinski                    cw.p('/* Common nested types */')
2431be5bea1cSJakub Kicinski                    break
2432be5bea1cSJakub Kicinski            for attr_set, struct in sorted(parsed.pure_nested_structs.items()):
2433be5bea1cSJakub Kicinski                if struct.request:
2434be5bea1cSJakub Kicinski                    print_req_policy_fwd(cw, struct)
2435be5bea1cSJakub Kicinski            cw.nl()
2436be5bea1cSJakub Kicinski
2437be5bea1cSJakub Kicinski            if parsed.kernel_policy == 'global':
2438be5bea1cSJakub Kicinski                cw.p(f"/* Global operation policy for {parsed.name} */")
2439be5bea1cSJakub Kicinski
2440be5bea1cSJakub Kicinski                struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy)
2441be5bea1cSJakub Kicinski                print_req_policy_fwd(cw, struct)
2442be5bea1cSJakub Kicinski                cw.nl()
2443be5bea1cSJakub Kicinski
2444be5bea1cSJakub Kicinski            if parsed.kernel_policy in {'per-op', 'split'}:
2445be5bea1cSJakub Kicinski                for op_name, op in parsed.ops.items():
2446be5bea1cSJakub Kicinski                    if 'do' in op and 'event' not in op:
24476f96ec73SJakub Kicinski                        ri = RenderInfo(cw, parsed, args.mode, op, "do")
2448be5bea1cSJakub Kicinski                        print_req_policy_fwd(cw, ri.struct['request'], ri=ri)
2449be5bea1cSJakub Kicinski                        cw.nl()
2450be5bea1cSJakub Kicinski
2451be5bea1cSJakub Kicinski            print_kernel_op_table_hdr(parsed, cw)
2452be5bea1cSJakub Kicinski            print_kernel_mcgrp_hdr(parsed, cw)
2453be5bea1cSJakub Kicinski            print_kernel_family_struct_hdr(parsed, cw)
2454be5bea1cSJakub Kicinski        else:
2455be5bea1cSJakub Kicinski            for _, struct in sorted(parsed.pure_nested_structs.items()):
2456be5bea1cSJakub Kicinski                if struct.request:
2457be5bea1cSJakub Kicinski                    cw.p('/* Common nested types */')
2458be5bea1cSJakub Kicinski                    break
2459be5bea1cSJakub Kicinski            for attr_set, struct in sorted(parsed.pure_nested_structs.items()):
2460be5bea1cSJakub Kicinski                if struct.request:
2461be5bea1cSJakub Kicinski                    print_req_policy(cw, struct)
2462be5bea1cSJakub Kicinski            cw.nl()
2463be5bea1cSJakub Kicinski
2464be5bea1cSJakub Kicinski            if parsed.kernel_policy == 'global':
2465be5bea1cSJakub Kicinski                cw.p(f"/* Global operation policy for {parsed.name} */")
2466be5bea1cSJakub Kicinski
2467be5bea1cSJakub Kicinski                struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy)
2468be5bea1cSJakub Kicinski                print_req_policy(cw, struct)
2469be5bea1cSJakub Kicinski                cw.nl()
2470be5bea1cSJakub Kicinski
2471be5bea1cSJakub Kicinski            for op_name, op in parsed.ops.items():
2472be5bea1cSJakub Kicinski                if parsed.kernel_policy in {'per-op', 'split'}:
2473eaf317e7SJakub Kicinski                    for op_mode in ['do', 'dump']:
2474be5bea1cSJakub Kicinski                        if op_mode in op and 'request' in op[op_mode]:
2475be5bea1cSJakub Kicinski                            cw.p(f"/* {op.enum_name} - {op_mode} */")
24766f96ec73SJakub Kicinski                            ri = RenderInfo(cw, parsed, args.mode, op, op_mode)
2477be5bea1cSJakub Kicinski                            print_req_policy(cw, ri.struct['request'], ri=ri)
2478be5bea1cSJakub Kicinski                            cw.nl()
2479be5bea1cSJakub Kicinski
2480be5bea1cSJakub Kicinski            print_kernel_op_table(parsed, cw)
2481be5bea1cSJakub Kicinski            print_kernel_mcgrp_src(parsed, cw)
2482be5bea1cSJakub Kicinski            print_kernel_family_struct_src(parsed, cw)
2483be5bea1cSJakub Kicinski
2484be5bea1cSJakub Kicinski    if args.mode == "user":
2485be5bea1cSJakub Kicinski        if args.header:
248621b6e302SJakub Kicinski            cw.p('/* Enums */')
248721b6e302SJakub Kicinski            put_op_name_fwd(parsed, cw)
248821b6e302SJakub Kicinski
248921b6e302SJakub Kicinski            for name, const in parsed.consts.items():
249021b6e302SJakub Kicinski                if isinstance(const, EnumSet):
249121b6e302SJakub Kicinski                    put_enum_to_str_fwd(parsed, cw, const)
249221b6e302SJakub Kicinski            cw.nl()
249321b6e302SJakub Kicinski
2494be5bea1cSJakub Kicinski            cw.p('/* Common nested types */')
24956afaa0efSJakub Kicinski            for attr_set, struct in parsed.pure_nested_structs.items():
24966f96ec73SJakub Kicinski                ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
2497be5bea1cSJakub Kicinski                print_type_full(ri, struct)
2498be5bea1cSJakub Kicinski
2499be5bea1cSJakub Kicinski            for op_name, op in parsed.ops.items():
2500be5bea1cSJakub Kicinski                cw.p(f"/* ============== {op.enum_name} ============== */")
2501be5bea1cSJakub Kicinski
2502be5bea1cSJakub Kicinski                if 'do' in op and 'event' not in op:
2503be5bea1cSJakub Kicinski                    cw.p(f"/* {op.enum_name} - do */")
25046f96ec73SJakub Kicinski                    ri = RenderInfo(cw, parsed, args.mode, op, "do")
2505be5bea1cSJakub Kicinski                    print_req_type(ri)
2506be5bea1cSJakub Kicinski                    print_req_type_helpers(ri)
2507be5bea1cSJakub Kicinski                    cw.nl()
2508be5bea1cSJakub Kicinski                    print_rsp_type(ri)
2509be5bea1cSJakub Kicinski                    print_rsp_type_helpers(ri)
2510be5bea1cSJakub Kicinski                    cw.nl()
2511be5bea1cSJakub Kicinski                    print_req_prototype(ri)
2512be5bea1cSJakub Kicinski                    cw.nl()
2513be5bea1cSJakub Kicinski
2514be5bea1cSJakub Kicinski                if 'dump' in op:
2515be5bea1cSJakub Kicinski                    cw.p(f"/* {op.enum_name} - dump */")
25166f96ec73SJakub Kicinski                    ri = RenderInfo(cw, parsed, args.mode, op, 'dump')
2517be5bea1cSJakub Kicinski                    if 'request' in op['dump']:
2518be5bea1cSJakub Kicinski                        print_req_type(ri)
2519be5bea1cSJakub Kicinski                        print_req_type_helpers(ri)
2520be5bea1cSJakub Kicinski                    if not ri.type_consistent:
2521be5bea1cSJakub Kicinski                        print_rsp_type(ri)
2522be5bea1cSJakub Kicinski                    print_wrapped_type(ri)
2523be5bea1cSJakub Kicinski                    print_dump_prototype(ri)
2524be5bea1cSJakub Kicinski                    cw.nl()
2525be5bea1cSJakub Kicinski
2526ced15688SJakub Kicinski                if op.has_ntf:
2527be5bea1cSJakub Kicinski                    cw.p(f"/* {op.enum_name} - notify */")
25286f96ec73SJakub Kicinski                    ri = RenderInfo(cw, parsed, args.mode, op, 'notify')
2529be5bea1cSJakub Kicinski                    if not ri.type_consistent:
25305605f102SJakub Kicinski                        raise Exception(f'Only notifications with consistent types supported ({op.name})')
2531be5bea1cSJakub Kicinski                    print_wrapped_type(ri)
2532be5bea1cSJakub Kicinski
25336da3424fSJakub Kicinski            for op_name, op in parsed.ntfs.items():
2534be5bea1cSJakub Kicinski                if 'event' in op:
25356f96ec73SJakub Kicinski                    ri = RenderInfo(cw, parsed, args.mode, op, 'event')
2536be5bea1cSJakub Kicinski                    cw.p(f"/* {op.enum_name} - event */")
2537be5bea1cSJakub Kicinski                    print_rsp_type(ri)
2538be5bea1cSJakub Kicinski                    cw.nl()
2539be5bea1cSJakub Kicinski                    print_wrapped_type(ri)
2540be5bea1cSJakub Kicinski            cw.nl()
2541be5bea1cSJakub Kicinski        else:
254221b6e302SJakub Kicinski            cw.p('/* Enums */')
254321b6e302SJakub Kicinski            put_op_name(parsed, cw)
254421b6e302SJakub Kicinski
254521b6e302SJakub Kicinski            for name, const in parsed.consts.items():
254621b6e302SJakub Kicinski                if isinstance(const, EnumSet):
254721b6e302SJakub Kicinski                    put_enum_to_str(parsed, cw, const)
254821b6e302SJakub Kicinski            cw.nl()
254921b6e302SJakub Kicinski
2550be5bea1cSJakub Kicinski            cw.p('/* Policies */')
2551168dea20SJakub Kicinski            for name in parsed.pure_nested_structs:
2552be5bea1cSJakub Kicinski                struct = Struct(parsed, name)
2553168dea20SJakub Kicinski                put_typol(cw, struct)
2554168dea20SJakub Kicinski            for name in parsed.root_sets:
2555be5bea1cSJakub Kicinski                struct = Struct(parsed, name)
2556be5bea1cSJakub Kicinski                put_typol(cw, struct)
2557be5bea1cSJakub Kicinski
2558be5bea1cSJakub Kicinski            cw.p('/* Common nested types */')
25596afaa0efSJakub Kicinski            for attr_set, struct in parsed.pure_nested_structs.items():
25606f96ec73SJakub Kicinski                ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
2561be5bea1cSJakub Kicinski
2562be5bea1cSJakub Kicinski                free_rsp_nested(ri, struct)
2563be5bea1cSJakub Kicinski                if struct.request:
2564be5bea1cSJakub Kicinski                    put_req_nested(ri, struct)
2565be5bea1cSJakub Kicinski                if struct.reply:
2566be5bea1cSJakub Kicinski                    parse_rsp_nested(ri, struct)
2567be5bea1cSJakub Kicinski
2568be5bea1cSJakub Kicinski            for op_name, op in parsed.ops.items():
2569be5bea1cSJakub Kicinski                cw.p(f"/* ============== {op.enum_name} ============== */")
2570be5bea1cSJakub Kicinski                if 'do' in op and 'event' not in op:
2571be5bea1cSJakub Kicinski                    cw.p(f"/* {op.enum_name} - do */")
25726f96ec73SJakub Kicinski                    ri = RenderInfo(cw, parsed, args.mode, op, "do")
25735d58f911SJakub Kicinski                    print_req_free(ri)
2574be5bea1cSJakub Kicinski                    print_rsp_free(ri)
2575be5bea1cSJakub Kicinski                    parse_rsp_msg(ri)
2576be5bea1cSJakub Kicinski                    print_req(ri)
2577be5bea1cSJakub Kicinski                    cw.nl()
2578be5bea1cSJakub Kicinski
2579be5bea1cSJakub Kicinski                if 'dump' in op:
2580be5bea1cSJakub Kicinski                    cw.p(f"/* {op.enum_name} - dump */")
25816f96ec73SJakub Kicinski                    ri = RenderInfo(cw, parsed, args.mode, op, "dump")
2582be5bea1cSJakub Kicinski                    if not ri.type_consistent:
2583be5bea1cSJakub Kicinski                        parse_rsp_msg(ri, deref=True)
2584be5bea1cSJakub Kicinski                    print_dump_type_free(ri)
2585be5bea1cSJakub Kicinski                    print_dump(ri)
2586be5bea1cSJakub Kicinski                    cw.nl()
2587be5bea1cSJakub Kicinski
2588ced15688SJakub Kicinski                if op.has_ntf:
2589be5bea1cSJakub Kicinski                    cw.p(f"/* {op.enum_name} - notify */")
25906f96ec73SJakub Kicinski                    ri = RenderInfo(cw, parsed, args.mode, op, 'notify')
2591be5bea1cSJakub Kicinski                    if not ri.type_consistent:
25925605f102SJakub Kicinski                        raise Exception(f'Only notifications with consistent types supported ({op.name})')
2593be5bea1cSJakub Kicinski                    print_ntf_type_free(ri)
2594be5bea1cSJakub Kicinski
25956da3424fSJakub Kicinski            for op_name, op in parsed.ntfs.items():
2596be5bea1cSJakub Kicinski                if 'event' in op:
2597be5bea1cSJakub Kicinski                    cw.p(f"/* {op.enum_name} - event */")
2598be5bea1cSJakub Kicinski
25996f96ec73SJakub Kicinski                    ri = RenderInfo(cw, parsed, args.mode, op, "do")
2600be5bea1cSJakub Kicinski                    parse_rsp_msg(ri)
2601be5bea1cSJakub Kicinski
26026f96ec73SJakub Kicinski                    ri = RenderInfo(cw, parsed, args.mode, op, "event")
2603be5bea1cSJakub Kicinski                    print_ntf_type_free(ri)
26048cb6afb3SJakub Kicinski            cw.nl()
26058cb6afb3SJakub Kicinski            render_user_family(parsed, cw, False)
26068cb6afb3SJakub Kicinski
2607be5bea1cSJakub Kicinski    if args.header:
2608be5bea1cSJakub Kicinski        cw.p(f'#endif /* {hdr_prot} */')
2609be5bea1cSJakub Kicinski
2610be5bea1cSJakub Kicinski
2611be5bea1cSJakub Kicinskiif __name__ == "__main__":
2612be5bea1cSJakub Kicinski    main()
2613