tools: ynl-gen: cleanup user space header includes
[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._silent_block = False
1021         self._ind = 0
1022         self._out = out_file
1023
1024     @classmethod
1025     def _is_cond(cls, line):
1026         return line.startswith('if') or line.startswith('while') or line.startswith('for')
1027
1028     def p(self, line, add_ind=0, eat_nl=False):
1029         if self._nl:
1030             if not eat_nl:
1031                 self._out.write('\n')
1032             self._nl = False
1033         ind = self._ind
1034         if line[-1] == ':':
1035             ind -= 1
1036         if self._silent_block:
1037             ind += 1
1038         self._silent_block = line.endswith(')') and CodeWriter._is_cond(line)
1039         if add_ind:
1040             ind += add_ind
1041         self._out.write('\t' * ind + line + '\n')
1042
1043     def nl(self):
1044         self._nl = True
1045
1046     def block_start(self, line=''):
1047         if line:
1048             line = line + ' '
1049         self.p(line + '{')
1050         self._ind += 1
1051
1052     def block_end(self, line=''):
1053         if line and line[0] not in {';', ','}:
1054             line = ' ' + line
1055         self._ind -= 1
1056         self.p('}' + line, eat_nl=True)
1057
1058     def write_doc_line(self, doc, indent=True):
1059         words = doc.split()
1060         line = ' *'
1061         for word in words:
1062             if len(line) + len(word) >= 79:
1063                 self.p(line)
1064                 line = ' *'
1065                 if indent:
1066                     line += '  '
1067             line += ' ' + word
1068         self.p(line)
1069
1070     def write_func_prot(self, qual_ret, name, args=None, doc=None, suffix=''):
1071         if not args:
1072             args = ['void']
1073
1074         if doc:
1075             self.p('/*')
1076             self.p(' * ' + doc)
1077             self.p(' */')
1078
1079         oneline = qual_ret
1080         if qual_ret[-1] != '*':
1081             oneline += ' '
1082         oneline += f"{name}({', '.join(args)}){suffix}"
1083
1084         if len(oneline) < 80:
1085             self.p(oneline)
1086             return
1087
1088         v = qual_ret
1089         if len(v) > 3:
1090             self.p(v)
1091             v = ''
1092         elif qual_ret[-1] != '*':
1093             v += ' '
1094         v += name + '('
1095         ind = '\t' * (len(v) // 8) + ' ' * (len(v) % 8)
1096         delta_ind = len(v) - len(ind)
1097         v += args[0]
1098         i = 1
1099         while i < len(args):
1100             next_len = len(v) + len(args[i])
1101             if v[0] == '\t':
1102                 next_len += delta_ind
1103             if next_len > 76:
1104                 self.p(v + ',')
1105                 v = ind
1106             else:
1107                 v += ', '
1108             v += args[i]
1109             i += 1
1110         self.p(v + ')' + suffix)
1111
1112     def write_func_lvar(self, local_vars):
1113         if not local_vars:
1114             return
1115
1116         if type(local_vars) is str:
1117             local_vars = [local_vars]
1118
1119         local_vars.sort(key=len, reverse=True)
1120         for var in local_vars:
1121             self.p(var)
1122         self.nl()
1123
1124     def write_func(self, qual_ret, name, body, args=None, local_vars=None):
1125         self.write_func_prot(qual_ret=qual_ret, name=name, args=args)
1126         self.write_func_lvar(local_vars=local_vars)
1127
1128         self.block_start()
1129         for line in body:
1130             self.p(line)
1131         self.block_end()
1132
1133     def writes_defines(self, defines):
1134         longest = 0
1135         for define in defines:
1136             if len(define[0]) > longest:
1137                 longest = len(define[0])
1138         longest = ((longest + 8) // 8) * 8
1139         for define in defines:
1140             line = '#define ' + define[0]
1141             line += '\t' * ((longest - len(define[0]) + 7) // 8)
1142             if type(define[1]) is int:
1143                 line += str(define[1])
1144             elif type(define[1]) is str:
1145                 line += '"' + define[1] + '"'
1146             self.p(line)
1147
1148     def write_struct_init(self, members):
1149         longest = max([len(x[0]) for x in members])
1150         longest += 1  # because we prepend a .
1151         longest = ((longest + 8) // 8) * 8
1152         for one in members:
1153             line = '.' + one[0]
1154             line += '\t' * ((longest - len(one[0]) - 1 + 7) // 8)
1155             line += '= ' + one[1] + ','
1156             self.p(line)
1157
1158
1159 scalars = {'u8', 'u16', 'u32', 'u64', 's32', 's64'}
1160
1161 direction_to_suffix = {
1162     'reply': '_rsp',
1163     'request': '_req',
1164     '': ''
1165 }
1166
1167 op_mode_to_wrapper = {
1168     'do': '',
1169     'dump': '_list',
1170     'notify': '_ntf',
1171     'event': '',
1172 }
1173
1174 _C_KW = {
1175     'do'
1176 }
1177
1178
1179 def rdir(direction):
1180     if direction == 'reply':
1181         return 'request'
1182     if direction == 'request':
1183         return 'reply'
1184     return direction
1185
1186
1187 def op_prefix(ri, direction, deref=False):
1188     suffix = f"_{ri.type_name}"
1189
1190     if not ri.op_mode or ri.op_mode == 'do':
1191         suffix += f"{direction_to_suffix[direction]}"
1192     else:
1193         if direction == 'request':
1194             suffix += '_req_dump'
1195         else:
1196             if ri.type_consistent:
1197                 if deref:
1198                     suffix += f"{direction_to_suffix[direction]}"
1199                 else:
1200                     suffix += op_mode_to_wrapper[ri.op_mode]
1201             else:
1202                 suffix += '_rsp'
1203                 suffix += '_dump' if deref else '_list'
1204
1205     return f"{ri.family['name']}{suffix}"
1206
1207
1208 def type_name(ri, direction, deref=False):
1209     return f"struct {op_prefix(ri, direction, deref=deref)}"
1210
1211
1212 def print_prototype(ri, direction, terminate=True, doc=None):
1213     suffix = ';' if terminate else ''
1214
1215     fname = ri.op.render_name
1216     if ri.op_mode == 'dump':
1217         fname += '_dump'
1218
1219     args = ['struct ynl_sock *ys']
1220     if 'request' in ri.op[ri.op_mode]:
1221         args.append(f"{type_name(ri, direction)} *" + f"{direction_to_suffix[direction][1:]}")
1222
1223     ret = 'int'
1224     if 'reply' in ri.op[ri.op_mode]:
1225         ret = f"{type_name(ri, rdir(direction))} *"
1226
1227     ri.cw.write_func_prot(ret, fname, args, doc=doc, suffix=suffix)
1228
1229
1230 def print_req_prototype(ri):
1231     print_prototype(ri, "request", doc=ri.op['doc'])
1232
1233
1234 def print_dump_prototype(ri):
1235     print_prototype(ri, "request")
1236
1237
1238 def put_typol(cw, struct):
1239     type_max = struct.attr_set.max_name
1240     cw.block_start(line=f'struct ynl_policy_attr {struct.render_name}_policy[{type_max} + 1] =')
1241
1242     for _, arg in struct.member_list():
1243         arg.attr_typol(cw)
1244
1245     cw.block_end(line=';')
1246     cw.nl()
1247
1248     cw.block_start(line=f'struct ynl_policy_nest {struct.render_name}_nest =')
1249     cw.p(f'.max_attr = {type_max},')
1250     cw.p(f'.table = {struct.render_name}_policy,')
1251     cw.block_end(line=';')
1252     cw.nl()
1253
1254
1255 def _put_enum_to_str_helper(cw, render_name, map_name, arg_name, enum=None):
1256     args = [f'int {arg_name}']
1257     if enum and not ('enum-name' in enum and not enum['enum-name']):
1258         args = [f'enum {render_name} {arg_name}']
1259     cw.write_func_prot('const char *', f'{render_name}_str', args)
1260     cw.block_start()
1261     if enum and enum.type == 'flags':
1262         cw.p(f'{arg_name} = ffs({arg_name}) - 1;')
1263     cw.p(f'if ({arg_name} < 0 || {arg_name} >= (int)MNL_ARRAY_SIZE({map_name}))')
1264     cw.p('return NULL;')
1265     cw.p(f'return {map_name}[{arg_name}];')
1266     cw.block_end()
1267     cw.nl()
1268
1269
1270 def put_op_name_fwd(family, cw):
1271     cw.write_func_prot('const char *', f'{family.name}_op_str', ['int op'], suffix=';')
1272
1273
1274 def put_op_name(family, cw):
1275     map_name = f'{family.name}_op_strmap'
1276     cw.block_start(line=f"static const char * const {map_name}[] =")
1277     for op_name, op in family.msgs.items():
1278         if op.rsp_value:
1279             if op.req_value == op.rsp_value:
1280                 cw.p(f'[{op.enum_name}] = "{op_name}",')
1281             else:
1282                 cw.p(f'[{op.rsp_value}] = "{op_name}",')
1283     cw.block_end(line=';')
1284     cw.nl()
1285
1286     _put_enum_to_str_helper(cw, family.name + '_op', map_name, 'op')
1287
1288
1289 def put_enum_to_str_fwd(family, cw, enum):
1290     args = [f'enum {enum.render_name} value']
1291     if 'enum-name' in enum and not enum['enum-name']:
1292         args = ['int value']
1293     cw.write_func_prot('const char *', f'{enum.render_name}_str', args, suffix=';')
1294
1295
1296 def put_enum_to_str(family, cw, enum):
1297     map_name = f'{enum.render_name}_strmap'
1298     cw.block_start(line=f"static const char * const {map_name}[] =")
1299     for entry in enum.entries.values():
1300         cw.p(f'[{entry.value}] = "{entry.name}",')
1301     cw.block_end(line=';')
1302     cw.nl()
1303
1304     _put_enum_to_str_helper(cw, enum.render_name, map_name, 'value', enum=enum)
1305
1306
1307 def put_req_nested(ri, struct):
1308     func_args = ['struct nlmsghdr *nlh',
1309                  'unsigned int attr_type',
1310                  f'{struct.ptr_name}obj']
1311
1312     ri.cw.write_func_prot('int', f'{struct.render_name}_put', func_args)
1313     ri.cw.block_start()
1314     ri.cw.write_func_lvar('struct nlattr *nest;')
1315
1316     ri.cw.p("nest = mnl_attr_nest_start(nlh, attr_type);")
1317
1318     for _, arg in struct.member_list():
1319         arg.attr_put(ri, "obj")
1320
1321     ri.cw.p("mnl_attr_nest_end(nlh, nest);")
1322
1323     ri.cw.nl()
1324     ri.cw.p('return 0;')
1325     ri.cw.block_end()
1326     ri.cw.nl()
1327
1328
1329 def _multi_parse(ri, struct, init_lines, local_vars):
1330     if struct.nested:
1331         iter_line = "mnl_attr_for_each_nested(attr, nested)"
1332     else:
1333         iter_line = "mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr))"
1334
1335     array_nests = set()
1336     multi_attrs = set()
1337     needs_parg = False
1338     for arg, aspec in struct.member_list():
1339         if aspec['type'] == 'array-nest':
1340             local_vars.append(f'const struct nlattr *attr_{aspec.c_name};')
1341             array_nests.add(arg)
1342         if 'multi-attr' in aspec:
1343             multi_attrs.add(arg)
1344         needs_parg |= 'nested-attributes' in aspec
1345     if array_nests or multi_attrs:
1346         local_vars.append('int i;')
1347     if needs_parg:
1348         local_vars.append('struct ynl_parse_arg parg;')
1349         init_lines.append('parg.ys = yarg->ys;')
1350
1351     all_multi = array_nests | multi_attrs
1352
1353     for anest in sorted(all_multi):
1354         local_vars.append(f"unsigned int n_{struct[anest].c_name} = 0;")
1355
1356     ri.cw.block_start()
1357     ri.cw.write_func_lvar(local_vars)
1358
1359     for line in init_lines:
1360         ri.cw.p(line)
1361     ri.cw.nl()
1362
1363     for arg in struct.inherited:
1364         ri.cw.p(f'dst->{arg} = {arg};')
1365
1366     for anest in sorted(all_multi):
1367         aspec = struct[anest]
1368         ri.cw.p(f"if (dst->{aspec.c_name})")
1369         ri.cw.p(f'return ynl_error_parse(yarg, "attribute already present ({struct.attr_set.name}.{aspec.name})");')
1370
1371     ri.cw.nl()
1372     ri.cw.block_start(line=iter_line)
1373
1374     first = True
1375     for _, arg in struct.member_list():
1376         good = arg.attr_get(ri, 'dst', first=first)
1377         # First may be 'unused' or 'pad', ignore those
1378         first &= not good
1379
1380     ri.cw.block_end()
1381     ri.cw.nl()
1382
1383     for anest in sorted(array_nests):
1384         aspec = struct[anest]
1385
1386         ri.cw.block_start(line=f"if (n_{aspec.c_name})")
1387         ri.cw.p(f"dst->{aspec.c_name} = calloc({aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
1388         ri.cw.p(f"dst->n_{aspec.c_name} = n_{aspec.c_name};")
1389         ri.cw.p('i = 0;')
1390         ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
1391         ri.cw.block_start(line=f"mnl_attr_for_each_nested(attr, attr_{aspec.c_name})")
1392         ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];")
1393         ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr, mnl_attr_get_type(attr)))")
1394         ri.cw.p('return MNL_CB_ERROR;')
1395         ri.cw.p('i++;')
1396         ri.cw.block_end()
1397         ri.cw.block_end()
1398     ri.cw.nl()
1399
1400     for anest in sorted(multi_attrs):
1401         aspec = struct[anest]
1402         ri.cw.block_start(line=f"if (n_{aspec.c_name})")
1403         ri.cw.p(f"dst->{aspec.c_name} = calloc(n_{aspec.c_name}, sizeof(*dst->{aspec.c_name}));")
1404         ri.cw.p(f"dst->n_{aspec.c_name} = n_{aspec.c_name};")
1405         ri.cw.p('i = 0;')
1406         if 'nested-attributes' in aspec:
1407             ri.cw.p(f"parg.rsp_policy = &{aspec.nested_render_name}_nest;")
1408         ri.cw.block_start(line=iter_line)
1409         ri.cw.block_start(line=f"if (mnl_attr_get_type(attr) == {aspec.enum_name})")
1410         if 'nested-attributes' in aspec:
1411             ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];")
1412             ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr))")
1413             ri.cw.p('return MNL_CB_ERROR;')
1414         elif aspec['type'] in scalars:
1415             t = aspec['type']
1416             if t[0] == 's':
1417                 t = 'u' + t[1:]
1418             ri.cw.p(f"dst->{aspec.c_name}[i] = mnl_attr_get_{t}(attr);")
1419         else:
1420             raise Exception('Nest parsing type not supported yet')
1421         ri.cw.p('i++;')
1422         ri.cw.block_end()
1423         ri.cw.block_end()
1424         ri.cw.block_end()
1425     ri.cw.nl()
1426
1427     if struct.nested:
1428         ri.cw.p('return 0;')
1429     else:
1430         ri.cw.p('return MNL_CB_OK;')
1431     ri.cw.block_end()
1432     ri.cw.nl()
1433
1434
1435 def parse_rsp_nested(ri, struct):
1436     func_args = ['struct ynl_parse_arg *yarg',
1437                  'const struct nlattr *nested']
1438     for arg in struct.inherited:
1439         func_args.append('__u32 ' + arg)
1440
1441     local_vars = ['const struct nlattr *attr;',
1442                   f'{struct.ptr_name}dst = yarg->data;']
1443     init_lines = []
1444
1445     ri.cw.write_func_prot('int', f'{struct.render_name}_parse', func_args)
1446
1447     _multi_parse(ri, struct, init_lines, local_vars)
1448
1449
1450 def parse_rsp_msg(ri, deref=False):
1451     if 'reply' not in ri.op[ri.op_mode] and ri.op_mode != 'event':
1452         return
1453
1454     func_args = ['const struct nlmsghdr *nlh',
1455                  'void *data']
1456
1457     local_vars = [f'{type_name(ri, "reply", deref=deref)} *dst;',
1458                   'struct ynl_parse_arg *yarg = data;',
1459                   'const struct nlattr *attr;']
1460     init_lines = ['dst = yarg->data;']
1461
1462     ri.cw.write_func_prot('int', f'{op_prefix(ri, "reply", deref=deref)}_parse', func_args)
1463
1464     _multi_parse(ri, ri.struct["reply"], init_lines, local_vars)
1465
1466
1467 def print_req(ri):
1468     ret_ok = '0'
1469     ret_err = '-1'
1470     direction = "request"
1471     local_vars = ['struct nlmsghdr *nlh;',
1472                   'int err;']
1473
1474     if 'reply' in ri.op[ri.op_mode]:
1475         ret_ok = 'rsp'
1476         ret_err = 'NULL'
1477         local_vars += [f'{type_name(ri, rdir(direction))} *rsp;',
1478                        'struct ynl_req_state yrs = { .yarg = { .ys = ys, }, };']
1479
1480     print_prototype(ri, direction, terminate=False)
1481     ri.cw.block_start()
1482     ri.cw.write_func_lvar(local_vars)
1483
1484     ri.cw.p(f"nlh = ynl_gemsg_start_req(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
1485
1486     ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
1487     if 'reply' in ri.op[ri.op_mode]:
1488         ri.cw.p(f"yrs.yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
1489     ri.cw.nl()
1490     for _, attr in ri.struct["request"].member_list():
1491         attr.attr_put(ri, "req")
1492     ri.cw.nl()
1493
1494     parse_arg = "NULL"
1495     if 'reply' in ri.op[ri.op_mode]:
1496         ri.cw.p('rsp = calloc(1, sizeof(*rsp));')
1497         ri.cw.p('yrs.yarg.data = rsp;')
1498         ri.cw.p(f"yrs.cb = {op_prefix(ri, 'reply')}_parse;")
1499         if ri.op.value is not None:
1500             ri.cw.p(f'yrs.rsp_cmd = {ri.op.enum_name};')
1501         else:
1502             ri.cw.p(f'yrs.rsp_cmd = {ri.op.rsp_value};')
1503         ri.cw.nl()
1504         parse_arg = '&yrs'
1505     ri.cw.p(f"err = ynl_exec(ys, nlh, {parse_arg});")
1506     ri.cw.p('if (err < 0)')
1507     if 'reply' in ri.op[ri.op_mode]:
1508         ri.cw.p('goto err_free;')
1509     else:
1510         ri.cw.p('return -1;')
1511     ri.cw.nl()
1512
1513     ri.cw.p(f"return {ret_ok};")
1514     ri.cw.nl()
1515
1516     if 'reply' in ri.op[ri.op_mode]:
1517         ri.cw.p('err_free:')
1518         ri.cw.p(f"{call_free(ri, rdir(direction), 'rsp')}")
1519         ri.cw.p(f"return {ret_err};")
1520
1521     ri.cw.block_end()
1522
1523
1524 def print_dump(ri):
1525     direction = "request"
1526     print_prototype(ri, direction, terminate=False)
1527     ri.cw.block_start()
1528     local_vars = ['struct ynl_dump_state yds = {};',
1529                   'struct nlmsghdr *nlh;',
1530                   'int err;']
1531
1532     for var in local_vars:
1533         ri.cw.p(f'{var}')
1534     ri.cw.nl()
1535
1536     ri.cw.p('yds.ys = ys;')
1537     ri.cw.p(f"yds.alloc_sz = sizeof({type_name(ri, rdir(direction))});")
1538     ri.cw.p(f"yds.cb = {op_prefix(ri, 'reply', deref=True)}_parse;")
1539     if ri.op.value is not None:
1540         ri.cw.p(f'yds.rsp_cmd = {ri.op.enum_name};')
1541     else:
1542         ri.cw.p(f'yds.rsp_cmd = {ri.op.rsp_value};')
1543     ri.cw.p(f"yds.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
1544     ri.cw.nl()
1545     ri.cw.p(f"nlh = ynl_gemsg_start_dump(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);")
1546
1547     if "request" in ri.op[ri.op_mode]:
1548         ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;")
1549         ri.cw.nl()
1550         for _, attr in ri.struct["request"].member_list():
1551             attr.attr_put(ri, "req")
1552     ri.cw.nl()
1553
1554     ri.cw.p('err = ynl_exec_dump(ys, nlh, &yds);')
1555     ri.cw.p('if (err < 0)')
1556     ri.cw.p('goto free_list;')
1557     ri.cw.nl()
1558
1559     ri.cw.p('return yds.first;')
1560     ri.cw.nl()
1561     ri.cw.p('free_list:')
1562     ri.cw.p(call_free(ri, rdir(direction), 'yds.first'))
1563     ri.cw.p('return NULL;')
1564     ri.cw.block_end()
1565
1566
1567 def call_free(ri, direction, var):
1568     return f"{op_prefix(ri, direction)}_free({var});"
1569
1570
1571 def free_arg_name(direction):
1572     if direction:
1573         return direction_to_suffix[direction][1:]
1574     return 'obj'
1575
1576
1577 def print_alloc_wrapper(ri, direction):
1578     name = op_prefix(ri, direction)
1579     ri.cw.write_func_prot(f'static inline struct {name} *', f"{name}_alloc", [f"void"])
1580     ri.cw.block_start()
1581     ri.cw.p(f'return calloc(1, sizeof(struct {name}));')
1582     ri.cw.block_end()
1583
1584
1585 def print_free_prototype(ri, direction, suffix=';'):
1586     name = op_prefix(ri, direction)
1587     arg = free_arg_name(direction)
1588     ri.cw.write_func_prot('void', f"{name}_free", [f"struct {name} *{arg}"], suffix=suffix)
1589
1590
1591 def _print_type(ri, direction, struct):
1592     suffix = f'_{ri.type_name}{direction_to_suffix[direction]}'
1593
1594     if ri.op_mode == 'dump':
1595         suffix += '_dump'
1596
1597     ri.cw.block_start(line=f"struct {ri.family['name']}{suffix}")
1598
1599     meta_started = False
1600     for _, attr in struct.member_list():
1601         for type_filter in ['len', 'bit']:
1602             line = attr.presence_member(ri.ku_space, type_filter)
1603             if line:
1604                 if not meta_started:
1605                     ri.cw.block_start(line=f"struct")
1606                     meta_started = True
1607                 ri.cw.p(line)
1608     if meta_started:
1609         ri.cw.block_end(line='_present;')
1610         ri.cw.nl()
1611
1612     for arg in struct.inherited:
1613         ri.cw.p(f"__u32 {arg};")
1614
1615     for _, attr in struct.member_list():
1616         attr.struct_member(ri)
1617
1618     ri.cw.block_end(line=';')
1619     ri.cw.nl()
1620
1621
1622 def print_type(ri, direction):
1623     _print_type(ri, direction, ri.struct[direction])
1624
1625
1626 def print_type_full(ri, struct):
1627     _print_type(ri, "", struct)
1628
1629
1630 def print_type_helpers(ri, direction, deref=False):
1631     print_free_prototype(ri, direction)
1632     ri.cw.nl()
1633
1634     if ri.ku_space == 'user' and direction == 'request':
1635         for _, attr in ri.struct[direction].member_list():
1636             attr.setter(ri, ri.attr_set, direction, deref=deref)
1637     ri.cw.nl()
1638
1639
1640 def print_req_type_helpers(ri):
1641     print_alloc_wrapper(ri, "request")
1642     print_type_helpers(ri, "request")
1643
1644
1645 def print_rsp_type_helpers(ri):
1646     if 'reply' not in ri.op[ri.op_mode]:
1647         return
1648     print_type_helpers(ri, "reply")
1649
1650
1651 def print_parse_prototype(ri, direction, terminate=True):
1652     suffix = "_rsp" if direction == "reply" else "_req"
1653     term = ';' if terminate else ''
1654
1655     ri.cw.write_func_prot('void', f"{ri.op.render_name}{suffix}_parse",
1656                           ['const struct nlattr **tb',
1657                            f"struct {ri.op.render_name}{suffix} *req"],
1658                           suffix=term)
1659
1660
1661 def print_req_type(ri):
1662     print_type(ri, "request")
1663
1664
1665 def print_req_free(ri):
1666     if 'request' not in ri.op[ri.op_mode]:
1667         return
1668     _free_type(ri, 'request', ri.struct['request'])
1669
1670
1671 def print_rsp_type(ri):
1672     if (ri.op_mode == 'do' or ri.op_mode == 'dump') and 'reply' in ri.op[ri.op_mode]:
1673         direction = 'reply'
1674     elif ri.op_mode == 'event':
1675         direction = 'reply'
1676     else:
1677         return
1678     print_type(ri, direction)
1679
1680
1681 def print_wrapped_type(ri):
1682     ri.cw.block_start(line=f"{type_name(ri, 'reply')}")
1683     if ri.op_mode == 'dump':
1684         ri.cw.p(f"{type_name(ri, 'reply')} *next;")
1685     elif ri.op_mode == 'notify' or ri.op_mode == 'event':
1686         ri.cw.p('__u16 family;')
1687         ri.cw.p('__u8 cmd;')
1688         ri.cw.p('struct ynl_ntf_base_type *next;')
1689         ri.cw.p(f"void (*free)({type_name(ri, 'reply')} *ntf);")
1690     ri.cw.p(f"{type_name(ri, 'reply', deref=True)} obj __attribute__ ((aligned (8)));")
1691     ri.cw.block_end(line=';')
1692     ri.cw.nl()
1693     print_free_prototype(ri, 'reply')
1694     ri.cw.nl()
1695
1696
1697 def _free_type_members_iter(ri, struct):
1698     for _, attr in struct.member_list():
1699         if attr.free_needs_iter():
1700             ri.cw.p('unsigned int i;')
1701             ri.cw.nl()
1702             break
1703
1704
1705 def _free_type_members(ri, var, struct, ref=''):
1706     for _, attr in struct.member_list():
1707         attr.free(ri, var, ref)
1708
1709
1710 def _free_type(ri, direction, struct):
1711     var = free_arg_name(direction)
1712
1713     print_free_prototype(ri, direction, suffix='')
1714     ri.cw.block_start()
1715     _free_type_members_iter(ri, struct)
1716     _free_type_members(ri, var, struct)
1717     if direction:
1718         ri.cw.p(f'free({var});')
1719     ri.cw.block_end()
1720     ri.cw.nl()
1721
1722
1723 def free_rsp_nested(ri, struct):
1724     _free_type(ri, "", struct)
1725
1726
1727 def print_rsp_free(ri):
1728     if 'reply' not in ri.op[ri.op_mode]:
1729         return
1730     _free_type(ri, 'reply', ri.struct['reply'])
1731
1732
1733 def print_dump_type_free(ri):
1734     sub_type = type_name(ri, 'reply')
1735
1736     print_free_prototype(ri, 'reply', suffix='')
1737     ri.cw.block_start()
1738     ri.cw.p(f"{sub_type} *next = rsp;")
1739     ri.cw.nl()
1740     ri.cw.block_start(line='while ((void *)next != YNL_LIST_END)')
1741     _free_type_members_iter(ri, ri.struct['reply'])
1742     ri.cw.p('rsp = next;')
1743     ri.cw.p('next = rsp->next;')
1744     ri.cw.nl()
1745
1746     _free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.')
1747     ri.cw.p(f'free(rsp);')
1748     ri.cw.block_end()
1749     ri.cw.block_end()
1750     ri.cw.nl()
1751
1752
1753 def print_ntf_type_free(ri):
1754     print_free_prototype(ri, 'reply', suffix='')
1755     ri.cw.block_start()
1756     _free_type_members_iter(ri, ri.struct['reply'])
1757     _free_type_members(ri, 'rsp', ri.struct['reply'], ref='obj.')
1758     ri.cw.p(f'free(rsp);')
1759     ri.cw.block_end()
1760     ri.cw.nl()
1761
1762
1763 def print_ntf_parse_prototype(family, cw, suffix=';'):
1764     cw.write_func_prot('struct ynl_ntf_base_type *', f"{family['name']}_ntf_parse",
1765                        ['struct ynl_sock *ys'], suffix=suffix)
1766
1767
1768 def print_ntf_type_parse(family, cw, ku_mode):
1769     print_ntf_parse_prototype(family, cw, suffix='')
1770     cw.block_start()
1771     cw.write_func_lvar(['struct genlmsghdr *genlh;',
1772                         'struct nlmsghdr *nlh;',
1773                         'struct ynl_parse_arg yarg = { .ys = ys, };',
1774                         'struct ynl_ntf_base_type *rsp;',
1775                         'int len, err;',
1776                         'mnl_cb_t parse;'])
1777     cw.p('len = mnl_socket_recvfrom(ys->sock, ys->rx_buf, MNL_SOCKET_BUFFER_SIZE);')
1778     cw.p('if (len < (ssize_t)(sizeof(*nlh) + sizeof(*genlh)))')
1779     cw.p('return NULL;')
1780     cw.nl()
1781     cw.p('nlh = (struct nlmsghdr *)ys->rx_buf;')
1782     cw.p('genlh = mnl_nlmsg_get_payload(nlh);')
1783     cw.nl()
1784     cw.block_start(line='switch (genlh->cmd)')
1785     for ntf_op in sorted(family.all_notify.keys()):
1786         op = family.ops[ntf_op]
1787         ri = RenderInfo(cw, family, ku_mode, op, ntf_op, "notify")
1788         for ntf in op['notify']['cmds']:
1789             cw.p(f"case {ntf.enum_name}:")
1790         cw.p(f"rsp = calloc(1, sizeof({type_name(ri, 'notify')}));")
1791         cw.p(f"parse = {op_prefix(ri, 'reply', deref=True)}_parse;")
1792         cw.p(f"yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
1793         cw.p(f"rsp->free = (void *){op_prefix(ri, 'notify')}_free;")
1794         cw.p('break;')
1795     for op_name, op in family.ops.items():
1796         if 'event' not in op:
1797             continue
1798         ri = RenderInfo(cw, family, ku_mode, op, op_name, "event")
1799         cw.p(f"case {op.enum_name}:")
1800         cw.p(f"rsp = calloc(1, sizeof({type_name(ri, 'event')}));")
1801         cw.p(f"parse = {op_prefix(ri, 'reply', deref=True)}_parse;")
1802         cw.p(f"yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;")
1803         cw.p(f"rsp->free = (void *){op_prefix(ri, 'notify')}_free;")
1804         cw.p('break;')
1805     cw.p('default:')
1806     cw.p('ynl_error_unknown_notification(ys, genlh->cmd);')
1807     cw.p('return NULL;')
1808     cw.block_end()
1809     cw.nl()
1810     cw.p('yarg.data = rsp->data;')
1811     cw.nl()
1812     cw.p(f"err = {cw.nlib.parse_cb_run('parse', '&yarg', True)};")
1813     cw.p('if (err < 0)')
1814     cw.p('goto err_free;')
1815     cw.nl()
1816     cw.p('rsp->family = nlh->nlmsg_type;')
1817     cw.p('rsp->cmd = genlh->cmd;')
1818     cw.p('return rsp;')
1819     cw.nl()
1820     cw.p('err_free:')
1821     cw.p('free(rsp);')
1822     cw.p('return NULL;')
1823     cw.block_end()
1824     cw.nl()
1825
1826
1827 def print_req_policy_fwd(cw, struct, ri=None, terminate=True):
1828     if terminate and ri and kernel_can_gen_family_struct(struct.family):
1829         return
1830
1831     if terminate:
1832         prefix = 'extern '
1833     else:
1834         if kernel_can_gen_family_struct(struct.family) and ri:
1835             prefix = 'static '
1836         else:
1837             prefix = ''
1838
1839     suffix = ';' if terminate else ' = {'
1840
1841     max_attr = struct.attr_max_val
1842     if ri:
1843         name = ri.op.render_name
1844         if ri.op.dual_policy:
1845             name += '_' + ri.op_mode
1846     else:
1847         name = struct.render_name
1848     cw.p(f"{prefix}const struct nla_policy {name}_nl_policy[{max_attr.enum_name} + 1]{suffix}")
1849
1850
1851 def print_req_policy(cw, struct, ri=None):
1852     print_req_policy_fwd(cw, struct, ri=ri, terminate=False)
1853     for _, arg in struct.member_list():
1854         arg.attr_policy(cw)
1855     cw.p("};")
1856
1857
1858 def kernel_can_gen_family_struct(family):
1859     return family.proto == 'genetlink'
1860
1861
1862 def print_kernel_op_table_fwd(family, cw, terminate):
1863     exported = not kernel_can_gen_family_struct(family)
1864
1865     if not terminate or exported:
1866         cw.p(f"/* Ops table for {family.name} */")
1867
1868         pol_to_struct = {'global': 'genl_small_ops',
1869                          'per-op': 'genl_ops',
1870                          'split': 'genl_split_ops'}
1871         struct_type = pol_to_struct[family.kernel_policy]
1872
1873         if not exported:
1874             cnt = ""
1875         elif family.kernel_policy == 'split':
1876             cnt = 0
1877             for op in family.ops.values():
1878                 if 'do' in op:
1879                     cnt += 1
1880                 if 'dump' in op:
1881                     cnt += 1
1882         else:
1883             cnt = len(family.ops)
1884
1885         qual = 'static const' if not exported else 'const'
1886         line = f"{qual} struct {struct_type} {family.name}_nl_ops[{cnt}]"
1887         if terminate:
1888             cw.p(f"extern {line};")
1889         else:
1890             cw.block_start(line=line + ' =')
1891
1892     if not terminate:
1893         return
1894
1895     cw.nl()
1896     for name in family.hooks['pre']['do']['list']:
1897         cw.write_func_prot('int', c_lower(name),
1898                            ['const struct genl_split_ops *ops',
1899                             'struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
1900     for name in family.hooks['post']['do']['list']:
1901         cw.write_func_prot('void', c_lower(name),
1902                            ['const struct genl_split_ops *ops',
1903                             'struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
1904     for name in family.hooks['pre']['dump']['list']:
1905         cw.write_func_prot('int', c_lower(name),
1906                            ['struct netlink_callback *cb'], suffix=';')
1907     for name in family.hooks['post']['dump']['list']:
1908         cw.write_func_prot('int', c_lower(name),
1909                            ['struct netlink_callback *cb'], suffix=';')
1910
1911     cw.nl()
1912
1913     for op_name, op in family.ops.items():
1914         if op.is_async:
1915             continue
1916
1917         if 'do' in op:
1918             name = c_lower(f"{family.name}-nl-{op_name}-doit")
1919             cw.write_func_prot('int', name,
1920                                ['struct sk_buff *skb', 'struct genl_info *info'], suffix=';')
1921
1922         if 'dump' in op:
1923             name = c_lower(f"{family.name}-nl-{op_name}-dumpit")
1924             cw.write_func_prot('int', name,
1925                                ['struct sk_buff *skb', 'struct netlink_callback *cb'], suffix=';')
1926     cw.nl()
1927
1928
1929 def print_kernel_op_table_hdr(family, cw):
1930     print_kernel_op_table_fwd(family, cw, terminate=True)
1931
1932
1933 def print_kernel_op_table(family, cw):
1934     print_kernel_op_table_fwd(family, cw, terminate=False)
1935     if family.kernel_policy == 'global' or family.kernel_policy == 'per-op':
1936         for op_name, op in family.ops.items():
1937             if op.is_async:
1938                 continue
1939
1940             cw.block_start()
1941             members = [('cmd', op.enum_name)]
1942             if 'dont-validate' in op:
1943                 members.append(('validate',
1944                                 ' | '.join([c_upper('genl-dont-validate-' + x)
1945                                             for x in op['dont-validate']])), )
1946             for op_mode in ['do', 'dump']:
1947                 if op_mode in op:
1948                     name = c_lower(f"{family.name}-nl-{op_name}-{op_mode}it")
1949                     members.append((op_mode + 'it', name))
1950             if family.kernel_policy == 'per-op':
1951                 struct = Struct(family, op['attribute-set'],
1952                                 type_list=op['do']['request']['attributes'])
1953
1954                 name = c_lower(f"{family.name}-{op_name}-nl-policy")
1955                 members.append(('policy', name))
1956                 members.append(('maxattr', struct.attr_max_val.enum_name))
1957             if 'flags' in op:
1958                 members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in op['flags']])))
1959             cw.write_struct_init(members)
1960             cw.block_end(line=',')
1961     elif family.kernel_policy == 'split':
1962         cb_names = {'do':   {'pre': 'pre_doit', 'post': 'post_doit'},
1963                     'dump': {'pre': 'start', 'post': 'done'}}
1964
1965         for op_name, op in family.ops.items():
1966             for op_mode in ['do', 'dump']:
1967                 if op.is_async or op_mode not in op:
1968                     continue
1969
1970                 cw.block_start()
1971                 members = [('cmd', op.enum_name)]
1972                 if 'dont-validate' in op:
1973                     members.append(('validate',
1974                                     ' | '.join([c_upper('genl-dont-validate-' + x)
1975                                                 for x in op['dont-validate']])), )
1976                 name = c_lower(f"{family.name}-nl-{op_name}-{op_mode}it")
1977                 if 'pre' in op[op_mode]:
1978                     members.append((cb_names[op_mode]['pre'], c_lower(op[op_mode]['pre'])))
1979                 members.append((op_mode + 'it', name))
1980                 if 'post' in op[op_mode]:
1981                     members.append((cb_names[op_mode]['post'], c_lower(op[op_mode]['post'])))
1982                 if 'request' in op[op_mode]:
1983                     struct = Struct(family, op['attribute-set'],
1984                                     type_list=op[op_mode]['request']['attributes'])
1985
1986                     if op.dual_policy:
1987                         name = c_lower(f"{family.name}-{op_name}-{op_mode}-nl-policy")
1988                     else:
1989                         name = c_lower(f"{family.name}-{op_name}-nl-policy")
1990                     members.append(('policy', name))
1991                     members.append(('maxattr', struct.attr_max_val.enum_name))
1992                 flags = (op['flags'] if 'flags' in op else []) + ['cmd-cap-' + op_mode]
1993                 members.append(('flags', ' | '.join([c_upper('genl-' + x) for x in flags])))
1994                 cw.write_struct_init(members)
1995                 cw.block_end(line=',')
1996
1997     cw.block_end(line=';')
1998     cw.nl()
1999
2000
2001 def print_kernel_mcgrp_hdr(family, cw):
2002     if not family.mcgrps['list']:
2003         return
2004
2005     cw.block_start('enum')
2006     for grp in family.mcgrps['list']:
2007         grp_id = c_upper(f"{family.name}-nlgrp-{grp['name']},")
2008         cw.p(grp_id)
2009     cw.block_end(';')
2010     cw.nl()
2011
2012
2013 def print_kernel_mcgrp_src(family, cw):
2014     if not family.mcgrps['list']:
2015         return
2016
2017     cw.block_start('static const struct genl_multicast_group ' + family.name + '_nl_mcgrps[] =')
2018     for grp in family.mcgrps['list']:
2019         name = grp['name']
2020         grp_id = c_upper(f"{family.name}-nlgrp-{name}")
2021         cw.p('[' + grp_id + '] = { "' + name + '", },')
2022     cw.block_end(';')
2023     cw.nl()
2024
2025
2026 def print_kernel_family_struct_hdr(family, cw):
2027     if not kernel_can_gen_family_struct(family):
2028         return
2029
2030     cw.p(f"extern struct genl_family {family.name}_nl_family;")
2031     cw.nl()
2032
2033
2034 def print_kernel_family_struct_src(family, cw):
2035     if not kernel_can_gen_family_struct(family):
2036         return
2037
2038     cw.block_start(f"struct genl_family {family.name}_nl_family __ro_after_init =")
2039     cw.p('.name\t\t= ' + family.fam_key + ',')
2040     cw.p('.version\t= ' + family.ver_key + ',')
2041     cw.p('.netnsok\t= true,')
2042     cw.p('.parallel_ops\t= true,')
2043     cw.p('.module\t\t= THIS_MODULE,')
2044     if family.kernel_policy == 'per-op':
2045         cw.p(f'.ops\t\t= {family.name}_nl_ops,')
2046         cw.p(f'.n_ops\t\t= ARRAY_SIZE({family.name}_nl_ops),')
2047     elif family.kernel_policy == 'split':
2048         cw.p(f'.split_ops\t= {family.name}_nl_ops,')
2049         cw.p(f'.n_split_ops\t= ARRAY_SIZE({family.name}_nl_ops),')
2050     if family.mcgrps['list']:
2051         cw.p(f'.mcgrps\t\t= {family.name}_nl_mcgrps,')
2052         cw.p(f'.n_mcgrps\t= ARRAY_SIZE({family.name}_nl_mcgrps),')
2053     cw.block_end(';')
2054
2055
2056 def uapi_enum_start(family, cw, obj, ckey='', enum_name='enum-name'):
2057     start_line = 'enum'
2058     if enum_name in obj:
2059         if obj[enum_name]:
2060             start_line = 'enum ' + c_lower(obj[enum_name])
2061     elif ckey and ckey in obj:
2062         start_line = 'enum ' + family.name + '_' + c_lower(obj[ckey])
2063     cw.block_start(line=start_line)
2064
2065
2066 def render_uapi(family, cw):
2067     hdr_prot = f"_UAPI_LINUX_{family.name.upper()}_H"
2068     cw.p('#ifndef ' + hdr_prot)
2069     cw.p('#define ' + hdr_prot)
2070     cw.nl()
2071
2072     defines = [(family.fam_key, family["name"]),
2073                (family.ver_key, family.get('version', 1))]
2074     cw.writes_defines(defines)
2075     cw.nl()
2076
2077     defines = []
2078     for const in family['definitions']:
2079         if const['type'] != 'const':
2080             cw.writes_defines(defines)
2081             defines = []
2082             cw.nl()
2083
2084         # Write kdoc for enum and flags (one day maybe also structs)
2085         if const['type'] == 'enum' or const['type'] == 'flags':
2086             enum = family.consts[const['name']]
2087
2088             if enum.has_doc():
2089                 cw.p('/**')
2090                 doc = ''
2091                 if 'doc' in enum:
2092                     doc = ' - ' + enum['doc']
2093                 cw.write_doc_line(enum.enum_name + doc)
2094                 for entry in enum.entries.values():
2095                     if entry.has_doc():
2096                         doc = '@' + entry.c_name + ': ' + entry['doc']
2097                         cw.write_doc_line(doc)
2098                 cw.p(' */')
2099
2100             uapi_enum_start(family, cw, const, 'name')
2101             name_pfx = const.get('name-prefix', f"{family.name}-{const['name']}-")
2102             for entry in enum.entries.values():
2103                 suffix = ','
2104                 if entry.value_change:
2105                     suffix = f" = {entry.user_value()}" + suffix
2106                 cw.p(entry.c_name + suffix)
2107
2108             if const.get('render-max', False):
2109                 cw.nl()
2110                 if const['type'] == 'flags':
2111                     max_name = c_upper(name_pfx + 'mask')
2112                     max_val = f' = {enum.get_mask()},'
2113                     cw.p(max_name + max_val)
2114                 else:
2115                     max_name = c_upper(name_pfx + 'max')
2116                     cw.p('__' + max_name + ',')
2117                     cw.p(max_name + ' = (__' + max_name + ' - 1)')
2118             cw.block_end(line=';')
2119             cw.nl()
2120         elif const['type'] == 'const':
2121             defines.append([c_upper(family.get('c-define-name',
2122                                                f"{family.name}-{const['name']}")),
2123                             const['value']])
2124
2125     if defines:
2126         cw.writes_defines(defines)
2127         cw.nl()
2128
2129     max_by_define = family.get('max-by-define', False)
2130
2131     for _, attr_set in family.attr_sets.items():
2132         if attr_set.subset_of:
2133             continue
2134
2135         cnt_name = c_upper(family.get('attr-cnt-name', f"__{attr_set.name_prefix}MAX"))
2136         max_value = f"({cnt_name} - 1)"
2137
2138         val = 0
2139         uapi_enum_start(family, cw, attr_set.yaml, 'enum-name')
2140         for _, attr in attr_set.items():
2141             suffix = ','
2142             if attr.value != val:
2143                 suffix = f" = {attr.value},"
2144                 val = attr.value
2145             val += 1
2146             cw.p(attr.enum_name + suffix)
2147         cw.nl()
2148         cw.p(cnt_name + ('' if max_by_define else ','))
2149         if not max_by_define:
2150             cw.p(f"{attr_set.max_name} = {max_value}")
2151         cw.block_end(line=';')
2152         if max_by_define:
2153             cw.p(f"#define {attr_set.max_name} {max_value}")
2154         cw.nl()
2155
2156     # Commands
2157     separate_ntf = 'async-prefix' in family['operations']
2158
2159     max_name = c_upper(family.get('cmd-max-name', f"{family.op_prefix}MAX"))
2160     cnt_name = c_upper(family.get('cmd-cnt-name', f"__{family.op_prefix}MAX"))
2161     max_value = f"({cnt_name} - 1)"
2162
2163     uapi_enum_start(family, cw, family['operations'], 'enum-name')
2164     val = 0
2165     for op in family.msgs.values():
2166         if separate_ntf and ('notify' in op or 'event' in op):
2167             continue
2168
2169         suffix = ','
2170         if op.value != val:
2171             suffix = f" = {op.value},"
2172             val = op.value
2173         cw.p(op.enum_name + suffix)
2174         val += 1
2175     cw.nl()
2176     cw.p(cnt_name + ('' if max_by_define else ','))
2177     if not max_by_define:
2178         cw.p(f"{max_name} = {max_value}")
2179     cw.block_end(line=';')
2180     if max_by_define:
2181         cw.p(f"#define {max_name} {max_value}")
2182     cw.nl()
2183
2184     if separate_ntf:
2185         uapi_enum_start(family, cw, family['operations'], enum_name='async-enum')
2186         for op in family.msgs.values():
2187             if separate_ntf and not ('notify' in op or 'event' in op):
2188                 continue
2189
2190             suffix = ','
2191             if 'value' in op:
2192                 suffix = f" = {op['value']},"
2193             cw.p(op.enum_name + suffix)
2194         cw.block_end(line=';')
2195         cw.nl()
2196
2197     # Multicast
2198     defines = []
2199     for grp in family.mcgrps['list']:
2200         name = grp['name']
2201         defines.append([c_upper(grp.get('c-define-name', f"{family.name}-mcgrp-{name}")),
2202                         f'{name}'])
2203     cw.nl()
2204     if defines:
2205         cw.writes_defines(defines)
2206         cw.nl()
2207
2208     cw.p(f'#endif /* {hdr_prot} */')
2209
2210
2211 def _render_user_ntf_entry(ri, op):
2212     ri.cw.block_start(line=f"[{op.enum_name}] = ")
2213     ri.cw.p(f".alloc_sz\t= sizeof({type_name(ri, 'event')}),")
2214     ri.cw.p(f".cb\t\t= {op_prefix(ri, 'reply', deref=True)}_parse,")
2215     ri.cw.p(f".policy\t\t= &{ri.struct['reply'].render_name}_nest,")
2216     ri.cw.p(f".free\t\t= (void *){op_prefix(ri, 'notify')}_free,")
2217     ri.cw.block_end(line=',')
2218
2219
2220 def render_user_family(family, cw, prototype):
2221     symbol = f'const struct ynl_family ynl_{family.c_name}_family'
2222     if prototype:
2223         cw.p(f'extern {symbol};')
2224         return
2225
2226     ntf = family.has_notifications()
2227     if ntf:
2228         cw.block_start(line=f"static const struct ynl_ntf_info {family['name']}_ntf_info[] = ")
2229         for ntf_op in sorted(family.all_notify.keys()):
2230             op = family.ops[ntf_op]
2231             ri = RenderInfo(cw, family, "user", op, ntf_op, "notify")
2232             for ntf in op['notify']['cmds']:
2233                 _render_user_ntf_entry(ri, ntf)
2234         for op_name, op in family.ops.items():
2235             if 'event' not in op:
2236                 continue
2237             ri = RenderInfo(cw, family, "user", op, op_name, "event")
2238             _render_user_ntf_entry(ri, op)
2239         cw.block_end(line=";")
2240         cw.nl()
2241
2242     cw.block_start(f'{symbol} = ')
2243     cw.p(f'.name\t\t= "{family.name}",')
2244     if ntf:
2245         cw.p(f".ntf_info\t= {family['name']}_ntf_info,")
2246         cw.p(f".ntf_info_size\t= MNL_ARRAY_SIZE({family['name']}_ntf_info),")
2247     cw.block_end(line=';')
2248
2249
2250 def find_kernel_root(full_path):
2251     sub_path = ''
2252     while True:
2253         sub_path = os.path.join(os.path.basename(full_path), sub_path)
2254         full_path = os.path.dirname(full_path)
2255         maintainers = os.path.join(full_path, "MAINTAINERS")
2256         if os.path.exists(maintainers):
2257             return full_path, sub_path[:-1]
2258
2259
2260 def main():
2261     parser = argparse.ArgumentParser(description='Netlink simple parsing generator')
2262     parser.add_argument('--mode', dest='mode', type=str, required=True)
2263     parser.add_argument('--spec', dest='spec', type=str, required=True)
2264     parser.add_argument('--header', dest='header', action='store_true', default=None)
2265     parser.add_argument('--source', dest='header', action='store_false')
2266     parser.add_argument('--user-header', nargs='+', default=[])
2267     parser.add_argument('-o', dest='out_file', type=str)
2268     args = parser.parse_args()
2269
2270     out_file = open(args.out_file, 'w+') if args.out_file else os.sys.stdout
2271
2272     if args.header is None:
2273         parser.error("--header or --source is required")
2274
2275     try:
2276         parsed = Family(args.spec)
2277         if parsed.license != '((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)':
2278             print('Spec license:', parsed.license)
2279             print('License must be: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)')
2280             os.sys.exit(1)
2281     except yaml.YAMLError as exc:
2282         print(exc)
2283         os.sys.exit(1)
2284         return
2285
2286     supported_models = ['unified']
2287     if args.mode == 'user':
2288         supported_models += ['directional']
2289     if parsed.msg_id_model not in supported_models:
2290         print(f'Message enum-model {parsed.msg_id_model} not supported for {args.mode} generation')
2291         os.sys.exit(1)
2292
2293     cw = CodeWriter(BaseNlLib(), out_file)
2294
2295     _, spec_kernel = find_kernel_root(args.spec)
2296     if args.mode == 'uapi' or args.header:
2297         cw.p(f'/* SPDX-License-Identifier: {parsed.license} */')
2298     else:
2299         cw.p(f'// SPDX-License-Identifier: {parsed.license}')
2300     cw.p("/* Do not edit directly, auto-generated from: */")
2301     cw.p(f"/*\t{spec_kernel} */")
2302     cw.p(f"/* YNL-GEN {args.mode} {'header' if args.header else 'source'} */")
2303     cw.nl()
2304
2305     if args.mode == 'uapi':
2306         render_uapi(parsed, cw)
2307         return
2308
2309     hdr_prot = f"_LINUX_{parsed.name.upper()}_GEN_H"
2310     if args.header:
2311         cw.p('#ifndef ' + hdr_prot)
2312         cw.p('#define ' + hdr_prot)
2313         cw.nl()
2314
2315     if args.mode == 'kernel':
2316         cw.p('#include <net/netlink.h>')
2317         cw.p('#include <net/genetlink.h>')
2318         cw.nl()
2319         if not args.header:
2320             if args.out_file:
2321                 cw.p(f'#include "{os.path.basename(args.out_file[:-2])}.h"')
2322             cw.nl()
2323         headers = ['uapi/' + parsed.uapi_header]
2324     else:
2325         cw.p('#include <stdlib.h>')
2326         cw.p('#include <string.h>')
2327         if args.header:
2328             cw.p('#include <linux/types.h>')
2329         else:
2330             cw.p(f'#include "{parsed.name}-user.h"')
2331             cw.p('#include "ynl.h"')
2332         headers = [parsed.uapi_header]
2333     for definition in parsed['definitions']:
2334         if 'header' in definition:
2335             headers.append(definition['header'])
2336     for one in headers:
2337         cw.p(f"#include <{one}>")
2338     cw.nl()
2339
2340     if args.mode == "user":
2341         if not args.header:
2342             cw.p("#include <libmnl/libmnl.h>")
2343             cw.p("#include <linux/genetlink.h>")
2344             cw.nl()
2345             for one in args.user_header:
2346                 cw.p(f'#include "{one}"')
2347         else:
2348             cw.p('struct ynl_sock;')
2349             cw.nl()
2350             render_user_family(parsed, cw, True)
2351         cw.nl()
2352
2353     if args.mode == "kernel":
2354         if args.header:
2355             for _, struct in sorted(parsed.pure_nested_structs.items()):
2356                 if struct.request:
2357                     cw.p('/* Common nested types */')
2358                     break
2359             for attr_set, struct in sorted(parsed.pure_nested_structs.items()):
2360                 if struct.request:
2361                     print_req_policy_fwd(cw, struct)
2362             cw.nl()
2363
2364             if parsed.kernel_policy == 'global':
2365                 cw.p(f"/* Global operation policy for {parsed.name} */")
2366
2367                 struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy)
2368                 print_req_policy_fwd(cw, struct)
2369                 cw.nl()
2370
2371             if parsed.kernel_policy in {'per-op', 'split'}:
2372                 for op_name, op in parsed.ops.items():
2373                     if 'do' in op and 'event' not in op:
2374                         ri = RenderInfo(cw, parsed, args.mode, op, op_name, "do")
2375                         print_req_policy_fwd(cw, ri.struct['request'], ri=ri)
2376                         cw.nl()
2377
2378             print_kernel_op_table_hdr(parsed, cw)
2379             print_kernel_mcgrp_hdr(parsed, cw)
2380             print_kernel_family_struct_hdr(parsed, cw)
2381         else:
2382             for _, struct in sorted(parsed.pure_nested_structs.items()):
2383                 if struct.request:
2384                     cw.p('/* Common nested types */')
2385                     break
2386             for attr_set, struct in sorted(parsed.pure_nested_structs.items()):
2387                 if struct.request:
2388                     print_req_policy(cw, struct)
2389             cw.nl()
2390
2391             if parsed.kernel_policy == 'global':
2392                 cw.p(f"/* Global operation policy for {parsed.name} */")
2393
2394                 struct = Struct(parsed, parsed.global_policy_set, type_list=parsed.global_policy)
2395                 print_req_policy(cw, struct)
2396                 cw.nl()
2397
2398             for op_name, op in parsed.ops.items():
2399                 if parsed.kernel_policy in {'per-op', 'split'}:
2400                     for op_mode in ['do', 'dump']:
2401                         if op_mode in op and 'request' in op[op_mode]:
2402                             cw.p(f"/* {op.enum_name} - {op_mode} */")
2403                             ri = RenderInfo(cw, parsed, args.mode, op, op_name, op_mode)
2404                             print_req_policy(cw, ri.struct['request'], ri=ri)
2405                             cw.nl()
2406
2407             print_kernel_op_table(parsed, cw)
2408             print_kernel_mcgrp_src(parsed, cw)
2409             print_kernel_family_struct_src(parsed, cw)
2410
2411     if args.mode == "user":
2412         if args.header:
2413             cw.p('/* Enums */')
2414             put_op_name_fwd(parsed, cw)
2415
2416             for name, const in parsed.consts.items():
2417                 if isinstance(const, EnumSet):
2418                     put_enum_to_str_fwd(parsed, cw, const)
2419             cw.nl()
2420
2421             cw.p('/* Common nested types */')
2422             for attr_set, struct in parsed.pure_nested_structs.items():
2423                 ri = RenderInfo(cw, parsed, args.mode, "", "", "", attr_set)
2424                 print_type_full(ri, struct)
2425
2426             for op_name, op in parsed.ops.items():
2427                 cw.p(f"/* ============== {op.enum_name} ============== */")
2428
2429                 if 'do' in op and 'event' not in op:
2430                     cw.p(f"/* {op.enum_name} - do */")
2431                     ri = RenderInfo(cw, parsed, args.mode, op, op_name, "do")
2432                     print_req_type(ri)
2433                     print_req_type_helpers(ri)
2434                     cw.nl()
2435                     print_rsp_type(ri)
2436                     print_rsp_type_helpers(ri)
2437                     cw.nl()
2438                     print_req_prototype(ri)
2439                     cw.nl()
2440
2441                 if 'dump' in op:
2442                     cw.p(f"/* {op.enum_name} - dump */")
2443                     ri = RenderInfo(cw, parsed, args.mode, op, op_name, 'dump')
2444                     if 'request' in op['dump']:
2445                         print_req_type(ri)
2446                         print_req_type_helpers(ri)
2447                     if not ri.type_consistent:
2448                         print_rsp_type(ri)
2449                     print_wrapped_type(ri)
2450                     print_dump_prototype(ri)
2451                     cw.nl()
2452
2453                 if 'notify' in op:
2454                     cw.p(f"/* {op.enum_name} - notify */")
2455                     ri = RenderInfo(cw, parsed, args.mode, op, op_name, 'notify')
2456                     if not ri.type_consistent:
2457                         raise Exception(f'Only notifications with consistent types supported ({op.name})')
2458                     print_wrapped_type(ri)
2459
2460                 if 'event' in op:
2461                     ri = RenderInfo(cw, parsed, args.mode, op, op_name, 'event')
2462                     cw.p(f"/* {op.enum_name} - event */")
2463                     print_rsp_type(ri)
2464                     cw.nl()
2465                     print_wrapped_type(ri)
2466
2467             if parsed.has_notifications():
2468                 cw.p('/* --------------- Common notification parsing --------------- */')
2469                 print_ntf_parse_prototype(parsed, cw)
2470             cw.nl()
2471         else:
2472             cw.p('/* Enums */')
2473             put_op_name(parsed, cw)
2474
2475             for name, const in parsed.consts.items():
2476                 if isinstance(const, EnumSet):
2477                     put_enum_to_str(parsed, cw, const)
2478             cw.nl()
2479
2480             cw.p('/* Policies */')
2481             for name in parsed.pure_nested_structs:
2482                 struct = Struct(parsed, name)
2483                 put_typol(cw, struct)
2484             for name in parsed.root_sets:
2485                 struct = Struct(parsed, name)
2486                 put_typol(cw, struct)
2487
2488             cw.p('/* Common nested types */')
2489             for attr_set, struct in parsed.pure_nested_structs.items():
2490                 ri = RenderInfo(cw, parsed, args.mode, "", "", "", attr_set)
2491
2492                 free_rsp_nested(ri, struct)
2493                 if struct.request:
2494                     put_req_nested(ri, struct)
2495                 if struct.reply:
2496                     parse_rsp_nested(ri, struct)
2497
2498             for op_name, op in parsed.ops.items():
2499                 cw.p(f"/* ============== {op.enum_name} ============== */")
2500                 if 'do' in op and 'event' not in op:
2501                     cw.p(f"/* {op.enum_name} - do */")
2502                     ri = RenderInfo(cw, parsed, args.mode, op, op_name, "do")
2503                     print_req_free(ri)
2504                     print_rsp_free(ri)
2505                     parse_rsp_msg(ri)
2506                     print_req(ri)
2507                     cw.nl()
2508
2509                 if 'dump' in op:
2510                     cw.p(f"/* {op.enum_name} - dump */")
2511                     ri = RenderInfo(cw, parsed, args.mode, op, op_name, "dump")
2512                     if not ri.type_consistent:
2513                         parse_rsp_msg(ri, deref=True)
2514                     print_dump_type_free(ri)
2515                     print_dump(ri)
2516                     cw.nl()
2517
2518                 if 'notify' in op:
2519                     cw.p(f"/* {op.enum_name} - notify */")
2520                     ri = RenderInfo(cw, parsed, args.mode, op, op_name, 'notify')
2521                     if not ri.type_consistent:
2522                         raise Exception(f'Only notifications with consistent types supported ({op.name})')
2523                     print_ntf_type_free(ri)
2524
2525                 if 'event' in op:
2526                     cw.p(f"/* {op.enum_name} - event */")
2527
2528                     ri = RenderInfo(cw, parsed, args.mode, op, op_name, "do")
2529                     parse_rsp_msg(ri)
2530
2531                     ri = RenderInfo(cw, parsed, args.mode, op, op_name, "event")
2532                     print_ntf_type_free(ri)
2533
2534             if parsed.has_notifications():
2535                 cw.p('/* --------------- Common notification parsing --------------- */')
2536                 print_ntf_type_parse(parsed, cw, args.mode)
2537
2538             cw.nl()
2539             render_user_family(parsed, cw, False)
2540
2541     if args.header:
2542         cw.p(f'#endif /* {hdr_prot} */')
2543
2544
2545 if __name__ == "__main__":
2546     main()