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  |