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