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