1#!/usr/bin/env python3 2 3# Copyright (c) 2022 The Chromium OS Authors 4# SPDX-License-Identifier: Apache-2.0 5 6"""This file contains a Python script which parses through Zephyr device tree using 7EDT.pickle generated at build and generates a XML file containing USB VIF policies""" 8 9import argparse 10import inspect 11import os 12import pickle 13import sys 14import xml.etree.ElementTree as ET 15 16from constants import dt_constants 17from constants import other_constants 18from constants import pdo_constants 19from constants import vif_element_constants 20from constants import xml_constants 21 22SCRIPTS_DIR = os.path.join(os.path.dirname(__file__), "..") 23sys.path.insert(0, os.path.join(SCRIPTS_DIR, 'dts', 'python-devicetree', 'src')) 24 25 26def get_value_attribute(data): 27 if not isinstance(data, str): 28 data = str(data) 29 return {other_constants.VALUE: data} 30 31 32def get_vif_element_name(name): 33 return xml_constants.XML_ELEMENT_NAME_PREFIX + ":" + dt_constants.DT_VIF_ELEMENTS.get( 34 name, name) 35 36 37def get_root(): 38 xml_root = ET.Element( 39 get_vif_element_name(xml_constants.XML_ROOT_ELEMENT_NAME)) 40 add_attributes_to_xml_element(xml_root, 41 xml_constants.XML_NAMESPACE_ATTRIBUTES) 42 return xml_root 43 44 45def add_attributes_to_xml_element(xml_ele, attributes): 46 for key, value in attributes.items(): 47 xml_ele.set(key, value) 48 49 50def add_element_to_xml(xml_ele, name, text=None, attributes=None): 51 new_xml_ele = ET.SubElement(xml_ele, get_vif_element_name(name)) 52 if text: 53 new_xml_ele.text = str(text) 54 if attributes: 55 add_attributes_to_xml_element(new_xml_ele, attributes) 56 return new_xml_ele 57 58 59def add_elements_to_xml(xml_ele, elements): 60 for element_name in elements: 61 text = elements[element_name].get(other_constants.TEXT, None) 62 attributes = elements[element_name].get(other_constants.ATTRIBUTES, 63 None) 64 new_xml_ele = add_element_to_xml(xml_ele, element_name, text, 65 attributes) 66 if other_constants.CHILD in elements[element_name]: 67 add_elements_to_xml(new_xml_ele, 68 elements[element_name][other_constants.CHILD]) 69 70 71def add_vif_spec_from_source_to_xml(xml_ele, source_xml_ele): 72 prefix = '{' + xml_constants.XML_VIF_NAMESPACE + '}' 73 for child in source_xml_ele: 74 element_name = child.tag[len(prefix):] 75 if element_name in xml_constants.VIF_SPEC_ELEMENTS_FROM_SOURCE_XML: 76 add_element_to_xml(xml_ele, element_name, child.text, 77 child.attrib) 78 79 80def is_simple_datatype(value): 81 if isinstance(value, (str, int, bool)): 82 return True 83 return False 84 85 86def get_pdo_type(pdo_value): 87 return pdo_value >> 30 88 89 90def get_xml_bool_value(value): 91 if value: 92 return other_constants.TRUE 93 return other_constants.FALSE 94 95 96def parse_and_add_sink_pdo_to_xml(xml_ele, pdo_value, pdo_info): 97 power_mw = 0 98 xml_ele = add_element_to_xml(xml_ele, vif_element_constants.SINK_PDO) 99 pdo_type = get_pdo_type(pdo_value) 100 101 if pdo_type == pdo_constants.PDO_TYPE_FIXED: 102 # As per USB PD specification Revision 3.1, Version 1.6, Table 6-16 Fixed supply PDO - Sink 103 current = pdo_value & 0x3ff 104 current_ma = current * 10 105 voltage = (pdo_value >> 10) & 0x3ff 106 voltage_mv = voltage * 50 107 power_mw = (current_ma * voltage_mv) // 1000 108 109 if pdo_value & (1 << 28): 110 pdo_info[vif_element_constants.HIGHER_CAPABILITY_SET] = True 111 pdo_info[ 112 vif_element_constants.FR_SWAP_REQD_TYPE_C_CURRENT_AS_INITIAL_SOURCE] = pdo_value & ( 113 3 << 23) 114 add_element_to_xml(xml_ele, vif_element_constants.SINK_PDO_VOLTAGE, 115 f'{voltage_mv} mV', 116 get_value_attribute(voltage)) 117 add_element_to_xml(xml_ele, vif_element_constants.SINK_PDO_OP_CURRENT, 118 f'{current_ma} mA', 119 get_value_attribute(current)) 120 elif pdo_type == pdo_constants.PDO_TYPE_BATTERY: 121 # As per USB PD specification Revision 3.1, Version 1.6, Table 6-18 Battery supply PDO - Sink 122 max_voltage = (pdo_value >> 20) & 0x3ff 123 max_voltage_mv = max_voltage * 50 124 min_voltage = (pdo_value >> 10) & 0x3ff 125 min_voltage_mv = min_voltage * 50 126 power = pdo_value & 0x3ff 127 power_mw = power * 250 128 129 add_element_to_xml(xml_ele, vif_element_constants.SINK_PDO_MIN_VOLTAGE, 130 f'{min_voltage_mv} mV', 131 get_value_attribute(min_voltage)) 132 add_element_to_xml(xml_ele, vif_element_constants.SINK_PDO_MAX_VOLTAGE, 133 f'{max_voltage_mv} mV', 134 get_value_attribute(max_voltage)) 135 add_element_to_xml(xml_ele, vif_element_constants.SINK_PDO_OP_POWER, 136 f'{power_mw} mW', 137 get_value_attribute(power)) 138 elif pdo_type == pdo_constants.PDO_TYPE_VARIABLE: 139 # As per USB PD specification Revision 3.1, Version 1.6, Table 6-17 Variable supply (non-Battery) PDO - Sink 140 max_voltage = (pdo_value >> 20) & 0x3ff 141 max_voltage_mv = max_voltage * 50 142 min_voltage = (pdo_value >> 10) & 0x3ff 143 min_voltage_mv = min_voltage * 50 144 current = pdo_value & 0x3ff 145 current_ma = current * 10 146 power_mw = (current_ma * max_voltage_mv) // 1000 147 148 add_element_to_xml(xml_ele, vif_element_constants.SINK_PDO_MIN_VOLTAGE, 149 f'{min_voltage_mv} mV', 150 get_value_attribute(min_voltage)) 151 add_element_to_xml(xml_ele, vif_element_constants.SINK_PDO_MAX_VOLTAGE, 152 f'{max_voltage_mv} mV', 153 get_value_attribute(max_voltage)) 154 add_element_to_xml(xml_ele, vif_element_constants.SINK_PDO_OP_CURRENT, 155 f'{current_ma} mA', 156 get_value_attribute(current)) 157 elif pdo_type == pdo_constants.PDO_TYPE_AUGUMENTED: 158 # As per USB PD specification Revision 3.1, Version 1.6, Table 6-19 Programmable Power supply APDO - Sink 159 pps = (pdo_value >> 28) & 0x03 160 if pps: 161 raise ValueError(f'ERROR: Invalid PDO_TYPE {pdo_value}') 162 pps_max_voltage = (pdo_value >> 17) & 0xff 163 pps_max_voltage_mv = pps_max_voltage * 100 164 pps_min_voltage = (pdo_value >> 8) & 0xff 165 pps_min_voltage_mv = pps_min_voltage * 100 166 pps_current = pdo_value & 0x7f 167 pps_current_ma = pps_current * 50 168 power_mw = (pps_current_ma * pps_max_voltage_mv) // 1000 169 170 add_element_to_xml(xml_ele, vif_element_constants.SINK_PDO_MIN_VOLTAGE, 171 f'{pps_min_voltage_mv} mV', 172 get_value_attribute(pps_min_voltage)) 173 add_element_to_xml(xml_ele, vif_element_constants.SINK_PDO_MAX_VOLTAGE, 174 f'{pps_max_voltage_mv} mV', 175 get_value_attribute(pps_max_voltage)) 176 add_element_to_xml(xml_ele, vif_element_constants.SINK_PDO_OP_CURRENT, 177 f'{pps_current_ma} mA', 178 get_value_attribute(pps_current)) 179 else: 180 raise ValueError(f'ERROR: Invalid PDO_TYPE {pdo_value}') 181 182 add_element_to_xml(xml_ele, vif_element_constants.SINK_PDO_SUPPLY_TYPE, 183 pdo_constants.PDO_TYPES[pdo_type], 184 get_value_attribute(pdo_type)) 185 return power_mw 186 187 188def parse_and_add_sink_pdos_to_xml(xml_ele, sink_pdos): 189 new_xml_ele = add_element_to_xml(xml_ele, dt_constants.SINK_PDOS) 190 snk_max_power = 0 191 pdo_info = dict() 192 193 for pdo_value in sink_pdos: 194 power_mv = parse_and_add_sink_pdo_to_xml(new_xml_ele, pdo_value, 195 pdo_info) 196 if power_mv > snk_max_power: 197 snk_max_power = power_mv 198 199 add_element_to_xml(xml_ele, vif_element_constants.PD_POWER_AS_SINK, 200 f'{snk_max_power} mW', 201 get_value_attribute(snk_max_power)) 202 add_element_to_xml(xml_ele, vif_element_constants.HIGHER_CAPABILITY_SET, 203 None, get_value_attribute(get_xml_bool_value( 204 vif_element_constants.HIGHER_CAPABILITY_SET in 205 pdo_info))) 206 fr_swap_required_type_c_current_as_initial_source = pdo_info[ 207 vif_element_constants.FR_SWAP_REQD_TYPE_C_CURRENT_AS_INITIAL_SOURCE] 208 add_element_to_xml(xml_ele, 209 vif_element_constants.FR_SWAP_REQD_TYPE_C_CURRENT_AS_INITIAL_SOURCE, 210 other_constants.FR_SWAP_REQD_TYPE_C_CURRENT_AS_INITIAL_SOURCE_VALUES[ 211 fr_swap_required_type_c_current_as_initial_source], 212 get_value_attribute( 213 fr_swap_required_type_c_current_as_initial_source)) 214 add_element_to_xml(xml_ele, vif_element_constants.NUM_SNK_PDOS, None, 215 get_value_attribute(len(sink_pdos))) 216 217 218def parse_and_add_component_to_xml(xml_ele, node): 219 add_element_to_xml(xml_ele, vif_element_constants.USB_PD_SUPPORT, None, 220 get_value_attribute(get_xml_bool_value( 221 not node.props[dt_constants.PD_DISABLE].val))) 222 223 if not node.props[dt_constants.PD_DISABLE].val: 224 power_role = node.props[dt_constants.POWER_ROLE].val 225 add_element_to_xml(xml_ele, vif_element_constants.PD_PORT_TYPE, 226 other_constants.PD_PORT_TYPE_VALUES[power_role][1], 227 get_value_attribute( 228 other_constants.PD_PORT_TYPE_VALUES[ 229 power_role][0])) 230 add_element_to_xml(xml_ele, vif_element_constants.TYPE_C_STATE_MACHINE, 231 other_constants.TYPE_C_STATE_MACHINE_VALUES[ 232 power_role][1], 233 get_value_attribute( 234 other_constants.TYPE_C_STATE_MACHINE_VALUES[ 235 power_role][0])) 236 237 if dt_constants.SINK_PDOS in node.props: 238 parse_and_add_sink_pdos_to_xml(xml_ele, 239 node.props[dt_constants.SINK_PDOS].val) 240 241 242def get_source_xml_root(source_xml_file): 243 return ET.parse(source_xml_file).getroot() 244 245 246def parse_args(): 247 parser = argparse.ArgumentParser(allow_abbrev=False) 248 parser.add_argument("--edt-pickle", required=True, 249 help="path to read the pickled edtlib.EDT object from") 250 parser.add_argument("--compatible", required=True, 251 help="device tree compatible to be parsed") 252 parser.add_argument("--vif-out", required=True, 253 help="path to write VIF policies to") 254 parser.add_argument("--vif-source-xml", required=True, 255 help="XML file containing high level VIF specifications") 256 return parser.parse_args() 257 258 259def main(): 260 global edtlib 261 262 args = parse_args() 263 with open(args.edt_pickle, 'rb') as f: 264 edt = pickle.load(f) 265 edtlib = inspect.getmodule(edt) 266 267 xml_root = get_root() 268 source_xml_root = get_source_xml_root(args.vif_source_xml) 269 add_elements_to_xml(xml_root, xml_constants.VIF_SPEC_ELEMENTS) 270 add_vif_spec_from_source_to_xml(xml_root, source_xml_root) 271 272 for node in edt.compat2nodes[args.compatible]: 273 xml_ele = add_element_to_xml(xml_root, vif_element_constants.COMPONENT) 274 parse_and_add_component_to_xml(xml_ele, node) 275 276 tree = ET.ElementTree(xml_root) 277 tree.write(args.vif_out, xml_declaration=True, 278 encoding=xml_constants.XML_ENCODING) 279 280 281if __name__ == "__main__": 282 main() 283