From acfb5308f5e51fd1f4428618d704ad0de2358871 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 22 Dec 2020 19:30:20 -0700 Subject: sandbox: Drop unnecessary test node The spl-test4 node deliberately has an invalid compatible string. This causes a warning from dtoc and the check it does is not really necessary. Drop it, to avoid the warning and associated confusion. Signed-off-by: Simon Glass --- tools/dtoc/dtoc_test_simple.dts | 5 ----- tools/dtoc/test_dtoc.py | 12 ------------ 2 files changed, 17 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtoc_test_simple.dts b/tools/dtoc/dtoc_test_simple.dts index fd168cb593..1c87b89192 100644 --- a/tools/dtoc/dtoc_test_simple.dts +++ b/tools/dtoc/dtoc_test_simple.dts @@ -44,11 +44,6 @@ longbytearray = [09 0a 0b 0c 0d 0e 0f 10]; }; - spl-test4 { - u-boot,dm-pre-reloc; - compatible = "sandbox,spl-test.2"; - }; - i2c@0 { compatible = "sandbox,i2c-test"; u-boot,dm-pre-reloc; diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 4913d95021..49ab75b85d 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -204,8 +204,6 @@ struct dtd_sandbox_spl_test { \tconst char *\tstringarray[3]; \tconst char *\tstringval; }; -struct dtd_sandbox_spl_test_2 { -}; ''', data) self.run_test(['platdata'], dtb_file, output) @@ -286,16 +284,6 @@ U_BOOT_DEVICE(spl_test3) = { \t.parent_idx\t= -1, }; -/* Node /spl-test4 index 5 */ -static struct dtd_sandbox_spl_test_2 dtv_spl_test4 = { -}; -U_BOOT_DEVICE(spl_test4) = { -\t.name\t\t= "sandbox_spl_test_2", -\t.plat\t= &dtv_spl_test4, -\t.plat_size\t= sizeof(dtv_spl_test4), -\t.parent_idx\t= -1, -}; - ''' + C_EMPTY_POPULATE_PHANDLE_DATA, data) def test_driver_alias(self): -- cgit From ccc3da77aec69ed5bddd70c0ee598e4d6fc6bdc1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Dec 2020 08:11:19 -0700 Subject: dtoc: Fix a few pylint warnings in dtb_platdata These have crept in again. Update the file to fix all but these ones: dtb_platdata.py:143:0: R0902: Too many instance attributes (10/7) (too-many-instance-attributes) dtb_platdata.py:713:0: R0913: Too many arguments (6/5) (too-many-arguments) Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 82671138a9..cc3e58a1cd 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -112,18 +112,19 @@ def get_value(ftype, value): str: String representation of the value """ if ftype == fdt.Type.INT: - return '%#x' % fdt_util.fdt32_to_cpu(value) + val = '%#x' % fdt_util.fdt32_to_cpu(value) elif ftype == fdt.Type.BYTE: char = value[0] - return '%#x' % (ord(char) if isinstance(char, str) else char) + val = '%#x' % (ord(char) if isinstance(char, str) else char) elif ftype == fdt.Type.STRING: # Handle evil ACPI backslashes by adding another backslash before them. # So "\\_SB.GPO0" in the device tree effectively stays like that in C - return '"%s"' % value.replace('\\', '\\\\') + val = '"%s"' % value.replace('\\', '\\\\') elif ftype == fdt.Type.BOOL: - return 'true' + val = 'true' else: # ftype == fdt.Type.INT64: - return '%#x' % value + val = '%#x' % value + return val def get_compat_name(node): """Get the node's list of compatible string as a C identifiers @@ -131,7 +132,7 @@ def get_compat_name(node): Args: node (fdt.Node): Node object to check Return: - List of C identifiers for all the compatible strings + list of str: List of C identifiers for all the compatible strings """ compat = node.props['compatible'].value if not isinstance(compat, list): @@ -139,7 +140,7 @@ def get_compat_name(node): return [conv_name_to_c(c) for c in compat] -class DtbPlatdata(object): +class DtbPlatdata(): """Provide a means to convert device tree binary data to platform data The output of this process is C structures which can be used in space- @@ -183,7 +184,7 @@ class DtbPlatdata(object): and a lookup in driver_aliases printing a warning in case of failure. Args: - node: Node object to check + node (Node): Node object to check Return: Tuple: Driver name associated with the first compatible string @@ -330,7 +331,7 @@ class DtbPlatdata(object): # The following re will search for driver names declared as # U_BOOT_DRIVER(driver_name) - drivers = re.findall('U_BOOT_DRIVER\((.*)\)', buff) + drivers = re.findall(r'U_BOOT_DRIVER\((.*)\)', buff) for driver in drivers: self._drivers.append(driver) @@ -338,7 +339,7 @@ class DtbPlatdata(object): # The following re will search for driver aliases declared as # U_BOOT_DRIVER_ALIAS(alias, driver_name) driver_aliases = re.findall( - 'U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)', + r'U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)', buff) for alias in driver_aliases: # pragma: no cover @@ -383,8 +384,8 @@ class DtbPlatdata(object): This adds each node to self._valid_nodes. Args: - root: Root node for scan - valid_nodes: List of Node objects to add to + root (Node): Root node for scan + valid_nodes (list of Node): List of Node objects to add to """ for node in root.subnodes: if 'compatible' in node.props: @@ -484,7 +485,7 @@ class DtbPlatdata(object): updated to match that width. Returns: - dict containing structures: + dict of dict: dict containing structures: key (str): Node name, as a C identifier value: dict containing structure fields: key (str): Field name @@ -559,7 +560,7 @@ class DtbPlatdata(object): doc/driver-model/of-plat.rst for more information. Args: - structs: dict containing structures: + structs (dict): dict containing structures: key (str): Node name, as a C identifier value: dict containing structure fields: key (str): Field name @@ -720,7 +721,7 @@ def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False, output (str): Name of output file warning_disabled (bool): True to avoid showing warnings about missing drivers - _drivers_additional (list): List of additional drivers to use during + drivers_additional (list): List of additional drivers to use during scanning Raises: ValueError: if args has no command, or an unknown command -- cgit From abf0c80292d62121d0c28cda27a92a10406428de Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Dec 2020 08:11:20 -0700 Subject: dtoc: Make _output_list a top-level function It is annoying to have this function inside its parent since it makes the parent longer and hard to read. Move it to the top level. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 80 +++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 40 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index cc3e58a1cd..372f756037 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -592,51 +592,51 @@ class DtbPlatdata(): self.out(';\n') self.out('};\n') + def _output_list(self, node, prop): + """Output the C code for a devicetree property that holds a list + + Args: + node (fdt.Node): Node to output + prop (fdt.Prop): Prop to output + """ + self.buf('{') + vals = [] + # For phandles, output a reference to the platform data + # of the target node. + info = self.get_phandle_argc(prop, node.name) + if info: + # Process the list as pairs of (phandle, id) + pos = 0 + for args in info.args: + phandle_cell = prop.value[pos] + phandle = fdt_util.fdt32_to_cpu(phandle_cell) + target_node = self._fdt.phandle_to_node[phandle] + arg_values = [] + for i in range(args): + arg_values.append( + str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i]))) + pos += 1 + args + vals.append('\t{%d, {%s}}' % (target_node.idx, + ', '.join(arg_values))) + for val in vals: + self.buf('\n\t\t%s,' % val) + else: + for val in prop.value: + vals.append(get_value(prop.type, val)) + + # Put 8 values per line to avoid very long lines. + for i in range(0, len(vals), 8): + if i: + self.buf(',\n\t\t') + self.buf(', '.join(vals[i:i + 8])) + self.buf('}') + def output_node(self, node): """Output the C code for a node Args: node (fdt.Node): node to output """ - def _output_list(node, prop): - """Output the C code for a devicetree property that holds a list - - Args: - node (fdt.Node): Node to output - prop (fdt.Prop): Prop to output - """ - self.buf('{') - vals = [] - # For phandles, output a reference to the platform data - # of the target node. - info = self.get_phandle_argc(prop, node.name) - if info: - # Process the list as pairs of (phandle, id) - pos = 0 - for args in info.args: - phandle_cell = prop.value[pos] - phandle = fdt_util.fdt32_to_cpu(phandle_cell) - target_node = self._fdt.phandle_to_node[phandle] - arg_values = [] - for i in range(args): - arg_values.append( - str(fdt_util.fdt32_to_cpu(prop.value[pos + 1 + i]))) - pos += 1 + args - vals.append('\t{%d, {%s}}' % (target_node.idx, - ', '.join(arg_values))) - for val in vals: - self.buf('\n\t\t%s,' % val) - else: - for val in prop.value: - vals.append(get_value(prop.type, val)) - - # Put 8 values per line to avoid very long lines. - for i in range(0, len(vals), 8): - if i: - self.buf(',\n\t\t') - self.buf(', '.join(vals[i:i + 8])) - self.buf('}') - struct_name, _ = self.get_normalized_compat_name(node) var_name = conv_name_to_c(node.name) self.buf('/* Node %s index %d */\n' % (node.path, node.idx)) @@ -651,7 +651,7 @@ class DtbPlatdata(): # Special handling for lists if isinstance(prop.value, list): - _output_list(node, prop) + self._output_list(node, prop) else: self.buf(get_value(prop.type, prop.value)) self.buf(',\n') -- cgit From 221ddc11586fa29b6fcaeae5ea06379f5c62e5e4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Dec 2020 08:11:21 -0700 Subject: dtoc: Output the device in a separate function Reduce the length of output_node() by moving the device-output functionality into a separate function. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 372f756037..2d701ac24d 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -631,6 +631,27 @@ class DtbPlatdata(): self.buf(', '.join(vals[i:i + 8])) self.buf('}') + def _declare_device(self, var_name, struct_name, node_parent): + """Add a device declaration to the output + + This declares a U_BOOT_DEVICE() for the device being processed + + Args: + var_name (str): C name for the node + struct_name (str): Name for the dt struct associated with the node + node_parent (Node): Parent of the node (or None if none) + """ + self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name) + self.buf('\t.name\t\t= "%s",\n' % struct_name) + self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, var_name)) + self.buf('\t.plat_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name)) + idx = -1 + if node_parent and node_parent in self._valid_nodes: + idx = node_parent.idx + self.buf('\t.parent_idx\t= %d,\n' % idx) + self.buf('};\n') + self.buf('\n') + def output_node(self, node): """Output the C code for a node @@ -657,17 +678,7 @@ class DtbPlatdata(): self.buf(',\n') self.buf('};\n') - # Add a device declaration - self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name) - self.buf('\t.name\t\t= "%s",\n' % struct_name) - self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, var_name)) - self.buf('\t.plat_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name)) - idx = -1 - if node.parent and node.parent in self._valid_nodes: - idx = node.parent.idx - self.buf('\t.parent_idx\t= %d,\n' % idx) - self.buf('};\n') - self.buf('\n') + self._declare_device(var_name, struct_name, node.parent) self.out(''.join(self.get_buf())) -- cgit From 161dac1dd86fdc4e819b32ab12f656d8d738f95a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Dec 2020 08:11:22 -0700 Subject: dtoc: Output the struct values in a separate function Reduce the length of output_node() futher by moving the struct-output functionality into a two separate functions. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 50 +++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 16 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 2d701ac24d..1d89f77a00 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -652,6 +652,39 @@ class DtbPlatdata(): self.buf('};\n') self.buf('\n') + def _output_prop(self, node, prop): + """Output a line containing the value of a struct member + + Args: + node (Node): Node being output + prop (Prop): Prop object to output + """ + if prop.name in PROP_IGNORE_LIST or prop.name[0] == '#': + return + member_name = conv_name_to_c(prop.name) + self.buf('\t%s= ' % tab_to(3, '.' + member_name)) + + # Special handling for lists + if isinstance(prop.value, list): + self._output_list(node, prop) + else: + self.buf(get_value(prop.type, prop.value)) + self.buf(',\n') + + def _output_values(self, var_name, struct_name, node): + """Output the definition of a device's struct values + + Args: + var_name (str): C name for the node + struct_name (str): Name for the dt struct associated with the node + node (Node): Node being output + """ + self.buf('static struct %s%s %s%s = {\n' % + (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name)) + for pname in sorted(node.props): + self._output_prop(node, node.props[pname]) + self.buf('};\n') + def output_node(self, node): """Output the C code for a node @@ -661,23 +694,8 @@ class DtbPlatdata(): struct_name, _ = self.get_normalized_compat_name(node) var_name = conv_name_to_c(node.name) self.buf('/* Node %s index %d */\n' % (node.path, node.idx)) - self.buf('static struct %s%s %s%s = {\n' % - (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name)) - for pname in sorted(node.props): - prop = node.props[pname] - if pname in PROP_IGNORE_LIST or pname[0] == '#': - continue - member_name = conv_name_to_c(prop.name) - self.buf('\t%s= ' % tab_to(3, '.' + member_name)) - - # Special handling for lists - if isinstance(prop.value, list): - self._output_list(node, prop) - else: - self.buf(get_value(prop.type, prop.value)) - self.buf(',\n') - self.buf('};\n') + self._output_values(var_name, struct_name, node) self._declare_device(var_name, struct_name, node.parent) self.out(''.join(self.get_buf())) -- cgit From 7d637c122d772e472a211f6aa3f33cbe3c3e243c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 23 Dec 2020 08:11:23 -0700 Subject: dtoc: Convert _drivers to a dict At present this member holds a simple list of driver names. Update it to be a dict of DriverInfo, with the name being the key. This will allow more information to be added about each driver, in future patches. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 26 ++++++++++++++++++++++---- tools/dtoc/test_dtoc.py | 10 ++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 1d89f77a00..5b1bb7e5fd 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -64,6 +64,22 @@ PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args']) PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name']) +class Driver: + """Information about a driver in U-Boot + + Attributes: + name: Name of driver. For U_BOOT_DRIVER(x) this is 'x' + """ + def __init__(self, name): + self.name = name + + def __eq__(self, other): + return self.name == other.name + + def __repr__(self): + return "Driver(name='%s')" % self.name + + def conv_name_to_c(name): """Convert a device-tree name to a C identifier @@ -156,7 +172,9 @@ class DtbPlatdata(): _outfile: The current output file (sys.stdout or a real file) _warning_disabled: true to disable warnings about driver names not found _lines: Stashed list of output lines for outputting in the future - _drivers: List of valid driver names found in drivers/ + _drivers: Dict of valid driver names found in drivers/ + key: Driver name + value: Driver for that driver _driver_aliases: Dict that holds aliases for driver names key: Driver alias declared with U_BOOT_DRIVER_ALIAS(driver_alias, driver_name) @@ -172,7 +190,7 @@ class DtbPlatdata(): self._outfile = None self._warning_disabled = warning_disabled self._lines = [] - self._drivers = [] + self._drivers = {} self._driver_aliases = {} self._drivers_additional = drivers_additional or [] @@ -196,7 +214,7 @@ class DtbPlatdata(): compat_list_c = get_compat_name(node) for compat_c in compat_list_c: - if not compat_c in self._drivers: + if not compat_c in self._drivers.keys(): compat_c = self._driver_aliases.get(compat_c) if not compat_c: continue @@ -334,7 +352,7 @@ class DtbPlatdata(): drivers = re.findall(r'U_BOOT_DRIVER\((.*)\)', buff) for driver in drivers: - self._drivers.append(driver) + self._drivers[driver] = Driver(driver) # The following re will search for driver aliases declared as # U_BOOT_DRIVER_ALIAS(alias, driver_name) diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 49ab75b85d..c76942c9e2 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -906,3 +906,13 @@ U_BOOT_DEVICE(spl_test2) = { with test_util.capture_sys_output() as (stdout, stderr): dtb_platdata.run_steps(['struct'], dtb_file, False, output, True, [driver_fn]) + + def testDriver(self): + """Test the Driver class""" + drv1 = dtb_platdata.Driver('fred') + drv2 = dtb_platdata.Driver('mary') + drv3 = dtb_platdata.Driver('fred') + self.assertEqual("Driver(name='fred')", str(drv1)) + self.assertEqual(drv1, drv3) + self.assertNotEqual(drv1, drv2) + self.assertNotEqual(drv2, drv3) -- cgit From 67b5ec54a5c19452c7aa33c693f281cc18a37d09 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:34:47 -0700 Subject: dtoc: Tidy up pylint warnings in test Tidy up this file to reduce the number of pylint warnings. Signed-off-by: Simon Glass --- tools/dtoc/test_dtoc.py | 156 ++++++++++++++++++++++++++---------------------- 1 file changed, 85 insertions(+), 71 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index c76942c9e2..4bf90444e1 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -12,21 +12,20 @@ tool. import collections import os import struct -import sys import tempfile import unittest -from dtoc import dtb_platdata from dtb_platdata import conv_name_to_c from dtb_platdata import get_compat_name from dtb_platdata import get_value from dtb_platdata import tab_to +from dtoc import dtb_platdata from dtoc import fdt from dtoc import fdt_util from patman import test_util from patman import tools -our_path = os.path.dirname(os.path.realpath(__file__)) +OUR_PATH = os.path.dirname(os.path.realpath(__file__)) HEADER = '''/* @@ -56,18 +55,21 @@ C_EMPTY_POPULATE_PHANDLE_DATA = '''void dm_populate_phandle_data(void) { } ''' +# This is a test so is allowed to access private things in the module it is +# testing +# pylint: disable=W0212 def get_dtb_file(dts_fname, capture_stderr=False): """Compile a .dts file to a .dtb Args: - dts_fname: Filename of .dts file in the current directory - capture_stderr: True to capture and discard stderr output + dts_fname (str): Filename of .dts file in the current directory + capture_stderr (bool): True to capture and discard stderr output Returns: - Filename of compiled file in output directory + str: Filename of compiled file in output directory """ - return fdt_util.EnsureCompiled(os.path.join(our_path, dts_fname), + return fdt_util.EnsureCompiled(os.path.join(OUR_PATH, dts_fname), capture_stderr=capture_stderr) @@ -80,20 +82,21 @@ class TestDtoc(unittest.TestCase): @classmethod def tearDownClass(cls): - tools._RemoveOutputDir() + tools.FinaliseOutputDir() - def _WritePythonString(self, fname, data): + @staticmethod + def _write_python_string(fname, data): """Write a string with tabs expanded as done in this Python file Args: - fname: Filename to write to - data: Raw string to convert + fname (str): Filename to write to + data (str): Raw string to convert """ data = data.replace('\t', '\\t') - with open(fname, 'w') as fd: - fd.write(data) + with open(fname, 'w') as fout: + fout.write(data) - def _CheckStrings(self, expected, actual): + def _check_strings(self, expected, actual): """Check that a string matches its expected value If the strings do not match, they are written to the /tmp directory in @@ -101,17 +104,24 @@ class TestDtoc(unittest.TestCase): easy comparison and update of the tests. Args: - expected: Expected string - actual: Actual string + expected (str): Expected string + actual (str): Actual string """ if expected != actual: - self._WritePythonString('/tmp/binman.expected', expected) - self._WritePythonString('/tmp/binman.actual', actual) + self._write_python_string('/tmp/binman.expected', expected) + self._write_python_string('/tmp/binman.actual', actual) print('Failures written to /tmp/binman.{expected,actual}') - self.assertEquals(expected, actual) + self.assertEqual(expected, actual) + @staticmethod + def run_test(args, dtb_file, output): + """Run a test using dtoc - def run_test(self, args, dtb_file, output): + Args: + args (list of str): List of arguments for dtoc + dtb_file (str): Filename of .dtb file + output (str): Filename of output file + """ dtb_platdata.run_steps(args, dtb_file, False, output, True) def test_name(self): @@ -160,7 +170,7 @@ class TestDtoc(unittest.TestCase): prop = Prop(['rockchip,rk3399-sdhci-5.1', 'arasan,sdhci-5.1', 'third']) node = Node({'compatible': prop}) self.assertEqual((['rockchip_rk3399_sdhci_5_1', - 'arasan_sdhci_5_1', 'third']), + 'arasan_sdhci_5_1', 'third']), get_compat_name(node)) def test_empty_file(self): @@ -185,7 +195,7 @@ class TestDtoc(unittest.TestCase): self.run_test(['struct'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(HEADER + ''' + self._check_strings(HEADER + ''' struct dtd_sandbox_i2c_test { }; struct dtd_sandbox_pmic_test { @@ -209,7 +219,7 @@ struct dtd_sandbox_spl_test { self.run_test(['platdata'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(C_HEADER + ''' + self._check_strings(C_HEADER + ''' /* Node /i2c@0 index 0 */ static struct dtd_sandbox_i2c_test dtv_i2c_at_0 = { }; @@ -293,7 +303,7 @@ U_BOOT_DEVICE(spl_test3) = { self.run_test(['struct'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(HEADER + ''' + self._check_strings(HEADER + ''' struct dtd_sandbox_gpio { \tconst char *\tgpio_bank_name; \tbool\t\tgpio_controller; @@ -304,7 +314,7 @@ struct dtd_sandbox_gpio { self.run_test(['platdata'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(C_HEADER + ''' + self._check_strings(C_HEADER + ''' /* Node /gpios@0 index 0 */ static struct dtd_sandbox_gpio dtv_gpios_at_0 = { \t.gpio_bank_name\t\t= "a", @@ -326,20 +336,20 @@ void dm_populate_phandle_data(void) { """Test output from a device tree file with an invalid driver""" dtb_file = get_dtb_file('dtoc_test_invalid_driver.dts') output = tools.GetOutputFilename('output') - with test_util.capture_sys_output() as (stdout, stderr): + with test_util.capture_sys_output() as _: dtb_platdata.run_steps(['struct'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self._CheckStrings(HEADER + ''' + self._check_strings(HEADER + ''' struct dtd_invalid { }; ''', data) - with test_util.capture_sys_output() as (stdout, stderr): + with test_util.capture_sys_output() as _: dtb_platdata.run_steps(['platdata'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self._CheckStrings(C_HEADER + ''' + self._check_strings(C_HEADER + ''' /* Node /spl-test index 0 */ static struct dtd_invalid dtv_spl_test = { }; @@ -361,7 +371,7 @@ void dm_populate_phandle_data(void) { self.run_test(['struct'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(HEADER + ''' + self._check_strings(HEADER + ''' struct dtd_source { \tstruct phandle_2_arg clocks[4]; }; @@ -373,7 +383,7 @@ struct dtd_target { self.run_test(['platdata'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(C_HEADER + ''' + self._check_strings(C_HEADER + ''' /* Node /phandle2-target index 0 */ static struct dtd_target dtv_phandle2_target = { \t.intval\t\t\t= 0x1, @@ -445,7 +455,7 @@ void dm_populate_phandle_data(void) { self.run_test(['struct'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(HEADER + ''' + self._check_strings(HEADER + ''' struct dtd_source { \tstruct phandle_0_arg clocks[1]; }; @@ -461,7 +471,7 @@ struct dtd_target { self.run_test(['platdata'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(C_HEADER + ''' + self._check_strings(C_HEADER + ''' /* Node /phandle-target index 1 */ static struct dtd_target dtv_phandle_target = { }; @@ -495,7 +505,7 @@ void dm_populate_phandle_data(void) { dtb_platdata.run_steps(['platdata'], dtb_file, False, output, True) with open(output) as infile: data = infile.read() - self._CheckStrings(C_HEADER + ''' + self._check_strings(C_HEADER + ''' /* Node /phandle2-target index 0 */ static struct dtd_target dtv_phandle2_target = { \t.intval\t\t\t= 0x1, @@ -565,20 +575,20 @@ void dm_populate_phandle_data(void) { dtb_file = get_dtb_file('dtoc_test_phandle_bad.dts', capture_stderr=True) output = tools.GetOutputFilename('output') - with self.assertRaises(ValueError) as e: + with self.assertRaises(ValueError) as exc: self.run_test(['struct'], dtb_file, output) self.assertIn("Cannot parse 'clocks' in node 'phandle-source'", - str(e.exception)) + str(exc.exception)) def test_phandle_bad2(self): """Test a phandle target missing its #*-cells property""" dtb_file = get_dtb_file('dtoc_test_phandle_bad2.dts', capture_stderr=True) output = tools.GetOutputFilename('output') - with self.assertRaises(ValueError) as e: + with self.assertRaises(ValueError) as exc: self.run_test(['struct'], dtb_file, output) self.assertIn("Node 'phandle-target' has no cells property", - str(e.exception)) + str(exc.exception)) def test_addresses64(self): """Test output from a node with a 'reg' property with na=2, ns=2""" @@ -587,7 +597,7 @@ void dm_populate_phandle_data(void) { self.run_test(['struct'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(HEADER + ''' + self._check_strings(HEADER + ''' struct dtd_test1 { \tfdt64_t\t\treg[2]; }; @@ -602,7 +612,7 @@ struct dtd_test3 { self.run_test(['platdata'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(C_HEADER + ''' + self._check_strings(C_HEADER + ''' /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x5678}, @@ -645,7 +655,7 @@ U_BOOT_DEVICE(test3) = { self.run_test(['struct'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(HEADER + ''' + self._check_strings(HEADER + ''' struct dtd_test1 { \tfdt32_t\t\treg[2]; }; @@ -657,7 +667,7 @@ struct dtd_test2 { self.run_test(['platdata'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(C_HEADER + ''' + self._check_strings(C_HEADER + ''' /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x5678}, @@ -689,7 +699,7 @@ U_BOOT_DEVICE(test2) = { self.run_test(['struct'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(HEADER + ''' + self._check_strings(HEADER + ''' struct dtd_test1 { \tfdt64_t\t\treg[2]; }; @@ -704,7 +714,7 @@ struct dtd_test3 { self.run_test(['platdata'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(C_HEADER + ''' + self._check_strings(C_HEADER + ''' /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x123400000000, 0x5678}, @@ -747,7 +757,7 @@ U_BOOT_DEVICE(test3) = { self.run_test(['struct'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(HEADER + ''' + self._check_strings(HEADER + ''' struct dtd_test1 { \tfdt64_t\t\treg[2]; }; @@ -762,7 +772,7 @@ struct dtd_test3 { self.run_test(['platdata'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(C_HEADER + ''' + self._check_strings(C_HEADER + ''' /* Node /test1 index 0 */ static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x567800000000}, @@ -803,20 +813,21 @@ U_BOOT_DEVICE(test3) = { # Capture stderr since dtc will emit warnings for this file dtb_file = get_dtb_file('dtoc_test_bad_reg.dts', capture_stderr=True) output = tools.GetOutputFilename('output') - with self.assertRaises(ValueError) as e: + with self.assertRaises(ValueError) as exc: self.run_test(['struct'], dtb_file, output) self.assertIn("Node 'spl-test' reg property is not an int", - str(e.exception)) + str(exc.exception)) def test_bad_reg2(self): """Test that a reg property with an invalid cell count is detected""" # Capture stderr since dtc will emit warnings for this file dtb_file = get_dtb_file('dtoc_test_bad_reg2.dts', capture_stderr=True) output = tools.GetOutputFilename('output') - with self.assertRaises(ValueError) as e: + with self.assertRaises(ValueError) as exc: self.run_test(['struct'], dtb_file, output) - self.assertIn("Node 'spl-test' reg property has 3 cells which is not a multiple of na + ns = 1 + 1)", - str(e.exception)) + self.assertIn( + "Node 'spl-test' reg property has 3 cells which is not a multiple of na + ns = 1 + 1)", + str(exc.exception)) def test_add_prop(self): """Test that a subequent node can add a new property to a struct""" @@ -825,7 +836,7 @@ U_BOOT_DEVICE(test3) = { self.run_test(['struct'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(HEADER + ''' + self._check_strings(HEADER + ''' struct dtd_sandbox_spl_test { \tfdt32_t\t\tintarray; \tfdt32_t\t\tintval; @@ -835,7 +846,7 @@ struct dtd_sandbox_spl_test { self.run_test(['platdata'], dtb_file, output) with open(output) as infile: data = infile.read() - self._CheckStrings(C_HEADER + ''' + self._check_strings(C_HEADER + ''' /* Node /spl-test index 0 */ static struct dtd_sandbox_spl_test dtv_spl_test = { \t.intval\t\t\t= 0x1, @@ -860,37 +871,40 @@ U_BOOT_DEVICE(spl_test2) = { ''' + C_EMPTY_POPULATE_PHANDLE_DATA, data) - def testStdout(self): + def test_stdout(self): """Test output to stdout""" dtb_file = get_dtb_file('dtoc_test_simple.dts') - with test_util.capture_sys_output() as (stdout, stderr): + with test_util.capture_sys_output() as _: self.run_test(['struct'], dtb_file, '-') - def testNoCommand(self): + def test_no_command(self): """Test running dtoc without a command""" - with self.assertRaises(ValueError) as e: + with self.assertRaises(ValueError) as exc: self.run_test([], '', '') self.assertIn("Please specify a command: struct, platdata", - str(e.exception)) + str(exc.exception)) - def testBadCommand(self): + def test_bad_command(self): """Test running dtoc with an invalid command""" dtb_file = get_dtb_file('dtoc_test_simple.dts') output = tools.GetOutputFilename('output') - with self.assertRaises(ValueError) as e: + with self.assertRaises(ValueError) as exc: self.run_test(['invalid-cmd'], dtb_file, output) self.assertIn("Unknown command 'invalid-cmd': (use: struct, platdata)", - str(e.exception)) + str(exc.exception)) - def testScanDrivers(self): + @staticmethod + def test_scan_drivers(): """Test running dtoc with additional drivers to scan""" dtb_file = get_dtb_file('dtoc_test_simple.dts') output = tools.GetOutputFilename('output') - with test_util.capture_sys_output() as (stdout, stderr): - dtb_platdata.run_steps(['struct'], dtb_file, False, output, True, - [None, '', 'tools/dtoc/dtoc_test_scan_drivers.cxx']) + with test_util.capture_sys_output() as _: + dtb_platdata.run_steps( + ['struct'], dtb_file, False, output, True, + [None, '', 'tools/dtoc/dtoc_test_scan_drivers.cxx']) - def testUnicodeError(self): + @staticmethod + def test_unicode_error(): """Test running dtoc with an invalid unicode file To be able to perform this test without adding a weird text file which @@ -900,14 +914,14 @@ U_BOOT_DEVICE(spl_test2) = { dtb_file = get_dtb_file('dtoc_test_simple.dts') output = tools.GetOutputFilename('output') driver_fn = '/tmp/' + next(tempfile._get_candidate_names()) - with open(driver_fn, 'wb+') as df: - df.write(b'\x81') + with open(driver_fn, 'wb+') as fout: + fout.write(b'\x81') - with test_util.capture_sys_output() as (stdout, stderr): + with test_util.capture_sys_output() as _: dtb_platdata.run_steps(['struct'], dtb_file, False, output, True, - [driver_fn]) + [driver_fn]) - def testDriver(self): + def test_driver(self): """Test the Driver class""" drv1 = dtb_platdata.Driver('fred') drv2 = dtb_platdata.Driver('mary') -- cgit From f62cea0e205c905632be3aefa82b10ef36ce8a25 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:34:48 -0700 Subject: dtoc: Use None to mean stdout At present dtoc uses '-' internally to mean that output should go to stdout. This is not necessary and None is more convenient. Update it. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 10 +++++----- tools/dtoc/main.py | 2 +- tools/dtoc/test_dtoc.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 5b1bb7e5fd..118b1a5f43 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -237,12 +237,12 @@ class DtbPlatdata(): file. Args: - fname (str): Filename to send output to, or '-' for stdout + fname (str): Filename to send output to, or None for stdout """ - if fname == '-': - self._outfile = sys.stdout - else: + if fname: self._outfile = open(fname, 'w') + else: + self._outfile = sys.stdout def out(self, line): """Output a string to the output file @@ -765,7 +765,7 @@ def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False, args (list): List of non-option arguments provided to the problem dtb_file (str): Filename of dtb file to process include_disabled (bool): True to include disabled nodes - output (str): Name of output file + output (str): Name of output file (None for stdout) warning_disabled (bool): True to avoid showing warnings about missing drivers drivers_additional (list): List of additional drivers to use during diff --git a/tools/dtoc/main.py b/tools/dtoc/main.py index b94d9c301f..7686c8784d 100755 --- a/tools/dtoc/main.py +++ b/tools/dtoc/main.py @@ -91,7 +91,7 @@ parser.add_option('-d', '--dtb-file', action='store', help='Specify the .dtb input file') parser.add_option('--include-disabled', action='store_true', help='Include disabled nodes') -parser.add_option('-o', '--output', action='store', default='-', +parser.add_option('-o', '--output', action='store', help='Select output filename') parser.add_option('-P', '--processes', type=int, help='set number of processes to use for running tests') diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 4bf90444e1..41a10eb400 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -875,7 +875,7 @@ U_BOOT_DEVICE(spl_test2) = { """Test output to stdout""" dtb_file = get_dtb_file('dtoc_test_simple.dts') with test_util.capture_sys_output() as _: - self.run_test(['struct'], dtb_file, '-') + self.run_test(['struct'], dtb_file, None) def test_no_command(self): """Test running dtoc without a command""" -- cgit From de846cbb307486a1533dcf4d5c28568412149ddb Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:34:49 -0700 Subject: dtoc: Test the stdout output Normally dtoc outputs to a file but it also offers a way to write output to stdout. At present the test for that does not actually check that the output is correct. Add this to the test. This uses a member variable to hold the expected text, so it can be used in muitiple places. Signed-off-by: Simon Glass --- tools/dtoc/test_dtoc.py | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 41a10eb400..7cf2a5187c 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -188,14 +188,7 @@ class TestDtoc(unittest.TestCase): self.assertEqual(C_HEADER.splitlines() + [''] + C_EMPTY_POPULATE_PHANDLE_DATA.splitlines(), lines) - def test_simple(self): - """Test output from some simple nodes with various types of data""" - dtb_file = get_dtb_file('dtoc_test_simple.dts') - output = tools.GetOutputFilename('output') - self.run_test(['struct'], dtb_file, output) - with open(output) as infile: - data = infile.read() - self._check_strings(HEADER + ''' + struct_text = HEADER + ''' struct dtd_sandbox_i2c_test { }; struct dtd_sandbox_pmic_test { @@ -214,12 +207,9 @@ struct dtd_sandbox_spl_test { \tconst char *\tstringarray[3]; \tconst char *\tstringval; }; -''', data) +''' - self.run_test(['platdata'], dtb_file, output) - with open(output) as infile: - data = infile.read() - self._check_strings(C_HEADER + ''' + platdata_text = C_HEADER + ''' /* Node /i2c@0 index 0 */ static struct dtd_sandbox_i2c_test dtv_i2c_at_0 = { }; @@ -294,7 +284,23 @@ U_BOOT_DEVICE(spl_test3) = { \t.parent_idx\t= -1, }; -''' + C_EMPTY_POPULATE_PHANDLE_DATA, data) +''' + C_EMPTY_POPULATE_PHANDLE_DATA + + def test_simple(self): + """Test output from some simple nodes with various types of data""" + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + self.run_test(['struct'], dtb_file, output) + with open(output) as infile: + data = infile.read() + + self._check_strings(self.struct_text, data) + + self.run_test(['platdata'], dtb_file, output) + with open(output) as infile: + data = infile.read() + + self._check_strings(self.platdata_text, data) def test_driver_alias(self): """Test output from a device tree file with a driver alias""" @@ -874,8 +880,9 @@ U_BOOT_DEVICE(spl_test2) = { def test_stdout(self): """Test output to stdout""" dtb_file = get_dtb_file('dtoc_test_simple.dts') - with test_util.capture_sys_output() as _: + with test_util.capture_sys_output() as (stdout, _): self.run_test(['struct'], dtb_file, None) + self._check_strings(self.struct_text, stdout.getvalue()) def test_no_command(self): """Test running dtoc without a command""" -- cgit From 192c111cfce0684fa1cbbd4a4bd3df03720ef7a6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:34:50 -0700 Subject: dtoc: Allow providing a directory to write files to At present dtoc writes only a single file on each invocation. U-Boot writes the two files it needs by separate invocations of dtoc. Since dtoc now scans all U-Boot driver source, this is fairly slow (about 1 second per file). It would be better if dtoc could write all the files at once. In preparation for this, add a way to specify an output directory for the files. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 8 ++++++-- tools/dtoc/main.py | 7 ++++++- tools/dtoc/test_dtoc.py | 14 +++++++------- 3 files changed, 19 insertions(+), 10 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 118b1a5f43..62a65d6214 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -757,8 +757,9 @@ class DtbPlatdata(): self.out(''.join(self.get_buf())) -def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False, - drivers_additional=None): + +def run_steps(args, dtb_file, include_disabled, output, output_dirs, + warning_disabled=False, drivers_additional=None): """Run all the steps of the dtoc tool Args: @@ -766,6 +767,9 @@ def run_steps(args, dtb_file, include_disabled, output, warning_disabled=False, dtb_file (str): Filename of dtb file to process include_disabled (bool): True to include disabled nodes output (str): Name of output file (None for stdout) + output_dirs (tuple of str): + Directory to put C output files + Directory to put H output files warning_disabled (bool): True to avoid showing warnings about missing drivers drivers_additional (list): List of additional drivers to use during diff --git a/tools/dtoc/main.py b/tools/dtoc/main.py index 7686c8784d..244c184ced 100755 --- a/tools/dtoc/main.py +++ b/tools/dtoc/main.py @@ -87,6 +87,10 @@ if __name__ != '__main__': parser = OptionParser() parser.add_option('-B', '--build-dir', type='string', default='b', help='Directory containing the build output') +parser.add_option('-c', '--c-output-dir', action='store', + help='Select output directory for C files') +parser.add_option('-C', '--h-output-dir', action='store', + help='Select output directory for H files (defaults to --c-output-di)') parser.add_option('-d', '--dtb-file', action='store', help='Specify the .dtb input file') parser.add_option('--include-disabled', action='store_true', @@ -111,4 +115,5 @@ elif options.test_coverage: else: dtb_platdata.run_steps(args, options.dtb_file, options.include_disabled, - options.output) + options.output, + [options.c_output_dir, options.h_output_dir]) diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 7cf2a5187c..b023a1e14a 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -122,7 +122,7 @@ class TestDtoc(unittest.TestCase): dtb_file (str): Filename of .dtb file output (str): Filename of output file """ - dtb_platdata.run_steps(args, dtb_file, False, output, True) + dtb_platdata.run_steps(args, dtb_file, False, output, [], True) def test_name(self): """Test conversion of device tree names to C identifiers""" @@ -343,7 +343,7 @@ void dm_populate_phandle_data(void) { dtb_file = get_dtb_file('dtoc_test_invalid_driver.dts') output = tools.GetOutputFilename('output') with test_util.capture_sys_output() as _: - dtb_platdata.run_steps(['struct'], dtb_file, False, output) + dtb_platdata.run_steps(['struct'], dtb_file, False, output, []) with open(output) as infile: data = infile.read() self._check_strings(HEADER + ''' @@ -352,7 +352,7 @@ struct dtd_invalid { ''', data) with test_util.capture_sys_output() as _: - dtb_platdata.run_steps(['platdata'], dtb_file, False, output) + dtb_platdata.run_steps(['platdata'], dtb_file, False, output, []) with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' @@ -508,7 +508,7 @@ void dm_populate_phandle_data(void) { """Test that phandle targets are generated when unsing cd-gpios""" dtb_file = get_dtb_file('dtoc_test_phandle_cd_gpios.dts') output = tools.GetOutputFilename('output') - dtb_platdata.run_steps(['platdata'], dtb_file, False, output, True) + dtb_platdata.run_steps(['platdata'], dtb_file, False, output, [], True) with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' @@ -907,7 +907,7 @@ U_BOOT_DEVICE(spl_test2) = { output = tools.GetOutputFilename('output') with test_util.capture_sys_output() as _: dtb_platdata.run_steps( - ['struct'], dtb_file, False, output, True, + ['struct'], dtb_file, False, output, [], True, [None, '', 'tools/dtoc/dtoc_test_scan_drivers.cxx']) @staticmethod @@ -925,8 +925,8 @@ U_BOOT_DEVICE(spl_test2) = { fout.write(b'\x81') with test_util.capture_sys_output() as _: - dtb_platdata.run_steps(['struct'], dtb_file, False, output, True, - [driver_fn]) + dtb_platdata.run_steps(['struct'], dtb_file, False, output, [], + True, [driver_fn]) def test_driver(self): """Test the Driver class""" -- cgit From be44f27156bf46807049a0e1c303626d05f781f8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:34:51 -0700 Subject: dtoc: Allow outputing to multiple files Implement the 'output directory' feature, allowing dtoc to write the output files separately to the supplied directories. This allows us to handle the struct and platdata output in one run of dtoc. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 96 +++++++++++++++++++++++++++++++++++++++++----- tools/dtoc/test_dtoc.py | 8 ++++ 2 files changed, 94 insertions(+), 10 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 62a65d6214..e2fddfd301 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -15,6 +15,7 @@ See doc/driver-model/of-plat.rst for more informaiton import collections import copy +from enum import IntEnum import os import re import sys @@ -49,6 +50,15 @@ TYPE_NAMES = { STRUCT_PREFIX = 'dtd_' VAL_PREFIX = 'dtv_' +class Ftype(IntEnum): + SOURCE, HEADER = range(2) + + +# This holds information about each type of output file dtoc can create +# type: Type of file (Ftype) +# fname: Filename excluding directory, e.g. 'dt-platdata.c' +OutputFile = collections.namedtuple('OutputFile', ['ftype', 'fname']) + # This holds information about a property which includes phandles. # # max_args: integer: Maximum number or arguments that any phandle uses (int). @@ -180,6 +190,8 @@ class DtbPlatdata(): U_BOOT_DRIVER_ALIAS(driver_alias, driver_name) value: Driver name declared with U_BOOT_DRIVER(driver_name) _drivers_additional: List of additional drivers to use during scanning + _dirname: Directory to hold output files, or None for none (all files + go to stdout) """ def __init__(self, dtb_fname, include_disabled, warning_disabled, drivers_additional=None): @@ -193,6 +205,7 @@ class DtbPlatdata(): self._drivers = {} self._driver_aliases = {} self._drivers_additional = drivers_additional or [] + self._dirnames = [None] * len(Ftype) def get_normalized_compat_name(self, node): """Get a node's normalized compat name @@ -230,20 +243,68 @@ class DtbPlatdata(): return compat_list_c[0], compat_list_c[1:] - def setup_output(self, fname): + def setup_output_dirs(self, output_dirs): + """Set up the output directories + + This should be done before setup_output() is called + + Args: + output_dirs (tuple of str): + Directory to use for C output files. + Use None to write files relative current directory + Directory to use for H output files. + Defaults to the C output dir + """ + def process_dir(ftype, dirname): + if dirname: + os.makedirs(dirname, exist_ok=True) + self._dirnames[ftype] = dirname + + if output_dirs: + c_dirname = output_dirs[0] + h_dirname = output_dirs[1] if len(output_dirs) > 1 else c_dirname + process_dir(Ftype.SOURCE, c_dirname) + process_dir(Ftype.HEADER, h_dirname) + + def setup_output(self, ftype, fname): """Set up the output destination Once this is done, future calls to self.out() will output to this - file. + file. The file used is as follows: + + self._dirnames[ftype] is None: output to fname, or stdout if None + self._dirnames[ftype] is not None: output to fname in that directory + + Calling this function multiple times will close the old file and open + the new one. If they are the same file, nothing happens and output will + continue to the same file. Args: - fname (str): Filename to send output to, or None for stdout + ftype (str): Type of file to create ('c' or 'h') + fname (str): Filename to send output to. If there is a directory in + self._dirnames for this file type, it will be put in that + directory """ - if fname: - self._outfile = open(fname, 'w') + dirname = self._dirnames[ftype] + if dirname: + pathname = os.path.join(dirname, fname) + if self._outfile: + self._outfile.close() + self._outfile = open(pathname, 'w') + elif fname: + if not self._outfile: + self._outfile = open(fname, 'w') else: self._outfile = sys.stdout + def finish_output(self): + """Finish outputing to a file + + This closes the output file, if one is in use + """ + if self._outfile != sys.stdout: + self._outfile.close() + def out(self, line): """Output a string to the output file @@ -758,6 +819,15 @@ class DtbPlatdata(): self.out(''.join(self.get_buf())) +# Types of output file we understand +# key: Command used to generate this file +# value: OutputFile for this command +OUTPUT_FILES = { + 'struct': OutputFile(Ftype.HEADER, 'dt-structs-gen.h'), + 'platdata': OutputFile(Ftype.SOURCE, 'dt-platdata.c'), + } + + def run_steps(args, dtb_file, include_disabled, output, output_dirs, warning_disabled=False, drivers_additional=None): """Run all the steps of the dtoc tool @@ -778,7 +848,9 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, ValueError: if args has no command, or an unknown command """ if not args: - raise ValueError('Please specify a command: struct, platdata') + raise ValueError('Please specify a command: struct, platdata, all') + if output and output_dirs and any(output_dirs): + raise ValueError('Must specify either output or output_dirs, not both') plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional) @@ -786,15 +858,19 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, plat.scan_dtb() plat.scan_tree() plat.scan_reg_sizes() - plat.setup_output(output) + plat.setup_output_dirs(output_dirs) structs = plat.scan_structs() plat.scan_phandles() for cmd in args[0].split(','): + outfile = OUTPUT_FILES.get(cmd) + if not outfile: + raise ValueError("Unknown command '%s': (use: %s)" % + (cmd, ', '.join(OUTPUT_FILES.keys()))) + plat.setup_output(outfile.ftype, + outfile.fname if output_dirs else output) if cmd == 'struct': plat.generate_structs(structs) elif cmd == 'platdata': plat.generate_tables() - else: - raise ValueError("Unknown command '%s': (use: struct, platdata)" % - cmd) + plat.finish_output() diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index b023a1e14a..6f9af905e8 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -884,6 +884,14 @@ U_BOOT_DEVICE(spl_test2) = { self.run_test(['struct'], dtb_file, None) self._check_strings(self.struct_text, stdout.getvalue()) + def test_multi_to_file(self): + """Test output of multiple pieces to a single file""" + dtb_file = get_dtb_file('dtoc_test_simple.dts') + output = tools.GetOutputFilename('output') + self.run_test(['struct,platdata'], dtb_file, output) + data = tools.ReadFile(output, binary=False) + self._check_strings(self.struct_text + self.platdata_text, data) + def test_no_command(self): """Test running dtoc without a command""" with self.assertRaises(ValueError) as exc: -- cgit From 10cbd3b7da12e993c67d56182d30294661a93619 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:34:52 -0700 Subject: dtoc: Add an 'all' command With upcoming changes, dtoc will output several files for different of-platdata components. Add a way to output all ava!ilable files at once ('all'), to the appropriate directories, without needing to specify each one invidually. This puts the commands in alphabetical order, so update the tests accordingly. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 7 +++++-- tools/dtoc/main.py | 6 +----- tools/dtoc/test_dtoc.py | 40 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 10 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index e2fddfd301..7bd1989113 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -862,11 +862,14 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, structs = plat.scan_structs() plat.scan_phandles() - for cmd in args[0].split(','): + cmds = args[0].split(',') + if 'all' in cmds: + cmds = sorted(OUTPUT_FILES.keys()) + for cmd in cmds: outfile = OUTPUT_FILES.get(cmd) if not outfile: raise ValueError("Unknown command '%s': (use: %s)" % - (cmd, ', '.join(OUTPUT_FILES.keys()))) + (cmd, ', '.join(sorted(OUTPUT_FILES.keys())))) plat.setup_output(outfile.ftype, outfile.fname if output_dirs else output) if cmd == 'struct': diff --git a/tools/dtoc/main.py b/tools/dtoc/main.py index 244c184ced..f82ee78268 100755 --- a/tools/dtoc/main.py +++ b/tools/dtoc/main.py @@ -13,11 +13,7 @@ having to link against libfdt. By putting the data from the device tree into C structures, normal C code can be used. This helps to reduce the size of the compiled program. -Dtoc produces two output files: - - dt-structs.h - contains struct definitions - dt-platdata.c - contains data from the device tree using the struct - definitions, as well as U-Boot driver definitions. +Dtoc produces several output files - see OUTPUT_FILES in dtb_platdata.py This tool is used in U-Boot to provide device tree data to SPL without increasing the code size of SPL. This supports the CONFIG_SPL_OF_PLATDATA diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 6f9af905e8..fb65f284ce 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -10,6 +10,7 @@ tool. """ import collections +import glob import os import struct import tempfile @@ -302,6 +303,11 @@ U_BOOT_DEVICE(spl_test3) = { self._check_strings(self.platdata_text, data) + # Try the 'all' command + self.run_test(['all'], dtb_file, output) + data = tools.ReadFile(output, binary=False) + self._check_strings(self.platdata_text + self.struct_text, data) + def test_driver_alias(self): """Test output from a device tree file with a driver alias""" dtb_file = get_dtb_file('dtoc_test_driver_alias.dts') @@ -888,9 +894,9 @@ U_BOOT_DEVICE(spl_test2) = { """Test output of multiple pieces to a single file""" dtb_file = get_dtb_file('dtoc_test_simple.dts') output = tools.GetOutputFilename('output') - self.run_test(['struct,platdata'], dtb_file, output) + self.run_test(['all'], dtb_file, output) data = tools.ReadFile(output, binary=False) - self._check_strings(self.struct_text + self.platdata_text, data) + self._check_strings(self.platdata_text + self.struct_text, data) def test_no_command(self): """Test running dtoc without a command""" @@ -905,7 +911,7 @@ U_BOOT_DEVICE(spl_test2) = { output = tools.GetOutputFilename('output') with self.assertRaises(ValueError) as exc: self.run_test(['invalid-cmd'], dtb_file, output) - self.assertIn("Unknown command 'invalid-cmd': (use: struct, platdata)", + self.assertIn("Unknown command 'invalid-cmd': (use: platdata, struct)", str(exc.exception)) @staticmethod @@ -945,3 +951,31 @@ U_BOOT_DEVICE(spl_test2) = { self.assertEqual(drv1, drv3) self.assertNotEqual(drv1, drv2) self.assertNotEqual(drv2, drv3) + + def test_output_conflict(self): + """Test a conflict between and output dirs and output file""" + with self.assertRaises(ValueError) as exc: + dtb_platdata.run_steps(['all'], None, False, 'out', ['cdir'], True) + self.assertIn("Must specify either output or output_dirs, not both", + str(exc.exception)) + + def test_output_dirs(self): + """Test outputting files to a directory""" + # Remove the directory so that files from other tests are not there + tools._RemoveOutputDir() + tools.PrepareOutputDir(None) + + # This should create the .dts and .dtb in the output directory + dtb_file = get_dtb_file('dtoc_test_simple.dts') + outdir = tools.GetOutputDir() + fnames = glob.glob(outdir + '/*') + self.assertEqual(2, len(fnames)) + + dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir], True) + fnames = glob.glob(outdir + '/*') + self.assertEqual(4, len(fnames)) + + leafs = set(os.path.basename(fname) for fname in fnames) + self.assertEqual( + {'dt-structs-gen.h', 'source.dts', 'dt-platdata.c', 'source.dtb'}, + leafs) -- cgit From 20e442ab2df355450006574fff178c746d254a18 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:34:54 -0700 Subject: dm: Rename U_BOOT_DEVICE() to U_BOOT_DRVINFO() The current macro is a misnomer since it does not declare a device directly. Instead, it declares driver_info record which U-Boot uses at runtime to create a device. The distinction seems somewhat minor most of the time, but is becomes quite confusing when we actually want to declare a device, with of-platdata. We are left trying to distinguish between a device which isn't actually device, and a device that is (perhaps an 'instance'?) It seems better to rename this macro to describe what it actually is. The macros is not widely used, since boards should use devicetree to declare devices. Rename it to U_BOOT_DRVINFO(), which indicates clearly that this is declaring a new driver_info record, not a device. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 8 +++--- tools/dtoc/test_dtoc.py | 66 +++++++++++++++++++++++----------------------- 2 files changed, 37 insertions(+), 37 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 7bd1989113..ebe5132e14 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -713,14 +713,14 @@ class DtbPlatdata(): def _declare_device(self, var_name, struct_name, node_parent): """Add a device declaration to the output - This declares a U_BOOT_DEVICE() for the device being processed + This declares a U_BOOT_DRVINFO() for the device being processed Args: var_name (str): C name for the node struct_name (str): Name for the dt struct associated with the node node_parent (Node): Parent of the node (or None if none) """ - self.buf('U_BOOT_DEVICE(%s) = {\n' % var_name) + self.buf('U_BOOT_DRVINFO(%s) = {\n' % var_name) self.buf('\t.name\t\t= "%s",\n' % struct_name) self.buf('\t.plat\t= &%s%s,\n' % (VAL_PREFIX, var_name)) self.buf('\t.plat_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name)) @@ -783,14 +783,14 @@ class DtbPlatdata(): """Generate device defintions for the platform data This writes out C platform data initialisation data and - U_BOOT_DEVICE() declarations for each valid node. Where a node has + U_BOOT_DRVINFO() declarations for each valid node. Where a node has multiple compatible strings, a #define is used to make them equivalent. See the documentation in doc/driver-model/of-plat.rst for more information. """ self.out_header() - self.out('/* Allow use of U_BOOT_DEVICE() in this file */\n') + self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n') self.out('#define DT_PLATDATA_C\n') self.out('\n') self.out('#include \n') diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index fb65f284ce..dbd4e3bf1d 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -44,7 +44,7 @@ C_HEADER = '''/* * This file was generated by dtoc from a .dtb (device tree binary) file. */ -/* Allow use of U_BOOT_DEVICE() in this file */ +/* Allow use of U_BOOT_DRVINFO() in this file */ #define DT_PLATDATA_C #include @@ -214,7 +214,7 @@ struct dtd_sandbox_spl_test { /* Node /i2c@0 index 0 */ static struct dtd_sandbox_i2c_test dtv_i2c_at_0 = { }; -U_BOOT_DEVICE(i2c_at_0) = { +U_BOOT_DRVINFO(i2c_at_0) = { \t.name\t\t= "sandbox_i2c_test", \t.plat\t= &dtv_i2c_at_0, \t.plat_size\t= sizeof(dtv_i2c_at_0), @@ -226,7 +226,7 @@ static struct dtd_sandbox_pmic_test dtv_pmic_at_9 = { \t.low_power\t\t= true, \t.reg\t\t\t= {0x9, 0x0}, }; -U_BOOT_DEVICE(pmic_at_9) = { +U_BOOT_DRVINFO(pmic_at_9) = { \t.name\t\t= "sandbox_pmic_test", \t.plat\t= &dtv_pmic_at_9, \t.plat_size\t= sizeof(dtv_pmic_at_9), @@ -246,7 +246,7 @@ static struct dtd_sandbox_spl_test dtv_spl_test = { \t.stringarray\t\t= {"multi-word", "message", ""}, \t.stringval\t\t= "message", }; -U_BOOT_DEVICE(spl_test) = { +U_BOOT_DRVINFO(spl_test) = { \t.name\t\t= "sandbox_spl_test", \t.plat\t= &dtv_spl_test, \t.plat_size\t= sizeof(dtv_spl_test), @@ -265,7 +265,7 @@ static struct dtd_sandbox_spl_test dtv_spl_test2 = { \t.stringarray\t\t= {"another", "multi-word", "message"}, \t.stringval\t\t= "message2", }; -U_BOOT_DEVICE(spl_test2) = { +U_BOOT_DRVINFO(spl_test2) = { \t.name\t\t= "sandbox_spl_test", \t.plat\t= &dtv_spl_test2, \t.plat_size\t= sizeof(dtv_spl_test2), @@ -278,7 +278,7 @@ static struct dtd_sandbox_spl_test dtv_spl_test3 = { \t\t0x0}, \t.stringarray\t\t= {"one", "", ""}, }; -U_BOOT_DEVICE(spl_test3) = { +U_BOOT_DRVINFO(spl_test3) = { \t.name\t\t= "sandbox_spl_test", \t.plat\t= &dtv_spl_test3, \t.plat_size\t= sizeof(dtv_spl_test3), @@ -333,7 +333,7 @@ static struct dtd_sandbox_gpio dtv_gpios_at_0 = { \t.gpio_controller\t= true, \t.sandbox_gpio_count\t= 0x14, }; -U_BOOT_DEVICE(gpios_at_0) = { +U_BOOT_DRVINFO(gpios_at_0) = { \t.name\t\t= "sandbox_gpio", \t.plat\t= &dtv_gpios_at_0, \t.plat_size\t= sizeof(dtv_gpios_at_0), @@ -365,7 +365,7 @@ struct dtd_invalid { /* Node /spl-test index 0 */ static struct dtd_invalid dtv_spl_test = { }; -U_BOOT_DEVICE(spl_test) = { +U_BOOT_DRVINFO(spl_test) = { \t.name\t\t= "invalid", \t.plat\t= &dtv_spl_test, \t.plat_size\t= sizeof(dtv_spl_test), @@ -400,7 +400,7 @@ struct dtd_target { static struct dtd_target dtv_phandle2_target = { \t.intval\t\t\t= 0x1, }; -U_BOOT_DEVICE(phandle2_target) = { +U_BOOT_DRVINFO(phandle2_target) = { \t.name\t\t= "target", \t.plat\t= &dtv_phandle2_target, \t.plat_size\t= sizeof(dtv_phandle2_target), @@ -411,7 +411,7 @@ U_BOOT_DEVICE(phandle2_target) = { static struct dtd_target dtv_phandle3_target = { \t.intval\t\t\t= 0x2, }; -U_BOOT_DEVICE(phandle3_target) = { +U_BOOT_DRVINFO(phandle3_target) = { \t.name\t\t= "target", \t.plat\t= &dtv_phandle3_target, \t.plat_size\t= sizeof(dtv_phandle3_target), @@ -422,7 +422,7 @@ U_BOOT_DEVICE(phandle3_target) = { static struct dtd_target dtv_phandle_target = { \t.intval\t\t\t= 0x0, }; -U_BOOT_DEVICE(phandle_target) = { +U_BOOT_DRVINFO(phandle_target) = { \t.name\t\t= "target", \t.plat\t= &dtv_phandle_target, \t.plat_size\t= sizeof(dtv_phandle_target), @@ -437,7 +437,7 @@ static struct dtd_source dtv_phandle_source = { \t\t\t{1, {12, 13}}, \t\t\t{4, {}},}, }; -U_BOOT_DEVICE(phandle_source) = { +U_BOOT_DRVINFO(phandle_source) = { \t.name\t\t= "source", \t.plat\t= &dtv_phandle_source, \t.plat_size\t= sizeof(dtv_phandle_source), @@ -449,7 +449,7 @@ static struct dtd_source dtv_phandle_source2 = { \t.clocks\t\t\t= { \t\t\t{4, {}},}, }; -U_BOOT_DEVICE(phandle_source2) = { +U_BOOT_DRVINFO(phandle_source2) = { \t.name\t\t= "source", \t.plat\t= &dtv_phandle_source2, \t.plat_size\t= sizeof(dtv_phandle_source2), @@ -487,7 +487,7 @@ struct dtd_target { /* Node /phandle-target index 1 */ static struct dtd_target dtv_phandle_target = { }; -U_BOOT_DEVICE(phandle_target) = { +U_BOOT_DRVINFO(phandle_target) = { \t.name\t\t= "target", \t.plat\t= &dtv_phandle_target, \t.plat_size\t= sizeof(dtv_phandle_target), @@ -499,7 +499,7 @@ static struct dtd_source dtv_phandle_source2 = { \t.clocks\t\t\t= { \t\t\t{1, {}},}, }; -U_BOOT_DEVICE(phandle_source2) = { +U_BOOT_DRVINFO(phandle_source2) = { \t.name\t\t= "source", \t.plat\t= &dtv_phandle_source2, \t.plat_size\t= sizeof(dtv_phandle_source2), @@ -522,7 +522,7 @@ void dm_populate_phandle_data(void) { static struct dtd_target dtv_phandle2_target = { \t.intval\t\t\t= 0x1, }; -U_BOOT_DEVICE(phandle2_target) = { +U_BOOT_DRVINFO(phandle2_target) = { \t.name\t\t= "target", \t.plat\t= &dtv_phandle2_target, \t.plat_size\t= sizeof(dtv_phandle2_target), @@ -533,7 +533,7 @@ U_BOOT_DEVICE(phandle2_target) = { static struct dtd_target dtv_phandle3_target = { \t.intval\t\t\t= 0x2, }; -U_BOOT_DEVICE(phandle3_target) = { +U_BOOT_DRVINFO(phandle3_target) = { \t.name\t\t= "target", \t.plat\t= &dtv_phandle3_target, \t.plat_size\t= sizeof(dtv_phandle3_target), @@ -544,7 +544,7 @@ U_BOOT_DEVICE(phandle3_target) = { static struct dtd_target dtv_phandle_target = { \t.intval\t\t\t= 0x0, }; -U_BOOT_DEVICE(phandle_target) = { +U_BOOT_DRVINFO(phandle_target) = { \t.name\t\t= "target", \t.plat\t= &dtv_phandle_target, \t.plat_size\t= sizeof(dtv_phandle_target), @@ -559,7 +559,7 @@ static struct dtd_source dtv_phandle_source = { \t\t\t{1, {12, 13}}, \t\t\t{4, {}},}, }; -U_BOOT_DEVICE(phandle_source) = { +U_BOOT_DRVINFO(phandle_source) = { \t.name\t\t= "source", \t.plat\t= &dtv_phandle_source, \t.plat_size\t= sizeof(dtv_phandle_source), @@ -571,7 +571,7 @@ static struct dtd_source dtv_phandle_source2 = { \t.cd_gpios\t\t= { \t\t\t{4, {}},}, }; -U_BOOT_DEVICE(phandle_source2) = { +U_BOOT_DRVINFO(phandle_source2) = { \t.name\t\t= "source", \t.plat\t= &dtv_phandle_source2, \t.plat_size\t= sizeof(dtv_phandle_source2), @@ -629,7 +629,7 @@ struct dtd_test3 { static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x5678}, }; -U_BOOT_DEVICE(test1) = { +U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", \t.plat\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), @@ -640,7 +640,7 @@ U_BOOT_DEVICE(test1) = { static struct dtd_test2 dtv_test2 = { \t.reg\t\t\t= {0x1234567890123456, 0x9876543210987654}, }; -U_BOOT_DEVICE(test2) = { +U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", \t.plat\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), @@ -651,7 +651,7 @@ U_BOOT_DEVICE(test2) = { static struct dtd_test3 dtv_test3 = { \t.reg\t\t\t= {0x1234567890123456, 0x9876543210987654, 0x2, 0x3}, }; -U_BOOT_DEVICE(test3) = { +U_BOOT_DRVINFO(test3) = { \t.name\t\t= "test3", \t.plat\t= &dtv_test3, \t.plat_size\t= sizeof(dtv_test3), @@ -684,7 +684,7 @@ struct dtd_test2 { static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x5678}, }; -U_BOOT_DEVICE(test1) = { +U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", \t.plat\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), @@ -695,7 +695,7 @@ U_BOOT_DEVICE(test1) = { static struct dtd_test2 dtv_test2 = { \t.reg\t\t\t= {0x12345678, 0x98765432, 0x2, 0x3}, }; -U_BOOT_DEVICE(test2) = { +U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", \t.plat\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), @@ -731,7 +731,7 @@ struct dtd_test3 { static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x123400000000, 0x5678}, }; -U_BOOT_DEVICE(test1) = { +U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", \t.plat\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), @@ -742,7 +742,7 @@ U_BOOT_DEVICE(test1) = { static struct dtd_test2 dtv_test2 = { \t.reg\t\t\t= {0x1234567890123456, 0x98765432}, }; -U_BOOT_DEVICE(test2) = { +U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", \t.plat\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), @@ -753,7 +753,7 @@ U_BOOT_DEVICE(test2) = { static struct dtd_test3 dtv_test3 = { \t.reg\t\t\t= {0x1234567890123456, 0x98765432, 0x2, 0x3}, }; -U_BOOT_DEVICE(test3) = { +U_BOOT_DRVINFO(test3) = { \t.name\t\t= "test3", \t.plat\t= &dtv_test3, \t.plat_size\t= sizeof(dtv_test3), @@ -789,7 +789,7 @@ struct dtd_test3 { static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x567800000000}, }; -U_BOOT_DEVICE(test1) = { +U_BOOT_DRVINFO(test1) = { \t.name\t\t= "test1", \t.plat\t= &dtv_test1, \t.plat_size\t= sizeof(dtv_test1), @@ -800,7 +800,7 @@ U_BOOT_DEVICE(test1) = { static struct dtd_test2 dtv_test2 = { \t.reg\t\t\t= {0x12345678, 0x9876543210987654}, }; -U_BOOT_DEVICE(test2) = { +U_BOOT_DRVINFO(test2) = { \t.name\t\t= "test2", \t.plat\t= &dtv_test2, \t.plat_size\t= sizeof(dtv_test2), @@ -811,7 +811,7 @@ U_BOOT_DEVICE(test2) = { static struct dtd_test3 dtv_test3 = { \t.reg\t\t\t= {0x12345678, 0x9876543210987654, 0x2, 0x3}, }; -U_BOOT_DEVICE(test3) = { +U_BOOT_DRVINFO(test3) = { \t.name\t\t= "test3", \t.plat\t= &dtv_test3, \t.plat_size\t= sizeof(dtv_test3), @@ -863,7 +863,7 @@ struct dtd_sandbox_spl_test { static struct dtd_sandbox_spl_test dtv_spl_test = { \t.intval\t\t\t= 0x1, }; -U_BOOT_DEVICE(spl_test) = { +U_BOOT_DRVINFO(spl_test) = { \t.name\t\t= "sandbox_spl_test", \t.plat\t= &dtv_spl_test, \t.plat_size\t= sizeof(dtv_spl_test), @@ -874,7 +874,7 @@ U_BOOT_DEVICE(spl_test) = { static struct dtd_sandbox_spl_test dtv_spl_test2 = { \t.intarray\t\t= 0x5, }; -U_BOOT_DEVICE(spl_test2) = { +U_BOOT_DRVINFO(spl_test2) = { \t.name\t\t= "sandbox_spl_test", \t.plat\t= &dtv_spl_test2, \t.plat_size\t= sizeof(dtv_spl_test2), -- cgit From 8629d30a32e7b4ff8323292364116c08033bd57b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:34:55 -0700 Subject: dm: Rename DM_GET_DEVICE() to DM_DRVINFO_GET() This does not get a device (struct udevice *) but a struct driver_info * so the name is confusing. Rename it accordingly. Since we plan to have several various of these macros, put GET at the end instead of the middle, so it is easier to spot the related macros. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index ebe5132e14..1a6a511b01 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -811,8 +811,8 @@ class DtbPlatdata(): nodes_to_output.remove(node) # Define dm_populate_phandle_data() which will add the linking between - # nodes using DM_GET_DEVICE - # dtv_dmc_at_xxx.clocks[0].node = DM_GET_DEVICE(clock_controller_at_xxx) + # nodes using DM_DRVINFO_GET + # dtv_dmc_at_xxx.clocks[0].node = DM_DRVINFO_GET(clock_controller_at_xxx) self.buf('void dm_populate_phandle_data(void) {\n') self.buf('}\n') -- cgit From bdf8fd76c0184373bbd31e6ee9a3a9430dcfc485 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:34:57 -0700 Subject: dm: Rename U_BOOT_DRIVER_ALIAS to DM_DRIVER_ALIAS We use the U_BOOT_ prefix (i.e. U_BOOT_DRIVER) to declare a driver but in every other case we just use DM_. Update the alias macros to use the DM_ prefix. We could perhaps rename U_BOOT_DRIVER() to DM_DRIVER(), but this macro is widely used and there is at least some benefit to indicating it us a U-Boot driver, particularly for code ported from Linux. So for now, let's keep that name. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 6 +++--- tools/dtoc/dtoc_test_scan_drivers.cxx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 1a6a511b01..e12aaa5399 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -187,7 +187,7 @@ class DtbPlatdata(): value: Driver for that driver _driver_aliases: Dict that holds aliases for driver names key: Driver alias declared with - U_BOOT_DRIVER_ALIAS(driver_alias, driver_name) + DM_DRIVER_ALIAS(driver_alias, driver_name) value: Driver name declared with U_BOOT_DRIVER(driver_name) _drivers_additional: List of additional drivers to use during scanning _dirname: Directory to hold output files, or None for none (all files @@ -416,9 +416,9 @@ class DtbPlatdata(): self._drivers[driver] = Driver(driver) # The following re will search for driver aliases declared as - # U_BOOT_DRIVER_ALIAS(alias, driver_name) + # DM_DRIVER_ALIAS(alias, driver_name) driver_aliases = re.findall( - r'U_BOOT_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)', + r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)', buff) for alias in driver_aliases: # pragma: no cover diff --git a/tools/dtoc/dtoc_test_scan_drivers.cxx b/tools/dtoc/dtoc_test_scan_drivers.cxx index 557c692ba2..f448767670 100644 --- a/tools/dtoc/dtoc_test_scan_drivers.cxx +++ b/tools/dtoc/dtoc_test_scan_drivers.cxx @@ -1 +1 @@ -U_BOOT_DRIVER_ALIAS(sandbox_gpio, sandbox_gpio_alias2) +DM_DRIVER_ALIAS(sandbox_gpio, sandbox_gpio_alias2) -- cgit From 5d9a3aa99cc7a1ed6502a98152bd0e13b4e05539 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:34:59 -0700 Subject: dtoc: Run tests using test_util Use the standard function for running tests and reported results. This allows the tests to run in parallel, which is a significant speed-up on most machines (e.g. 4.5 seconds -> 1.5s on mine). Signed-off-by: Simon Glass --- tools/dtoc/main.py | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/main.py b/tools/dtoc/main.py index f82ee78268..9d0b3915c0 100755 --- a/tools/dtoc/main.py +++ b/tools/dtoc/main.py @@ -38,10 +38,11 @@ sys.path.insert(0, os.path.join(our_path, from dtoc import dtb_platdata from patman import test_util -def run_tests(args): +def run_tests(processes, args): """Run all the test we have for dtoc Args: + processes: Number of processes to use to run tests (None=same as #CPUs) args: List of positional args provided to dtoc. This can hold a test name to execute (as in 'dtoc -t test_empty_file', for example) """ @@ -50,25 +51,13 @@ def run_tests(args): result = unittest.TestResult() sys.argv = [sys.argv[0]] test_name = args and args[0] or None - for module in (test_dtoc.TestDtoc,): - if test_name: - try: - suite = unittest.TestLoader().loadTestsFromName(test_name, module) - except AttributeError: - continue - else: - suite = unittest.TestLoader().loadTestsFromTestCase(module) - suite.run(result) - - print(result) - for _, err in result.errors: - print(err) - for _, err in result.failures: - print(err) - if result.errors or result.failures: - print('dtoc tests FAILED') - return 1 - return 0 + + test_util.RunTestSuites( + result, debug=True, verbosity=1, test_preserve_dirs=False, + processes=processes, test_name=test_name, toolpath=[], + test_class_list=[test_dtoc.TestDtoc,]) + + return test_util.ReportResult('binman', test_name, result) def RunTestCoverage(): """Run the tests and check that we get 100% coverage""" @@ -103,7 +92,7 @@ parser.add_option('-T', '--test-coverage', action='store_true', # Run our meagre tests if options.test: - ret_code = run_tests(args) + ret_code = run_tests(options.processes, args) sys.exit(ret_code) elif options.test_coverage: -- cgit From d1055d681af40963d1c4b1a517ae131f738983f4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:35:00 -0700 Subject: dtoc: Add a header comment to each generated file It is currently fairly obvious what the two generated files are for, but this will change as more are added. It is helpful for readers to describe the purpose of each file. Add a header commment field to OutputFile and use it to generate a comment at the top of each file. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 30 ++++++++++++++++++++---------- tools/dtoc/test_dtoc.py | 6 ++++-- 2 files changed, 24 insertions(+), 12 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index e12aaa5399..b3d777e0c5 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -56,8 +56,10 @@ class Ftype(IntEnum): # This holds information about each type of output file dtoc can create # type: Type of file (Ftype) -# fname: Filename excluding directory, e.g. 'dt-platdata.c' -OutputFile = collections.namedtuple('OutputFile', ['ftype', 'fname']) +# fname: Filename excluding directory, e.g. 'dt-plat.c' +# hdr_comment: Comment explaining the purpose of the file +OutputFile = collections.namedtuple('OutputFile', + ['ftype', 'fname', 'hdr_comment']) # This holds information about a property which includes phandles. # @@ -331,15 +333,20 @@ class DtbPlatdata(): self._lines = [] return lines - def out_header(self): - """Output a message indicating that this is an auto-generated file""" + def out_header(self, outfile): + """Output a message indicating that this is an auto-generated file + + Args: + outfile: OutputFile describing the file being generated + """ self.out('''/* * DO NOT MODIFY * - * This file was generated by dtoc from a .dtb (device tree binary) file. + * %s. + * This was generated by dtoc from a .dtb (device tree binary) file. */ -''') +''' % outfile.hdr_comment) def get_phandle_argc(self, prop, node_name): """Check if a node contains phandles @@ -646,7 +653,6 @@ class DtbPlatdata(): value: Prop object with field information """ - self.out_header() self.out('#include \n') self.out('#include \n') @@ -789,7 +795,6 @@ class DtbPlatdata(): See the documentation in doc/driver-model/of-plat.rst for more information. """ - self.out_header() self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n') self.out('#define DT_PLATDATA_C\n') self.out('\n') @@ -823,8 +828,12 @@ class DtbPlatdata(): # key: Command used to generate this file # value: OutputFile for this command OUTPUT_FILES = { - 'struct': OutputFile(Ftype.HEADER, 'dt-structs-gen.h'), - 'platdata': OutputFile(Ftype.SOURCE, 'dt-platdata.c'), + 'struct': + OutputFile(Ftype.HEADER, 'dt-structs-gen.h', + 'Defines the structs used to hold devicetree data'), + 'platdata': + OutputFile(Ftype.SOURCE, 'dt-platdata.c', + 'Declares the U_BOOT_DRIVER() records and platform data'), } @@ -872,6 +881,7 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, (cmd, ', '.join(sorted(OUTPUT_FILES.keys())))) plat.setup_output(outfile.ftype, outfile.fname if output_dirs else output) + plat.out_header(outfile) if cmd == 'struct': plat.generate_structs(structs) elif cmd == 'platdata': diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index dbd4e3bf1d..2e4dd2b24d 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -32,7 +32,8 @@ OUR_PATH = os.path.dirname(os.path.realpath(__file__)) HEADER = '''/* * DO NOT MODIFY * - * This file was generated by dtoc from a .dtb (device tree binary) file. + * Defines the structs used to hold devicetree data. + * This was generated by dtoc from a .dtb (device tree binary) file. */ #include @@ -41,7 +42,8 @@ HEADER = '''/* C_HEADER = '''/* * DO NOT MODIFY * - * This file was generated by dtoc from a .dtb (device tree binary) file. + * Declares the U_BOOT_DRIVER() records and platform data. + * This was generated by dtoc from a .dtb (device tree binary) file. */ /* Allow use of U_BOOT_DRVINFO() in this file */ -- cgit From f31fa99a9ed92c223fbf56e07eae57e7bdea19ae Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:35:01 -0700 Subject: dtoc: Rename dt-platdata.c to dt-plat.c Use this new name to be consistent with the rest of U-Boot, which talks about 'plat' for the platform data, which is what this file holds. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 4 ++-- tools/dtoc/test_dtoc.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index b3d777e0c5..e07a5d3a1c 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -796,7 +796,7 @@ class DtbPlatdata(): information. """ self.out('/* Allow use of U_BOOT_DRVINFO() in this file */\n') - self.out('#define DT_PLATDATA_C\n') + self.out('#define DT_PLAT_C\n') self.out('\n') self.out('#include \n') self.out('#include \n') @@ -832,7 +832,7 @@ OUTPUT_FILES = { OutputFile(Ftype.HEADER, 'dt-structs-gen.h', 'Defines the structs used to hold devicetree data'), 'platdata': - OutputFile(Ftype.SOURCE, 'dt-platdata.c', + OutputFile(Ftype.SOURCE, 'dt-plat.c', 'Declares the U_BOOT_DRIVER() records and platform data'), } diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 2e4dd2b24d..d56cd311fa 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -47,7 +47,7 @@ C_HEADER = '''/* */ /* Allow use of U_BOOT_DRVINFO() in this file */ -#define DT_PLATDATA_C +#define DT_PLAT_C #include #include @@ -979,5 +979,5 @@ U_BOOT_DRVINFO(spl_test2) = { leafs = set(os.path.basename(fname) for fname in fnames) self.assertEqual( - {'dt-structs-gen.h', 'source.dts', 'dt-platdata.c', 'source.dtb'}, + {'dt-structs-gen.h', 'source.dts', 'dt-plat.c', 'source.dtb'}, leafs) -- cgit From a7d5f96ef1cf13b83a143d6977e2937bcc1b6c75 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:35:02 -0700 Subject: dtoc: Add the method for each command to OutputFile Rather than the if/else construct, update OutputFile with the method to call to process each command. This is easier to maintain as the number of commands increases. Rename generate_tables to generate_plat since it better describes what is being generated ('plat' is the U-Boot name for platform data). With this, each output method needs to have the same signature. Store the output structures in a member variable instead of using parameters, to accomplish this. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 42 ++++++++++++++++-------------------------- 1 file changed, 16 insertions(+), 26 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index e07a5d3a1c..4696ff6309 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -59,7 +59,7 @@ class Ftype(IntEnum): # fname: Filename excluding directory, e.g. 'dt-plat.c' # hdr_comment: Comment explaining the purpose of the file OutputFile = collections.namedtuple('OutputFile', - ['ftype', 'fname', 'hdr_comment']) + ['ftype', 'fname', 'method', 'hdr_comment']) # This holds information about a property which includes phandles. # @@ -194,6 +194,11 @@ class DtbPlatdata(): _drivers_additional: List of additional drivers to use during scanning _dirname: Directory to hold output files, or None for none (all files go to stdout) + _struct_data (dict): OrderedDict of dtplat structures to output + key (str): Node name, as a C identifier + value: dict containing structure fields: + key (str): Field name + value: Prop object with field information """ def __init__(self, dtb_fname, include_disabled, warning_disabled, drivers_additional=None): @@ -208,6 +213,7 @@ class DtbPlatdata(): self._driver_aliases = {} self._drivers_additional = drivers_additional or [] self._dirnames = [None] * len(Ftype) + self._struct_data = collections.OrderedDict() def get_normalized_compat_name(self, node): """Get a node's normalized compat name @@ -570,14 +576,9 @@ class DtbPlatdata(): Once the widest property is determined, all other properties are updated to match that width. - Returns: - dict of dict: dict containing structures: - key (str): Node name, as a C identifier - value: dict containing structure fields: - key (str): Field name - value: Prop object with field information + The results are written to self._struct_data """ - structs = collections.OrderedDict() + structs = self._struct_data for node in self._valid_nodes: node_name, _ = self.get_normalized_compat_name(node) fields = {} @@ -608,8 +609,6 @@ class DtbPlatdata(): if name not in PROP_IGNORE_LIST and name[0] != '#': prop.Widen(struct[name]) - return structs - def scan_phandles(self): """Figure out what phandles each node uses @@ -638,21 +637,14 @@ class DtbPlatdata(): pos += 1 + args - def generate_structs(self, structs): + def generate_structs(self): """Generate struct defintions for the platform data This writes out the body of a header file consisting of structure definitions for node in self._valid_nodes. See the documentation in doc/driver-model/of-plat.rst for more information. - - Args: - structs (dict): dict containing structures: - key (str): Node name, as a C identifier - value: dict containing structure fields: - key (str): Field name - value: Prop object with field information - """ + structs = self._struct_data self.out('#include \n') self.out('#include \n') @@ -785,7 +777,7 @@ class DtbPlatdata(): self.out(''.join(self.get_buf())) - def generate_tables(self): + def generate_plat(self): """Generate device defintions for the platform data This writes out C platform data initialisation data and @@ -830,9 +822,10 @@ class DtbPlatdata(): OUTPUT_FILES = { 'struct': OutputFile(Ftype.HEADER, 'dt-structs-gen.h', + DtbPlatdata.generate_structs, 'Defines the structs used to hold devicetree data'), 'platdata': - OutputFile(Ftype.SOURCE, 'dt-plat.c', + OutputFile(Ftype.SOURCE, 'dt-plat.c', DtbPlatdata.generate_plat, 'Declares the U_BOOT_DRIVER() records and platform data'), } @@ -868,7 +861,7 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, plat.scan_tree() plat.scan_reg_sizes() plat.setup_output_dirs(output_dirs) - structs = plat.scan_structs() + plat.scan_structs() plat.scan_phandles() cmds = args[0].split(',') @@ -882,8 +875,5 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, plat.setup_output(outfile.ftype, outfile.fname if output_dirs else output) plat.out_header(outfile) - if cmd == 'struct': - plat.generate_structs(structs) - elif cmd == 'platdata': - plat.generate_tables() + outfile.method(plat) plat.finish_output() -- cgit From 1e0f3f46bd9afed12b331cbe945abd4046250ed5 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:35:03 -0700 Subject: dtoc: Allow specifying the base directory for tests The base directory of U-Boot, where the source is, it currently calculated from the directory of the dtb_platdata.py script. If this is installed elsewhere that will not work. Also it is inconvenient for tests. Add a parameter to allow specifying this base directory. To test this, pass a temporary directory with some files in it and check that they are passed to scan_driver(). Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 18 ++++++++++++------ tools/dtoc/test_dtoc.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 4696ff6309..acb9689ea1 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -199,6 +199,7 @@ class DtbPlatdata(): value: dict containing structure fields: key (str): Field name value: Prop object with field information + _basedir (str): Base directory of source tree """ def __init__(self, dtb_fname, include_disabled, warning_disabled, drivers_additional=None): @@ -214,6 +215,7 @@ class DtbPlatdata(): self._drivers_additional = drivers_additional or [] self._dirnames = [None] * len(Ftype) self._struct_data = collections.OrderedDict() + self._basedir = None def get_normalized_compat_name(self, node): """Get a node's normalized compat name @@ -439,15 +441,17 @@ class DtbPlatdata(): continue self._driver_aliases[alias[1]] = alias[0] - def scan_drivers(self): + def scan_drivers(self, basedir=None): """Scan the driver folders to build a list of driver names and aliases This procedure will populate self._drivers and self._driver_aliases """ - basedir = sys.argv[0].replace('tools/dtoc/dtoc', '') - if basedir == '': - basedir = './' + if not basedir: + basedir = sys.argv[0].replace('tools/dtoc/dtoc', '') + if basedir == '': + basedir = './' + self._basedir = basedir for (dirpath, _, filenames) in os.walk(basedir): for fname in filenames: if not fname.endswith('.c'): @@ -831,7 +835,7 @@ OUTPUT_FILES = { def run_steps(args, dtb_file, include_disabled, output, output_dirs, - warning_disabled=False, drivers_additional=None): + warning_disabled=False, drivers_additional=None, basedir=None): """Run all the steps of the dtoc tool Args: @@ -846,6 +850,8 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, drivers drivers_additional (list): List of additional drivers to use during scanning + basedir (str): Base directory of U-Boot source code. Defaults to the + grandparent of this file's directory Raises: ValueError: if args has no command, or an unknown command """ @@ -856,7 +862,7 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, drivers_additional) - plat.scan_drivers() + plat.scan_drivers(basedir) plat.scan_dtb() plat.scan_tree() plat.scan_reg_sizes() diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index d56cd311fa..c517fd5c4e 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -12,9 +12,11 @@ tool. import collections import glob import os +import shutil import struct import tempfile import unittest +from unittest import mock from dtb_platdata import conv_name_to_c from dtb_platdata import get_compat_name @@ -981,3 +983,35 @@ U_BOOT_DRVINFO(spl_test2) = { self.assertEqual( {'dt-structs-gen.h', 'source.dts', 'dt-plat.c', 'source.dtb'}, leafs) + + def test_scan_dirs(self): + """Test scanning of source directories""" + def add_file(fname): + pathname = os.path.join(indir, fname) + dirname = os.path.dirname(pathname) + os.makedirs(dirname, exist_ok=True) + tools.WriteFile(pathname, '', binary=False) + fname_list.append(pathname) + + try: + outdir = tools.GetOutputDir() + indir = tempfile.mkdtemp(prefix='dtoc.') + dtb_file = get_dtb_file('dtoc_test_simple.dts') + + fname_list = [] + add_file('fname.c') + add_file('dir/fname2.c') + + # Mock out scan_driver and check that it is called with the + # expected files + with mock.patch.object(dtb_platdata.DtbPlatdata, "scan_driver") \ + as mocked: + dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir], + True, basedir=indir) + self.assertEqual(2, len(mocked.mock_calls)) + self.assertEqual(mock.call(fname_list[0]), + mocked.mock_calls[0]) + self.assertEqual(mock.call(fname_list[1]), + mocked.mock_calls[1]) + finally: + shutil.rmtree(indir) -- cgit From 9eca08dc5986c9e00bb68a4529ec9404a282bc57 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:35:04 -0700 Subject: dtoc: Output nodes in order Previously we had to worry about nodes being output before those that they depended on, thus causing build errors. So the current algorithm is careful to output nodes in the right order. We now use a different method for outputting phandles that does not involve pointers. Also we plan to add a 'declarations' header file to declare all drivers as 'extern'. Update the code to drop the dependency checking and output in a simple loop. This makes the output easier to follow since drivers are in order of thier indices (0, 1, ...), which is also the order it appears in in the linker list. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 13 ++-------- tools/dtoc/test_dtoc.py | 64 +++++++++++++++++++++++----------------------- 2 files changed, 34 insertions(+), 43 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index acb9689ea1..4cdbd60c9a 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -798,18 +798,9 @@ class DtbPlatdata(): self.out('#include \n') self.out('#include \n') self.out('\n') - nodes_to_output = list(self._valid_nodes) - - # Keep outputing nodes until there is none left - while nodes_to_output: - node = nodes_to_output[0] - # Output all the node's dependencies first - for req_node in node.phandles: - if req_node in nodes_to_output: - self.output_node(req_node) - nodes_to_output.remove(req_node) + + for node in self._valid_nodes: self.output_node(node) - nodes_to_output.remove(node) # Define dm_populate_phandle_data() which will add the linking between # nodes using DM_DRVINFO_GET diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index c517fd5c4e..f17d2e4b45 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -422,17 +422,6 @@ U_BOOT_DRVINFO(phandle3_target) = { \t.parent_idx\t= -1, }; -/* Node /phandle-target index 4 */ -static struct dtd_target dtv_phandle_target = { -\t.intval\t\t\t= 0x0, -}; -U_BOOT_DRVINFO(phandle_target) = { -\t.name\t\t= "target", -\t.plat\t= &dtv_phandle_target, -\t.plat_size\t= sizeof(dtv_phandle_target), -\t.parent_idx\t= -1, -}; - /* Node /phandle-source index 2 */ static struct dtd_source dtv_phandle_source = { \t.clocks\t\t\t= { @@ -460,6 +449,17 @@ U_BOOT_DRVINFO(phandle_source2) = { \t.parent_idx\t= -1, }; +/* Node /phandle-target index 4 */ +static struct dtd_target dtv_phandle_target = { +\t.intval\t\t\t= 0x0, +}; +U_BOOT_DRVINFO(phandle_target) = { +\t.name\t\t= "target", +\t.plat\t= &dtv_phandle_target, +\t.plat_size\t= sizeof(dtv_phandle_target), +\t.parent_idx\t= -1, +}; + void dm_populate_phandle_data(void) { } ''', data) @@ -488,16 +488,6 @@ struct dtd_target { with open(output) as infile: data = infile.read() self._check_strings(C_HEADER + ''' -/* Node /phandle-target index 1 */ -static struct dtd_target dtv_phandle_target = { -}; -U_BOOT_DRVINFO(phandle_target) = { -\t.name\t\t= "target", -\t.plat\t= &dtv_phandle_target, -\t.plat_size\t= sizeof(dtv_phandle_target), -\t.parent_idx\t= -1, -}; - /* Node /phandle-source2 index 0 */ static struct dtd_source dtv_phandle_source2 = { \t.clocks\t\t\t= { @@ -510,6 +500,16 @@ U_BOOT_DRVINFO(phandle_source2) = { \t.parent_idx\t= -1, }; +/* Node /phandle-target index 1 */ +static struct dtd_target dtv_phandle_target = { +}; +U_BOOT_DRVINFO(phandle_target) = { +\t.name\t\t= "target", +\t.plat\t= &dtv_phandle_target, +\t.plat_size\t= sizeof(dtv_phandle_target), +\t.parent_idx\t= -1, +}; + void dm_populate_phandle_data(void) { } ''', data) @@ -544,17 +544,6 @@ U_BOOT_DRVINFO(phandle3_target) = { \t.parent_idx\t= -1, }; -/* Node /phandle-target index 4 */ -static struct dtd_target dtv_phandle_target = { -\t.intval\t\t\t= 0x0, -}; -U_BOOT_DRVINFO(phandle_target) = { -\t.name\t\t= "target", -\t.plat\t= &dtv_phandle_target, -\t.plat_size\t= sizeof(dtv_phandle_target), -\t.parent_idx\t= -1, -}; - /* Node /phandle-source index 2 */ static struct dtd_source dtv_phandle_source = { \t.cd_gpios\t\t= { @@ -582,6 +571,17 @@ U_BOOT_DRVINFO(phandle_source2) = { \t.parent_idx\t= -1, }; +/* Node /phandle-target index 4 */ +static struct dtd_target dtv_phandle_target = { +\t.intval\t\t\t= 0x0, +}; +U_BOOT_DRVINFO(phandle_target) = { +\t.name\t\t= "target", +\t.plat\t= &dtv_phandle_target, +\t.plat_size\t= sizeof(dtv_phandle_target), +\t.parent_idx\t= -1, +}; + void dm_populate_phandle_data(void) { } ''', data) -- cgit From d960f0db289144a80553e4d226d5dd145d63926a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:35:05 -0700 Subject: dtoc: Drop dm_populate_phandle_data() This has not been needed since parent information was added and we started using indicies for references to other drivers instead of pointers. It was kept around in the expectation that it might be needed later. However with the latest updates, it doesn't seem likely that we'll need this in the foreseeable future. Drop dm_populate_phandle_data() from dtoc and driver model. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 6 ------ tools/dtoc/test_dtoc.py | 29 +++++++---------------------- 2 files changed, 7 insertions(+), 28 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index 4cdbd60c9a..f1c09d629f 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -802,12 +802,6 @@ class DtbPlatdata(): for node in self._valid_nodes: self.output_node(node) - # Define dm_populate_phandle_data() which will add the linking between - # nodes using DM_DRVINFO_GET - # dtv_dmc_at_xxx.clocks[0].node = DM_DRVINFO_GET(clock_controller_at_xxx) - self.buf('void dm_populate_phandle_data(void) {\n') - self.buf('}\n') - self.out(''.join(self.get_buf())) diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index f17d2e4b45..bcbf58c45b 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -56,10 +56,6 @@ C_HEADER = '''/* #include ''' -C_EMPTY_POPULATE_PHANDLE_DATA = '''void dm_populate_phandle_data(void) { -} -''' - # This is a test so is allowed to access private things in the module it is # testing # pylint: disable=W0212 @@ -190,8 +186,7 @@ class TestDtoc(unittest.TestCase): self.run_test(['platdata'], dtb_file, output) with open(output) as infile: lines = infile.read().splitlines() - self.assertEqual(C_HEADER.splitlines() + [''] + - C_EMPTY_POPULATE_PHANDLE_DATA.splitlines(), lines) + self.assertEqual(C_HEADER.splitlines() + [''], lines) struct_text = HEADER + ''' struct dtd_sandbox_i2c_test { @@ -289,7 +284,7 @@ U_BOOT_DRVINFO(spl_test3) = { \t.parent_idx\t= -1, }; -''' + C_EMPTY_POPULATE_PHANDLE_DATA +''' def test_simple(self): """Test output from some simple nodes with various types of data""" @@ -344,8 +339,6 @@ U_BOOT_DRVINFO(gpios_at_0) = { \t.parent_idx\t= -1, }; -void dm_populate_phandle_data(void) { -} ''', data) def test_invalid_driver(self): @@ -376,8 +369,6 @@ U_BOOT_DRVINFO(spl_test) = { \t.parent_idx\t= -1, }; -void dm_populate_phandle_data(void) { -} ''', data) def test_phandle(self): @@ -460,8 +451,6 @@ U_BOOT_DRVINFO(phandle_target) = { \t.parent_idx\t= -1, }; -void dm_populate_phandle_data(void) { -} ''', data) def test_phandle_single(self): @@ -510,8 +499,6 @@ U_BOOT_DRVINFO(phandle_target) = { \t.parent_idx\t= -1, }; -void dm_populate_phandle_data(void) { -} ''', data) def test_phandle_cd_gpio(self): @@ -582,8 +569,6 @@ U_BOOT_DRVINFO(phandle_target) = { \t.parent_idx\t= -1, }; -void dm_populate_phandle_data(void) { -} ''', data) def test_phandle_bad(self): @@ -662,7 +647,7 @@ U_BOOT_DRVINFO(test3) = { \t.parent_idx\t= -1, }; -''' + C_EMPTY_POPULATE_PHANDLE_DATA, data) +''', data) def test_addresses32(self): """Test output from a node with a 'reg' property with na=1, ns=1""" @@ -706,7 +691,7 @@ U_BOOT_DRVINFO(test2) = { \t.parent_idx\t= -1, }; -''' + C_EMPTY_POPULATE_PHANDLE_DATA, data) +''', data) def test_addresses64_32(self): """Test output from a node with a 'reg' property with na=2, ns=1""" @@ -764,7 +749,7 @@ U_BOOT_DRVINFO(test3) = { \t.parent_idx\t= -1, }; -''' + C_EMPTY_POPULATE_PHANDLE_DATA, data) +''', data) def test_addresses32_64(self): """Test output from a node with a 'reg' property with na=1, ns=2""" @@ -822,7 +807,7 @@ U_BOOT_DRVINFO(test3) = { \t.parent_idx\t= -1, }; -''' + C_EMPTY_POPULATE_PHANDLE_DATA, data) +''', data) def test_bad_reg(self): """Test that a reg property with an invalid type generates an error""" @@ -885,7 +870,7 @@ U_BOOT_DRVINFO(spl_test2) = { \t.parent_idx\t= -1, }; -''' + C_EMPTY_POPULATE_PHANDLE_DATA, data) +''', data) def test_stdout(self): """Test output to stdout""" -- cgit From a542a70c2256e2250dfb876fd394e967d6a47156 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:35:06 -0700 Subject: dtoc: Split source-code scanning to a separate file Before expanding the scanning features any more, move this into a separate file. This will make it easier to maintain in the future. In particular, it reduces the size of dtb_platdata.py and allows us to add tests specifically for scanning, without going through that file. The pieces moved are the Driver class, the scanning code and the various naming functions, since they mostly depend on the scanning results. So far there is are no separate tests for src_scan. These will be added as new functionality appears. This introduces no functional change. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 174 +++--------------------------------------- tools/dtoc/src_scan.py | 185 +++++++++++++++++++++++++++++++++++++++++++++ tools/dtoc/test_dtoc.py | 14 ++-- 3 files changed, 204 insertions(+), 169 deletions(-) create mode 100644 tools/dtoc/src_scan.py (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index f1c09d629f..ad642f3062 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -22,6 +22,8 @@ import sys from dtoc import fdt from dtoc import fdt_util +from dtoc import src_scan +from dtoc.src_scan import conv_name_to_c # When we see these properties we ignore them - i.e. do not create a structure # member @@ -76,39 +78,6 @@ PhandleInfo = collections.namedtuple('PhandleInfo', ['max_args', 'args']) PhandleLink = collections.namedtuple('PhandleLink', ['var_node', 'dev_name']) -class Driver: - """Information about a driver in U-Boot - - Attributes: - name: Name of driver. For U_BOOT_DRIVER(x) this is 'x' - """ - def __init__(self, name): - self.name = name - - def __eq__(self, other): - return self.name == other.name - - def __repr__(self): - return "Driver(name='%s')" % self.name - - -def conv_name_to_c(name): - """Convert a device-tree name to a C identifier - - This uses multiple replace() calls instead of re.sub() since it is faster - (400ms for 1m calls versus 1000ms for the 're' version). - - Args: - name (str): Name to convert - Return: - str: String containing the C version of this name - """ - new = name.replace('@', '_at_') - new = new.replace('-', '_') - new = new.replace(',', '_') - new = new.replace('.', '_') - return new - def tab_to(num_tabs, line): """Append tabs to a line of text to reach a tab stop. @@ -154,19 +123,6 @@ def get_value(ftype, value): val = '%#x' % value return val -def get_compat_name(node): - """Get the node's list of compatible string as a C identifiers - - Args: - node (fdt.Node): Node object to check - Return: - list of str: List of C identifiers for all the compatible strings - """ - compat = node.props['compatible'].value - if not isinstance(compat, list): - compat = [compat] - return [conv_name_to_c(c) for c in compat] - class DtbPlatdata(): """Provide a means to convert device tree binary data to platform data @@ -176,22 +132,15 @@ class DtbPlatdata(): code is not affordable. Properties: + _scan: Scan object, for scanning and reporting on useful information + from the U-Boot source code _fdt: Fdt object, referencing the device tree _dtb_fname: Filename of the input device tree binary file _valid_nodes: A list of Node object with compatible strings. The list is ordered by conv_name_to_c(node.name) _include_disabled: true to include nodes marked status = "disabled" _outfile: The current output file (sys.stdout or a real file) - _warning_disabled: true to disable warnings about driver names not found _lines: Stashed list of output lines for outputting in the future - _drivers: Dict of valid driver names found in drivers/ - key: Driver name - value: Driver for that driver - _driver_aliases: Dict that holds aliases for driver names - key: Driver alias declared with - DM_DRIVER_ALIAS(driver_alias, driver_name) - value: Driver name declared with U_BOOT_DRIVER(driver_name) - _drivers_additional: List of additional drivers to use during scanning _dirname: Directory to hold output files, or None for none (all files go to stdout) _struct_data (dict): OrderedDict of dtplat structures to output @@ -201,58 +150,18 @@ class DtbPlatdata(): value: Prop object with field information _basedir (str): Base directory of source tree """ - def __init__(self, dtb_fname, include_disabled, warning_disabled, - drivers_additional=None): + def __init__(self, scan, dtb_fname, include_disabled): + self._scan = scan self._fdt = None self._dtb_fname = dtb_fname self._valid_nodes = None self._include_disabled = include_disabled self._outfile = None - self._warning_disabled = warning_disabled self._lines = [] - self._drivers = {} - self._driver_aliases = {} - self._drivers_additional = drivers_additional or [] self._dirnames = [None] * len(Ftype) self._struct_data = collections.OrderedDict() self._basedir = None - def get_normalized_compat_name(self, node): - """Get a node's normalized compat name - - Returns a valid driver name by retrieving node's list of compatible - string as a C identifier and performing a check against _drivers - and a lookup in driver_aliases printing a warning in case of failure. - - Args: - node (Node): Node object to check - Return: - Tuple: - Driver name associated with the first compatible string - List of C identifiers for all the other compatible strings - (possibly empty) - In case of no match found, the return will be the same as - get_compat_name() - """ - compat_list_c = get_compat_name(node) - - for compat_c in compat_list_c: - if not compat_c in self._drivers.keys(): - compat_c = self._driver_aliases.get(compat_c) - if not compat_c: - continue - - aliases_c = compat_list_c - if compat_c in aliases_c: - aliases_c.remove(compat_c) - return compat_c, aliases_c - - if not self._warning_disabled: - print('WARNING: the driver %s was not found in the driver list' - % (compat_list_c[0])) - - return compat_list_c[0], compat_list_c[1:] - def setup_output_dirs(self, output_dirs): """Set up the output directories @@ -407,65 +316,6 @@ class DtbPlatdata(): return PhandleInfo(max_args, args) return None - def scan_driver(self, fname): - """Scan a driver file to build a list of driver names and aliases - - This procedure will populate self._drivers and self._driver_aliases - - Args - fname: Driver filename to scan - """ - with open(fname, encoding='utf-8') as inf: - try: - buff = inf.read() - except UnicodeDecodeError: - # This seems to happen on older Python versions - print("Skipping file '%s' due to unicode error" % fname) - return - - # The following re will search for driver names declared as - # U_BOOT_DRIVER(driver_name) - drivers = re.findall(r'U_BOOT_DRIVER\((.*)\)', buff) - - for driver in drivers: - self._drivers[driver] = Driver(driver) - - # The following re will search for driver aliases declared as - # DM_DRIVER_ALIAS(alias, driver_name) - driver_aliases = re.findall( - r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)', - buff) - - for alias in driver_aliases: # pragma: no cover - if len(alias) != 2: - continue - self._driver_aliases[alias[1]] = alias[0] - - def scan_drivers(self, basedir=None): - """Scan the driver folders to build a list of driver names and aliases - - This procedure will populate self._drivers and self._driver_aliases - - """ - if not basedir: - basedir = sys.argv[0].replace('tools/dtoc/dtoc', '') - if basedir == '': - basedir = './' - self._basedir = basedir - for (dirpath, _, filenames) in os.walk(basedir): - for fname in filenames: - if not fname.endswith('.c'): - continue - self.scan_driver(dirpath + '/' + fname) - - for fname in self._drivers_additional: - if not isinstance(fname, str) or len(fname) == 0: - continue - if fname[0] == '/': - self.scan_driver(fname) - else: - self.scan_driver(basedir + '/' + fname) - def scan_dtb(self): """Scan the device tree to obtain a tree of nodes and properties @@ -584,7 +434,7 @@ class DtbPlatdata(): """ structs = self._struct_data for node in self._valid_nodes: - node_name, _ = self.get_normalized_compat_name(node) + node_name, _ = self._scan.get_normalized_compat_name(node) fields = {} # Get a list of all the valid properties in this node. @@ -607,7 +457,7 @@ class DtbPlatdata(): structs[node_name] = fields for node in self._valid_nodes: - node_name, _ = self.get_normalized_compat_name(node) + node_name, _ = self._scan.get_normalized_compat_name(node) struct = structs[node_name] for name, prop in node.props.items(): if name not in PROP_IGNORE_LIST and name[0] != '#': @@ -772,7 +622,7 @@ class DtbPlatdata(): Args: node (fdt.Node): node to output """ - struct_name, _ = self.get_normalized_compat_name(node) + struct_name, _ = self._scan.get_normalized_compat_name(node) var_name = conv_name_to_c(node.name) self.buf('/* Node %s index %d */\n' % (node.path, node.idx)) @@ -845,9 +695,9 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, if output and output_dirs and any(output_dirs): raise ValueError('Must specify either output or output_dirs, not both') - plat = DtbPlatdata(dtb_file, include_disabled, warning_disabled, - drivers_additional) - plat.scan_drivers(basedir) + scan = src_scan.Scanner(basedir, drivers_additional, warning_disabled) + plat = DtbPlatdata(scan, dtb_file, include_disabled) + scan.scan_drivers() plat.scan_dtb() plat.scan_tree() plat.scan_reg_sizes() diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py new file mode 100644 index 0000000000..185a6d9284 --- /dev/null +++ b/tools/dtoc/src_scan.py @@ -0,0 +1,185 @@ +#!/usr/bin/python +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2017 Google, Inc +# Written by Simon Glass +# + +"""Scanning of U-Boot source for drivers and structs + +This scans the source tree to find out things about all instances of +U_BOOT_DRIVER(), UCLASS_DRIVER and all struct declarations in header files. + +See doc/driver-model/of-plat.rst for more informaiton +""" + +import os +import re +import sys + + +def conv_name_to_c(name): + """Convert a device-tree name to a C identifier + + This uses multiple replace() calls instead of re.sub() since it is faster + (400ms for 1m calls versus 1000ms for the 're' version). + + Args: + name (str): Name to convert + Return: + str: String containing the C version of this name + """ + new = name.replace('@', '_at_') + new = new.replace('-', '_') + new = new.replace(',', '_') + new = new.replace('.', '_') + return new + +def get_compat_name(node): + """Get the node's list of compatible string as a C identifiers + + Args: + node (fdt.Node): Node object to check + Return: + list of str: List of C identifiers for all the compatible strings + """ + compat = node.props['compatible'].value + if not isinstance(compat, list): + compat = [compat] + return [conv_name_to_c(c) for c in compat] + + +class Driver: + """Information about a driver in U-Boot + + Attributes: + name: Name of driver. For U_BOOT_DRIVER(x) this is 'x' + """ + def __init__(self, name): + self.name = name + + def __eq__(self, other): + return self.name == other.name + + def __repr__(self): + return "Driver(name='%s')" % self.name + + +class Scanner: + """Scanning of the U-Boot source tree + + Properties: + _basedir (str): Base directory of U-Boot source code. Defaults to the + grandparent of this file's directory + _drivers: Dict of valid driver names found in drivers/ + key: Driver name + value: Driver for that driver + _driver_aliases: Dict that holds aliases for driver names + key: Driver alias declared with + DM_DRIVER_ALIAS(driver_alias, driver_name) + value: Driver name declared with U_BOOT_DRIVER(driver_name) + _drivers_additional (list or str): List of additional drivers to use + during scanning + _warning_disabled: true to disable warnings about driver names not found + """ + def __init__(self, basedir, drivers_additional, warning_disabled): + """Set up a new Scanner + """ + if not basedir: + basedir = sys.argv[0].replace('tools/dtoc/dtoc', '') + if basedir == '': + basedir = './' + self._basedir = basedir + self._drivers = {} + self._driver_aliases = {} + self._drivers_additional = drivers_additional or [] + self._warning_disabled = warning_disabled + + def get_normalized_compat_name(self, node): + """Get a node's normalized compat name + + Returns a valid driver name by retrieving node's list of compatible + string as a C identifier and performing a check against _drivers + and a lookup in driver_aliases printing a warning in case of failure. + + Args: + node (Node): Node object to check + Return: + Tuple: + Driver name associated with the first compatible string + List of C identifiers for all the other compatible strings + (possibly empty) + In case of no match found, the return will be the same as + get_compat_name() + """ + compat_list_c = get_compat_name(node) + + for compat_c in compat_list_c: + if not compat_c in self._drivers.keys(): + compat_c = self._driver_aliases.get(compat_c) + if not compat_c: + continue + + aliases_c = compat_list_c + if compat_c in aliases_c: + aliases_c.remove(compat_c) + return compat_c, aliases_c + + if not self._warning_disabled: + print('WARNING: the driver %s was not found in the driver list' + % (compat_list_c[0])) + + return compat_list_c[0], compat_list_c[1:] + + def scan_driver(self, fname): + """Scan a driver file to build a list of driver names and aliases + + This procedure will populate self._drivers and self._driver_aliases + + Args + fname: Driver filename to scan + """ + with open(fname, encoding='utf-8') as inf: + try: + buff = inf.read() + except UnicodeDecodeError: + # This seems to happen on older Python versions + print("Skipping file '%s' due to unicode error" % fname) + return + + # The following re will search for driver names declared as + # U_BOOT_DRIVER(driver_name) + drivers = re.findall(r'U_BOOT_DRIVER\((.*)\)', buff) + + for driver in drivers: + self._drivers[driver] = Driver(driver) + + # The following re will search for driver aliases declared as + # DM_DRIVER_ALIAS(alias, driver_name) + driver_aliases = re.findall( + r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)', + buff) + + for alias in driver_aliases: # pragma: no cover + if len(alias) != 2: + continue + self._driver_aliases[alias[1]] = alias[0] + + def scan_drivers(self): + """Scan the driver folders to build a list of driver names and aliases + + This procedure will populate self._drivers and self._driver_aliases + """ + for (dirpath, _, filenames) in os.walk(self._basedir): + for fname in filenames: + if not fname.endswith('.c'): + continue + self.scan_driver(dirpath + '/' + fname) + + for fname in self._drivers_additional: + if not isinstance(fname, str) or len(fname) == 0: + continue + if fname[0] == '/': + self.scan_driver(fname) + else: + self.scan_driver(self._basedir + '/' + fname) diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index bcbf58c45b..8e8dd847c1 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -18,13 +18,14 @@ import tempfile import unittest from unittest import mock -from dtb_platdata import conv_name_to_c -from dtb_platdata import get_compat_name from dtb_platdata import get_value from dtb_platdata import tab_to from dtoc import dtb_platdata from dtoc import fdt from dtoc import fdt_util +from dtoc import src_scan +from dtoc.src_scan import conv_name_to_c +from dtoc.src_scan import get_compat_name from patman import test_util from patman import tools @@ -933,9 +934,9 @@ U_BOOT_DRVINFO(spl_test2) = { def test_driver(self): """Test the Driver class""" - drv1 = dtb_platdata.Driver('fred') - drv2 = dtb_platdata.Driver('mary') - drv3 = dtb_platdata.Driver('fred') + drv1 = src_scan.Driver('fred') + drv2 = src_scan.Driver('mary') + drv3 = src_scan.Driver('fred') self.assertEqual("Driver(name='fred')", str(drv1)) self.assertEqual(drv1, drv3) self.assertNotEqual(drv1, drv2) @@ -989,8 +990,7 @@ U_BOOT_DRVINFO(spl_test2) = { # Mock out scan_driver and check that it is called with the # expected files - with mock.patch.object(dtb_platdata.DtbPlatdata, "scan_driver") \ - as mocked: + with mock.patch.object(src_scan.Scanner, "scan_driver") as mocked: dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir], True, basedir=indir) self.assertEqual(2, len(mocked.mock_calls)) -- cgit From 10ea9c0b059c37e6b2026fe1334d1d57c465984d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:35:07 -0700 Subject: dtoc: Move src_scan tests to a separate file Move the tests related to scanning into their own class, updating them to avoid using dtb_platdata as a pass-through. Signed-off-by: Simon Glass --- tools/dtoc/dtb_platdata.py | 2 +- tools/dtoc/main.py | 5 ++- tools/dtoc/src_scan.py | 4 +- tools/dtoc/test_dtoc.py | 73 ------------------------------------ tools/dtoc/test_src_scan.py | 91 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 78 deletions(-) create mode 100644 tools/dtoc/test_src_scan.py (limited to 'tools/dtoc') diff --git a/tools/dtoc/dtb_platdata.py b/tools/dtoc/dtb_platdata.py index ad642f3062..b7abaed67a 100644 --- a/tools/dtoc/dtb_platdata.py +++ b/tools/dtoc/dtb_platdata.py @@ -695,7 +695,7 @@ def run_steps(args, dtb_file, include_disabled, output, output_dirs, if output and output_dirs and any(output_dirs): raise ValueError('Must specify either output or output_dirs, not both') - scan = src_scan.Scanner(basedir, drivers_additional, warning_disabled) + scan = src_scan.Scanner(basedir, warning_disabled, drivers_additional) plat = DtbPlatdata(scan, dtb_file, include_disabled) scan.scan_drivers() plat.scan_dtb() diff --git a/tools/dtoc/main.py b/tools/dtoc/main.py index 9d0b3915c0..b0ad0f3952 100755 --- a/tools/dtoc/main.py +++ b/tools/dtoc/main.py @@ -46,7 +46,8 @@ def run_tests(processes, args): args: List of positional args provided to dtoc. This can hold a test name to execute (as in 'dtoc -t test_empty_file', for example) """ - import test_dtoc + from dtoc import test_src_scan + from dtoc import test_dtoc result = unittest.TestResult() sys.argv = [sys.argv[0]] @@ -55,7 +56,7 @@ def run_tests(processes, args): test_util.RunTestSuites( result, debug=True, verbosity=1, test_preserve_dirs=False, processes=processes, test_name=test_name, toolpath=[], - test_class_list=[test_dtoc.TestDtoc,]) + test_class_list=[test_dtoc.TestDtoc,test_src_scan.TestSrcScan]) return test_util.ReportResult('binman', test_name, result) diff --git a/tools/dtoc/src_scan.py b/tools/dtoc/src_scan.py index 185a6d9284..f63c9fc166 100644 --- a/tools/dtoc/src_scan.py +++ b/tools/dtoc/src_scan.py @@ -78,11 +78,11 @@ class Scanner: key: Driver alias declared with DM_DRIVER_ALIAS(driver_alias, driver_name) value: Driver name declared with U_BOOT_DRIVER(driver_name) + _warning_disabled: true to disable warnings about driver names not found _drivers_additional (list or str): List of additional drivers to use during scanning - _warning_disabled: true to disable warnings about driver names not found """ - def __init__(self, basedir, drivers_additional, warning_disabled): + def __init__(self, basedir, warning_disabled, drivers_additional): """Set up a new Scanner """ if not basedir: diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index 8e8dd847c1..d961d67b8f 100755 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -12,18 +12,14 @@ tool. import collections import glob import os -import shutil import struct -import tempfile import unittest -from unittest import mock from dtb_platdata import get_value from dtb_platdata import tab_to from dtoc import dtb_platdata from dtoc import fdt from dtoc import fdt_util -from dtoc import src_scan from dtoc.src_scan import conv_name_to_c from dtoc.src_scan import get_compat_name from patman import test_util @@ -904,44 +900,6 @@ U_BOOT_DRVINFO(spl_test2) = { self.assertIn("Unknown command 'invalid-cmd': (use: platdata, struct)", str(exc.exception)) - @staticmethod - def test_scan_drivers(): - """Test running dtoc with additional drivers to scan""" - dtb_file = get_dtb_file('dtoc_test_simple.dts') - output = tools.GetOutputFilename('output') - with test_util.capture_sys_output() as _: - dtb_platdata.run_steps( - ['struct'], dtb_file, False, output, [], True, - [None, '', 'tools/dtoc/dtoc_test_scan_drivers.cxx']) - - @staticmethod - def test_unicode_error(): - """Test running dtoc with an invalid unicode file - - To be able to perform this test without adding a weird text file which - would produce issues when using checkpatch.pl or patman, generate the - file at runtime and then process it. - """ - dtb_file = get_dtb_file('dtoc_test_simple.dts') - output = tools.GetOutputFilename('output') - driver_fn = '/tmp/' + next(tempfile._get_candidate_names()) - with open(driver_fn, 'wb+') as fout: - fout.write(b'\x81') - - with test_util.capture_sys_output() as _: - dtb_platdata.run_steps(['struct'], dtb_file, False, output, [], - True, [driver_fn]) - - def test_driver(self): - """Test the Driver class""" - drv1 = src_scan.Driver('fred') - drv2 = src_scan.Driver('mary') - drv3 = src_scan.Driver('fred') - self.assertEqual("Driver(name='fred')", str(drv1)) - self.assertEqual(drv1, drv3) - self.assertNotEqual(drv1, drv2) - self.assertNotEqual(drv2, drv3) - def test_output_conflict(self): """Test a conflict between and output dirs and output file""" with self.assertRaises(ValueError) as exc: @@ -969,34 +927,3 @@ U_BOOT_DRVINFO(spl_test2) = { self.assertEqual( {'dt-structs-gen.h', 'source.dts', 'dt-plat.c', 'source.dtb'}, leafs) - - def test_scan_dirs(self): - """Test scanning of source directories""" - def add_file(fname): - pathname = os.path.join(indir, fname) - dirname = os.path.dirname(pathname) - os.makedirs(dirname, exist_ok=True) - tools.WriteFile(pathname, '', binary=False) - fname_list.append(pathname) - - try: - outdir = tools.GetOutputDir() - indir = tempfile.mkdtemp(prefix='dtoc.') - dtb_file = get_dtb_file('dtoc_test_simple.dts') - - fname_list = [] - add_file('fname.c') - add_file('dir/fname2.c') - - # Mock out scan_driver and check that it is called with the - # expected files - with mock.patch.object(src_scan.Scanner, "scan_driver") as mocked: - dtb_platdata.run_steps(['all'], dtb_file, False, None, [outdir], - True, basedir=indir) - self.assertEqual(2, len(mocked.mock_calls)) - self.assertEqual(mock.call(fname_list[0]), - mocked.mock_calls[0]) - self.assertEqual(mock.call(fname_list[1]), - mocked.mock_calls[1]) - finally: - shutil.rmtree(indir) diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py new file mode 100644 index 0000000000..d25da5760a --- /dev/null +++ b/tools/dtoc/test_src_scan.py @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2020 Google LLC +# + +"""Tests for the src_scan module + +This includes unit tests for scanning of the source code +""" + +import os +import shutil +import tempfile +import unittest +from unittest import mock + +from dtoc import src_scan +from patman import tools + +# This is a test so is allowed to access private things in the module it is +# testing +# pylint: disable=W0212 + +class TestSrcScan(unittest.TestCase): + """Tests for src_scan""" + @classmethod + def setUpClass(cls): + tools.PrepareOutputDir(None) + + @classmethod + def tearDownClass(cls): + tools.FinaliseOutputDir() + + @classmethod + def test_scan_drivers(cls): + """Test running dtoc with additional drivers to scan""" + scan = src_scan.Scanner( + None, True, [None, '', 'tools/dtoc/dtoc_test_scan_drivers.cxx']) + scan.scan_drivers() + + @classmethod + def test_unicode_error(cls): + """Test running dtoc with an invalid unicode file + + To be able to perform this test without adding a weird text file which + would produce issues when using checkpatch.pl or patman, generate the + file at runtime and then process it. + """ + driver_fn = '/tmp/' + next(tempfile._get_candidate_names()) + with open(driver_fn, 'wb+') as fout: + fout.write(b'\x81') + + src_scan.Scanner(None, True, [driver_fn]) + + def test_driver(self): + """Test the Driver class""" + drv1 = src_scan.Driver('fred') + drv2 = src_scan.Driver('mary') + drv3 = src_scan.Driver('fred') + self.assertEqual("Driver(name='fred')", str(drv1)) + self.assertEqual(drv1, drv3) + self.assertNotEqual(drv1, drv2) + self.assertNotEqual(drv2, drv3) + + def test_scan_dirs(self): + """Test scanning of source directories""" + def add_file(fname): + pathname = os.path.join(indir, fname) + dirname = os.path.dirname(pathname) + os.makedirs(dirname, exist_ok=True) + tools.WriteFile(pathname, '', binary=False) + fname_list.append(pathname) + + try: + indir = tempfile.mkdtemp(prefix='dtoc.') + + fname_list = [] + add_file('fname.c') + add_file('dir/fname2.c') + + # Mock out scan_driver and check that it is called with the + # expected files + with mock.patch.object(src_scan.Scanner, "scan_driver") as mocked: + scan = src_scan.Scanner(indir, True, None) + scan.scan_drivers() + self.assertEqual(2, len(mocked.mock_calls)) + self.assertEqual(mock.call(fname_list[0]), + mocked.mock_calls[0]) + self.assertEqual(mock.call(fname_list[1]), + mocked.mock_calls[1]) + finally: + shutil.rmtree(indir) -- cgit From 970349a96dac3ad46c33851b1a773bfe3f1d4b33 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 28 Dec 2020 20:35:08 -0700 Subject: dtoc: Tidy up src_scan tests Some of these tests don't actually check anything. Add a few more checks to complete the tests. Also add a simple scan test that does the basics. Signed-off-by: Simon Glass --- tools/dtoc/test_src_scan.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'tools/dtoc') diff --git a/tools/dtoc/test_src_scan.py b/tools/dtoc/test_src_scan.py index d25da5760a..7d686530d6 100644 --- a/tools/dtoc/test_src_scan.py +++ b/tools/dtoc/test_src_scan.py @@ -14,6 +14,7 @@ import unittest from unittest import mock from dtoc import src_scan +from patman import test_util from patman import tools # This is a test so is allowed to access private things in the module it is @@ -30,15 +31,26 @@ class TestSrcScan(unittest.TestCase): def tearDownClass(cls): tools.FinaliseOutputDir() - @classmethod - def test_scan_drivers(cls): - """Test running dtoc with additional drivers to scan""" + def test_simple(self): + """Simple test of scanning drivers""" + scan = src_scan.Scanner(None, True, None) + scan.scan_drivers() + self.assertIn('sandbox_gpio', scan._drivers) + self.assertIn('sandbox_gpio_alias', scan._driver_aliases) + self.assertEqual('sandbox_gpio', + scan._driver_aliases['sandbox_gpio_alias']) + self.assertNotIn('sandbox_gpio_alias2', scan._driver_aliases) + + def test_additional(self): + """Test with additional drivers to scan""" scan = src_scan.Scanner( None, True, [None, '', 'tools/dtoc/dtoc_test_scan_drivers.cxx']) scan.scan_drivers() + self.assertIn('sandbox_gpio_alias2', scan._driver_aliases) + self.assertEqual('sandbox_gpio', + scan._driver_aliases['sandbox_gpio_alias2']) - @classmethod - def test_unicode_error(cls): + def test_unicode_error(self): """Test running dtoc with an invalid unicode file To be able to perform this test without adding a weird text file which @@ -49,7 +61,11 @@ class TestSrcScan(unittest.TestCase): with open(driver_fn, 'wb+') as fout: fout.write(b'\x81') - src_scan.Scanner(None, True, [driver_fn]) + scan = src_scan.Scanner(None, True, [driver_fn]) + with test_util.capture_sys_output() as (stdout, _): + scan.scan_drivers() + self.assertRegex(stdout.getvalue(), + r"Skipping file '.*' due to unicode error\s*") def test_driver(self): """Test the Driver class""" -- cgit