xref: /linux/tools/net/ynl/ynl-gen-c.py (revision 73aea586d6c58f55799f6130e19321ff7b574c3d)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
3
4import argparse
5import collections
6import filecmp
7import os
8import re
9import shutil
10import tempfile
11import yaml
12
13from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, SpecEnumEntry
14
15
16def c_upper(name):
17    return name.upper().replace('-', '_')
18
19
20def c_lower(name):
21    return name.lower().replace('-', '_')
22
23
24def limit_to_number(name):
25    """
26    Turn a string limit like u32-max or s64-min into its numerical value
27    """
28    if name[0] == 'u' and name.endswith('-min'):
29        return 0
30    width = int(name[1:-4])
31    if name[0] == 's':
32        width -= 1
33    value = (1 << width) - 1
34    if name[0] == 's' and name.endswith('-min'):
35        value = -value - 1
36    return value
37
38
39class BaseNlLib:
40    def get_family_id(self):
41        return 'ys->family_id'
42
43    def parse_cb_run(self, cb, data, is_dump=False, indent=1):
44        ind = '\n\t\t' + '\t' * indent + ' '
45        if is_dump:
46            return f"mnl_cb_run2(ys->rx_buf, len, 0, 0, {cb}, {data},{ind}ynl_cb_array, NLMSG_MIN_TYPE)"
47        else:
48            return f"mnl_cb_run2(ys->rx_buf, len, ys->seq, ys->portid,{ind}{cb}, {data},{ind}" + \
49                   "ynl_cb_array, NLMSG_MIN_TYPE)"
50
51
52class Type(SpecAttr):
53    def __init__(self, family, attr_set, attr, value):
54        super().__init__(family, attr_set, attr, value)
55
56        self.attr = attr
57        self.attr_set = attr_set
58        self.type = attr['type']
59        self.checks = attr.get('checks', {})
60
61        self.request = False
62        self.reply = False
63
64        if 'len' in attr:
65            self.len = attr['len']
66
67        if 'nested-attributes' in attr:
68            self.nested_attrs = attr['nested-attributes']
69            if self.nested_attrs == family.name:
70                self.nested_render_name = f"{family.name}"
71            else:
72                self.nested_render_name = f"{family.name}_{c_lower(self.nested_attrs)}"
73
74            if self.nested_attrs in self.family.consts:
75                self.nested_struct_type = 'struct ' + self.nested_render_name + '_'
76            else:
77                self.nested_struct_type = 'struct ' + self.nested_render_name
78
79        self.c_name = c_lower(self.name)
80        if self.c_name in _C_KW:
81            self.c_name += '_'
82
83        # Added by resolve():
84        self.enum_name = None
85        delattr(self, "enum_name")
86
87    def get_limit(self, limit, default=None):
88        value = self.checks.get(limit, default)
89        if value is None:
90            return value
91        if not isinstance(value, int):
92            value = limit_to_number(value)
93        return value
94
95    def resolve(self):
96        if 'name-prefix' in self.attr:
97            enum_name = f"{self.attr['name-prefix']}{self.name}"
98        else:
99            enum_name = f"{self.attr_set.name_prefix}{self.name}"
100        self.enum_name = c_upper(enum_name)
101
102    def is_multi_val(self):
103        return None
104
105    def is_scalar(self):
106        return self.type in {'u8', 'u16', 'u32', 'u64', 's32', 's64'}
107
108    def presence_type(self):
109        return 'bit'
110
111    def presence_member(self, space, type_filter):
112        if self.presence_type() != type_filter:
113            return
114
115        if self.presence_type() == 'bit':
116            pfx = '__' if space == 'user' else ''
117            return f"{pfx}u32 {self.c_name}:1;"
118
119        if self.presence_type() == 'len':
120            pfx = '__' if space == 'user' else ''
121            return f"{pfx}u32 {self.c_name}_len;"
122
123    def _complex_member_type(self, ri):
124        return None
125
126    def free_needs_iter(self):
127        return False
128
129    def free(self, ri, var, ref):
130        if self.is_multi_val() or self.presence_type() == 'len':
131            ri.cw.p(f'free({var}->{ref}{self.c_name});')
132
133    def arg_member(self, ri):
134        member = self._complex_member_type(ri)
135        if member:
136            arg = [member + ' *' + self.c_name]
137            if self.presence_type() == 'count':
138                arg += ['unsigned int n_' + self.c_name]
139            return arg
140        raise Exception(f"Struct member not implemented for class type {self.type}")
141
142    def struct_member(self, ri):
143        if self.is_multi_val():
144            ri.cw.p(f"unsigned int n_{self.c_name};")
145        member = self._complex_member_type(ri)
146        if member:
147            ptr = '*' if self.is_multi_val() else ''
148            ri.cw.p(f"{member} {ptr}{self.c_name};")
149            return
150        members = self.arg_member(ri)
151        for one in members:
152            ri.cw.p(one + ';')
153
154    def _attr_policy(self, policy):
155        return '{ .type = ' + policy + ', }'
156
157    def attr_policy(self, cw):
158        policy = c_upper('nla-' + self.attr['type'])
159
160        spec = self._attr_policy(policy)
161        cw.p(f"\t[{self.enum_name}] = {spec},")
162
163    def _mnl_type(self):
164        # mnl does not have helpers for signed integer types
165        # turn signed type into unsigned
166        # this only makes sense for scalar types
167        t = self.type
168        if t[0] == 's':
169            t = 'u' + t[1:]
170        return t
171
172    def _attr_typol(self):
173        raise Exception(f"Type policy not implemented for class type {self.type}")
174
175    def attr_typol(self, cw):
176        typol = self._attr_typol()
177        cw.p(f'[{self.enum_name}] = {"{"} .name = "{self.name}", {typol}{"}"},')
178
179    def _attr_put_line(self, ri, var, line):
180        if self.presence_type() == 'bit':
181            ri.cw.p(f"if ({var}->_present.{self.c_name})")
182        elif self.presence_type() == 'len':
183            ri.cw.p(f"if ({var}->_present.{self.c_name}_len)")
184        ri.cw.p(f"{line};")
185
186    def _attr_put_simple(self, ri, var, put_type):
187        line = f"mnl_attr_put_{put_type}(nlh, {self.enum_name}, {var}->{self.c_name})"
188        self._attr_put_line(ri, var, line)
189
190    def attr_put(self, ri, var):
191        raise Exception(f"Put not implemented for class type {self.type}")
192
193    def _attr_get(self, ri, var):
194        raise Exception(f"Attr get not implemented for class type {self.type}")
195
196    def attr_get(self, ri, var, first):
197        lines, init_lines, local_vars = self._attr_get(ri, var)
198        if type(lines) is str:
199            lines = [lines]
200        if type(init_lines) is str:
201            init_lines = [init_lines]
202
203        kw = 'if' if first else 'else if'
204        ri.cw.block_start(line=f"{kw} (type == {self.enum_name})")
205        if local_vars:
206            for local in local_vars:
207                ri.cw.p(local)
208            ri.cw.nl()
209
210        if not self.is_multi_val():
211            ri.cw.p("if (ynl_attr_validate(yarg, attr))")
212            ri.cw.p("return MNL_CB_ERROR;")
213            if self.presence_type() == 'bit':
214                ri.cw.p(f"{var}->_present.{self.c_name} = 1;")
215
216        if init_lines:
217            ri.cw.nl()
218            for line in init_lines:
219                ri.cw.p(line)
220
221        for line in lines:
222            ri.cw.p(line)
223        ri.cw.block_end()
224        return True
225
226    def _setter_lines(self, ri, member, presence):
227        raise Exception(f"Setter not implemented for class type {self.type}")
228
229    def setter(self, ri, space, direction, deref=False, ref=None):
230        ref = (ref if ref else []) + [self.c_name]
231        var = "req"
232        member = f"{var}->{'.'.join(ref)}"
233
234        code = []
235        presence = ''
236        for i in range(0, len(ref)):
237            presence = f"{var}->{'.'.join(ref[:i] + [''])}_present.{ref[i]}"
238            if self.presence_type() == 'bit':
239                code.append(presence + ' = 1;')
240        code += self._setter_lines(ri, member, presence)
241
242        func_name = f"{op_prefix(ri, direction, deref=deref)}_set_{'_'.join(ref)}"
243        free = bool([x for x in code if 'free(' in x])
244        alloc = bool([x for x in code if 'alloc(' in x])
245        if free and not alloc:
246            func_name = '__' + func_name
247        ri.cw.write_func('static inline void', func_name, body=code,
248                         args=[f'{type_name(ri, direction, deref=deref)} *{var}'] + self.arg_member(ri))
249
250
251class TypeUnused(Type):
252    def presence_type(self):
253        return ''
254
255    def arg_member(self, ri):
256        return []
257
258    def _attr_get(self, ri, var):
259        return ['return MNL_CB_ERROR;'], None, None
260
261    def _attr_typol(self):
262        return '.type = YNL_PT_REJECT, '
263
264    def attr_policy(self, cw):
265        pass
266
267
268class TypePad(Type):
269    def presence_type(self):
270        return ''
271
272    def arg_member(self, ri):
273        return []
274
275    def _attr_typol(self):
276        return '.type = YNL_PT_IGNORE, '
277
278    def attr_put(self, ri, var):
279        pass
280
281    def attr_get(self, ri, var, first):
282        pass
283
284    def attr_policy(self, cw):
285        pass
286
287    def setter(self, ri, space, direction, deref=False, ref=None):
288        pass
289
290
291class TypeScalar(Type):
292    def __init__(self, family, attr_set, attr, value):
293        super().__init__(family, attr_set, attr, value)
294
295        self.byte_order_comment = ''
296        if 'byte-order' in attr:
297            self.byte_order_comment = f" /* {attr['byte-order']} */"
298
299        if 'enum' in self.attr:
300            enum = self.family.consts[self.attr['enum']]
301            low, high = enum.value_range()
302            if 'min' not in self.checks:
303                if low != 0 or self.type[0] == 's':
304                    self.checks['min'] = low
305            if 'max' not in self.checks:
306                self.checks['max'] = high
307
308        if 'min' in self.checks and 'max' in self.checks:
309            if self.get_limit('min') > self.get_limit('max'):
310                raise Exception(f'Invalid limit for "{self.name}" min: {self.get_limit("min")} max: {self.get_limit("max")}')
311            self.checks['range'] = True
312
313        low = min(self.get_limit('min', 0), self.get_limit('max', 0))
314        high = max(self.get_limit('min', 0), self.get_limit('max', 0))
315        if low < 0 and self.type[0] == 'u':
316            raise Exception(f'Invalid limit for "{self.name}" negative limit for unsigned type')
317        if low < -32768 or high > 32767:
318            self.checks['full-range'] = True
319
320        # Added by resolve():
321        self.is_bitfield = None
322        delattr(self, "is_bitfield")
323        self.type_name = None
324        delattr(self, "type_name")
325
326    def resolve(self):
327        self.resolve_up(super())
328
329        if 'enum-as-flags' in self.attr and self.attr['enum-as-flags']:
330            self.is_bitfield = True
331        elif 'enum' in self.attr:
332            self.is_bitfield = self.family.consts[self.attr['enum']]['type'] == 'flags'
333        else:
334            self.is_bitfield = False
335
336        maybe_enum = not self.is_bitfield and 'enum' in self.attr
337        if maybe_enum and self.family.consts[self.attr['enum']].enum_name:
338            self.type_name = f"enum {self.family.name}_{c_lower(self.attr['enum'])}"
339        elif self.is_auto_scalar:
340            self.type_name = '__' + self.type[0] + '64'
341        else:
342            self.type_name = '__' + self.type
343
344    def mnl_type(self):
345        return self._mnl_type()
346
347    def _attr_policy(self, policy):
348        if 'flags-mask' in self.checks or self.is_bitfield:
349            if self.is_bitfield:
350                enum = self.family.consts[self.attr['enum']]
351                mask = enum.get_mask(as_flags=True)
352            else:
353                flags = self.family.consts[self.checks['flags-mask']]
354                flag_cnt = len(flags['entries'])
355                mask = (1 << flag_cnt) - 1
356            return f"NLA_POLICY_MASK({policy}, 0x{mask:x})"
357        elif 'full-range' in self.checks:
358            return f"NLA_POLICY_FULL_RANGE({policy}, &{c_lower(self.enum_name)}_range)"
359        elif 'range' in self.checks:
360            return f"NLA_POLICY_RANGE({policy}, {self.get_limit('min')}, {self.get_limit('max')})"
361        elif 'min' in self.checks:
362            return f"NLA_POLICY_MIN({policy}, {self.get_limit('min')})"
363        elif 'max' in self.checks:
364            return f"NLA_POLICY_MAX({policy}, {self.get_limit('max')})"
365        return super()._attr_policy(policy)
366
367    def _attr_typol(self):
368        return f'.type = YNL_PT_U{c_upper(self.type[1:])}, '
369
370    def arg_member(self, ri):
371        return [f'{self.type_name} {self.c_name}{self.byte_order_comment}']
372
373    def attr_put(self, ri, var):
374        self._attr_put_simple(ri, var, self.mnl_type())
375
376    def _attr_get(self, ri, var):
377        return f"{var}->{self.c_name} = mnl_attr_get_{self.mnl_type()}(attr);", None, None
378
379    def _setter_lines(self, ri, member, presence):
380        return [f"{member} = {self.c_name};"]
381
382
383class TypeFlag(Type):
384    def arg_member(self, ri):
385        return []
386
387    def _attr_typol(self):
388        return '.type = YNL_PT_FLAG, '
389
390    def attr_put(self, ri, var):
391        self._attr_put_line(ri, var, f"mnl_attr_put(nlh, {self.enum_name}, 0, NULL)")
392
393    def _attr_get(self, ri, var):
394        return [], None, None
395
396    def _setter_lines(self, ri, member, presence):
397        return []
398
399
400class TypeString(Type):
401    def arg_member(self, ri):
402        return [f"const char *{self.c_name}"]
403
404    def presence_type(self):
405        return 'len'
406
407    def struct_member(self, ri):
408        ri.cw.p(f"char *{self.c_name};")
409
410    def _attr_typol(self):
411        return f'.type = YNL_PT_NUL_STR, '
412
413    def _attr_policy(self, policy):
414        if 'exact-len' in self.checks:
415            mem = 'NLA_POLICY_EXACT_LEN(' + str(self.checks['exact-len']) + ')'
416        else:
417            mem = '{ .type = ' + policy
418            if 'max-len' in self.checks:
419                mem += ', .len = ' + str(self.get_limit('max-len'))
420            mem += ', }'
421        return mem
422
423    def attr_policy(self, cw):
424        if self.checks.get('unterminated-ok', False):
425            policy = 'NLA_STRING'
426        else:
427            policy = 'NLA_NUL_STRING'
428
429        spec = self._attr_policy(policy)
430        cw.p(f"\t[{self.enum_name}] = {spec},")
431
432    def attr_put(self, ri, var):
433        self._attr_put_simple(ri, var, 'strz')
434
435    def _attr_get(self, ri, var):
436        len_mem = var + '->_present.' + self.c_name + '_len'
437        return [f"{len_mem} = len;",
438                f"{var}->{self.c_name} = malloc(len + 1);",
439                f"memcpy({var}->{self.c_name}, mnl_attr_get_str(attr), len);",
440                f"{var}->{self.c_name}[len] = 0;"], \
441               ['len = strnlen(mnl_attr_get_str(attr), mnl_attr_get_payload_len(attr));'], \
442               ['unsigned int len;']
443
444    def _setter_lines(self, ri, member, presence):
445        return [f"free({member});",
446                f"{presence}_len = strlen({self.c_name});",
447                f"{member} = malloc({presence}_len + 1);",
448                f'memcpy({member}, {self.c_name}, {presence}_len);',
449                f'{member}[{presence}_len] = 0;']
450
451
452class TypeBinary(Type):
453    def arg_member(self, ri):
454        return [f"const void *{self.c_name}", 'size_t len']
455
456    def presence_type(self):
457        return 'len'
458
459    def struct_member(self, ri):
460        ri.cw.p(f"void *{self.c_name};")
461
462    def _attr_typol(self):
463        return f'.type = YNL_PT_BINARY,'
464
465    def _attr_policy(self, policy):
466        if 'exact-len' in self.checks:
467            mem = 'NLA_POLICY_EXACT_LEN(' + str(self.checks['exact-len']) + ')'
468        else:
469            mem = '{ '
470            if len(self.checks) == 1 and 'min-len' in self.checks:
471                mem += '.len = ' + str(self.get_limit('min-len'))
472            elif len(self.checks) == 0:
473                mem += '.type = NLA_BINARY'
474            else:
475                raise Exception('One or more of binary type checks not implemented, yet')
476            mem += ', }'
477        return mem
478
479    def attr_put(self, ri, var):
480        self._attr_put_line(ri, var, f"mnl_attr_put(nlh, {self.enum_name}, " +
481                            f"{var}->_present.{self.c_name}_len, {var}->{self.c_name})")
482
483    def _attr_get(self, ri, var):
484        len_mem = var + '->_present.' + self.c_name + '_len'
485        return [f"{len_mem} = len;",
486                f"{var}->{self.c_name} = malloc(len);",
487                f"memcpy({var}->{self.c_name}, mnl_attr_get_payload(attr), len);"], \
488               ['len = mnl_attr_get_payload_len(attr);'], \
489               ['unsigned int len;']
490
491    def _setter_lines(self, ri, member, presence):
492        return [f"free({member});",
493                f"{presence}_len = len;",
494                f"{member} = malloc({presence}_len);",
495                f'memcpy({member}, {self.c_name}, {presence}_len);']
496
497
498class TypeBitfield32(Type):
499    def _complex_member_type(self, ri):
500        return "struct nla_bitfield32"
501
502    def _attr_typol(self):
503        return f'.type = YNL_PT_BITFIELD32, '
504
505    def _attr_policy(self, policy):
506        if not 'enum' in self.attr:
507            raise Exception('Enum required for bitfield32 attr')
508        enum = self.family.consts[self.attr['enum']]
509        mask = enum.get_mask(as_flags=True)
510        return f"NLA_POLICY_BITFIELD32({mask})"
511
512    def attr_put(self, ri, var):
513        line = f"mnl_attr_put(nlh, {self.enum_name}, sizeof(struct nla_bitfield32), &{var}->{self.c_name})"
514        self._attr_put_line(ri, var, line)
515
516    def _attr_get(self, ri, var):
517        return f"memcpy(&{var}->{self.c_name}, mnl_attr_get_payload(attr), sizeof(struct nla_bitfield32));", None, None
518
519    def _setter_lines(self, ri, member, presence):
520        return [f"memcpy(&{member}, {self.c_name}, sizeof(struct nla_bitfield32));"]
521
522
523class TypeNest(Type):
524    def _complex_member_type(self, ri):
525        return self.nested_struct_type
526
527    def free(self, ri, var, ref):
528        ri.cw.p(f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name});')
529
530    def _attr_typol(self):
531        return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
532
533    def _attr_policy(self, policy):
534        return 'NLA_POLICY_NESTED(' + self.nested_render_name + '_nl_policy)'
535
536    def attr_put(self, ri, var):
537        self._attr_put_line(ri, var, f"{self.nested_render_name}_put(nlh, " +
538                            f"{self.enum_name}, &{var}->{self.c_name})")
539
540    def _attr_get(self, ri, var):
541        get_lines = [f"if ({self.nested_render_name}_parse(&parg, attr))",
542                     "return MNL_CB_ERROR;"]
543        init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;",
544                      f"parg.data = &{var}->{self.c_name};"]
545        return get_lines, init_lines, None
546
547    def setter(self, ri, space, direction, deref=False, ref=None):
548        ref = (ref if ref else []) + [self.c_name]
549
550        for _, attr in ri.family.pure_nested_structs[self.nested_attrs].member_list():
551            attr.setter(ri, self.nested_attrs, direction, deref=deref, ref=ref)
552
553
554class TypeMultiAttr(Type):
555    def __init__(self, family, attr_set, attr, value, base_type):
556        super().__init__(family, attr_set, attr, value)
557
558        self.base_type = base_type
559
560    def is_multi_val(self):
561        return True
562
563    def presence_type(self):
564        return 'count'
565
566    def mnl_type(self):
567        return self._mnl_type()
568
569    def _complex_member_type(self, ri):
570        if 'type' not in self.attr or self.attr['type'] == 'nest':
571            return self.nested_struct_type
572        elif self.attr['type'] in scalars:
573            scalar_pfx = '__' if ri.ku_space == 'user' else ''
574            return scalar_pfx + self.attr['type']
575        else:
576            raise Exception(f"Sub-type {self.attr['type']} not supported yet")
577
578    def free_needs_iter(self):
579        return 'type' not in self.attr or self.attr['type'] == 'nest'
580
581    def free(self, ri, var, ref):
582        if self.attr['type'] in scalars:
583            ri.cw.p(f"free({var}->{ref}{self.c_name});")
584        elif 'type' not in self.attr or self.attr['type'] == 'nest':
585            ri.cw.p(f"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)")
586            ri.cw.p(f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);')
587            ri.cw.p(f"free({var}->{ref}{self.c_name});")
588        else:
589            raise Exception(f"Free of MultiAttr sub-type {self.attr['type']} not supported yet")
590
591    def _attr_policy(self, policy):
592        return self.base_type._attr_policy(policy)
593
594    def _attr_typol(self):
595        return self.base_type._attr_typol()
596
597    def _attr_get(self, ri, var):
598        return f'n_{self.c_name}++;', None, None
599
600    def attr_put(self, ri, var):
601        if self.attr['type'] in scalars:
602            put_type = self.mnl_type()
603            ri.cw.p(f"for (unsigned int i = 0; i < {var}->n_{self.c_name}; i++)")
604            ri.cw.p(f"mnl_attr_put_{put_type}(nlh, {self.enum_name}, {var}->{self.c_name}[i]);")
605        elif 'type' not in self.attr or self.attr['type'] == 'nest':
606            ri.cw.p(f"for (unsigned int i = 0; i < {var}->n_{self.c_name}; i++)")
607            self._attr_put_line(ri, var, f"{self.nested_render_name}_put(nlh, " +
608                                f"{self.enum_name}, &{var}->{self.c_name}[i])")
609        else:
610            raise Exception(f"Put of MultiAttr sub-type {self.attr['type']} not supported yet")
611
612    def _setter_lines(self, ri, member, presence):
613        # For multi-attr we have a count, not presence, hack up the presence
614        presence = presence[:-(len('_present.') + len(self.c_name))] + "n_" + self.c_name
615        return [f"free({member});",
616                f"{member} = {self.c_name};",
617                f"{presence} = n_{self.c_name};"]
618
619
620class TypeArrayNest(Type):
621    def is_multi_val(self):
622        return True
623
624    def presence_type(self):
625        return 'count'
626
627    def _complex_member_type(self, ri):
628        if 'sub-type' not in self.attr or self.attr['sub-type'] == 'nest':
629            return self.nested_struct_type
630        elif self.attr['sub-type'] in scalars:
631            scalar_pfx = '__' if ri.ku_space == 'user' else ''
632            return scalar_pfx + self.attr['sub-type']
633        else:
634            raise Exception(f"Sub-type {self.attr['sub-type']} not supported yet")
635
636    def _attr_typol(self):
637        return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
638
639    def _attr_get(self, ri, var):
640        local_vars = ['const struct nlattr *attr2;']
641        get_lines = [f'attr_{self.c_name} = attr;',
642                     'mnl_attr_for_each_nested(attr2, attr)',
643                     f'\t{var}->n_{self.c_name}++;']
644        return get_lines, None, local_vars
645
646
647class TypeNestTypeValue(Type):
648    def _complex_member_type(self, ri):
649        return self.nested_struct_type
650
651    def _attr_typol(self):
652        return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
653
654    def _attr_get(self, ri, var):
655        prev = 'attr'
656        tv_args = ''
657        get_lines = []
658        local_vars = []
659        init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;",
660                      f"parg.data = &{var}->{self.c_name};"]
661        if 'type-value' in self.attr:
662            tv_names = [c_lower(x) for x in self.attr["type-value"]]
663            local_vars += [f'const struct nlattr *attr_{", *attr_".join(tv_names)};']
664            local_vars += [f'__u32 {", ".join(tv_names)};']
665            for level in self.attr["type-value"]:
666                level = c_lower(level)
667                get_lines += [f'attr_{level} = mnl_attr_get_payload({prev});']
668                get_lines += [f'{level} = mnl_attr_get_type(attr_{level});']
669                prev = 'attr_' + level
670
671            tv_args = f", {', '.join(tv_names)}"
672
673        get_lines += [f"{self.nested_render_name}_parse(&parg, {prev}{tv_args});"]
674        return get_lines, init_lines, local_vars
675
676
677class Struct:
678    def __init__(self, family, space_name, type_list=None, inherited=None):
679        self.family = family
680        self.space_name = space_name
681        self.attr_set = family.attr_sets[space_name]
682        # Use list to catch comparisons with empty sets
683        self._inherited = inherited if inherited is not None else []
684        self.inherited = []
685
686        self.nested = type_list is None
687        if family.name == c_lower(space_name):
688            self.render_name = f"{family.name}"
689        else:
690            self.render_name = f"{family.name}_{c_lower(space_name)}"
691        self.struct_name = 'struct ' + self.render_name
692        if self.nested and space_name in family.consts:
693            self.struct_name += '_'
694        self.ptr_name = self.struct_name + ' *'
695
696        self.request = False
697        self.reply = False
698
699        self.attr_list = []
700        self.attrs = dict()
701        if type_list is not None:
702            for t in type_list:
703                self.attr_list.append((t, self.attr_set[t]),)
704        else:
705            for t in self.attr_set:
706                self.attr_list.append((t, self.attr_set[t]),)
707
708        max_val = 0
709        self.attr_max_val = None
710        for name, attr in self.attr_list:
711            if attr.value >= max_val:
712                max_val = attr.value
713                self.attr_max_val = attr
714            self.attrs[name] = attr
715
716    def __iter__(self):
717        yield from self.attrs
718
719    def __getitem__(self, key):
720        return self.attrs[key]
721
722    def member_list(self):
723        return self.attr_list
724
725    def set_inherited(self, new_inherited):
726        if self._inherited != new_inherited:
727            raise Exception("Inheriting different members not supported")
728        self.inherited = [c_lower(x) for x in sorted(self._inherited)]
729
730
731class EnumEntry(SpecEnumEntry):
732    def __init__(self, enum_set, yaml, prev, value_start):
733        super().__init__(enum_set, yaml, prev, value_start)
734
735        if prev:
736            self.value_change = (self.value != prev.value + 1)
737        else:
738            self.value_change = (self.value != 0)
739        self.value_change = self.value_change or self.enum_set['type'] == 'flags'
740
741        # Added by resolve:
742        self.c_name = None
743        delattr(self, "c_name")
744
745    def resolve(self):
746        self.resolve_up(super())
747
748        self.c_name = c_upper(self.enum_set.value_pfx + self.name)
749
750
751class EnumSet(SpecEnumSet):
752    def __init__(self, family, yaml):
753        self.render_name = c_lower(family.name + '-' + yaml['name'])
754
755        if 'enum-name' in yaml:
756            if yaml['enum-name']:
757                self.enum_name = 'enum ' + c_lower(yaml['enum-name'])
758            else:
759                self.enum_name = None
760        else:
761            self.enum_name = 'enum ' + self.render_name
762
763        self.value_pfx = yaml.get('name-prefix', f"{family.name}-{yaml['name']}-")
764
765        super().__init__(family, yaml)
766
767    def new_entry(self, entry, prev_entry, value_start):
768        return EnumEntry(self, entry, prev_entry, value_start)
769
770    def value_range(self):
771        low = min([x.value for x in self.entries.values()])
772        high = max([x.value for x in self.entries.values()])
773
774        if high - low + 1 != len(self.entries):
775            raise Exception("Can't get value range for a noncontiguous enum")
776
777        return low, high
778
779
780class AttrSet(SpecAttrSet):
781    def __init__(self, family, yaml):
782        super().__init__(family, yaml)
783
784        if self.subset_of is None:
785            if 'name-prefix' in yaml:
786                pfx = yaml['name-prefix']
787            elif self.name == family.name:
788                pfx = family.name + '-a-'
789            else:
790                pfx = f"{family.name}-a-{self.name}-"
791            self.name_prefix = c_upper(pfx)
792            self.max_name = c_upper(self.yaml.get('attr-max-name', f"{self.name_prefix}max"))
793            self.cnt_name = c_upper(self.yaml.get('attr-cnt-name', f"__{self.name_prefix}max"))
794        else:
795            self.name_prefix = family.attr_sets[self.subset_of].name_prefix
796            self.max_name = family.attr_sets[self.subset_of].max_name
797            self.cnt_name = family.attr_sets[self.subset_of].cnt_name
798
799        # Added by resolve:
800        self.c_name = None
801        delattr(self, "c_name")
802
803    def resolve(self):
804        self.c_name = c_lower(self.name)
805        if self.c_name in _C_KW:
806            self.c_name += '_'
807        if self.c_name == self.family.c_name:
808            self.c_name = ''
809
810    def new_attr(self, elem, value):
811        if elem['type'] in scalars:
812            t = TypeScalar(self.family, self, elem, value)
813        elif elem['type'] == 'unused':
814            t = TypeUnused(self.family, self, elem, value)
815        elif elem['type'] == 'pad':
816            t = TypePad(self.family, self, elem, value)
817        elif elem['type'] == 'flag':
818            t = TypeFlag(self.family, self, elem, value)
819        elif elem['type'] == 'string':
820            t = TypeString(self.family, self, elem, value)
821        elif elem['type'] == 'binary':
822            t = TypeBinary(self.family, self, elem, value)
823        elif elem['type'] == 'bitfield32':
824            t = TypeBitfield32(self.family, self, elem, value)
825        elif elem['type'] == 'nest':
826            t = TypeNest(self.family, self, elem, value)
827        elif elem['type'] == 'array-nest':
828            t = TypeArrayNest(self.family, self, elem, value)
829        elif elem['type'] == 'nest-type-value':
830            t = TypeNestTypeValue(self.family, self, elem, value)
831        else:
832            raise Exception(f"No typed class for type {elem['type']}")
833
834        if 'multi-attr' in elem and elem['multi-attr']:
835            t = TypeMultiAttr(self.family, self, elem, value, t)
836
837        return t
838
839
840class Operation(SpecOperation):
841    def __init__(self, family, yaml, req_value, rsp_value):
842        super().__init__(family, yaml, req_value, rsp_value)
843
844        self.render_name = family.name + '_' + c_lower(self.name)
845
846        self.dual_policy = ('do' in yaml and 'request' in yaml['do']) and \
847                         ('dump' in yaml and 'request' in yaml['dump'])
848
849        self.has_ntf = False
850
851        # Added by resolve:
852        self.enum_name = None
853        delattr(self, "enum_name")
854
855    def resolve(self):
856        self.resolve_up(super())
857
858        if not self.is_async:
859            self.enum_name = self.family.op_prefix + c_upper(self.name)
860        else:
861            self.enum_name = self.family.async_op_prefix + c_upper(self.name)
862
863    def mark_has_ntf(self):
864        self.has_ntf = True
865
866
867class Family(SpecFamily):
868    def __init__(self, file_name, exclude_ops):
869        # Added by resolve:
870        self.c_name = None
871        delattr(self, "c_name")
872        self.op_prefix = None
873        delattr(self, "op_prefix")
874        self.async_op_prefix = None
875        delattr(self, "async_op_prefix")
876        self.mcgrps = None
877        delattr(self, "mcgrps")
878        self.consts = None
879        delattr(self, "consts")
880        self.hooks = None
881        delattr(self, "hooks")
882
883        super().__init__(file_name, exclude_ops=exclude_ops)
884
885        self.fam_key = c_upper(self.yaml.get('c-family-name', self.yaml["name"] + '_FAMILY_NAME'))
886        self.ver_key = c_upper(self.yaml.get('c-version-name', self.yaml["name"] + '_FAMILY_VERSION'))
887
888        if 'definitions' not in self.yaml:
889            self.yaml['definitions'] = []
890
891        if 'uapi-header' in self.yaml:
892            self.uapi_header = self.yaml['uapi-header']
893        else:
894            self.uapi_header = f"linux/{self.name}.h"
895        if self.uapi_header.startswith("linux/") and self.uapi_header.endswith('.h'):
896            self.uapi_header_name = self.uapi_header[6:-2]
897        else:
898            self.uapi_header_name = self.name
899
900    def resolve(self):
901        self.resolve_up(super())
902
903        if self.yaml.get('protocol', 'genetlink') not in {'genetlink', 'genetlink-c', 'genetlink-legacy'}:
904            raise Exception("Codegen only supported for genetlink")
905
906        self.c_name = c_lower(self.name)
907        if 'name-prefix' in self.yaml['operations']:
908            self.op_prefix = c_upper(self.yaml['operations']['name-prefix'])
909        else:
910            self.op_prefix = c_upper(self.yaml['name'] + '-cmd-')
911        if 'async-prefix' in self.yaml['operations']:
912            self.async_op_prefix = c_upper(self.yaml['operations']['async-prefix'])
913        else:
914            self.async_op_prefix = self.op_prefix
915
916        self.mcgrps = self.yaml.get('mcast-groups', {'list': []})
917
918        self.hooks = dict()
919        for when in ['pre', 'post']:
920            self.hooks[when] = dict()
921            for op_mode in ['do', 'dump']:
922                self.hooks[when][op_mode] = dict()
923                self.hooks[when][op_mode]['set'] = set()
924                self.hooks[when][op_mode]['list'] = []
925
926        # dict space-name -> 'request': set(attrs), 'reply': set(attrs)
927        self.root_sets = dict()
928        # dict space-name -> set('request', 'reply')
929        self.pure_nested_structs = dict()
930
931        self._mark_notify()
932        self._mock_up_events()
933
934        self._load_root_sets()
935        self._load_nested_sets()
936        self._load_attr_use()
937        self._load_hooks()
938
939        self.kernel_policy = self.yaml.get('kernel-policy', 'split')
940        if self.kernel_policy == 'global':
941            self._load_global_policy()
942
943    def new_enum(self, elem):
944        return EnumSet(self, elem)
945
946    def new_attr_set(self, elem):
947        return AttrSet(self, elem)
948
949    def new_operation(self, elem, req_value, rsp_value):
950        return Operation(self, elem, req_value, rsp_value)
951
952    def _mark_notify(self):
953        for op in self.msgs.values():
954            if 'notify' in op:
955                self.ops[op['notify']].mark_has_ntf()
956
957    # Fake a 'do' equivalent of all events, so that we can render their response parsing
958    def _mock_up_events(self):
959        for op in self.yaml['operations']['list']:
960            if 'event' in op:
961                op['do'] = {
962                    'reply': {
963                        'attributes': op['event']['attributes']
964                    }
965                }
966
967    def _load_root_sets(self):
968        for op_name, op in self.msgs.items():
969            if 'attribute-set' not in op:
970                continue
971
972            req_attrs = set()
973            rsp_attrs = set()
974            for op_mode in ['do', 'dump']:
975                if op_mode in op and 'request' in op[op_mode]:
976                    req_attrs.update(set(op[op_mode]['request']['attributes']))
977                if op_mode in op and 'reply' in op[op_mode]:
978                    rsp_attrs.update(set(op[op_mode]['reply']['attributes']))
979            if 'event' in op:
980                rsp_attrs.update(set(op['event']['attributes']))
981
982            if op['attribute-set'] not in self.root_sets:
983                self.root_sets[op['attribute-set']] = {'request': req_attrs, 'reply': rsp_attrs}
984            else:
985                self.root_sets[op['attribute-set']]['request'].update(req_attrs)
986                self.root_sets[op['attribute-set']]['reply'].update(rsp_attrs)
987
988    def _load_nested_sets(self):
989        attr_set_queue = list(self.root_sets.keys())
990        attr_set_seen = set(self.root_sets.keys())
991
992        while len(attr_set_queue):
993            a_set = attr_set_queue.pop(0)
994            for attr, spec in self.attr_sets[a_set].items():
995                if 'nested-attributes' not in spec:
996                    continue
997
998                nested = spec['nested-attributes']
999                if nested not in attr_set_seen:
1000                    attr_set_queue.append(nested)
1001                    attr_set_seen.add(nested)
1002
1003                inherit = set()
1004                if nested not in self.root_sets:
1005                    if nested not in self.pure_nested_structs:
1006                        self.pure_nested_structs[nested] = Struct(self, nested, inherited=inherit)
1007                else:
1008                    raise Exception(f'Using attr set as root and nested not supported - {nested}')
1009
1010                if 'type-value' in spec:
1011                    if nested in self.root_sets:
1012                        raise Exception("Inheriting members to a space used as root not supported")
1013                    inherit.update(set(spec['type-value']))
1014                elif spec['type'] == 'array-nest':
1015                    inherit.add('idx')
1016                self.pure_nested_structs[nested].set_inherited(inherit)
1017
1018        for root_set, rs_members in self.root_sets.items():
1019            for attr, spec in self.attr_sets[root_set].items():
1020                if 'nested-attributes' in spec:
1021                    nested = spec['nested-attributes']
1022                    if attr in rs_members['request']:
1023                        self.pure_nested_structs[nested].request = True
1024                    if attr in rs_members['reply']:
1025                        self.pure_nested_structs[nested].reply = True
1026
1027        # Try to reorder according to dependencies
1028        pns_key_list = list(self.pure_nested_structs.keys())
1029        pns_key_seen = set()
1030        rounds = len(pns_key_list)**2  # it's basically bubble sort
1031        for _ in range(rounds):
1032            if len(pns_key_list) == 0:
1033                break
1034            name = pns_key_list.pop(0)
1035            finished = True
1036            for _, spec in self.attr_sets[name].items():
1037                if 'nested-attributes' in spec:
1038                    if spec['nested-attributes'] not in pns_key_seen:
1039                        # Dicts are sorted, this will make struct last
1040                        struct = self.pure_nested_structs.pop(name)
1041                        self.pure_nested_structs[name] = struct
1042                        finished = False
1043                        break
1044            if finished:
1045                pns_key_seen.add(name)
1046            else:
1047                pns_key_list.append(name)
1048        # Propagate the request / reply
1049        for attr_set, struct in reversed(self.pure_nested_structs.items()):
1050            for _, spec in self.attr_sets[attr_set].items():
1051                if 'nested-attributes' in spec:
1052                    child = self.pure_nested_structs.get(spec['nested-attributes'])
1053                    if child:
1054                        child.request |= struct.request
1055                        child.reply |= struct.reply
1056
1057    def _load_attr_use(self):
1058        for _, struct in self.pure_nested_structs.items():
1059            if struct.request:
1060                for _, arg in struct.member_list():
1061                    arg.request = True
1062            if struct.reply:
1063                for _, arg in struct.member_list():
1064                    arg.reply = True
1065
1066        for root_set, rs_members in self.root_sets.items():
1067            for attr, spec in self.attr_sets[root_set].items():
1068                if attr in rs_members['request']:
1069                    spec.request = True
1070                if attr in rs_members['reply']:
1071                    spec.reply = True
1072
1073    def _load_global_policy(self):
1074        global_set = set()
1075        attr_set_name = None
1076        for op_name, op in self.ops.items():
1077            if not op:
1078                continue
1079            if 'attribute-set' not in op:
1080                continue
1081
1082            if attr_set_name is None:
1083                attr_set_name = op['attribute-set']
1084            if attr_set_name != op['attribute-set']:
1085                raise Exception('For a global policy all ops must use the same set')
1086
1087            for op_mode in ['do', 'dump']:
1088                if op_mode in op:
1089                    req = op[op_mode].get('request')
1090                    if req:
1091                        global_set.update(req.get('attributes', []))
1092
1093        self.global_policy = []
1094        self.global_policy_set = attr_set_name
1095        for attr in self.attr_sets[attr_set_name]:
1096            if attr in global_set:
1097                self.global_policy.append(attr)
1098
1099    def _load_hooks(self):
1100        for op in self.ops.values():
1101            for op_mode in ['do', 'dump']:
1102                if op_mode not in op:
1103                    continue
1104                for when in ['pre', 'post']:
1105                    if when not in op[op_mode]:
1106                        continue
1107                    name = op[op_mode][when]
1108                    if name in self.hooks[when][op_mode]['set']:
1109                        continue
1110                    self.hooks[when][op_mode]['set'].add(name)
1111                    self.hooks[when][op_mode]['list'].append(name)
1112
1113
1114class RenderInfo:
1115    def __init__(self, cw, family, ku_space, op, op_mode, attr_set=None):
1116        self.family = family
1117        self.nl = cw.nlib
1118        self.ku_space = ku_space
1119        self.op_mode = op_mode
1120        self.op = op
1121
1122        # 'do' and 'dump' response parsing is identical
1123        self.type_consistent = True
1124        if op_mode != 'do' and 'dump' in op:
1125            if 'do' in op:
1126                if ('reply' in op['do']) != ('reply' in op["dump"]):
1127                    self.type_consistent = False
1128                elif 'reply' in op['do'] and op["do"]["reply"] != op["dump"]["reply"]:
1129                    self.type_consistent = False
1130            else:
1131                self.type_consistent = False
1132
1133        self.attr_set = attr_set
1134        if not self.attr_set:
1135            self.attr_set = op['attribute-set']
1136
1137        self.type_name_conflict = False
1138        if op:
1139            self.type_name = c_lower(op.name)
1140        else:
1141            self.type_name = c_lower(attr_set)
1142            if attr_set in family.consts:
1143                self.type_name_conflict = True
1144
1145        self.cw = cw
1146
1147        self.struct = dict()
1148        if op_mode == 'notify':
1149            op_mode = 'do'
1150        for op_dir in ['request', 'reply']:
1151            if op:
1152                type_list = []
1153                if op_dir in op[op_mode]:
1154                    type_list = op[op_mode][op_dir]['attributes']
1155                self.struct[op_dir] = Struct(family, self.attr_set, type_list=type_list)
1156        if op_mode == 'event':
1157            self.struct['reply'] = Struct(family, self.attr_set, type_list=op['event']['attributes'])
1158
1159
1160class CodeWriter:
1161    def __init__(self, nlib, out_file=None):
1162        self.nlib = nlib
1163
1164        self._nl = False
1165        self._block_end = False
1166        self._silent_block = False
1167        self._ind = 0
1168        self._ifdef_block = None
1169        if out_file is None:
1170            self._out = os.sys.stdout
1171        else:
1172            self._out = tempfile.NamedTemporaryFile('w+')
1173            self._out_file = out_file
1174
1175    def __del__(self):
1176        self.close_out_file()
1177
1178    def close_out_file(self):
1179        if self._out == os.sys.stdout:
1180            return
1181        # Avoid modifying the file if contents didn't change
1182        self._out.flush()
1183        if os.path.isfile(self._out_file) and filecmp.cmp(self._out.name, self._out_file, shallow=False):
1184            return
1185        with open(self._out_file, 'w+') as out_file:
1186            self._out.seek(0)
1187            shutil.copyfileobj(self._out, out_file)
1188            self._out.close()
1189        self._out = os.sys.stdout
1190
1191    @classmethod
1192    def _is_cond(cls, line):
1193        return line.startswith('if') or line.startswith('while') or line.startswith('for')
1194
1195    def p(self, line, add_ind=0):
1196        if self._block_end:
1197            self._block_end = False
1198            if line.startswith('else'):
1199                line = '} ' + line
1200            else:
1201                self._out.write('\t' * self._ind + '}\n')
1202
1203        if self._nl:
1204            self._out.write('\n')
1205            self._nl = False
1206
1207        ind = self._ind
1208        if line[-1] == ':':
1209            ind -= 1
1210        if self._silent_block:
1211            ind += 1
1212        self._silent_block = line.endswith(')') and CodeWriter._is_cond(line)
1213        if line[0] == '#':
1214            ind = 0
1215        if add_ind:
1216            ind += add_ind
1217        self._out.write('\t' * ind + line + '\n')
1218
1219    def nl(self):
1220        self._nl = True
1221
1222    def block_start(self, line=''):
1223        if line:
1224            line = line + ' '
1225        self.p(line + '{')
1226        self._ind += 1
1227
1228    def block_end(self, line=''):
1229        if line and line[0] not in {';', ','}:
1230            line = ' ' + line
1231        self._ind -= 1
1232        self._nl = False
1233        if not line:
1234            # Delay printing closing bracket in case "else" comes next
1235            if self._block_end:
1236                self._out.write('\t' * (self._ind + 1) + '}\n')
1237            self._block_end = True
1238        else:
1239            self.p('}' + line)
1240
1241    def write_doc_line(self, doc, indent=True):
1242        words = doc.split()
1243        line = ' *'
1244        for word in words:
1245            if len(line) + len(word) >= 79:
1246                self.p(line)
1247                line = ' *'
1248                if indent:
1249                    line += '  '
1250            line += ' ' + word
1251        self.p(line)
1252
1253    def write_func_prot(self, qual_ret, name, args=None, doc=None, suffix=''):
1254        if not args:
1255            args = ['void']
1256
1257        if doc:
1258            self.p('/*')
1259            self.p(' * ' + doc)
1260            self.p(' */')
1261
1262        oneline = qual_ret
1263        if qual_ret[-1] != '*':
1264            oneline += ' '
1265        oneline += f"{name}({', '.join(args)}){suffix}"
1266
1267        if len(oneline) < 80:
1268            self.p(oneline)
1269            return
1270
1271        v = qual_ret
1272        if len(v) > 3:
1273            self.p(v)
1274            v = ''
1275        elif qual_ret[-1] != '*':
1276            v += ' '
1277        v += name + '('
1278        ind = '\t' * (len(v) // 8) + ' ' * (len(v) % 8)
1279        delta_ind = len(v) - len(ind)
1280        v += args[0]
1281        i = 1
1282        while i < len(args):
1283            next_len = len(v) + len(args[i])
1284            if v[0] == '\t':
1285                next_len += delta_ind
1286            if next_len > 76:
1287                self.p(v + ',')
1288                v = ind
1289            else:
1290                v += ', '
1291            v += args[i]
1292            i += 1
1293        self.p(v + ')' + suffix)
1294
1295    def write_func_lvar(self, local_vars):
1296        if not local_vars:
1297            return
1298
1299        if type(local_vars) is str:
1300            local_vars = [local_vars]
1301
1302        local_vars.sort(key=len, reverse=True)
1303        for var in local_vars:
1304            self.p(var)
1305        self.nl()
1306
1307    def write_func(self, qual_ret, name, body, args=None, local_vars=None):
1308        self.write_func_prot(qual_ret=qual_ret, name=name, args=args)
1309        self.write_func_lvar(local_vars=local_vars)
1310
1311        self.block_start()
1312        for line in body:
1313            self.p(line)
1314        self.block_end()
1315
1316    def writes_defines(self, defines):
1317        longest = 0
1318        for define in defines:
1319            if len(define[0]) > longest:
1320                longest = len(define[0])
1321        longest = ((longest + 8) // 8) * 8
1322        for define in defines:
1323            line = '#define ' + define[0]
1324            line += '\t' * ((longest - len(define[0]) + 7) // 8)
1325            if type(define[1]) is int:
1326                line += str(define[1])
1327            elif type(define[1]) is str:
1328                line += '"' + define[1] + '"'
1329            self.p(line)
1330
1331    def write_struct_init(self, members):
1332        longest = max([len(x[0]) for x in members])
1333        longest += 1  # because we prepend a .
1334        longest = ((longest + 8) // 8) * 8
1335        for one in members:
1336            line = '.' + one[0]
1337            line += '\t' * ((longest - len(one[0]) - 1 + 7) // 8)
1338            line += '= ' + str(one[1]) + ','
1339            self.p(line)
1340
1341    def ifdef_block(self, config):
1342        config_option = None
1343        if config:
1344            config_option = 'CONFIG_' + c_upper(config)
1345        if self._ifdef_block == config_option:
1346            return
1347
1348        if self._ifdef_block:
1349            self.p('#endif /* ' + self._ifdef_block + ' */')
1350        if config_option:
1351            self.p('#ifdef ' + config_option)
1352        self._ifdef_block = config_option
1353
1354
1355scalars = {'u8', 'u16', 'u32', 'u64', 's32', 's64', 'uint', 'sint'}
1356
1357direction_to_suffix = {
1358    'reply': '_rsp',
1359    'request': '_req',
1360    '': ''
1361}
1362
1363op_mode_to_wrapper = {
1364    'do': '',
1365    'dump': '_list',
1366    'notify': '_ntf',
1367    'event': '',
1368}
1369
1370_C_KW = {
1371    'auto',
1372    'bool',
1373    'break',
1374    'case',
1375    'char',
1376    'const',
1377    'continue',
1378    'default',
1379    'do',
1380    'double',
1381    'else',
1382    'enum',
1383    'extern',
1384    'float',
1385    'for',
1386    'goto',
1387    'if',
1388    'inline',
1389    'int',
1390    'long',
1391    'register',
1392    'return',
1393    'short',
1394    'signed',
1395    'sizeof',
1396    'static',
1397    'struct',
1398    'switch',
1399    'typedef',
1400    'union',
1401    'unsigned',
1402    'void',
1403    'volatile',
1404    'while'
1405}
1406
1407
1408def rdir(direction):
1409    if direction == 'reply':
1410        return 'request'
1411    if direction == 'request':
1412        return 'reply'
1413    return direction
1414
1415
1416def op_prefix(ri, direction, deref=False):
1417    suffix = f"_{ri.type_name}"
1418
1419    if not ri.op_mode or ri.op_mode == 'do':
1420        suffix += f"{direction_to_suffix[direction]}"
1421    else:
1422        if direction == 'request':
1423            suffix += '_req_dump'
1424        else:
1425            if ri.type_consistent:
1426                if deref:
1427                    suffix += f"{direction_to_suffix[direction]}"
1428                else:
1429                    suffix += op_mode_to_wrapper[ri.op_mode]
1430            else:
1431                suffix += '_rsp'
1432                suffix += '_dump' if deref else '_list'
1433
1434    return f"{ri.family['name']}{suffix}"
1435
1436
1437def type_name(ri, direction, deref=False):
1438    return f"struct {op_prefix(ri, direction, deref=deref)}"
1439
1440
1441def print_prototype(ri, direction, terminate=True, doc=None):
1442    suffix = ';' if terminate else ''
1443
1444    fname = ri.op.render_name
1445    if ri.op_mode == 'dump':
1446        fname += '_dump'
1447
1448    args = ['struct ynl_sock *ys']
1449    if 'request' in ri.op[ri.op_mode]:
1450        args.append(f"{type_name(ri, direction)} *" + f"{direction_to_suffix[direction][1:]}")
1451
1452    ret = 'int'
1453    if 'reply' in ri.op[ri.op_mode]:
1454        ret = f"{type_name(ri, rdir(direction))} *"
1455
1456    ri.cw.write_func_prot(ret, fname, args, doc=doc, suffix=suffix)
1457
1458
1459def print_req_prototype(ri):
1460    print_prototype(ri, "request", doc=ri.op['doc'])
1461
1462
1463def print_dump_prototype(ri):
1464    print_prototype(ri, "request")
1465
1466
1467def put_typol(cw, struct):
1468    type_max = struct.attr_set.max_name
1469    cw.block_start(line=f'struct ynl_policy_attr {struct.render_name}_policy[{type_max} + 1] =')
1470
1471    for _, arg in struct.member_list():
1472        arg.attr_typol(cw)
1473
1474    cw.block_end(line=';')
1475    cw.nl()
1476
1477    cw.block_start(line=f'struct ynl_policy_nest {struct.render_name}_nest =')
1478    cw.p(f'.max_attr = {type_max},')
1479    cw.p(f'.table = {struct.render_name}_policy,')
1480    cw.block_end(line=';')
1481    cw.nl()
1482
1483
1484def _put_enum_to_str_helper(cw, render_name, map_name, arg_name, enum=None):
1485    args = [f'int {arg_name}']
1486    if enum and not ('enum-name' in enum and not enum['enum-name']):
1487        args = [f'enum {render_name} {arg_name}']
1488    cw.write_func_prot('const char *', f'{render_name}_str', args)
1489    cw.block_start()
1490    if enum and enum.type == 'flags':
1491        cw.p(f'{arg_name} = ffs({arg_name}) - 1;')
1492    cw.p(f'if ({arg_name} < 0 || {arg_name} >= (int)MNL_ARRAY_SIZE({map_name}))')
1493    cw.p('return NULL;')
1494    cw.p(f'return {map_name}[{arg_name}];')
1495    cw.block_end()
1496    cw.nl()
1497
1498
1499def put_op_name_fwd(family, cw):
1500    cw.write_func_prot('const char *', f'{family.name}_op_str', ['int op'], suffix=';')
1501
1502
1503def put_op_name(family, cw):
1504    map_name = f'{family.name}_op_strmap'
1505    cw.block_start(line=f"static const char * const {map_name}[] =")
1506    for op_name, op in family.msgs.items():
1507        if op.rsp_value:
1508            if op.req_value == op.rsp_value:
1509                cw.p(f'[{op.enum_name}] = "{op_name}",')
1510            else:
1511                cw.p(f'[{op.rsp_value}] = "{op_name}",')
1512    cw.block_end(line=';')
1513    cw.nl()
1514
1515    _put_enum_to_str_helper(cw, family.name + '_op', map_name, 'op')
1516
1517
1518def put_enum_to_str_fwd(family, cw, enum):
1519    args = [f'enum {enum.render_name} value']
1520    if 'enum-name' in enum and not enum['enum-name']:
1521        args = ['int value']
1522    cw.write_func_prot('const char *', f'{enum.render_name}_str', args, suffix=';')
1523
1524
1525def put_enum_to_str(family, cw, enum):
1526    map_name = f'{enum.render_name}_strmap'
1527    cw.block_start(line=f"static const char * const {map_name}[] =")
1528    for entry in enum.entries.values():
1529        cw.p(f'[{entry.value}] = "{entry.name}",')
1530    cw.block_end(line=';')
1531    cw.nl()
1532
1533    _put_enum_to_str_helper(cw, enum.render_name, map_name, 'value', enum=enum)
1534
1535
1536def put_req_nested(ri, struct):
1537    func_args = ['struct nlmsghdr *nlh',
1538                 'unsigned int attr_type',
1539                 f'{struct.ptr_name}obj']
1540
1541    ri.cw.write_func_prot('int', f'{struct.render_name}_put', func_args)
1542    ri.cw.block_start()
1543    ri.cw.write_func_lvar('struct nlattr *nest;')
1544
1545    ri.cw.p("nest = mnl_attr_nest_start(nlh, attr_type);")
1546
1547    for _, arg in struct.member_list():
1548        arg.attr_put(ri, "obj")
1549
1550    ri.cw.p("mnl_attr_nest_end(nlh, nest);")
1551
1552    ri.cw.nl()
1553    ri.cw.p('return 0;')
1554    ri.cw.block_end()
1555    ri.cw.nl()
1556
1557
1558def _multi_parse(ri, struct, init_lines, local_vars):
1559    if struct.nested:
1560        iter_line = "mnl_attr_for_each_nested(attr, nested)"
1561    else:
1562        iter_line = "mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr))"
1563
1564    array_nests = set()
1565    multi_attrs = set()
1566    needs_parg = False
1567    for arg, aspec in struct.member_list():
1568        if aspec['type'] == 'array-nest':
1569            local_vars.append(f'const struct nlattr *attr_{aspec.c_name};')
1570            array_nests.add(arg)
1571        if 'multi-attr' in aspec:
1572            multi_attrs.add(arg)
1573        needs_parg |= 'nested-attributes' in aspec
1574    if array_nests or multi_attrs:
1575        local_vars.append('int i;')
1576    if needs_parg:
1577        local_vars.append('struct ynl_parse_arg parg;')
1578        init_lines.append('parg.ys = yarg->ys;')
1579
1580    all_multi = array_nests | multi_attrs
1581
1582    for anest in sorted(all_multi):
1583        local_vars.append(f"unsigned int n_{struct[anest].c_name} = 0;")
1584
1585    ri.cw.block_start()
1586    ri.cw.write_func_lvar(local_vars)
1587
1588    for line in init_lines:
1589        ri.cw.p(line)
1590    ri.cw.nl()
1591
1592    for arg in struct.inherited:
1593        ri.cw.p(f'dst->{arg} = {arg};')
1594
1595    for anest in sorted(all_multi):
1596        aspec = struct[anest]
1597        ri.cw.p(f"if (dst->{aspec.c_name})")
1598        ri.cw.p(f'return ynl_error_parse(yarg, "attribute already present ({struct.attr_set.name}.{aspec.name})");')
1599
1600    ri.cw.nl()
1601    ri.cw.block_start(line=iter_line)
1602    ri.cw.p('unsigned int type = mnl_attr_get_type(attr);')
1603    ri.cw.nl()
1604
1605    first = True
1606    for _, arg in struct.member_list():
1607        good = arg.attr_get(ri, 'dst', first=first)
1608        # First may be 'unused' or 'pad', ignore those
1609        first &= not good
1610
1611    ri.cw.block_end()
1612    ri.cw.nl()
1613
1614    for anest in sorted(array_nests):
1615        aspec = struct[anest]
1616
1617        ri.cw.block_start(line=f"if (n_{aspec.c_name})")
1618        ri.cw.p(f"dst->{aspec.c_name} = calloc({aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
1619        ri.cw.p(f"dst->n_{aspec.c_name} = n_{aspec.c_name};")
1620        ri.cw.p('i = 0;')
1621        ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
1622        ri.cw.block_start(line=f"mnl_attr_for_each_nested(attr, attr_{aspec.c_name})")
1623        ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];")
1624        ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr, mnl_attr_get_type(attr)))")
1625        ri.cw.p('return MNL_CB_ERROR;')
1626        ri.cw.p('i++;')
1627        ri.cw.block_end()
1628        ri.cw.block_end()
1629    ri.cw.nl()
1630
1631    for anest in sorted(multi_attrs):
1632        aspec = struct[anest]
1633        ri.cw.block_start(line=f"if (n_{aspec.c_name})")
1634        ri.cw.p(f"dst->{aspec.c_name} = calloc(n_{aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
1635        ri.cw.p(f"dst->n_{aspec.c_name} = n_{aspec.c_name};")
1636        ri.cw.p('i = 0;')
1637        if 'nested-attributes' in aspec:
1638            ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
1639        ri.cw.block_start(line=iter_line)
1640        ri.cw.block_start(line=f"if (mnl_attr_get_type(attr) == {aspec.enum_name})")
1641        if 'nested-attributes' in aspec:
1642            ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];")
1643            ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr))")
1644            ri.cw.p('return MNL_CB_ERROR;')
1645        elif aspec.type in scalars:
1646            ri.cw.p(f"dst->{aspec.c_name}[i] = mnl_attr_get_{aspec.mnl_type()}(attr);")
1647        else:
1648            raise Exception('Nest parsing type not supported yet')
1649        ri.cw.p('i++;')
1650        ri.cw.block_end()
1651        ri.cw.block_end()
1652        ri.cw.block_end()
1653    ri.cw.nl()
1654
1655    if struct.nested:
1656        ri.cw.p('return 0;')
1657    else:
1658        ri.cw.p('return MNL_CB_OK;')
1659    ri.cw.block_end()
1660    ri.cw.nl()
1661
1662
1663def parse_rsp_nested(ri, struct):
1664    func_args = ['struct ynl_parse_arg *yarg',
1665                 'const struct nlattr *nested']
1666    for arg in struct.inherited:
1667        func_args.append('__u32 ' + arg)
1668
1669    local_vars = ['const struct nlattr *attr;',
1670                  f'{struct.ptr_name}dst = yarg->data;']
1671    init_lines = []
1672
1673    ri.cw.write_func_prot('int', f'{struct.render_name}_parse', func_args)
1674
1675    _multi_parse(ri, struct, init_lines, local_vars)
1676
1677
1678def parse_rsp_msg(ri, deref=False):
1679    if 'reply' not in ri.op[ri.op_mode] and ri.op_mode != 'event':
1680        return
1681
1682    func_args = ['const struct nlmsghdr *nlh',
1683                 'void *data']
1684
1685    local_vars = [f'{type_name(ri, "reply", deref=deref)} *dst;',
1686                  'struct ynl_parse_arg *yarg = data;',
1687                  'const struct nlattr *attr;']
1688    init_lines = ['dst = yarg->data;']
1689
1690    ri.cw.write_func_prot('int', f'{op_prefix(ri, "reply", deref=deref)}_parse', func_args)
1691
1692    if ri.struct["reply"].member_list():
1693        _multi_parse(ri, ri.struct["reply"], init_lines, local_vars)
1694    else:
1695        # Empty reply
1696        ri.cw.block_start()
1697        ri.cw.p('return MNL_CB_OK;')
1698        ri.cw.block_end()
1699        ri.cw.nl()
1700
1701
1702def print_req(ri):
1703    ret_ok = '0'
1704    ret_err = '-1'
1705    direction = "request"
1706    local_vars = ['struct nlmsghdr *nlh;',
1707                  'int err;']
1708
1709    if 'reply' in ri.op[ri.op_mode]:
1710        ret_ok = 'rsp'
1711        ret_err = 'NULL'
1712        local_vars += [f'{type_name(ri, rdir(direction))} *rsp;',
1713                       'struct ynl_req_state yrs = { .yarg = { .ys = ys, }, };']
1714
1715    print_prototype(ri, direction, terminate=False)
1716    ri.cw.block_start()
1717    ri.cw.write_func_lvar(local_vars)
1718
1719    ri.cw.p(f"nlh = ynl_gemsg_start_req(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
1720
1721    ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
1722    if 'reply' in ri.op[ri.op_mode]:
1723        ri.cw.p(f"yrs.yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
1724    ri.cw.nl()
1725    for _, attr in ri.struct["request"].member_list():
1726        attr.attr_put(ri, "req")
1727    ri.cw.nl()
1728
1729    parse_arg = "NULL"
1730    if 'reply' in ri.op[ri.op_mode]:
1731        ri.cw.p('rsp = calloc(1, sizeof(*rsp));')
1732        ri.cw.p('yrs.yarg.data = rsp;')
1733        ri.cw.p(f"yrs.cb = {op_prefix(ri, 'reply')}_parse;")
1734        if ri.op.value is not None:
1735            ri.cw.p(f'yrs.rsp_cmd = {ri.op.enum_name};')
1736        else:
1737            ri.cw.p(f'yrs.rsp_cmd = {ri.op.rsp_value};')
1738        ri.cw.nl()
1739        parse_arg = '&yrs'
1740    ri.cw.p(f"err = ynl_exec(ys, nlh, {parse_arg});")
1741    ri.cw.p('if (err < 0)')
1742    if 'reply' in ri.op[ri.op_mode]:
1743        ri.cw.p('goto err_free;')
1744    else:
1745        ri.cw.p('return -1;')
1746    ri.cw.nl()
1747
1748    ri.cw.p(f"return {ret_ok};")
1749    ri.cw.nl()
1750
1751    if 'reply' in ri.op[ri.op_mode]:
1752        ri.cw.p('err_free:')
1753        ri.cw.p(f"{call_free(ri, rdir(direction), 'rsp')}")
1754        ri.cw.p(f"return {ret_err};")
1755
1756    ri.cw.block_end()
1757
1758
1759def print_dump(ri):
1760    direction = "request"
1761    print_prototype(ri, direction, terminate=False)
1762    ri.cw.block_start()
1763    local_vars = ['struct ynl_dump_state yds = {};',
1764                  'struct nlmsghdr *nlh;',
1765                  'int err;']
1766
1767    for var in local_vars:
1768        ri.cw.p(f'{var}')
1769    ri.cw.nl()
1770
1771    ri.cw.p('yds.ys = ys;')
1772    ri.cw.p(f"yds.alloc_sz = sizeof({type_name(ri, rdir(direction))});")
1773    ri.cw.p(f"yds.cb = {op_prefix(ri, 'reply', deref=True)}_parse;")
1774    if ri.op.value is not None:
1775        ri.cw.p(f'yds.rsp_cmd = {ri.op.enum_name};')
1776    else:
1777        ri.cw.p(f'yds.rsp_cmd = {ri.op.rsp_value};')
1778    ri.cw.p(f"yds.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
1779    ri.cw.nl()
1780    ri.cw.p(f"nlh = ynl_gemsg_start_dump(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
1781
1782    if "request" in ri.op[ri.op_mode]:
1783        ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
1784        ri.cw.nl()
1785        for _, attr in ri.struct["request"].member_list():
1786            attr.attr_put(ri, "req")
1787    ri.cw.nl()
1788
1789    ri.cw.p('err = ynl_exec_dump(ys, nlh, &yds);')
1790    ri.cw.p('if (err < 0)')
1791    ri.cw.p('goto free_list;')
1792    ri.cw.nl()
1793
1794    ri.cw.p('return yds.first;')
1795    ri.cw.nl()
1796    ri.cw.p('free_list:')
1797    ri.cw.p(call_free(ri, rdir(direction), 'yds.first'))
1798    ri.cw.p('return NULL;')
1799    ri.cw.block_end()
1800
1801
1802def call_free(ri, direction, var):
1803    return f"{op_prefix(ri, direction)}_free({var});"
1804
1805
1806def free_arg_name(direction):
1807    if direction:
1808        return direction_to_suffix[direction][1:]
1809    return 'obj'
1810
1811
1812def print_alloc_wrapper(ri, direction):
1813    name = op_prefix(ri, direction)
1814    ri.cw.write_func_prot(f'static inline struct {name} *', f"{name}_alloc", [f"void"])
1815    ri.cw.block_start()
1816    ri.cw.p(f'return calloc(1, sizeof(struct {name}));')
1817    ri.cw.block_end()
1818
1819
1820def print_free_prototype(ri, direction, suffix=';'):
1821    name = op_prefix(ri, direction)
1822    struct_name = name
1823    if ri.type_name_conflict:
1824        struct_name += '_'
1825    arg = free_arg_name(direction)
1826    ri.cw.write_func_prot('void', f"{name}_free", [f"struct {struct_name} *{arg}"], suffix=suffix)
1827
1828
1829def _print_type(ri, direction, struct):
1830    suffix = f'_{ri.type_name}{direction_to_suffix[direction]}'
1831    if not direction and ri.type_name_conflict:
1832        suffix += '_'
1833
1834    if ri.op_mode == 'dump':
1835        suffix += '_dump'
1836
1837    ri.cw.block_start(line=f"struct {ri.family['name']}{suffix}")
1838
1839    meta_started = False
1840    for _, attr in struct.member_list():
1841        for type_filter in ['len', 'bit']:
1842            line = attr.presence_member(ri.ku_space, type_filter)
1843            if line:
1844                if not meta_started:
1845                    ri.cw.block_start(line=f"struct")
1846                    meta_started = True
1847                ri.cw.p(line)
1848    if meta_started:
1849        ri.cw.block_end(line='_present;')
1850        ri.cw.nl()
1851
1852    for arg in struct.inherited:
1853        ri.cw.p(f"__u32 {arg};")
1854
1855    for _, attr in struct.member_list():
1856        attr.struct_member(ri)
1857
1858    ri.cw.block_end(line=';')
1859    ri.cw.nl()
1860
1861
1862def print_type(ri, direction):
1863    _print_type(ri, direction, ri.struct[direction])
1864
1865
1866def print_type_full(ri, struct):
1867    _print_type(ri, "", struct)
1868
1869
1870def print_type_helpers(ri, direction, deref=False):
1871    print_free_prototype(ri, direction)
1872    ri.cw.nl()
1873
1874    if ri.ku_space == 'user' and direction == 'request':
1875        for _, attr in ri.struct[direction].member_list():
1876            attr.setter(ri, ri.attr_set, direction, deref=deref)
1877    ri.cw.nl()
1878
1879
1880def print_req_type_helpers(ri):
1881    if len(ri.struct["request"].attr_list) == 0:
1882        return
1883    print_alloc_wrapper(ri, "request")
1884    print_type_helpers(ri, "request")
1885
1886
1887def print_rsp_type_helpers(ri):
1888    if 'reply' not in ri.op[ri.op_mode]:
1889        return
1890    print_type_helpers(ri, "reply")
1891
1892
1893def print_parse_prototype(ri, direction, terminate=True):
1894    suffix = "_rsp" if direction == "reply" else "_req"
1895    term = ';' if terminate else ''
1896
1897    ri.cw.write_func_prot('void', f"{ri.op.render_name}{suffix}_parse",
1898                          ['const struct nlattr **tb',
1899                           f"struct {ri.op.render_name}{suffix} *req"],
1900                          suffix=term)
1901
1902
1903def print_req_type(ri):
1904    if len(ri.struct["request"].attr_list) == 0:
1905        return
1906    print_type(ri, "request")
1907
1908
1909def print_req_free(ri):
1910    if 'request' not in ri.op[ri.op_mode]:
1911        return
1912    _free_type(ri, 'request', ri.struct['request'])
1913
1914
1915def print_rsp_type(ri):
1916    if (ri.op_mode == 'do' or ri.op_mode == 'dump') and 'reply' in ri.op[ri.op_mode]:
1917        direction = 'reply'
1918    elif ri.op_mode == 'event':
1919        direction = 'reply'
1920    else:
1921        return
1922    print_type(ri, direction)
1923
1924
1925def print_wrapped_type(ri):
1926    ri.cw.block_start(line=f"{type_name(ri, 'reply')}")
1927    if ri.op_mode == 'dump':
1928        ri.cw.p(f"{type_name(ri, 'reply')} *next;")
1929    elif ri.op_mode == 'notify' or ri.op_mode == 'event':
1930        ri.cw.p('__u16 family;')
1931        ri.cw.p('__u8 cmd;')
1932        ri.cw.p('struct ynl_ntf_base_type *next;')
1933        ri.cw.p(f"void (*free)({type_name(ri, 'reply')} *ntf);")
1934    ri.cw.p(f"{type_name(ri, 'reply', deref=True)} obj __attribute__((aligned(8)));")
1935    ri.cw.block_end(line=';')
1936    ri.cw.nl()
1937    print_free_prototype(ri, 'reply')
1938    ri.cw.nl()
1939
1940
1941def _free_type_members_iter(ri, struct):
1942    for _, attr in struct.member_list():
1943        if attr.free_needs_iter():
1944            ri.cw.p('unsigned int i;')
1945            ri.cw.nl()
1946            break
1947
1948
1949def _free_type_members(ri, var, struct, ref=''):
1950    for _, attr in struct.member_list():
1951        attr.free(ri, var, ref)
1952
1953
1954def _free_type(ri, direction, struct):
1955    var = free_arg_name(direction)
1956
1957    print_free_prototype(ri, direction, suffix='')
1958    ri.cw.block_start()
1959    _free_type_members_iter(ri, struct)
1960    _free_type_members(ri, var, struct)
1961    if direction:
1962        ri.cw.p(f'free({var});')
1963    ri.cw.block_end()
1964    ri.cw.nl()
1965
1966
1967def free_rsp_nested(ri, struct):
1968    _free_type(ri, "", struct)
1969
1970
1971def print_rsp_free(ri):
1972    if 'reply' not in ri.op[ri.op_mode]:
1973        return
1974    _free_type(ri, 'reply', ri.struct['reply'])
1975
1976
1977def print_dump_type_free(ri):
1978    sub_type = type_name(ri, 'reply')
1979
1980    print_free_prototype(ri, 'reply', suffix='')
1981    ri.cw.block_start()
1982    ri.cw.p(f"{sub_type} *next = rsp;")
1983    ri.cw.nl()
1984    ri.cw.block_start(line='while ((void *)next != YNL_LIST_END)')
1985    _free_type_members_iter(ri, ri.struct['reply'])
1986    ri.cw.p('rsp = next;')
1987    ri.cw.p('next = rsp->next;')
1988    ri.cw.nl()
1989
1990    _free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.')
1991    ri.cw.p(f'free(rsp);')
1992    ri.cw.block_end()
1993    ri.cw.block_end()
1994    ri.cw.nl()
1995
1996
1997def print_ntf_type_free(ri):
1998    print_free_prototype(ri, 'reply', suffix='')
1999    ri.cw.block_start()
2000    _free_type_members_iter(ri, ri.struct['reply'])
2001    _free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.')
2002    ri.cw.p(f'free(rsp);')
2003    ri.cw.block_end()
2004    ri.cw.nl()
2005
2006
2007def print_req_policy_fwd(cw, struct, ri=None, terminate=True):
2008    if terminate and ri and policy_should_be_static(struct.family):
2009        return
2010
2011    if terminate:
2012        prefix = 'extern '
2013    else:
2014        if ri and policy_should_be_static(struct.family):
2015            prefix = 'static '
2016        else:
2017            prefix = ''
2018
2019    suffix = ';' if terminate else ' = {'
2020
2021    max_attr = struct.attr_max_val
2022    if ri:
2023        name = ri.op.render_name
2024        if ri.op.dual_policy:
2025            name += '_' + ri.op_mode
2026    else:
2027        name = struct.render_name
2028    cw.p(f"{prefix}const struct nla_policy {name}_nl_policy[{max_attr.enum_name} + 1]{suffix}")
2029
2030
2031def print_req_policy(cw, struct, ri=None):
2032    if ri and ri.op:
2033        cw.ifdef_block(ri.op.get('config-cond', None))
2034    print_req_policy_fwd(cw, struct, ri=ri, terminate=False)
2035    for _, arg in struct.member_list():
2036        arg.attr_policy(cw)
2037    cw.p("};")
2038    cw.ifdef_block(None)
2039    cw.nl()
2040
2041
2042def kernel_can_gen_family_struct(family):
2043    return family.proto == 'genetlink'
2044
2045
2046def policy_should_be_static(family):
2047    return family.kernel_policy == 'split' or kernel_can_gen_family_struct(family)
2048
2049
2050def print_kernel_policy_ranges(family, cw):
2051    first = True
2052    for _, attr_set in family.attr_sets.items():
2053        if attr_set.subset_of:
2054            continue
2055
2056        for _, attr in attr_set.items():
2057            if not attr.request:
2058                continue
2059            if 'full-range' not in attr.checks:
2060                continue
2061
2062            if first:
2063                cw.p('/* Integer value ranges */')
2064                first = False
2065
2066            sign = '' if attr.type[0] == 'u' else '_signed'
2067            cw.block_start(line=f'static const struct netlink_range_validation{sign} {c_lower(attr.enum_name)}_range =')
2068            members = []
2069            if 'min' in attr.checks:
2070                members.append(('min', attr.get_limit('min')))
2071            if 'max' in attr.checks:
2072                members.append(('max', attr.get_limit('max')))
2073            cw.write_struct_init(members)
2074            cw.block_end(line=';')
2075            cw.nl()
2076
2077
2078def print_kernel_op_table_fwd(family, cw, terminate):
2079    exported = not kernel_can_gen_family_struct(family)
2080
2081    if not terminate or exported:
2082        cw.p(f"/* Ops table for {family.name} */")
2083
2084        pol_to_struct = {'global': 'genl_small_ops',
2085                         'per-op': 'genl_ops',
2086                         'split': 'genl_split_ops'}
2087        struct_type = pol_to_struct[family.kernel_policy]
2088
2089        if not exported:
2090            cnt = ""
2091        elif family.kernel_policy == 'split':
2092            cnt = 0
2093            for op in family.ops.values():
2094                if 'do' in op:
2095                    cnt += 1
2096                if 'dump' in op:
2097                    cnt += 1
2098        else:
2099            cnt = len(family.ops)
2100
2101        qual = 'static const' if not exported else 'const'
2102        line = f"{qual} struct {struct_type} {family.name}_nl_ops[{cnt}]"
2103        if terminate:
2104            cw.p(f"extern {line};")
2105        else:
2106            cw.block_start(line=line + ' =')
2107
2108    if not terminate:
2109        return
2110
2111    cw.nl()
2112    for name in family.hooks['pre']['do']['list']:
2113        cw.write_func_prot('int', c_lower(name),
2114                           ['const struct genl_split_ops *ops',
2115                            'struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
2116    for name in family.hooks['post']['do']['list']:
2117        cw.write_func_prot('void', c_lower(name),
2118                           ['const struct genl_split_ops *ops',
2119                            'struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
2120    for name in family.hooks['pre']['dump']['list']:
2121        cw.write_func_prot('int', c_lower(name),
2122                           ['struct netlink_callback *cb'], suffix=';')
2123    for name in family.hooks['post']['dump']['list']:
2124        cw.write_func_prot('int', c_lower(name),
2125                           ['struct netlink_callback *cb'], suffix=';')
2126
2127    cw.nl()
2128
2129    for op_name, op in family.ops.items():
2130        if op.is_async:
2131            continue
2132
2133        if 'do' in op:
2134            name = c_lower(f"{family.name}-nl-{op_name}-doit")
2135            cw.write_func_prot('int', name,
2136                               ['struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
2137
2138        if 'dump' in op:
2139            name = c_lower(f"{family.name}-nl-{op_name}-dumpit")
2140            cw.write_func_prot('int', name,
2141                               ['struct sk_buff *skb', 'struct netlink_callback *cb'], suffix=';')
2142    cw.nl()
2143
2144
2145def print_kernel_op_table_hdr(family, cw):
2146    print_kernel_op_table_fwd(family, cw, terminate=True)
2147
2148
2149def print_kernel_op_table(family, cw):
2150    print_kernel_op_table_fwd(family, cw, terminate=False)
2151    if family.kernel_policy == 'global' or family.kernel_policy == 'per-op':
2152        for op_name, op in family.ops.items():
2153            if op.is_async:
2154                continue
2155
2156            cw.ifdef_block(op.get('config-cond', None))
2157            cw.block_start()
2158            members = [('cmd', op.enum_name)]
2159            if 'dont-validate' in op:
2160                members.append(('validate',
2161                                ' | '.join([c_upper('genl-dont-validate-' + x)
2162                                            for x in op['dont-validate']])), )
2163            for op_mode in ['do', 'dump']:
2164                if op_mode in op:
2165                    name = c_lower(f"{family.name}-nl-{op_name}-{op_mode}it")
2166                    members.append((op_mode + 'it', name))
2167            if family.kernel_policy == 'per-op':
2168                struct = Struct(family, op['attribute-set'],
2169                                type_list=op['do']['request']['attributes'])
2170
2171                name = c_lower(f"{family.name}-{op_name}-nl-policy")
2172                members.append(('policy', name))
2173                members.append(('maxattr', struct.attr_max_val.enum_name))
2174            if 'flags' in op:
2175                members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in op['flags']])))
2176            cw.write_struct_init(members)
2177            cw.block_end(line=',')
2178    elif family.kernel_policy == 'split':
2179        cb_names = {'do':   {'pre': 'pre_doit', 'post': 'post_doit'},
2180                    'dump': {'pre': 'start', 'post': 'done'}}
2181
2182        for op_name, op in family.ops.items():
2183            for op_mode in ['do', 'dump']:
2184                if op.is_async or op_mode not in op:
2185                    continue
2186
2187                cw.ifdef_block(op.get('config-cond', None))
2188                cw.block_start()
2189                members = [('cmd', op.enum_name)]
2190                if 'dont-validate' in op:
2191                    dont_validate = []
2192                    for x in op['dont-validate']:
2193                        if op_mode == 'do' and x in ['dump', 'dump-strict']:
2194                            continue
2195                        if op_mode == "dump" and x == 'strict':
2196                            continue
2197                        dont_validate.append(x)
2198
2199                    if dont_validate:
2200                        members.append(('validate',
2201                                        ' | '.join([c_upper('genl-dont-validate-' + x)
2202                                                    for x in dont_validate])), )
2203                name = c_lower(f"{family.name}-nl-{op_name}-{op_mode}it")
2204                if 'pre' in op[op_mode]:
2205                    members.append((cb_names[op_mode]['pre'], c_lower(op[op_mode]['pre'])))
2206                members.append((op_mode + 'it', name))
2207                if 'post' in op[op_mode]:
2208                    members.append((cb_names[op_mode]['post'], c_lower(op[op_mode]['post'])))
2209                if 'request' in op[op_mode]:
2210                    struct = Struct(family, op['attribute-set'],
2211                                    type_list=op[op_mode]['request']['attributes'])
2212
2213                    if op.dual_policy:
2214                        name = c_lower(f"{family.name}-{op_name}-{op_mode}-nl-policy")
2215                    else:
2216                        name = c_lower(f"{family.name}-{op_name}-nl-policy")
2217                    members.append(('policy', name))
2218                    members.append(('maxattr', struct.attr_max_val.enum_name))
2219                flags = (op['flags'] if 'flags' in op else []) + ['cmd-cap-' + op_mode]
2220                members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in flags])))
2221                cw.write_struct_init(members)
2222                cw.block_end(line=',')
2223    cw.ifdef_block(None)
2224
2225    cw.block_end(line=';')
2226    cw.nl()
2227
2228
2229def print_kernel_mcgrp_hdr(family, cw):
2230    if not family.mcgrps['list']:
2231        return
2232
2233    cw.block_start('enum')
2234    for grp in family.mcgrps['list']:
2235        grp_id = c_upper(f"{family.name}-nlgrp-{grp['name']},")
2236        cw.p(grp_id)
2237    cw.block_end(';')
2238    cw.nl()
2239
2240
2241def print_kernel_mcgrp_src(family, cw):
2242    if not family.mcgrps['list']:
2243        return
2244
2245    cw.block_start('static const struct genl_multicast_group ' + family.name + '_nl_mcgrps[] =')
2246    for grp in family.mcgrps['list']:
2247        name = grp['name']
2248        grp_id = c_upper(f"{family.name}-nlgrp-{name}")
2249        cw.p('[' + grp_id + '] = { "' + name + '", },')
2250    cw.block_end(';')
2251    cw.nl()
2252
2253
2254def print_kernel_family_struct_hdr(family, cw):
2255    if not kernel_can_gen_family_struct(family):
2256        return
2257
2258    cw.p(f"extern struct genl_family {family.name}_nl_family;")
2259    cw.nl()
2260
2261
2262def print_kernel_family_struct_src(family, cw):
2263    if not kernel_can_gen_family_struct(family):
2264        return
2265
2266    cw.block_start(f"struct genl_family {family.name}_nl_family __ro_after_init =")
2267    cw.p('.name\t\t= ' + family.fam_key + ',')
2268    cw.p('.version\t= ' + family.ver_key + ',')
2269    cw.p('.netnsok\t= true,')
2270    cw.p('.parallel_ops\t= true,')
2271    cw.p('.module\t\t= THIS_MODULE,')
2272    if family.kernel_policy == 'per-op':
2273        cw.p(f'.ops\t\t= {family.name}_nl_ops,')
2274        cw.p(f'.n_ops\t\t= ARRAY_SIZE({family.name}_nl_ops),')
2275    elif family.kernel_policy == 'split':
2276        cw.p(f'.split_ops\t= {family.name}_nl_ops,')
2277        cw.p(f'.n_split_ops\t= ARRAY_SIZE({family.name}_nl_ops),')
2278    if family.mcgrps['list']:
2279        cw.p(f'.mcgrps\t\t= {family.name}_nl_mcgrps,')
2280        cw.p(f'.n_mcgrps\t= ARRAY_SIZE({family.name}_nl_mcgrps),')
2281    cw.block_end(';')
2282
2283
2284def uapi_enum_start(family, cw, obj, ckey='', enum_name='enum-name'):
2285    start_line = 'enum'
2286    if enum_name in obj:
2287        if obj[enum_name]:
2288            start_line = 'enum ' + c_lower(obj[enum_name])
2289    elif ckey and ckey in obj:
2290        start_line = 'enum ' + family.name + '_' + c_lower(obj[ckey])
2291    cw.block_start(line=start_line)
2292
2293
2294def render_uapi(family, cw):
2295    hdr_prot = f"_UAPI_LINUX_{c_upper(family.uapi_header_name)}_H"
2296    cw.p('#ifndef ' + hdr_prot)
2297    cw.p('#define ' + hdr_prot)
2298    cw.nl()
2299
2300    defines = [(family.fam_key, family["name"]),
2301               (family.ver_key, family.get('version', 1))]
2302    cw.writes_defines(defines)
2303    cw.nl()
2304
2305    defines = []
2306    for const in family['definitions']:
2307        if const['type'] != 'const':
2308            cw.writes_defines(defines)
2309            defines = []
2310            cw.nl()
2311
2312        # Write kdoc for enum and flags (one day maybe also structs)
2313        if const['type'] == 'enum' or const['type'] == 'flags':
2314            enum = family.consts[const['name']]
2315
2316            if enum.has_doc():
2317                cw.p('/**')
2318                doc = ''
2319                if 'doc' in enum:
2320                    doc = ' - ' + enum['doc']
2321                cw.write_doc_line(enum.enum_name + doc)
2322                for entry in enum.entries.values():
2323                    if entry.has_doc():
2324                        doc = '@' + entry.c_name + ': ' + entry['doc']
2325                        cw.write_doc_line(doc)
2326                cw.p(' */')
2327
2328            uapi_enum_start(family, cw, const, 'name')
2329            name_pfx = const.get('name-prefix', f"{family.name}-{const['name']}-")
2330            for entry in enum.entries.values():
2331                suffix = ','
2332                if entry.value_change:
2333                    suffix = f" = {entry.user_value()}" + suffix
2334                cw.p(entry.c_name + suffix)
2335
2336            if const.get('render-max', False):
2337                cw.nl()
2338                cw.p('/* private: */')
2339                if const['type'] == 'flags':
2340                    max_name = c_upper(name_pfx + 'mask')
2341                    max_val = f' = {enum.get_mask()},'
2342                    cw.p(max_name + max_val)
2343                else:
2344                    max_name = c_upper(name_pfx + 'max')
2345                    cw.p('__' + max_name + ',')
2346                    cw.p(max_name + ' = (__' + max_name + ' - 1)')
2347            cw.block_end(line=';')
2348            cw.nl()
2349        elif const['type'] == 'const':
2350            defines.append([c_upper(family.get('c-define-name',
2351                                               f"{family.name}-{const['name']}")),
2352                            const['value']])
2353
2354    if defines:
2355        cw.writes_defines(defines)
2356        cw.nl()
2357
2358    max_by_define = family.get('max-by-define', False)
2359
2360    for _, attr_set in family.attr_sets.items():
2361        if attr_set.subset_of:
2362            continue
2363
2364        max_value = f"({attr_set.cnt_name} - 1)"
2365
2366        val = 0
2367        uapi_enum_start(family, cw, attr_set.yaml, 'enum-name')
2368        for _, attr in attr_set.items():
2369            suffix = ','
2370            if attr.value != val:
2371                suffix = f" = {attr.value},"
2372                val = attr.value
2373            val += 1
2374            cw.p(attr.enum_name + suffix)
2375        cw.nl()
2376        cw.p(attr_set.cnt_name + ('' if max_by_define else ','))
2377        if not max_by_define:
2378            cw.p(f"{attr_set.max_name} = {max_value}")
2379        cw.block_end(line=';')
2380        if max_by_define:
2381            cw.p(f"#define {attr_set.max_name} {max_value}")
2382        cw.nl()
2383
2384    # Commands
2385    separate_ntf = 'async-prefix' in family['operations']
2386
2387    max_name = c_upper(family.get('cmd-max-name', f"{family.op_prefix}MAX"))
2388    cnt_name = c_upper(family.get('cmd-cnt-name', f"__{family.op_prefix}MAX"))
2389    max_value = f"({cnt_name} - 1)"
2390
2391    uapi_enum_start(family, cw, family['operations'], 'enum-name')
2392    val = 0
2393    for op in family.msgs.values():
2394        if separate_ntf and ('notify' in op or 'event' in op):
2395            continue
2396
2397        suffix = ','
2398        if op.value != val:
2399            suffix = f" = {op.value},"
2400            val = op.value
2401        cw.p(op.enum_name + suffix)
2402        val += 1
2403    cw.nl()
2404    cw.p(cnt_name + ('' if max_by_define else ','))
2405    if not max_by_define:
2406        cw.p(f"{max_name} = {max_value}")
2407    cw.block_end(line=';')
2408    if max_by_define:
2409        cw.p(f"#define {max_name} {max_value}")
2410    cw.nl()
2411
2412    if separate_ntf:
2413        uapi_enum_start(family, cw, family['operations'], enum_name='async-enum')
2414        for op in family.msgs.values():
2415            if separate_ntf and not ('notify' in op or 'event' in op):
2416                continue
2417
2418            suffix = ','
2419            if 'value' in op:
2420                suffix = f" = {op['value']},"
2421            cw.p(op.enum_name + suffix)
2422        cw.block_end(line=';')
2423        cw.nl()
2424
2425    # Multicast
2426    defines = []
2427    for grp in family.mcgrps['list']:
2428        name = grp['name']
2429        defines.append([c_upper(grp.get('c-define-name', f"{family.name}-mcgrp-{name}")),
2430                        f'{name}'])
2431    cw.nl()
2432    if defines:
2433        cw.writes_defines(defines)
2434        cw.nl()
2435
2436    cw.p(f'#endif /* {hdr_prot} */')
2437
2438
2439def _render_user_ntf_entry(ri, op):
2440    ri.cw.block_start(line=f"[{op.enum_name}] = ")
2441    ri.cw.p(f".alloc_sz\t= sizeof({type_name(ri, 'event')}),")
2442    ri.cw.p(f".cb\t\t= {op_prefix(ri, 'reply', deref=True)}_parse,")
2443    ri.cw.p(f".policy\t\t= &{ri.struct['reply'].render_name}_nest,")
2444    ri.cw.p(f".free\t\t= (void *){op_prefix(ri, 'notify')}_free,")
2445    ri.cw.block_end(line=',')
2446
2447
2448def render_user_family(family, cw, prototype):
2449    symbol = f'const struct ynl_family ynl_{family.c_name}_family'
2450    if prototype:
2451        cw.p(f'extern {symbol};')
2452        return
2453
2454    if family.ntfs:
2455        cw.block_start(line=f"static const struct ynl_ntf_info {family['name']}_ntf_info[] = ")
2456        for ntf_op_name, ntf_op in family.ntfs.items():
2457            if 'notify' in ntf_op:
2458                op = family.ops[ntf_op['notify']]
2459                ri = RenderInfo(cw, family, "user", op, "notify")
2460            elif 'event' in ntf_op:
2461                ri = RenderInfo(cw, family, "user", ntf_op, "event")
2462            else:
2463                raise Exception('Invalid notification ' + ntf_op_name)
2464            _render_user_ntf_entry(ri, ntf_op)
2465        for op_name, op in family.ops.items():
2466            if 'event' not in op:
2467                continue
2468            ri = RenderInfo(cw, family, "user", op, "event")
2469            _render_user_ntf_entry(ri, op)
2470        cw.block_end(line=";")
2471        cw.nl()
2472
2473    cw.block_start(f'{symbol} = ')
2474    cw.p(f'.name\t\t= "{family.name}",')
2475    if family.ntfs:
2476        cw.p(f".ntf_info\t= {family['name']}_ntf_info,")
2477        cw.p(f".ntf_info_size\t= MNL_ARRAY_SIZE({family['name']}_ntf_info),")
2478    cw.block_end(line=';')
2479
2480
2481def family_contains_bitfield32(family):
2482    for _, attr_set in family.attr_sets.items():
2483        if attr_set.subset_of:
2484            continue
2485        for _, attr in attr_set.items():
2486            if attr.type == "bitfield32":
2487                return True
2488    return False
2489
2490
2491def find_kernel_root(full_path):
2492    sub_path = ''
2493    while True:
2494        sub_path = os.path.join(os.path.basename(full_path), sub_path)
2495        full_path = os.path.dirname(full_path)
2496        maintainers = os.path.join(full_path, "MAINTAINERS")
2497        if os.path.exists(maintainers):
2498            return full_path, sub_path[:-1]
2499
2500
2501def main():
2502    parser = argparse.ArgumentParser(description='Netlink simple parsing generator')
2503    parser.add_argument('--mode', dest='mode', type=str, required=True)
2504    parser.add_argument('--spec', dest='spec', type=str, required=True)
2505    parser.add_argument('--header', dest='header', action='store_true', default=None)
2506    parser.add_argument('--source', dest='header', action='store_false')
2507    parser.add_argument('--user-header', nargs='+', default=[])
2508    parser.add_argument('--exclude-op', action='append', default=[])
2509    parser.add_argument('-o', dest='out_file', type=str, default=None)
2510    args = parser.parse_args()
2511
2512    if args.header is None:
2513        parser.error("--header or --source is required")
2514
2515    exclude_ops = [re.compile(expr) for expr in args.exclude_op]
2516
2517    try:
2518        parsed = Family(args.spec, exclude_ops)
2519        if parsed.license != '((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)':
2520            print('Spec license:', parsed.license)
2521            print('License must be: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)')
2522            os.sys.exit(1)
2523    except yaml.YAMLError as exc:
2524        print(exc)
2525        os.sys.exit(1)
2526        return
2527
2528    supported_models = ['unified']
2529    if args.mode in ['user', 'kernel']:
2530        supported_models += ['directional']
2531    if parsed.msg_id_model not in supported_models:
2532        print(f'Message enum-model {parsed.msg_id_model} not supported for {args.mode} generation')
2533        os.sys.exit(1)
2534
2535    cw = CodeWriter(BaseNlLib(), args.out_file)
2536
2537    _, spec_kernel = find_kernel_root(args.spec)
2538    if args.mode == 'uapi' or args.header:
2539        cw.p(f'/* SPDX-License-Identifier: {parsed.license} */')
2540    else:
2541        cw.p(f'// SPDX-License-Identifier: {parsed.license}')
2542    cw.p("/* Do not edit directly, auto-generated from: */")
2543    cw.p(f"/*\t{spec_kernel} */")
2544    cw.p(f"/* YNL-GEN {args.mode} {'header' if args.header else 'source'} */")
2545    if args.exclude_op or args.user_header:
2546        line = ''
2547        line += ' --user-header '.join([''] + args.user_header)
2548        line += ' --exclude-op '.join([''] + args.exclude_op)
2549        cw.p(f'/* YNL-ARG{line} */')
2550    cw.nl()
2551
2552    if args.mode == 'uapi':
2553        render_uapi(parsed, cw)
2554        return
2555
2556    hdr_prot = f"_LINUX_{parsed.name.upper()}_GEN_H"
2557    if args.header:
2558        cw.p('#ifndef ' + hdr_prot)
2559        cw.p('#define ' + hdr_prot)
2560        cw.nl()
2561
2562    if args.mode == 'kernel':
2563        cw.p('#include <net/netlink.h>')
2564        cw.p('#include <net/genetlink.h>')
2565        cw.nl()
2566        if not args.header:
2567            if args.out_file:
2568                cw.p(f'#include "{os.path.basename(args.out_file[:-2])}.h"')
2569            cw.nl()
2570        headers = ['uapi/' + parsed.uapi_header]
2571    else:
2572        cw.p('#include <stdlib.h>')
2573        cw.p('#include <string.h>')
2574        if args.header:
2575            cw.p('#include <linux/types.h>')
2576            if family_contains_bitfield32(parsed):
2577                cw.p('#include <linux/netlink.h>')
2578        else:
2579            cw.p(f'#include "{parsed.name}-user.h"')
2580            cw.p('#include "ynl.h"')
2581        headers = [parsed.uapi_header]
2582    for definition in parsed['definitions']:
2583        if 'header' in definition:
2584            headers.append(definition['header'])
2585    for one in headers:
2586        cw.p(f"#include <{one}>")
2587    cw.nl()
2588
2589    if args.mode == "user":
2590        if not args.header:
2591            cw.p("#include <libmnl/libmnl.h>")
2592            cw.p("#include <linux/genetlink.h>")
2593            cw.nl()
2594            for one in args.user_header:
2595                cw.p(f'#include "{one}"')
2596        else:
2597            cw.p('struct ynl_sock;')
2598            cw.nl()
2599            render_user_family(parsed, cw, True)
2600        cw.nl()
2601
2602    if args.mode == "kernel":
2603        if args.header:
2604            for _, struct in sorted(parsed.pure_nested_structs.items()):
2605                if struct.request:
2606                    cw.p('/* Common nested types */')
2607                    break
2608            for attr_set, struct in sorted(parsed.pure_nested_structs.items()):
2609                if struct.request:
2610                    print_req_policy_fwd(cw, struct)
2611            cw.nl()
2612
2613            if parsed.kernel_policy == 'global':
2614                cw.p(f"/* Global operation policy for {parsed.name} */")
2615
2616                struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy)
2617                print_req_policy_fwd(cw, struct)
2618                cw.nl()
2619
2620            if parsed.kernel_policy in {'per-op', 'split'}:
2621                for op_name, op in parsed.ops.items():
2622                    if 'do' in op and 'event' not in op:
2623                        ri = RenderInfo(cw, parsed, args.mode, op, "do")
2624                        print_req_policy_fwd(cw, ri.struct['request'], ri=ri)
2625                        cw.nl()
2626
2627            print_kernel_op_table_hdr(parsed, cw)
2628            print_kernel_mcgrp_hdr(parsed, cw)
2629            print_kernel_family_struct_hdr(parsed, cw)
2630        else:
2631            print_kernel_policy_ranges(parsed, cw)
2632
2633            for _, struct in sorted(parsed.pure_nested_structs.items()):
2634                if struct.request:
2635                    cw.p('/* Common nested types */')
2636                    break
2637            for attr_set, struct in sorted(parsed.pure_nested_structs.items()):
2638                if struct.request:
2639                    print_req_policy(cw, struct)
2640            cw.nl()
2641
2642            if parsed.kernel_policy == 'global':
2643                cw.p(f"/* Global operation policy for {parsed.name} */")
2644
2645                struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy)
2646                print_req_policy(cw, struct)
2647                cw.nl()
2648
2649            for op_name, op in parsed.ops.items():
2650                if parsed.kernel_policy in {'per-op', 'split'}:
2651                    for op_mode in ['do', 'dump']:
2652                        if op_mode in op and 'request' in op[op_mode]:
2653                            cw.p(f"/* {op.enum_name} - {op_mode} */")
2654                            ri = RenderInfo(cw, parsed, args.mode, op, op_mode)
2655                            print_req_policy(cw, ri.struct['request'], ri=ri)
2656                            cw.nl()
2657
2658            print_kernel_op_table(parsed, cw)
2659            print_kernel_mcgrp_src(parsed, cw)
2660            print_kernel_family_struct_src(parsed, cw)
2661
2662    if args.mode == "user":
2663        if args.header:
2664            cw.p('/* Enums */')
2665            put_op_name_fwd(parsed, cw)
2666
2667            for name, const in parsed.consts.items():
2668                if isinstance(const, EnumSet):
2669                    put_enum_to_str_fwd(parsed, cw, const)
2670            cw.nl()
2671
2672            cw.p('/* Common nested types */')
2673            for attr_set, struct in parsed.pure_nested_structs.items():
2674                ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
2675                print_type_full(ri, struct)
2676
2677            for op_name, op in parsed.ops.items():
2678                cw.p(f"/* ============== {op.enum_name} ============== */")
2679
2680                if 'do' in op and 'event' not in op:
2681                    cw.p(f"/* {op.enum_name} - do */")
2682                    ri = RenderInfo(cw, parsed, args.mode, op, "do")
2683                    print_req_type(ri)
2684                    print_req_type_helpers(ri)
2685                    cw.nl()
2686                    print_rsp_type(ri)
2687                    print_rsp_type_helpers(ri)
2688                    cw.nl()
2689                    print_req_prototype(ri)
2690                    cw.nl()
2691
2692                if 'dump' in op:
2693                    cw.p(f"/* {op.enum_name} - dump */")
2694                    ri = RenderInfo(cw, parsed, args.mode, op, 'dump')
2695                    print_req_type(ri)
2696                    print_req_type_helpers(ri)
2697                    if not ri.type_consistent:
2698                        print_rsp_type(ri)
2699                    print_wrapped_type(ri)
2700                    print_dump_prototype(ri)
2701                    cw.nl()
2702
2703                if op.has_ntf:
2704                    cw.p(f"/* {op.enum_name} - notify */")
2705                    ri = RenderInfo(cw, parsed, args.mode, op, 'notify')
2706                    if not ri.type_consistent:
2707                        raise Exception(f'Only notifications with consistent types supported ({op.name})')
2708                    print_wrapped_type(ri)
2709
2710            for op_name, op in parsed.ntfs.items():
2711                if 'event' in op:
2712                    ri = RenderInfo(cw, parsed, args.mode, op, 'event')
2713                    cw.p(f"/* {op.enum_name} - event */")
2714                    print_rsp_type(ri)
2715                    cw.nl()
2716                    print_wrapped_type(ri)
2717            cw.nl()
2718        else:
2719            cw.p('/* Enums */')
2720            put_op_name(parsed, cw)
2721
2722            for name, const in parsed.consts.items():
2723                if isinstance(const, EnumSet):
2724                    put_enum_to_str(parsed, cw, const)
2725            cw.nl()
2726
2727            cw.p('/* Policies */')
2728            for name in parsed.pure_nested_structs:
2729                struct = Struct(parsed, name)
2730                put_typol(cw, struct)
2731            for name in parsed.root_sets:
2732                struct = Struct(parsed, name)
2733                put_typol(cw, struct)
2734
2735            cw.p('/* Common nested types */')
2736            for attr_set, struct in parsed.pure_nested_structs.items():
2737                ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
2738
2739                free_rsp_nested(ri, struct)
2740                if struct.request:
2741                    put_req_nested(ri, struct)
2742                if struct.reply:
2743                    parse_rsp_nested(ri, struct)
2744
2745            for op_name, op in parsed.ops.items():
2746                cw.p(f"/* ============== {op.enum_name} ============== */")
2747                if 'do' in op and 'event' not in op:
2748                    cw.p(f"/* {op.enum_name} - do */")
2749                    ri = RenderInfo(cw, parsed, args.mode, op, "do")
2750                    print_req_free(ri)
2751                    print_rsp_free(ri)
2752                    parse_rsp_msg(ri)
2753                    print_req(ri)
2754                    cw.nl()
2755
2756                if 'dump' in op:
2757                    cw.p(f"/* {op.enum_name} - dump */")
2758                    ri = RenderInfo(cw, parsed, args.mode, op, "dump")
2759                    if not ri.type_consistent:
2760                        parse_rsp_msg(ri, deref=True)
2761                    print_dump_type_free(ri)
2762                    print_dump(ri)
2763                    cw.nl()
2764
2765                if op.has_ntf:
2766                    cw.p(f"/* {op.enum_name} - notify */")
2767                    ri = RenderInfo(cw, parsed, args.mode, op, 'notify')
2768                    if not ri.type_consistent:
2769                        raise Exception(f'Only notifications with consistent types supported ({op.name})')
2770                    print_ntf_type_free(ri)
2771
2772            for op_name, op in parsed.ntfs.items():
2773                if 'event' in op:
2774                    cw.p(f"/* {op.enum_name} - event */")
2775
2776                    ri = RenderInfo(cw, parsed, args.mode, op, "do")
2777                    parse_rsp_msg(ri)
2778
2779                    ri = RenderInfo(cw, parsed, args.mode, op, "event")
2780                    print_ntf_type_free(ri)
2781            cw.nl()
2782            render_user_family(parsed, cw, False)
2783
2784    if args.header:
2785        cw.p(f'#endif /* {hdr_prot} */')
2786
2787
2788if __name__ == "__main__":
2789    main()
2790