1# Copyright (C) 2022 Intel Corporation. 2# 3# SPDX-License-Identifier: BSD-3-Clause 4# 5 6import re, os 7import logging 8 9from extractors.helpers import add_child, get_node, get_bdf_from_realpath 10from collections import defaultdict 11from pathlib import Path 12 13SYS_INPUT_DEVICES_CLASS_PATH = "/sys/class/input/" 14SYS_TTY_DEVICES_CLASS_PATH = "/sys/class/tty/" 15SYS_DISPLAYS_INFO_PATH = "/sys/class/drm/" 16 17def add_child_with_file_contents(parent_node, tag, filepath, translations = {}): 18 try: 19 with open(filepath, "r") as f: 20 res = f.read().strip() 21 if res in translations.keys(): 22 add_child(parent_node, tag, translations[res]) 23 else: 24 add_child(parent_node, tag, res) 25 except Exception as e: 26 logging.warning(f"Failed to read the data from {filepath}: {e}") 27 28def get_input_ids(): 29 input_ids = list() 30 root_regex = re.compile("input([0-9]+)") 31 for root in filter(lambda x: x.startswith("input"), os.listdir(SYS_INPUT_DEVICES_CLASS_PATH)): 32 m = root_regex.match(root) 33 if m: 34 input_ids.append(int(m.group(1))) 35 return sorted(input_ids) 36 37def extract_inputs(device_classes_node): 38 inputs_node = add_child(device_classes_node, "inputs", None) 39 input_ids = get_input_ids() 40 for id in input_ids: 41 input_node = add_child(inputs_node, "input", None) 42 add_child_with_file_contents(input_node, "name", f"/sys/class/input/input{id}/name") 43 add_child_with_file_contents(input_node, "phys", f"/sys/class/input/input{id}/phys") 44 45def get_serial_devs(): 46 return sorted(filter(lambda x: x.startswith("ttyS"), os.listdir(SYS_TTY_DEVICES_CLASS_PATH)), key=lambda x:int(x[4:])) 47 48def extract_ttys(device_classes_node): 49 ttys_node = add_child(device_classes_node, "ttys", None) 50 for serial_dev in get_serial_devs(): 51 serial_node = add_child(ttys_node, "serial") 52 add_child(serial_node, "dev_path", f"/dev/{serial_dev}") 53 add_child_with_file_contents(serial_node, "type", f"{SYS_TTY_DEVICES_CLASS_PATH}{serial_dev}/type") 54 55def extract_display(board_etree): 56 display_regex = re.compile("(card[0-9])-(DP|HDMI|VGA)-.*") 57 display_types = { 58 "DP": "DisplayPort (DP)", 59 "HDMI": "High Definition Multimedia Interface (HDMI)", 60 "VGA": "Video Graphics Array (VGA)", 61 } 62 display_ids = defaultdict(lambda: 0) 63 for root in filter(lambda x: x.startswith("card"), os.listdir(SYS_DISPLAYS_INFO_PATH)): 64 displays_path = SYS_DISPLAYS_INFO_PATH + root 65 status_path = f"{displays_path}/status" 66 device_path = f"{displays_path}/device/device" 67 m = display_regex.match(root) 68 if m: 69 try: 70 assert Path(status_path).exists(), \ 71 f"{status_path} does not exist and connection status of {root} cannot be detected. Failed to add " \ 72 f"{root} to board XML." 73 assert Path(device_path).exists(), \ 74 f"{device_path} does not exist and the graphics card which {root} is connected to cannot be detected. " \ 75 f"Failed to add {root} to board XML" 76 with open(f"{status_path}", "r") as f: 77 if f.read().strip() == "connected": 78 bus, device, function = \ 79 get_bdf_from_realpath(f"{device_path}") 80 adr = hex((device << 16) + function) 81 bus_node = get_node(board_etree, f"//bus[@type='pci' and @address='{hex(bus)}']") 82 if bus_node is None: 83 devices_node = get_node(board_etree, "//devices") 84 bus_node = add_child(devices_node, "bus", type="pci", address=hex(bus)) 85 device_node = get_node(bus_node, f"./device[@address='{adr}']") 86 if device_node is None: 87 device_node = add_child(bus_node, "device", None, address=adr) 88 bdf = (bus, device, function) 89 display_id = display_ids[bdf] 90 add_child(device_node, "display", f"{display_id}", type=display_types[m.group(2)]) 91 display_ids[bdf] += 1 92 except Exception as e: 93 logging.warning(f"{e}") 94 95def extract_topology(device_classes_node, board_etree): 96 extract_inputs(device_classes_node) 97 extract_ttys(device_classes_node) 98 extract_display(board_etree) 99 100def extract(args, board_etree): 101 device_classes_node = get_node(board_etree, "//device-classes") 102 extract_topology(device_classes_node, board_etree) 103