~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/devices/probe/test_discoverable_devices.py

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 
 17 import argparse
 18 import glob
 19 import os
 20 import re
 21 import sys
 22 import yaml
 23 
 24 # Allow ksft module to be imported from different directory
 25 this_dir = os.path.dirname(os.path.realpath(__file__))
 26 sys.path.append(os.path.join(this_dir, "../../kselftest/"))
 27 
 28 import ksft
 29 
 30 pci_controllers = []
 31 usb_controllers = []
 32 
 33 sysfs_usb_devices = "/sys/bus/usb/devices/"
 34 
 35 
 36 def find_pci_controller_dirs():
 37     sysfs_devices = "/sys/devices"
 38     pci_controller_sysfs_dir = "pci[0-9a-f]{4}:[0-9a-f]{2}"
 39 
 40     dir_regex = re.compile(pci_controller_sysfs_dir)
 41     for path, dirs, _ in os.walk(sysfs_devices):
 42         for d in dirs:
 43             if dir_regex.match(d):
 44                 pci_controllers.append(os.path.join(path, d))
 45 
 46 
 47 def find_usb_controller_dirs():
 48     usb_controller_sysfs_dir = r"usb[\d]+"
 49 
 50     dir_regex = re.compile(usb_controller_sysfs_dir)
 51     for d in os.scandir(sysfs_usb_devices):
 52         if dir_regex.match(d.name):
 53             usb_controllers.append(os.path.realpath(d.path))
 54 
 55 
 56 def get_dt_mmio(sysfs_dev_dir):
 57     re_dt_mmio = re.compile("OF_FULLNAME=.*@([0-9a-f]+)")
 58     dt_mmio = None
 59 
 60     # PCI controllers' sysfs don't have an of_node, so have to read it from the
 61     # parent
 62     while not dt_mmio:
 63         try:
 64             with open(os.path.join(sysfs_dev_dir, "uevent")) as f:
 65                 dt_mmio = re_dt_mmio.search(f.read()).group(1)
 66                 return dt_mmio
 67         except:
 68             pass
 69         sysfs_dev_dir = os.path.dirname(sysfs_dev_dir)
 70 
 71 
 72 def get_of_fullname(sysfs_dev_dir):
 73     re_of_fullname = re.compile("OF_FULLNAME=(.*)")
 74     of_full_name = None
 75 
 76     # PCI controllers' sysfs don't have an of_node, so have to read it from the
 77     # parent
 78     while not of_full_name:
 79         try:
 80             with open(os.path.join(sysfs_dev_dir, "uevent")) as f:
 81                 of_fullname = re_of_fullname.search(f.read()).group(1)
 82                 return of_fullname
 83         except:
 84             pass
 85         sysfs_dev_dir = os.path.dirname(sysfs_dev_dir)
 86 
 87 
 88 def get_acpi_uid(sysfs_dev_dir):
 89     with open(os.path.join(sysfs_dev_dir, "firmware_node", "uid")) as f:
 90         return f.read()
 91 
 92 
 93 def get_usb_version(sysfs_dev_dir):
 94     re_usb_version = re.compile(r"PRODUCT=.*/(\d)/.*")
 95     with open(os.path.join(sysfs_dev_dir, "uevent")) as f:
 96         return int(re_usb_version.search(f.read()).group(1))
 97 
 98 
 99 def get_usb_busnum(sysfs_dev_dir):
100     re_busnum = re.compile("BUSNUM=(.*)")
101     with open(os.path.join(sysfs_dev_dir, "uevent")) as f:
102         return int(re_busnum.search(f.read()).group(1))
103 
104 
105 def find_controller_in_sysfs(controller, parent_sysfs=None):
106     if controller["type"] == "pci-controller":
107         controllers = pci_controllers
108     elif controller["type"] == "usb-controller":
109         controllers = usb_controllers
110 
111     result_controllers = []
112 
113     for c in controllers:
114         if parent_sysfs and parent_sysfs not in c:
115             continue
116 
117         if controller.get("dt-mmio"):
118             if str(controller["dt-mmio"]) != get_dt_mmio(c):
119                 continue
120 
121         if controller.get("of-fullname-regex"):
122             re_of_fullname = re.compile(str(controller["of-fullname-regex"]))
123             if not re_of_fullname.match(get_of_fullname(c)):
124                 continue
125 
126         if controller.get("usb-version"):
127             if controller["usb-version"] != get_usb_version(c):
128                 continue
129 
130         if controller.get("acpi-uid"):
131             if controller["acpi-uid"] != get_acpi_uid(c):
132                 continue
133 
134         result_controllers.append(c)
135 
136     return result_controllers
137 
138 
139 def is_controller(device):
140     return device.get("type") and "controller" in device.get("type")
141 
142 
143 def path_to_dir(parent_sysfs, dev_type, path):
144     if dev_type == "usb-device":
145         usb_dev_sysfs_fmt = "{}-{}"
146         busnum = get_usb_busnum(parent_sysfs)
147         dirname = os.path.join(
148             sysfs_usb_devices, usb_dev_sysfs_fmt.format(busnum, path)
149         )
150         return [os.path.realpath(dirname)]
151     else:
152         pci_dev_sysfs_fmt = "????:??:{}"
153         path_glob = ""
154         for dev_func in path.split("/"):
155             dev_func = dev_func.zfill(4)
156             path_glob = os.path.join(path_glob, pci_dev_sysfs_fmt.format(dev_func))
157 
158         dir_list = glob.glob(os.path.join(parent_sysfs, path_glob))
159 
160         return dir_list
161 
162 
163 def find_in_sysfs(device, parent_sysfs=None):
164     if parent_sysfs and device.get("path"):
165         pathdirs = path_to_dir(
166             parent_sysfs, device["meta"]["type"], str(device["path"])
167         )
168         if len(pathdirs) != 1:
169             # Early return to report error
170             return pathdirs
171         pathdir = pathdirs[0]
172         sysfs_path = os.path.join(parent_sysfs, pathdir)
173     else:
174         sysfs_path = parent_sysfs
175 
176     if is_controller(device):
177         return find_controller_in_sysfs(device, sysfs_path)
178     else:
179         return [sysfs_path]
180 
181 
182 def check_driver_presence(sysfs_dir, current_node):
183     if current_node["meta"]["type"] == "usb-device":
184         usb_intf_fmt = "*-*:*.{}"
185 
186         interfaces = []
187         for i in current_node["interfaces"]:
188             interfaces.append((i, usb_intf_fmt.format(i)))
189 
190         for intf_num, intf_dir_fmt in interfaces:
191             test_name = f"{current_node['meta']['pathname']}.{intf_num}.driver"
192 
193             intf_dirs = glob.glob(os.path.join(sysfs_dir, intf_dir_fmt))
194             if len(intf_dirs) != 1:
195                 ksft.test_result_fail(test_name)
196                 continue
197             intf_dir = intf_dirs[0]
198 
199             driver_link = os.path.join(sysfs_dir, intf_dir, "driver")
200             ksft.test_result(os.path.isdir(driver_link), test_name)
201     else:
202         driver_link = os.path.join(sysfs_dir, "driver")
203         test_name = current_node["meta"]["pathname"] + ".driver"
204         ksft.test_result(os.path.isdir(driver_link), test_name)
205 
206 
207 def generate_pathname(device):
208     pathname = ""
209 
210     if device.get("path"):
211         pathname = str(device["path"])
212 
213     if device.get("type"):
214         dev_type = device["type"]
215         if device.get("usb-version"):
216             dev_type = dev_type.replace("usb", "usb" + str(device["usb-version"]))
217         if device.get("acpi-uid") is not None:
218             dev_type = dev_type.replace("pci", "pci" + str(device["acpi-uid"]))
219         pathname = pathname + "/" + dev_type
220 
221     if device.get("dt-mmio"):
222         pathname += "@" + str(device["dt-mmio"])
223 
224     if device.get("of-fullname-regex"):
225         pathname += "-" + str(device["of-fullname-regex"])
226 
227     if device.get("name"):
228         pathname = pathname + "/" + device["name"]
229 
230     return pathname
231 
232 
233 def fill_meta_keys(child, parent=None):
234     child["meta"] = {}
235 
236     if parent:
237         child["meta"]["type"] = parent["type"].replace("controller", "device")
238 
239     pathname = generate_pathname(child)
240     if parent:
241         pathname = parent["meta"]["pathname"] + "/" + pathname
242     child["meta"]["pathname"] = pathname
243 
244 
245 def parse_device_tree_node(current_node, parent_sysfs=None):
246     if not parent_sysfs:
247         fill_meta_keys(current_node)
248 
249     sysfs_dirs = find_in_sysfs(current_node, parent_sysfs)
250     if len(sysfs_dirs) != 1:
251         if len(sysfs_dirs) == 0:
252             ksft.test_result_fail(
253                 f"Couldn't find in sysfs: {current_node['meta']['pathname']}"
254             )
255         else:
256             ksft.test_result_fail(
257                 f"Found multiple sysfs entries for {current_node['meta']['pathname']}: {sysfs_dirs}"
258             )
259         return
260     sysfs_dir = sysfs_dirs[0]
261 
262     if not is_controller(current_node):
263         ksft.test_result(
264             os.path.exists(sysfs_dir), current_node["meta"]["pathname"] + ".device"
265         )
266         check_driver_presence(sysfs_dir, current_node)
267     else:
268         for child_device in current_node["devices"]:
269             fill_meta_keys(child_device, current_node)
270             parse_device_tree_node(child_device, sysfs_dir)
271 
272 
273 def count_tests(device_trees):
274     test_count = 0
275 
276     def parse_node(device):
277         nonlocal test_count
278         if device.get("devices"):
279             for child in device["devices"]:
280                 parse_node(child)
281         else:
282             if device.get("interfaces"):
283                 test_count += len(device["interfaces"])
284             else:
285                 test_count += 1
286             test_count += 1
287 
288     for device_tree in device_trees:
289         parse_node(device_tree)
290 
291     return test_count
292 
293 
294 def get_board_filenames():
295     filenames = []
296 
297     platform_compatible_file = "/proc/device-tree/compatible"
298     if os.path.exists(platform_compatible_file):
299         with open(platform_compatible_file) as f:
300             for line in f:
301                 filenames.extend(line.split("\0"))
302     else:
303         dmi_id_dir = "/sys/devices/virtual/dmi/id"
304         vendor_dmi_file = os.path.join(dmi_id_dir, "sys_vendor")
305         product_dmi_file = os.path.join(dmi_id_dir, "product_name")
306 
307         with open(vendor_dmi_file) as f:
308             vendor = f.read().replace("\n", "")
309         with open(product_dmi_file) as f:
310             product = f.read().replace("\n", "")
311 
312         filenames = [vendor + "," + product]
313 
314     return filenames
315 
316 
317 def run_test(yaml_file):
318     ksft.print_msg(f"Using board file: {yaml_file}")
319 
320     with open(yaml_file) as f:
321         device_trees = yaml.safe_load(f)
322 
323     ksft.set_plan(count_tests(device_trees))
324 
325     for device_tree in device_trees:
326         parse_device_tree_node(device_tree)
327 
328 
329 parser = argparse.ArgumentParser()
330 parser.add_argument(
331     "--boards-dir", default="boards", help="Directory containing the board YAML files"
332 )
333 args = parser.parse_args()
334 
335 find_pci_controller_dirs()
336 find_usb_controller_dirs()
337 
338 ksft.print_header()
339 
340 if not os.path.exists(args.boards_dir):
341     ksft.print_msg(f"Boards directory '{args.boards_dir}' doesn't exist")
342     ksft.exit_fail()
343 
344 board_file = ""
345 for board_filename in get_board_filenames():
346     full_board_filename = os.path.join(args.boards_dir, board_filename + ".yaml")
347 
348     if os.path.exists(full_board_filename):
349         board_file = full_board_filename
350         break
351 
352 if not board_file:
353     ksft.print_msg("No matching board file found")
354     ksft.exit_fail()
355 
356 run_test(board_file)
357 
358 ksft.finished()

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php