
Previously, dtoc could only process the top-level nodes which led to device nodes in hierarchical trees to be ignored. E.g. the mmc0 node in the following example would be ignored, as only the soc node was processed:
/ { soc { mmc0 { /* ... */ }; }; };
This introduces a recursive helper method ScanNode, which is used by ScanTree to recursively parse the entire tree hierarchy.
Signed-off-by: Philipp Tomsich philipp.tomsich@theobroma-systems.com --- tools/dtoc/dtoc.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/tools/dtoc/dtoc.py b/tools/dtoc/dtoc.py index 6df7b0d..fe8b0c2 100755 --- a/tools/dtoc/dtoc.py +++ b/tools/dtoc/dtoc.py @@ -66,290 +66,307 @@ def TabTo(num_tabs, str): 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- constrained encvironments where the ~3KB code overhead of device tree code is not affordable.
Properties: 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 _options: Command-line options _phandle_node: A dict of nodes indexed by phandle number (1, 2...) _outfile: The current output file (sys.stdout or a real file) _lines: Stashed list of output lines for outputting in the future _phandle_node: A dict of Nodes indexed by phandle (an integer) """ def __init__(self, dtb_fname, options): self._dtb_fname = dtb_fname self._valid_nodes = None self._options = options self._phandle_node = {} self._outfile = None self._lines = []
def SetupOutput(self, fname): """Set up the output destination
Once this is done, future calls to self.Out() will output to this file.
Args: fname: Filename to send output to, or '-' for stdout """ if fname == '-': self._outfile = sys.stdout else: self._outfile = open(fname, 'w')
def Out(self, str): """Output a string to the output file
Args: str: String to output """ self._outfile.write(str)
def Buf(self, str): """Buffer up a string to send later
Args: str: String to add to our 'buffer' list """ self._lines.append(str)
def GetBuf(self): """Get the contents of the output buffer, and clear it
Returns: The output buffer, which is then cleared for future use """ lines = self._lines self._lines = [] return lines
def GetValue(self, type, value): """Get a value as a C expression
For integers this returns a byte-swapped (little-endian) hex string For bytes this returns a hex string, e.g. 0x12 For strings this returns a literal string enclosed in quotes For booleans this return 'true'
Args: type: Data type (fdt_util) value: Data value, as a string of bytes """ if type == fdt.TYPE_INT: return '%#x' % fdt_util.fdt32_to_cpu(value) elif type == fdt.TYPE_BYTE: return '%#x' % ord(value[0]) elif type == fdt.TYPE_STRING: return '"%s"' % value elif type == fdt.TYPE_BOOL: return 'true'
def GetCompatName(self, node): """Get a node's first compatible string as a C identifier
Args: node: Node object to check Return: C identifier for the first compatible string """ compat = node.props['compatible'].value if type(compat) == list: compat = compat[0] return Conv_name_to_c(compat)
def ScanDtb(self): """Scan the device tree to obtain a tree of notes and properties
Once this is done, self.fdt.GetRoot() can be called to obtain the device tree root node, and progress from there. """ self.fdt = fdt_select.FdtScan(self._dtb_fname)
+ def ScanNode(self, root): + for node in root.subnodes: + if 'compatible' in node.props: + status = node.props.get('status') + if (not options.include_disabled and not status or + status.value != 'disabled'): + self._valid_nodes.append(node) + phandle_prop = node.props.get('phandle') + if phandle_prop: + phandle = phandle_prop.GetPhandle() + self._phandle_node[phandle] = node + + # recurse to handle any subnodes + self.ScanNode(node); + def ScanTree(self): """Scan the device tree for useful information
This fills in the following properties: _phandle_node: A dict of Nodes indexed by phandle (an integer) _valid_nodes: A list of nodes we wish to consider include in the platform data """ - node_list = [] self._phandle_node = {} + self._valid_nodes = [] + return self.ScanNode(self.fdt.GetRoot()); + for node in self.fdt.GetRoot().subnodes: if 'compatible' in node.props: status = node.props.get('status') if (not options.include_disabled and not status or status.value != 'disabled'): node_list.append(node) phandle_prop = node.props.get('phandle') if phandle_prop: phandle = phandle_prop.GetPhandle() self._phandle_node[phandle] = node
self._valid_nodes = node_list
def IsPhandle(self, prop): """Check if a node contains phandles
We have no reliable way of detecting whether a node uses a phandle or not. As an interim measure, use a list of known property names.
Args: prop: Prop object to check Return: True if the object value contains phandles, else False """ if prop.name in ['clocks']: return True return False
def ScanStructs(self): """Scan the device tree building up the C structures we will use.
Build a dict keyed by C struct name containing a dict of Prop object for each struct field (keyed by property name). Where the same struct appears multiple times, try to use the 'widest' property, i.e. the one with a type which can express all others.
Once the widest property is determined, all other properties are updated to match that width. """ structs = {} for node in self._valid_nodes: node_name = self.GetCompatName(node) fields = {}
# Get a list of all the valid properties in this node. for name, prop in node.props.items(): if name not in PROP_IGNORE_LIST and name[0] != '#': fields[name] = copy.deepcopy(prop)
# If we've seen this node_name before, update the existing struct. if node_name in structs: struct = structs[node_name] for name, prop in fields.items(): oldprop = struct.get(name) if oldprop: oldprop.Widen(prop) else: struct[name] = prop
# Otherwise store this as a new struct. else: structs[node_name] = fields
upto = 0 for node in self._valid_nodes: node_name = self.GetCompatName(node) struct = structs[node_name] for name, prop in node.props.items(): if name not in PROP_IGNORE_LIST and name[0] != '#': prop.Widen(struct[name]) upto += 1 return structs
def GenerateStructs(self, structs): """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 README.of-plat for more information. """ self.Out('#include <stdbool.h>\n') self.Out('#include <libfdt.h>\n')
# Output the struct definition for name in sorted(structs): self.Out('struct %s%s {\n' % (STRUCT_PREFIX, name)); for pname in sorted(structs[name]): prop = structs[name][pname] if self.IsPhandle(prop): # For phandles, include a reference to the target self.Out('\t%s%s[%d]' % (TabTo(2, 'struct phandle_2_cell'), Conv_name_to_c(prop.name), len(prop.value) / 2)) else: ptype = TYPE_NAMES[prop.type] self.Out('\t%s%s' % (TabTo(2, ptype), Conv_name_to_c(prop.name))) if type(prop.value) == list: self.Out('[%d]' % len(prop.value)) self.Out(';\n') self.Out('};\n')
def GenerateTables(self): """Generate device defintions for the platform data
This writes out C platform data initialisation data and U_BOOT_DEVICE() declarations for each valid node. See the documentation in README.of-plat for more information. """ self.Out('#include <common.h>\n') self.Out('#include <dm.h>\n') self.Out('#include <dt-structs.h>\n') self.Out('\n') node_txt_list = [] for node in self._valid_nodes: struct_name = self.GetCompatName(node) var_name = Conv_name_to_c(node.name) self.Buf('static struct %s%s %s%s = {\n' % (STRUCT_PREFIX, struct_name, VAL_PREFIX, var_name)) for pname, prop in node.props.items(): if pname in PROP_IGNORE_LIST or pname[0] == '#': continue ptype = TYPE_NAMES[prop.type] member_name = Conv_name_to_c(prop.name) self.Buf('\t%s= ' % TabTo(3, '.' + member_name))
# Special handling for lists if type(prop.value) == list: self.Buf('{') vals = [] # For phandles, output a reference to the platform data # of the target node. if self.IsPhandle(prop): # Process the list as pairs of (phandle, id) it = iter(prop.value) for phandle_cell, id_cell in zip(it, it): phandle = fdt_util.fdt32_to_cpu(phandle_cell) id = fdt_util.fdt32_to_cpu(id_cell) target_node = self._phandle_node[phandle] name = Conv_name_to_c(target_node.name) vals.append('{&%s%s, %d}' % (VAL_PREFIX, name, id)) else: for val in prop.value: vals.append(self.GetValue(prop.type, val)) self.Buf(', '.join(vals)) self.Buf('}') else: self.Buf(self.GetValue(prop.type, prop.value)) 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.platdata\t= &%s%s,\n' % (VAL_PREFIX, var_name)) self.Buf('\t.platdata_size\t= sizeof(%s%s),\n' % (VAL_PREFIX, var_name)) self.Buf('};\n') self.Buf('\n')
# Output phandle target nodes first, since they may be referenced # by others if 'phandle' in node.props: self.Out(''.join(self.GetBuf())) else: node_txt_list.append(self.GetBuf())
# Output all the nodes which are not phandle targets themselves, but # may reference them. This avoids the need for forward declarations. for node_txt in node_txt_list: self.Out(''.join(node_txt))