import libfdt
from libfdt import QUIET_NOTFOUND
from u_boot_pylib import tools
+from u_boot_pylib import tout
# This deals with a device tree, presenting it as an assortment of Node and
# Prop objects, representing nodes and properties, respectively. This file
fdt_obj.setprop(node.Offset(), self.name, self.bytes)
self.dirty = False
+ def purge(self):
+ """Set a property offset to None
+
+ The property remains in the tree structure and will be recreated when
+ the FDT is synced
+ """
+ self._offset = None
class Node:
"""A device tree node
"""
return self.AddData(prop_name, struct.pack('>I', val))
- def AddSubnode(self, name):
- """Add a new subnode to the node
+ def Subnode(self, name):
+ """Create new subnode for the node
Args:
name: name of node to add
New subnode that was created
"""
path = self.path + '/' + name
- subnode = Node(self._fdt, self, None, name, path)
+ return Node(self._fdt, self, None, name, path)
+
+ def AddSubnode(self, name):
+ """Add a new subnode to the node, after all other subnodes
+
+ Args:
+ name: name of node to add
+
+ Returns:
+ New subnode that was created
+ """
+ subnode = self.Subnode(name)
self.subnodes.append(subnode)
return subnode
+ def insert_subnode(self, name):
+ """Add a new subnode to the node, before all other subnodes
+
+ This deletes other subnodes and sets their offset to None, so that they
+ will be recreated after this one.
+
+ Args:
+ name: name of node to add
+
+ Returns:
+ New subnode that was created
+ """
+ # Deleting a node invalidates the offsets of all following nodes, so
+ # process in reverse order so that the offset of each node remains valid
+ # until deletion.
+ for subnode in reversed(self.subnodes):
+ subnode.purge(True)
+ subnode = self.Subnode(name)
+ self.subnodes.insert(0, subnode)
+ return subnode
+
+ def purge(self, delete_it=False):
+ """Purge this node, setting offset to None and deleting from FDT"""
+ if self._offset is not None:
+ if delete_it:
+ CheckErr(self._fdt._fdt_obj.del_node(self.Offset()),
+ "Node '%s': delete" % self.path)
+ self._offset = None
+ self._fdt.Invalidate()
+
+ for prop in self.props.values():
+ prop.purge()
+
+ for subnode in self.subnodes:
+ subnode.purge(False)
+
+ def move_to_first(self):
+ """Move the current node to first in its parent's node list"""
+ parent = self.parent
+ if parent.subnodes and parent.subnodes[0] == self:
+ return
+ for subnode in reversed(parent.subnodes):
+ subnode.purge(True)
+
+ new_subnodes = [self]
+ for subnode in parent.subnodes:
+ #subnode.purge(False)
+ if subnode != self:
+ new_subnodes.append(subnode)
+ parent.subnodes = new_subnodes
+
def Delete(self):
"""Delete a node
prop.Sync(auto_resize)
return added
+ def merge_props(self, src):
+ """Copy missing properties (except 'phandle') from another node
+
+ Args:
+ src (Node): Node containing properties to copy
+
+ Adds properties which are present in src but not in this node. Any
+ 'phandle' property is not copied since this might result in two nodes
+ with the same phandle, thus making phandle references ambiguous.
+ """
+ for name, src_prop in src.props.items():
+ if name != 'phandle' and name not in self.props:
+ self.props[name] = Prop(self, None, name, src_prop.bytes)
+
+ def copy_node(self, src):
+ """Copy a node and all its subnodes into this node
+
+ Args:
+ src (Node): Node to copy
+
+ Returns:
+ Node: Resulting destination node
+
+ This works recursively.
+
+ The new node is put before all other nodes. If the node already
+ exists, just its subnodes and properties are copied, placing them before
+ any existing subnodes. Properties which exist in the destination node
+ already are not copied.
+ """
+ dst = self.FindNode(src.name)
+ if dst:
+ dst.move_to_first()
+ else:
+ dst = self.insert_subnode(src.name)
+ dst.merge_props(src)
+
+ # Process in reverse order so that they appear correctly in the result,
+ # since copy_node() puts the node first in the list
+ for node in reversed(src.subnodes):
+ dst.copy_node(node)
+ return dst
+
class Fdt:
"""Provides simple access to a flat device tree blob using libfdts.
self.assertIn("Internal error, node '/spl-test' name mismatch 'i2c@0'",
str(exc.exception))
+ def test_copy_node(self):
+ """Test copy_node() function"""
+ def do_copy_checks(dtb, dst, expect_none):
+ self.assertEqual(
+ ['/dest/base', '/dest/first@0', '/dest/existing'],
+ [n.path for n in dst.subnodes])
+
+ chk = dtb.GetNode('/dest/base')
+ self.assertTrue(chk)
+ self.assertEqual(
+ {'compatible', 'bootph-all', '#address-cells', '#size-cells'},
+ chk.props.keys())
+
+ # Check the first property
+ prop = chk.props['bootph-all']
+ self.assertEqual('bootph-all', prop.name)
+ self.assertEqual(True, prop.value)
+ self.assertEqual(chk.path, prop._node.path)
+
+ # Check the second property
+ prop2 = chk.props['compatible']
+ self.assertEqual('compatible', prop2.name)
+ self.assertEqual('sandbox,i2c', prop2.value)
+ self.assertEqual(chk.path, prop2._node.path)
+
+ base = chk.FindNode('base')
+ self.assertTrue(chk)
+
+ first = dtb.GetNode('/dest/base/first@0')
+ self.assertTrue(first)
+ over = dtb.GetNode('/dest/base/over')
+ self.assertTrue(over)
+
+ # Make sure that the phandle for 'over' is not copied
+ self.assertNotIn('phandle', over.props.keys())
+
+ second = dtb.GetNode('/dest/base/second')
+ self.assertTrue(second)
+ self.assertEqual([over.name, first.name, second.name],
+ [n.name for n in chk.subnodes])
+ self.assertEqual(chk, over.parent)
+ self.assertEqual(
+ {'bootph-all', 'compatible', 'reg', 'low-power'},
+ over.props.keys())
+
+ if expect_none:
+ self.assertIsNone(prop._offset)
+ self.assertIsNone(prop2._offset)
+ self.assertIsNone(over._offset)
+ else:
+ self.assertTrue(prop._offset)
+ self.assertTrue(prop2._offset)
+ self.assertTrue(over._offset)
+
+ # Now check ordering of the subnodes
+ self.assertEqual(
+ ['second1', 'second2', 'second3', 'second4'],
+ [n.name for n in second.subnodes])
+
+ dtb = fdt.FdtScan(find_dtb_file('dtoc_test_copy.dts'))
+ tmpl = dtb.GetNode('/base')
+ dst = dtb.GetNode('/dest')
+ dst.copy_node(tmpl)
+
+ do_copy_checks(dtb, dst, expect_none=True)
+
+ dtb.Sync(auto_resize=True)
+
+ # Now check that the FDT looks correct
+ new_dtb = fdt.Fdt.FromData(dtb.GetContents())
+ new_dtb.Scan()
+ dst = new_dtb.GetNode('/dest')
+ do_copy_checks(new_dtb, dst, expect_none=False)
+
class TestProp(unittest.TestCase):
"""Test operation of the Prop class"""