xref: /kernel/linux/linux-6.6/tools/net/ynl/lib/nlspec.py (revision 62306a36)
162306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci
362306a36Sopenharmony_ciimport collections
462306a36Sopenharmony_ciimport importlib
562306a36Sopenharmony_ciimport os
662306a36Sopenharmony_ciimport yaml
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci# To be loaded dynamically as needed
1062306a36Sopenharmony_cijsonschema = None
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ciclass SpecElement:
1462306a36Sopenharmony_ci    """Netlink spec element.
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci    Abstract element of the Netlink spec. Implements the dictionary interface
1762306a36Sopenharmony_ci    for access to the raw spec. Supports iterative resolution of dependencies
1862306a36Sopenharmony_ci    across elements and class inheritance levels. The elements of the spec
1962306a36Sopenharmony_ci    may refer to each other, and although loops should be very rare, having
2062306a36Sopenharmony_ci    to maintain correct ordering of instantiation is painful, so the resolve()
2162306a36Sopenharmony_ci    method should be used to perform parts of init which require access to
2262306a36Sopenharmony_ci    other parts of the spec.
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci    Attributes:
2562306a36Sopenharmony_ci        yaml        raw spec as loaded from the spec file
2662306a36Sopenharmony_ci        family      back reference to the full family
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci        name        name of the entity as listed in the spec (optional)
2962306a36Sopenharmony_ci        ident_name  name which can be safely used as identifier in code (optional)
3062306a36Sopenharmony_ci    """
3162306a36Sopenharmony_ci    def __init__(self, family, yaml):
3262306a36Sopenharmony_ci        self.yaml = yaml
3362306a36Sopenharmony_ci        self.family = family
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci        if 'name' in self.yaml:
3662306a36Sopenharmony_ci            self.name = self.yaml['name']
3762306a36Sopenharmony_ci            self.ident_name = self.name.replace('-', '_')
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci        self._super_resolved = False
4062306a36Sopenharmony_ci        family.add_unresolved(self)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci    def __getitem__(self, key):
4362306a36Sopenharmony_ci        return self.yaml[key]
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci    def __contains__(self, key):
4662306a36Sopenharmony_ci        return key in self.yaml
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci    def get(self, key, default=None):
4962306a36Sopenharmony_ci        return self.yaml.get(key, default)
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci    def resolve_up(self, up):
5262306a36Sopenharmony_ci        if not self._super_resolved:
5362306a36Sopenharmony_ci            up.resolve()
5462306a36Sopenharmony_ci            self._super_resolved = True
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci    def resolve(self):
5762306a36Sopenharmony_ci        pass
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ciclass SpecEnumEntry(SpecElement):
6162306a36Sopenharmony_ci    """ Entry within an enum declared in the Netlink spec.
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci    Attributes:
6462306a36Sopenharmony_ci        doc         documentation string
6562306a36Sopenharmony_ci        enum_set    back reference to the enum
6662306a36Sopenharmony_ci        value       numerical value of this enum (use accessors in most situations!)
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci    Methods:
6962306a36Sopenharmony_ci        raw_value   raw value, i.e. the id in the enum, unlike user value which is a mask for flags
7062306a36Sopenharmony_ci        user_value   user value, same as raw value for enums, for flags it's the mask
7162306a36Sopenharmony_ci    """
7262306a36Sopenharmony_ci    def __init__(self, enum_set, yaml, prev, value_start):
7362306a36Sopenharmony_ci        if isinstance(yaml, str):
7462306a36Sopenharmony_ci            yaml = {'name': yaml}
7562306a36Sopenharmony_ci        super().__init__(enum_set.family, yaml)
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci        self.doc = yaml.get('doc', '')
7862306a36Sopenharmony_ci        self.enum_set = enum_set
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci        if 'value' in yaml:
8162306a36Sopenharmony_ci            self.value = yaml['value']
8262306a36Sopenharmony_ci        elif prev:
8362306a36Sopenharmony_ci            self.value = prev.value + 1
8462306a36Sopenharmony_ci        else:
8562306a36Sopenharmony_ci            self.value = value_start
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci    def has_doc(self):
8862306a36Sopenharmony_ci        return bool(self.doc)
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci    def raw_value(self):
9162306a36Sopenharmony_ci        return self.value
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci    def user_value(self, as_flags=None):
9462306a36Sopenharmony_ci        if self.enum_set['type'] == 'flags' or as_flags:
9562306a36Sopenharmony_ci            return 1 << self.value
9662306a36Sopenharmony_ci        else:
9762306a36Sopenharmony_ci            return self.value
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ciclass SpecEnumSet(SpecElement):
10162306a36Sopenharmony_ci    """ Enum type
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci    Represents an enumeration (list of numerical constants)
10462306a36Sopenharmony_ci    as declared in the "definitions" section of the spec.
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci    Attributes:
10762306a36Sopenharmony_ci        type            enum or flags
10862306a36Sopenharmony_ci        entries         entries by name
10962306a36Sopenharmony_ci        entries_by_val  entries by value
11062306a36Sopenharmony_ci    Methods:
11162306a36Sopenharmony_ci        get_mask      for flags compute the mask of all defined values
11262306a36Sopenharmony_ci    """
11362306a36Sopenharmony_ci    def __init__(self, family, yaml):
11462306a36Sopenharmony_ci        super().__init__(family, yaml)
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci        self.type = yaml['type']
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci        prev_entry = None
11962306a36Sopenharmony_ci        value_start = self.yaml.get('value-start', 0)
12062306a36Sopenharmony_ci        self.entries = dict()
12162306a36Sopenharmony_ci        self.entries_by_val = dict()
12262306a36Sopenharmony_ci        for entry in self.yaml['entries']:
12362306a36Sopenharmony_ci            e = self.new_entry(entry, prev_entry, value_start)
12462306a36Sopenharmony_ci            self.entries[e.name] = e
12562306a36Sopenharmony_ci            self.entries_by_val[e.raw_value()] = e
12662306a36Sopenharmony_ci            prev_entry = e
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci    def new_entry(self, entry, prev_entry, value_start):
12962306a36Sopenharmony_ci        return SpecEnumEntry(self, entry, prev_entry, value_start)
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci    def has_doc(self):
13262306a36Sopenharmony_ci        if 'doc' in self.yaml:
13362306a36Sopenharmony_ci            return True
13462306a36Sopenharmony_ci        for entry in self.entries.values():
13562306a36Sopenharmony_ci            if entry.has_doc():
13662306a36Sopenharmony_ci                return True
13762306a36Sopenharmony_ci        return False
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci    def get_mask(self, as_flags=None):
14062306a36Sopenharmony_ci        mask = 0
14162306a36Sopenharmony_ci        for e in self.entries.values():
14262306a36Sopenharmony_ci            mask += e.user_value(as_flags)
14362306a36Sopenharmony_ci        return mask
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ciclass SpecAttr(SpecElement):
14762306a36Sopenharmony_ci    """ Single Netlink atttribute type
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci    Represents a single attribute type within an attr space.
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci    Attributes:
15262306a36Sopenharmony_ci        value         numerical ID when serialized
15362306a36Sopenharmony_ci        attr_set      Attribute Set containing this attr
15462306a36Sopenharmony_ci        is_multi      bool, attr may repeat multiple times
15562306a36Sopenharmony_ci        struct_name   string, name of struct definition
15662306a36Sopenharmony_ci        sub_type      string, name of sub type
15762306a36Sopenharmony_ci        len           integer, optional byte length of binary types
15862306a36Sopenharmony_ci        display_hint  string, hint to help choose format specifier
15962306a36Sopenharmony_ci                      when displaying the value
16062306a36Sopenharmony_ci    """
16162306a36Sopenharmony_ci    def __init__(self, family, attr_set, yaml, value):
16262306a36Sopenharmony_ci        super().__init__(family, yaml)
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci        self.value = value
16562306a36Sopenharmony_ci        self.attr_set = attr_set
16662306a36Sopenharmony_ci        self.is_multi = yaml.get('multi-attr', False)
16762306a36Sopenharmony_ci        self.struct_name = yaml.get('struct')
16862306a36Sopenharmony_ci        self.sub_type = yaml.get('sub-type')
16962306a36Sopenharmony_ci        self.byte_order = yaml.get('byte-order')
17062306a36Sopenharmony_ci        self.len = yaml.get('len')
17162306a36Sopenharmony_ci        self.display_hint = yaml.get('display-hint')
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ciclass SpecAttrSet(SpecElement):
17562306a36Sopenharmony_ci    """ Netlink Attribute Set class.
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci    Represents a ID space of attributes within Netlink.
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci    Note that unlike other elements, which expose contents of the raw spec
18062306a36Sopenharmony_ci    via the dictionary interface Attribute Set exposes attributes by name.
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci    Attributes:
18362306a36Sopenharmony_ci        attrs      ordered dict of all attributes (indexed by name)
18462306a36Sopenharmony_ci        attrs_by_val  ordered dict of all attributes (indexed by value)
18562306a36Sopenharmony_ci        subset_of  parent set if this is a subset, otherwise None
18662306a36Sopenharmony_ci    """
18762306a36Sopenharmony_ci    def __init__(self, family, yaml):
18862306a36Sopenharmony_ci        super().__init__(family, yaml)
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci        self.subset_of = self.yaml.get('subset-of', None)
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci        self.attrs = collections.OrderedDict()
19362306a36Sopenharmony_ci        self.attrs_by_val = collections.OrderedDict()
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci        if self.subset_of is None:
19662306a36Sopenharmony_ci            val = 1
19762306a36Sopenharmony_ci            for elem in self.yaml['attributes']:
19862306a36Sopenharmony_ci                if 'value' in elem:
19962306a36Sopenharmony_ci                    val = elem['value']
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci                attr = self.new_attr(elem, val)
20262306a36Sopenharmony_ci                self.attrs[attr.name] = attr
20362306a36Sopenharmony_ci                self.attrs_by_val[attr.value] = attr
20462306a36Sopenharmony_ci                val += 1
20562306a36Sopenharmony_ci        else:
20662306a36Sopenharmony_ci            real_set = family.attr_sets[self.subset_of]
20762306a36Sopenharmony_ci            for elem in self.yaml['attributes']:
20862306a36Sopenharmony_ci                attr = real_set[elem['name']]
20962306a36Sopenharmony_ci                self.attrs[attr.name] = attr
21062306a36Sopenharmony_ci                self.attrs_by_val[attr.value] = attr
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci    def new_attr(self, elem, value):
21362306a36Sopenharmony_ci        return SpecAttr(self.family, self, elem, value)
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci    def __getitem__(self, key):
21662306a36Sopenharmony_ci        return self.attrs[key]
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci    def __contains__(self, key):
21962306a36Sopenharmony_ci        return key in self.attrs
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci    def __iter__(self):
22262306a36Sopenharmony_ci        yield from self.attrs
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci    def items(self):
22562306a36Sopenharmony_ci        return self.attrs.items()
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ciclass SpecStructMember(SpecElement):
22962306a36Sopenharmony_ci    """Struct member attribute
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci    Represents a single struct member attribute.
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci    Attributes:
23462306a36Sopenharmony_ci        type        string, type of the member attribute
23562306a36Sopenharmony_ci        byte_order  string or None for native byte order
23662306a36Sopenharmony_ci        enum        string, name of the enum definition
23762306a36Sopenharmony_ci        len         integer, optional byte length of binary types
23862306a36Sopenharmony_ci        display_hint  string, hint to help choose format specifier
23962306a36Sopenharmony_ci                      when displaying the value
24062306a36Sopenharmony_ci    """
24162306a36Sopenharmony_ci    def __init__(self, family, yaml):
24262306a36Sopenharmony_ci        super().__init__(family, yaml)
24362306a36Sopenharmony_ci        self.type = yaml['type']
24462306a36Sopenharmony_ci        self.byte_order = yaml.get('byte-order')
24562306a36Sopenharmony_ci        self.enum = yaml.get('enum')
24662306a36Sopenharmony_ci        self.len = yaml.get('len')
24762306a36Sopenharmony_ci        self.display_hint = yaml.get('display-hint')
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ciclass SpecStruct(SpecElement):
25162306a36Sopenharmony_ci    """Netlink struct type
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci    Represents a C struct definition.
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci    Attributes:
25662306a36Sopenharmony_ci        members   ordered list of struct members
25762306a36Sopenharmony_ci    """
25862306a36Sopenharmony_ci    def __init__(self, family, yaml):
25962306a36Sopenharmony_ci        super().__init__(family, yaml)
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci        self.members = []
26262306a36Sopenharmony_ci        for member in yaml.get('members', []):
26362306a36Sopenharmony_ci            self.members.append(self.new_member(family, member))
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci    def new_member(self, family, elem):
26662306a36Sopenharmony_ci        return SpecStructMember(family, elem)
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci    def __iter__(self):
26962306a36Sopenharmony_ci        yield from self.members
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci    def items(self):
27262306a36Sopenharmony_ci        return self.members.items()
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ciclass SpecOperation(SpecElement):
27662306a36Sopenharmony_ci    """Netlink Operation
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci    Information about a single Netlink operation.
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci    Attributes:
28162306a36Sopenharmony_ci        value           numerical ID when serialized, None if req/rsp values differ
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci        req_value       numerical ID when serialized, user -> kernel
28462306a36Sopenharmony_ci        rsp_value       numerical ID when serialized, user <- kernel
28562306a36Sopenharmony_ci        is_call         bool, whether the operation is a call
28662306a36Sopenharmony_ci        is_async        bool, whether the operation is a notification
28762306a36Sopenharmony_ci        is_resv         bool, whether the operation does not exist (it's just a reserved ID)
28862306a36Sopenharmony_ci        attr_set        attribute set name
28962306a36Sopenharmony_ci        fixed_header    string, optional name of fixed header struct
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci        yaml            raw spec as loaded from the spec file
29262306a36Sopenharmony_ci    """
29362306a36Sopenharmony_ci    def __init__(self, family, yaml, req_value, rsp_value):
29462306a36Sopenharmony_ci        super().__init__(family, yaml)
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci        self.value = req_value if req_value == rsp_value else None
29762306a36Sopenharmony_ci        self.req_value = req_value
29862306a36Sopenharmony_ci        self.rsp_value = rsp_value
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci        self.is_call = 'do' in yaml or 'dump' in yaml
30162306a36Sopenharmony_ci        self.is_async = 'notify' in yaml or 'event' in yaml
30262306a36Sopenharmony_ci        self.is_resv = not self.is_async and not self.is_call
30362306a36Sopenharmony_ci        self.fixed_header = self.yaml.get('fixed-header', family.fixed_header)
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci        # Added by resolve:
30662306a36Sopenharmony_ci        self.attr_set = None
30762306a36Sopenharmony_ci        delattr(self, "attr_set")
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci    def resolve(self):
31062306a36Sopenharmony_ci        self.resolve_up(super())
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci        if 'attribute-set' in self.yaml:
31362306a36Sopenharmony_ci            attr_set_name = self.yaml['attribute-set']
31462306a36Sopenharmony_ci        elif 'notify' in self.yaml:
31562306a36Sopenharmony_ci            msg = self.family.msgs[self.yaml['notify']]
31662306a36Sopenharmony_ci            attr_set_name = msg['attribute-set']
31762306a36Sopenharmony_ci        elif self.is_resv:
31862306a36Sopenharmony_ci            attr_set_name = ''
31962306a36Sopenharmony_ci        else:
32062306a36Sopenharmony_ci            raise Exception(f"Can't resolve attribute set for op '{self.name}'")
32162306a36Sopenharmony_ci        if attr_set_name:
32262306a36Sopenharmony_ci            self.attr_set = self.family.attr_sets[attr_set_name]
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ciclass SpecMcastGroup(SpecElement):
32662306a36Sopenharmony_ci    """Netlink Multicast Group
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci    Information about a multicast group.
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci    Value is only used for classic netlink families that use the
33162306a36Sopenharmony_ci    netlink-raw schema. Genetlink families use dynamic ID allocation
33262306a36Sopenharmony_ci    where the ids of multicast groups get resolved at runtime. Value
33362306a36Sopenharmony_ci    will be None for genetlink families.
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci    Attributes:
33662306a36Sopenharmony_ci        name      name of the mulitcast group
33762306a36Sopenharmony_ci        value     integer id of this multicast group for netlink-raw or None
33862306a36Sopenharmony_ci        yaml      raw spec as loaded from the spec file
33962306a36Sopenharmony_ci    """
34062306a36Sopenharmony_ci    def __init__(self, family, yaml):
34162306a36Sopenharmony_ci        super().__init__(family, yaml)
34262306a36Sopenharmony_ci        self.value = self.yaml.get('value')
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ciclass SpecFamily(SpecElement):
34662306a36Sopenharmony_ci    """ Netlink Family Spec class.
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci    Netlink family information loaded from a spec (e.g. in YAML).
34962306a36Sopenharmony_ci    Takes care of unfolding implicit information which can be skipped
35062306a36Sopenharmony_ci    in the spec itself for brevity.
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci    The class can be used like a dictionary to access the raw spec
35362306a36Sopenharmony_ci    elements but that's usually a bad idea.
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci    Attributes:
35662306a36Sopenharmony_ci        proto     protocol type (e.g. genetlink)
35762306a36Sopenharmony_ci        msg_id_model   enum-model for operations (unified, directional etc.)
35862306a36Sopenharmony_ci        license   spec license (loaded from an SPDX tag on the spec)
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci        attr_sets  dict of attribute sets
36162306a36Sopenharmony_ci        msgs       dict of all messages (index by name)
36262306a36Sopenharmony_ci        ops        dict of all valid requests / responses
36362306a36Sopenharmony_ci        ntfs       dict of all async events
36462306a36Sopenharmony_ci        consts     dict of all constants/enums
36562306a36Sopenharmony_ci        fixed_header  string, optional name of family default fixed header struct
36662306a36Sopenharmony_ci        mcast_groups  dict of all multicast groups (index by name)
36762306a36Sopenharmony_ci    """
36862306a36Sopenharmony_ci    def __init__(self, spec_path, schema_path=None, exclude_ops=None):
36962306a36Sopenharmony_ci        with open(spec_path, "r") as stream:
37062306a36Sopenharmony_ci            prefix = '# SPDX-License-Identifier: '
37162306a36Sopenharmony_ci            first = stream.readline().strip()
37262306a36Sopenharmony_ci            if not first.startswith(prefix):
37362306a36Sopenharmony_ci                raise Exception('SPDX license tag required in the spec')
37462306a36Sopenharmony_ci            self.license = first[len(prefix):]
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci            stream.seek(0)
37762306a36Sopenharmony_ci            spec = yaml.safe_load(stream)
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci        self._resolution_list = []
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci        super().__init__(self, spec)
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci        self._exclude_ops = exclude_ops if exclude_ops else []
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci        self.proto = self.yaml.get('protocol', 'genetlink')
38662306a36Sopenharmony_ci        self.msg_id_model = self.yaml['operations'].get('enum-model', 'unified')
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci        if schema_path is None:
38962306a36Sopenharmony_ci            schema_path = os.path.dirname(os.path.dirname(spec_path)) + f'/{self.proto}.yaml'
39062306a36Sopenharmony_ci        if schema_path:
39162306a36Sopenharmony_ci            global jsonschema
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci            with open(schema_path, "r") as stream:
39462306a36Sopenharmony_ci                schema = yaml.safe_load(stream)
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci            if jsonschema is None:
39762306a36Sopenharmony_ci                jsonschema = importlib.import_module("jsonschema")
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci            jsonschema.validate(self.yaml, schema)
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci        self.attr_sets = collections.OrderedDict()
40262306a36Sopenharmony_ci        self.msgs = collections.OrderedDict()
40362306a36Sopenharmony_ci        self.req_by_value = collections.OrderedDict()
40462306a36Sopenharmony_ci        self.rsp_by_value = collections.OrderedDict()
40562306a36Sopenharmony_ci        self.ops = collections.OrderedDict()
40662306a36Sopenharmony_ci        self.ntfs = collections.OrderedDict()
40762306a36Sopenharmony_ci        self.consts = collections.OrderedDict()
40862306a36Sopenharmony_ci        self.mcast_groups = collections.OrderedDict()
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci        last_exception = None
41162306a36Sopenharmony_ci        while len(self._resolution_list) > 0:
41262306a36Sopenharmony_ci            resolved = []
41362306a36Sopenharmony_ci            unresolved = self._resolution_list
41462306a36Sopenharmony_ci            self._resolution_list = []
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci            for elem in unresolved:
41762306a36Sopenharmony_ci                try:
41862306a36Sopenharmony_ci                    elem.resolve()
41962306a36Sopenharmony_ci                except (KeyError, AttributeError) as e:
42062306a36Sopenharmony_ci                    self._resolution_list.append(elem)
42162306a36Sopenharmony_ci                    last_exception = e
42262306a36Sopenharmony_ci                    continue
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci                resolved.append(elem)
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci            if len(resolved) == 0:
42762306a36Sopenharmony_ci                raise last_exception
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci    def new_enum(self, elem):
43062306a36Sopenharmony_ci        return SpecEnumSet(self, elem)
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci    def new_attr_set(self, elem):
43362306a36Sopenharmony_ci        return SpecAttrSet(self, elem)
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci    def new_struct(self, elem):
43662306a36Sopenharmony_ci        return SpecStruct(self, elem)
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci    def new_operation(self, elem, req_val, rsp_val):
43962306a36Sopenharmony_ci        return SpecOperation(self, elem, req_val, rsp_val)
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci    def new_mcast_group(self, elem):
44262306a36Sopenharmony_ci        return SpecMcastGroup(self, elem)
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci    def add_unresolved(self, elem):
44562306a36Sopenharmony_ci        self._resolution_list.append(elem)
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci    def _dictify_ops_unified(self):
44862306a36Sopenharmony_ci        self.fixed_header = self.yaml['operations'].get('fixed-header')
44962306a36Sopenharmony_ci        val = 1
45062306a36Sopenharmony_ci        for elem in self.yaml['operations']['list']:
45162306a36Sopenharmony_ci            if 'value' in elem:
45262306a36Sopenharmony_ci                val = elem['value']
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci            op = self.new_operation(elem, val, val)
45562306a36Sopenharmony_ci            val += 1
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci            self.msgs[op.name] = op
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci    def _dictify_ops_directional(self):
46062306a36Sopenharmony_ci        self.fixed_header = self.yaml['operations'].get('fixed-header')
46162306a36Sopenharmony_ci        req_val = rsp_val = 1
46262306a36Sopenharmony_ci        for elem in self.yaml['operations']['list']:
46362306a36Sopenharmony_ci            if 'notify' in elem or 'event' in elem:
46462306a36Sopenharmony_ci                if 'value' in elem:
46562306a36Sopenharmony_ci                    rsp_val = elem['value']
46662306a36Sopenharmony_ci                req_val_next = req_val
46762306a36Sopenharmony_ci                rsp_val_next = rsp_val + 1
46862306a36Sopenharmony_ci                req_val = None
46962306a36Sopenharmony_ci            elif 'do' in elem or 'dump' in elem:
47062306a36Sopenharmony_ci                mode = elem['do'] if 'do' in elem else elem['dump']
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci                v = mode.get('request', {}).get('value', None)
47362306a36Sopenharmony_ci                if v:
47462306a36Sopenharmony_ci                    req_val = v
47562306a36Sopenharmony_ci                v = mode.get('reply', {}).get('value', None)
47662306a36Sopenharmony_ci                if v:
47762306a36Sopenharmony_ci                    rsp_val = v
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci                rsp_inc = 1 if 'reply' in mode else 0
48062306a36Sopenharmony_ci                req_val_next = req_val + 1
48162306a36Sopenharmony_ci                rsp_val_next = rsp_val + rsp_inc
48262306a36Sopenharmony_ci            else:
48362306a36Sopenharmony_ci                raise Exception("Can't parse directional ops")
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci            if req_val == req_val_next:
48662306a36Sopenharmony_ci                req_val = None
48762306a36Sopenharmony_ci            if rsp_val == rsp_val_next:
48862306a36Sopenharmony_ci                rsp_val = None
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci            skip = False
49162306a36Sopenharmony_ci            for exclude in self._exclude_ops:
49262306a36Sopenharmony_ci                skip |= bool(exclude.match(elem['name']))
49362306a36Sopenharmony_ci            if not skip:
49462306a36Sopenharmony_ci                op = self.new_operation(elem, req_val, rsp_val)
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci            req_val = req_val_next
49762306a36Sopenharmony_ci            rsp_val = rsp_val_next
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci            self.msgs[op.name] = op
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci    def find_operation(self, name):
50262306a36Sopenharmony_ci      """
50362306a36Sopenharmony_ci      For a given operation name, find and return operation spec.
50462306a36Sopenharmony_ci      """
50562306a36Sopenharmony_ci      for op in self.yaml['operations']['list']:
50662306a36Sopenharmony_ci        if name == op['name']:
50762306a36Sopenharmony_ci          return op
50862306a36Sopenharmony_ci      return None
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci    def resolve(self):
51162306a36Sopenharmony_ci        self.resolve_up(super())
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci        definitions = self.yaml.get('definitions', [])
51462306a36Sopenharmony_ci        for elem in definitions:
51562306a36Sopenharmony_ci            if elem['type'] == 'enum' or elem['type'] == 'flags':
51662306a36Sopenharmony_ci                self.consts[elem['name']] = self.new_enum(elem)
51762306a36Sopenharmony_ci            elif elem['type'] == 'struct':
51862306a36Sopenharmony_ci                self.consts[elem['name']] = self.new_struct(elem)
51962306a36Sopenharmony_ci            else:
52062306a36Sopenharmony_ci                self.consts[elem['name']] = elem
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci        for elem in self.yaml['attribute-sets']:
52362306a36Sopenharmony_ci            attr_set = self.new_attr_set(elem)
52462306a36Sopenharmony_ci            self.attr_sets[elem['name']] = attr_set
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci        if self.msg_id_model == 'unified':
52762306a36Sopenharmony_ci            self._dictify_ops_unified()
52862306a36Sopenharmony_ci        elif self.msg_id_model == 'directional':
52962306a36Sopenharmony_ci            self._dictify_ops_directional()
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci        for op in self.msgs.values():
53262306a36Sopenharmony_ci            if op.req_value is not None:
53362306a36Sopenharmony_ci                self.req_by_value[op.req_value] = op
53462306a36Sopenharmony_ci            if op.rsp_value is not None:
53562306a36Sopenharmony_ci                self.rsp_by_value[op.rsp_value] = op
53662306a36Sopenharmony_ci            if not op.is_async and 'attribute-set' in op:
53762306a36Sopenharmony_ci                self.ops[op.name] = op
53862306a36Sopenharmony_ci            elif op.is_async:
53962306a36Sopenharmony_ci                self.ntfs[op.name] = op
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci        mcgs = self.yaml.get('mcast-groups')
54262306a36Sopenharmony_ci        if mcgs:
54362306a36Sopenharmony_ci            for elem in mcgs['list']:
54462306a36Sopenharmony_ci                mcg = self.new_mcast_group(elem)
54562306a36Sopenharmony_ci                self.mcast_groups[elem['name']] = mcg
546