brintos

brintos / linux-shallow public Read only

0
0
Text · 10.4 KiB · d7a2bb9 Raw
359 lines · python
1#!/usr/bin/python32# SPDX-License-Identifier: GPL-2.03#4# Copyright (c) 2023 Collabora Ltd5#6# This script tests for presence and driver binding of devices from discoverable7# buses (ie USB, PCI).8#9# The per-platform YAML file defining the devices to be tested is stored inside10# the boards/ directory and chosen based on DT compatible or DMI IDs (sys_vendor11# and product_name).12#13# See boards/google,spherion.yaml and boards/'Dell Inc.,XPS 13 9300.yaml' for14# the description and examples of the file structure and vocabulary.15#16 17import argparse18import glob19import os20import re21import sys22import yaml23 24# Allow ksft module to be imported from different directory25this_dir = os.path.dirname(os.path.realpath(__file__))26sys.path.append(os.path.join(this_dir, "../../kselftest/"))27 28import ksft29 30pci_controllers = []31usb_controllers = []32 33sysfs_usb_devices = "/sys/bus/usb/devices/"34 35 36def 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 47def 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 56def get_dt_mmio(sysfs_dev_dir):57    re_dt_mmio = re.compile("OF_FULLNAME=.*@([0-9a-f]+)")58    dt_mmio = None59 60    # PCI controllers' sysfs don't have an of_node, so have to read it from the61    # parent62    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_mmio67        except:68            pass69        sysfs_dev_dir = os.path.dirname(sysfs_dev_dir)70 71 72def get_of_fullname(sysfs_dev_dir):73    re_of_fullname = re.compile("OF_FULLNAME=(.*)")74    of_full_name = None75 76    # PCI controllers' sysfs don't have an of_node, so have to read it from the77    # parent78    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_fullname83        except:84            pass85        sysfs_dev_dir = os.path.dirname(sysfs_dev_dir)86 87 88def 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 93def 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 99def 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 105def find_controller_in_sysfs(controller, parent_sysfs=None):106    if controller["type"] == "pci-controller":107        controllers = pci_controllers108    elif controller["type"] == "usb-controller":109        controllers = usb_controllers110 111    result_controllers = []112 113    for c in controllers:114        if parent_sysfs and parent_sysfs not in c:115            continue116 117        if controller.get("dt-mmio"):118            if str(controller["dt-mmio"]) != get_dt_mmio(c):119                continue120 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                continue125 126        if controller.get("usb-version"):127            if controller["usb-version"] != get_usb_version(c):128                continue129 130        if controller.get("acpi-uid"):131            if controller["acpi-uid"] != get_acpi_uid(c):132                continue133 134        result_controllers.append(c)135 136    return result_controllers137 138 139def is_controller(device):140    return device.get("type") and "controller" in device.get("type")141 142 143def 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_list161 162 163def 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 error170            return pathdirs171        pathdir = pathdirs[0]172        sysfs_path = os.path.join(parent_sysfs, pathdir)173    else:174        sysfs_path = parent_sysfs175 176    if is_controller(device):177        return find_controller_in_sysfs(device, sysfs_path)178    else:179        return [sysfs_path]180 181 182def 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                continue197            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 207def 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_type220 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 pathname231 232 233def 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"] + "/" + pathname242    child["meta"]["pathname"] = pathname243 244 245def 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        return260    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 273def count_tests(device_trees):274    test_count = 0275 276    def parse_node(device):277        nonlocal test_count278        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 += 1286            test_count += 1287 288    for device_tree in device_trees:289        parse_node(device_tree)290 291    return test_count292 293 294def 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 filenames315 316 317def 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 329parser = argparse.ArgumentParser()330parser.add_argument(331    "--boards-dir", default="boards", help="Directory containing the board YAML files"332)333args = parser.parse_args()334 335find_pci_controller_dirs()336find_usb_controller_dirs()337 338ksft.print_header()339 340if 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 344board_file = ""345for 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_filename350        break351 352if not board_file:353    ksft.print_msg("No matching board file found")354    ksft.exit_fail()355 356run_test(board_file)357 358ksft.finished()359