1# Copyright (C) 2021-2022 Intel Corporation.
2#
3# SPDX-License-Identifier: BSD-3-Clause
4#
5
6import os
7import ctypes
8from collections import namedtuple
9
10from pcieparser.header import header
11from pcieparser.caps import capabilities
12from pcieparser.extcaps import extended_capabilities
13
14class PCIConfigSpace(namedtuple("PCIConfigSpace", ["header", "caps", "extcaps"])):
15    def __repr__(self):
16        acc = str(self.header)
17        for cap in self.caps:
18            acc += "\n"
19            acc += str(cap)
20        for extcap in self.extcaps:
21            acc += "\n"
22            acc += str(extcap)
23        return acc
24
25    def caps_as_dict(self):
26        if not hasattr(self, "_caps_as_dict"):
27            self._caps_as_dict = dict()
28            for cap in self.caps:
29                self._caps_as_dict[cap.name] = cap
30            for cap in self.extcaps:
31                self._caps_as_dict[cap.name] = cap
32        return self._caps_as_dict
33
34    def has_cap(self, cap_name):
35        return cap_name in self.caps_as_dict().keys()
36
37    def get_cap(self, cap_name):
38        return self.caps_as_dict().get(cap_name)
39
40def parse_config_space(path):
41    try:
42        data = open(os.path.join(path, "config"), mode='rb').read()
43        hdr = header(data)
44        caps = capabilities(data, hdr.capability_pointer) if hasattr(hdr, 'capability_pointer') else []
45        config_space = PCIConfigSpace(hdr, caps, [])
46        # While PCI Express specification requires that a PCIe endpoint must have an extended capability header at
47        # offset 100h of its configuration space, we do see real PCIe endpoints not meeting this requirement
48        # occasionally. Thus, check the length of the configuration space as well before trying to parse its extended
49        # capability list.
50        if config_space.has_cap("PCI Express") and len(data) >= 260:
51            extcaps = extended_capabilities(data)
52            config_space = PCIConfigSpace(hdr, caps, extcaps)
53        return config_space
54    except FileNotFoundError:
55        return None
56