diff options
Diffstat (limited to 'tools/net/ynl/lib/nlspec.py')
-rw-r--r-- | tools/net/ynl/lib/nlspec.py | 135 |
1 files changed, 124 insertions, 11 deletions
diff --git a/tools/net/ynl/lib/nlspec.py b/tools/net/ynl/lib/nlspec.py index 71da568e2c28..d04450c2a44a 100644 --- a/tools/net/ynl/lib/nlspec.py +++ b/tools/net/ynl/lib/nlspec.py @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause import collections import importlib @@ -57,6 +57,92 @@ class SpecElement: pass +class SpecEnumEntry(SpecElement): + """ Entry within an enum declared in the Netlink spec. + + Attributes: + doc documentation string + enum_set back reference to the enum + value numerical value of this enum (use accessors in most situations!) + + Methods: + raw_value raw value, i.e. the id in the enum, unlike user value which is a mask for flags + user_value user value, same as raw value for enums, for flags it's the mask + """ + def __init__(self, enum_set, yaml, prev, value_start): + if isinstance(yaml, str): + yaml = {'name': yaml} + super().__init__(enum_set.family, yaml) + + self.doc = yaml.get('doc', '') + self.enum_set = enum_set + + if 'value' in yaml: + self.value = yaml['value'] + elif prev: + self.value = prev.value + 1 + else: + self.value = value_start + + def has_doc(self): + return bool(self.doc) + + def raw_value(self): + return self.value + + def user_value(self): + if self.enum_set['type'] == 'flags': + return 1 << self.value + else: + return self.value + + +class SpecEnumSet(SpecElement): + """ Enum type + + Represents an enumeration (list of numerical constants) + as declared in the "definitions" section of the spec. + + Attributes: + type enum or flags + entries entries by name + entries_by_val entries by value + Methods: + get_mask for flags compute the mask of all defined values + """ + def __init__(self, family, yaml): + super().__init__(family, yaml) + + self.type = yaml['type'] + + prev_entry = None + value_start = self.yaml.get('value-start', 0) + self.entries = dict() + self.entries_by_val = dict() + for entry in self.yaml['entries']: + e = self.new_entry(entry, prev_entry, value_start) + self.entries[e.name] = e + self.entries_by_val[e.raw_value()] = e + prev_entry = e + + def new_entry(self, entry, prev_entry, value_start): + return SpecEnumEntry(self, entry, prev_entry, value_start) + + def has_doc(self): + if 'doc' in self.yaml: + return True + for entry in self.entries.values(): + if entry.has_doc(): + return True + return False + + def get_mask(self): + mask = 0 + for e in self.entries.values(): + mask += e.user_value() + return mask + + class SpecAttr(SpecElement): """ Single Netlink atttribute type @@ -95,15 +181,22 @@ class SpecAttrSet(SpecElement): self.attrs = collections.OrderedDict() self.attrs_by_val = collections.OrderedDict() - val = 0 - for elem in self.yaml['attributes']: - if 'value' in elem: - val = elem['value'] + if self.subset_of is None: + val = 1 + for elem in self.yaml['attributes']: + if 'value' in elem: + val = elem['value'] - attr = self.new_attr(elem, val) - self.attrs[attr.name] = attr - self.attrs_by_val[attr.value] = attr - val += 1 + attr = self.new_attr(elem, val) + self.attrs[attr.name] = attr + self.attrs_by_val[attr.value] = attr + val += 1 + else: + real_set = family.attr_sets[self.subset_of] + for elem in self.yaml['attributes']: + attr = real_set[elem['name']] + self.attrs[attr.name] = attr + self.attrs_by_val[attr.value] = attr def new_attr(self, elem, value): return SpecAttr(self.family, self, elem, value) @@ -181,14 +274,23 @@ class SpecFamily(SpecElement): Attributes: proto protocol type (e.g. genetlink) + license spec license (loaded from an SPDX tag on the spec) attr_sets dict of attribute sets msgs dict of all messages (index by name) msgs_by_value dict of all messages (indexed by name) ops dict of all valid requests / responses + consts dict of all constants/enums """ def __init__(self, spec_path, schema_path=None): with open(spec_path, "r") as stream: + prefix = '# SPDX-License-Identifier: ' + first = stream.readline().strip() + if not first.startswith(prefix): + raise Exception('SPDX license tag required in the spec') + self.license = first[len(prefix):] + + stream.seek(0) spec = yaml.safe_load(stream) self._resolution_list = [] @@ -215,6 +317,7 @@ class SpecFamily(SpecElement): self.req_by_value = collections.OrderedDict() self.rsp_by_value = collections.OrderedDict() self.ops = collections.OrderedDict() + self.consts = collections.OrderedDict() last_exception = None while len(self._resolution_list) > 0: @@ -235,6 +338,9 @@ class SpecFamily(SpecElement): if len(resolved) == 0: raise last_exception + def new_enum(self, elem): + return SpecEnumSet(self, elem) + def new_attr_set(self, elem): return SpecAttrSet(self, elem) @@ -245,7 +351,7 @@ class SpecFamily(SpecElement): self._resolution_list.append(elem) def _dictify_ops_unified(self): - val = 0 + val = 1 for elem in self.yaml['operations']['list']: if 'value' in elem: val = elem['value'] @@ -256,7 +362,7 @@ class SpecFamily(SpecElement): self.msgs[op.name] = op def _dictify_ops_directional(self): - req_val = rsp_val = 0 + req_val = rsp_val = 1 for elem in self.yaml['operations']['list']: if 'notify' in elem: if 'value' in elem: @@ -289,6 +395,13 @@ class SpecFamily(SpecElement): def resolve(self): self.resolve_up(super()) + definitions = self.yaml.get('definitions', []) + for elem in definitions: + if elem['type'] == 'enum' or elem['type'] == 'flags': + self.consts[elem['name']] = self.new_enum(elem) + else: + self.consts[elem['name']] = elem + for elem in self.yaml['attribute-sets']: attr_set = self.new_attr_set(elem) self.attr_sets[elem['name']] = attr_set |