Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1#!/usr/bin/python3
2# SPDX-License-Identifier: GPL-2.0
3#
4# Copyright (c) 2023 Collabora Ltd
5#
6# This script tests for presence and driver binding of devices from discoverable
7# buses (ie USB, PCI).
8#
9# The per-platform YAML file defining the devices to be tested is stored inside
10# the boards/ directory and chosen based on DT compatible or DMI IDs (sys_vendor
11# and product_name).
12#
13# See boards/google,spherion.yaml and boards/'Dell Inc.,XPS 13 9300.yaml' for
14# the description and examples of the file structure and vocabulary.
15#
16
17import glob
18import ksft
19import os
20import re
21import sys
22import yaml
23
24pci_controllers = []
25usb_controllers = []
26
27sysfs_usb_devices = "/sys/bus/usb/devices/"
28
29
30def find_pci_controller_dirs():
31 sysfs_devices = "/sys/devices"
32 pci_controller_sysfs_dir = "pci[0-9a-f]{4}:[0-9a-f]{2}"
33
34 dir_regex = re.compile(pci_controller_sysfs_dir)
35 for path, dirs, _ in os.walk(sysfs_devices):
36 for d in dirs:
37 if dir_regex.match(d):
38 pci_controllers.append(os.path.join(path, d))
39
40
41def find_usb_controller_dirs():
42 usb_controller_sysfs_dir = "usb[\d]+"
43
44 dir_regex = re.compile(usb_controller_sysfs_dir)
45 for d in os.scandir(sysfs_usb_devices):
46 if dir_regex.match(d.name):
47 usb_controllers.append(os.path.realpath(d.path))
48
49
50def get_dt_mmio(sysfs_dev_dir):
51 re_dt_mmio = re.compile("OF_FULLNAME=.*@([0-9a-f]+)")
52 dt_mmio = None
53
54 # PCI controllers' sysfs don't have an of_node, so have to read it from the
55 # parent
56 while not dt_mmio:
57 try:
58 with open(os.path.join(sysfs_dev_dir, "uevent")) as f:
59 dt_mmio = re_dt_mmio.search(f.read()).group(1)
60 return dt_mmio
61 except:
62 pass
63 sysfs_dev_dir = os.path.dirname(sysfs_dev_dir)
64
65
66def get_acpi_uid(sysfs_dev_dir):
67 with open(os.path.join(sysfs_dev_dir, "firmware_node", "uid")) as f:
68 return f.read()
69
70
71def get_usb_version(sysfs_dev_dir):
72 re_usb_version = re.compile("PRODUCT=.*/(\d)/.*")
73 with open(os.path.join(sysfs_dev_dir, "uevent")) as f:
74 return int(re_usb_version.search(f.read()).group(1))
75
76
77def get_usb_busnum(sysfs_dev_dir):
78 re_busnum = re.compile("BUSNUM=(.*)")
79 with open(os.path.join(sysfs_dev_dir, "uevent")) as f:
80 return int(re_busnum.search(f.read()).group(1))
81
82
83def find_controller_in_sysfs(controller, parent_sysfs=None):
84 if controller["type"] == "pci-controller":
85 controllers = pci_controllers
86 elif controller["type"] == "usb-controller":
87 controllers = usb_controllers
88
89 result_controllers = []
90
91 for c in controllers:
92 if parent_sysfs and parent_sysfs not in c:
93 continue
94
95 if controller.get("dt-mmio"):
96 if str(controller["dt-mmio"]) != get_dt_mmio(c):
97 continue
98
99 if controller.get("usb-version"):
100 if controller["usb-version"] != get_usb_version(c):
101 continue
102
103 if controller.get("acpi-uid"):
104 if controller["acpi-uid"] != get_acpi_uid(c):
105 continue
106
107 result_controllers.append(c)
108
109 return result_controllers
110
111
112def is_controller(device):
113 return device.get("type") and "controller" in device.get("type")
114
115
116def path_to_dir(parent_sysfs, dev_type, path):
117 if dev_type == "usb-device":
118 usb_dev_sysfs_fmt = "{}-{}"
119 busnum = get_usb_busnum(parent_sysfs)
120 dirname = os.path.join(
121 sysfs_usb_devices, usb_dev_sysfs_fmt.format(busnum, path)
122 )
123 return [os.path.realpath(dirname)]
124 else:
125 pci_dev_sysfs_fmt = "????:??:{}"
126 path_glob = ""
127 for dev_func in path.split("/"):
128 dev_func = dev_func.zfill(4)
129 path_glob = os.path.join(path_glob, pci_dev_sysfs_fmt.format(dev_func))
130
131 dir_list = glob.glob(os.path.join(parent_sysfs, path_glob))
132
133 return dir_list
134
135
136def find_in_sysfs(device, parent_sysfs=None):
137 if parent_sysfs and device.get("path"):
138 pathdirs = path_to_dir(
139 parent_sysfs, device["meta"]["type"], str(device["path"])
140 )
141 if len(pathdirs) != 1:
142 # Early return to report error
143 return pathdirs
144 pathdir = pathdirs[0]
145 sysfs_path = os.path.join(parent_sysfs, pathdir)
146 else:
147 sysfs_path = parent_sysfs
148
149 if is_controller(device):
150 return find_controller_in_sysfs(device, sysfs_path)
151 else:
152 return [sysfs_path]
153
154
155def check_driver_presence(sysfs_dir, current_node):
156 if current_node["meta"]["type"] == "usb-device":
157 usb_intf_fmt = "*-*:*.{}"
158
159 interfaces = []
160 for i in current_node["interfaces"]:
161 interfaces.append((i, usb_intf_fmt.format(i)))
162
163 for intf_num, intf_dir_fmt in interfaces:
164 test_name = f"{current_node['meta']['pathname']}.{intf_num}.driver"
165
166 intf_dirs = glob.glob(os.path.join(sysfs_dir, intf_dir_fmt))
167 if len(intf_dirs) != 1:
168 ksft.test_result_fail(test_name)
169 continue
170 intf_dir = intf_dirs[0]
171
172 driver_link = os.path.join(sysfs_dir, intf_dir, "driver")
173 ksft.test_result(os.path.isdir(driver_link), test_name)
174 else:
175 driver_link = os.path.join(sysfs_dir, "driver")
176 test_name = current_node["meta"]["pathname"] + ".driver"
177 ksft.test_result(os.path.isdir(driver_link), test_name)
178
179
180def generate_pathname(device):
181 pathname = ""
182
183 if device.get("path"):
184 pathname = str(device["path"])
185
186 if device.get("type"):
187 dev_type = device["type"]
188 if device.get("usb-version"):
189 dev_type = dev_type.replace("usb", "usb" + str(device["usb-version"]))
190 if device.get("acpi-uid") is not None:
191 dev_type = dev_type.replace("pci", "pci" + str(device["acpi-uid"]))
192 pathname = pathname + "/" + dev_type
193
194 if device.get("dt-mmio"):
195 pathname += "@" + str(device["dt-mmio"])
196
197 if device.get("name"):
198 pathname = pathname + "/" + device["name"]
199
200 return pathname
201
202
203def fill_meta_keys(child, parent=None):
204 child["meta"] = {}
205
206 if parent:
207 child["meta"]["type"] = parent["type"].replace("controller", "device")
208
209 pathname = generate_pathname(child)
210 if parent:
211 pathname = parent["meta"]["pathname"] + "/" + pathname
212 child["meta"]["pathname"] = pathname
213
214
215def parse_device_tree_node(current_node, parent_sysfs=None):
216 if not parent_sysfs:
217 fill_meta_keys(current_node)
218
219 sysfs_dirs = find_in_sysfs(current_node, parent_sysfs)
220 if len(sysfs_dirs) != 1:
221 if len(sysfs_dirs) == 0:
222 ksft.test_result_fail(
223 f"Couldn't find in sysfs: {current_node['meta']['pathname']}"
224 )
225 else:
226 ksft.test_result_fail(
227 f"Found multiple sysfs entries for {current_node['meta']['pathname']}: {sysfs_dirs}"
228 )
229 return
230 sysfs_dir = sysfs_dirs[0]
231
232 if not is_controller(current_node):
233 ksft.test_result(
234 os.path.exists(sysfs_dir), current_node["meta"]["pathname"] + ".device"
235 )
236 check_driver_presence(sysfs_dir, current_node)
237 else:
238 for child_device in current_node["devices"]:
239 fill_meta_keys(child_device, current_node)
240 parse_device_tree_node(child_device, sysfs_dir)
241
242
243def count_tests(device_trees):
244 test_count = 0
245
246 def parse_node(device):
247 nonlocal test_count
248 if device.get("devices"):
249 for child in device["devices"]:
250 parse_node(child)
251 else:
252 if device.get("interfaces"):
253 test_count += len(device["interfaces"])
254 else:
255 test_count += 1
256 test_count += 1
257
258 for device_tree in device_trees:
259 parse_node(device_tree)
260
261 return test_count
262
263
264def get_board_filenames():
265 filenames = []
266
267 platform_compatible_file = "/proc/device-tree/compatible"
268 if os.path.exists(platform_compatible_file):
269 with open(platform_compatible_file) as f:
270 for line in f:
271 filenames.extend(line.split("\0"))
272 else:
273 dmi_id_dir = "/sys/devices/virtual/dmi/id"
274 vendor_dmi_file = os.path.join(dmi_id_dir, "sys_vendor")
275 product_dmi_file = os.path.join(dmi_id_dir, "product_name")
276
277 with open(vendor_dmi_file) as f:
278 vendor = f.read().replace("\n", "")
279 with open(product_dmi_file) as f:
280 product = f.read().replace("\n", "")
281
282 filenames = [vendor + "," + product]
283
284 return filenames
285
286
287def run_test(yaml_file):
288 ksft.print_msg(f"Using board file: {yaml_file}")
289
290 with open(yaml_file) as f:
291 device_trees = yaml.safe_load(f)
292
293 ksft.set_plan(count_tests(device_trees))
294
295 for device_tree in device_trees:
296 parse_device_tree_node(device_tree)
297
298
299find_pci_controller_dirs()
300find_usb_controller_dirs()
301
302ksft.print_header()
303
304board_file = ""
305for board_filename in get_board_filenames():
306 full_board_filename = os.path.join("boards", board_filename + ".yaml")
307
308 if os.path.exists(full_board_filename):
309 board_file = full_board_filename
310 break
311
312if not board_file:
313 ksft.print_msg("No matching board file found")
314 ksft.exit_fail()
315
316run_test(board_file)
317
318ksft.finished()