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