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