dtoc: Support deleting device tree properties
[platform/kernel/u-boot.git] / tools / dtoc / fdt_normal.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2016 Google, Inc
4 # Written by Simon Glass <sjg@chromium.org>
5 #
6 # SPDX-License-Identifier:      GPL-2.0+
7 #
8
9 import struct
10 import sys
11
12 import fdt
13 from fdt import Fdt, NodeBase, PropBase
14 import fdt_util
15 import libfdt
16
17 # This deals with a device tree, presenting it as a list of Node and Prop
18 # objects, representing nodes and properties, respectively.
19 #
20 # This implementation uses a libfdt Python library to access the device tree,
21 # so it is fairly efficient.
22
23 def CheckErr(errnum, msg):
24     if errnum:
25         raise ValueError('Error %d: %s: %s' %
26             (errnum, libfdt.fdt_strerror(errnum), msg))
27
28 class Prop(PropBase):
29     """A device tree property
30
31     Properties:
32         name: Property name (as per the device tree)
33         value: Property value as a string of bytes, or a list of strings of
34             bytes
35         type: Value type
36     """
37     def __init__(self, node, offset, name, bytes):
38         PropBase.__init__(self, node, offset, name)
39         self.bytes = bytes
40         if not bytes:
41             self.type = fdt.TYPE_BOOL
42             self.value = True
43             return
44         self.type, self.value = self.BytesToValue(bytes)
45
46 class Node(NodeBase):
47     """A device tree node
48
49     Properties:
50         offset: Integer offset in the device tree
51         name: Device tree node tname
52         path: Full path to node, along with the node name itself
53         _fdt: Device tree object
54         subnodes: A list of subnodes for this node, each a Node object
55         props: A dict of properties for this node, each a Prop object.
56             Keyed by property name
57     """
58     def __init__(self, fdt, offset, name, path):
59         NodeBase.__init__(self, fdt, offset, name, path)
60
61     def Offset(self):
62         """Returns the offset of a node, after checking the cache
63
64         This should be used instead of self._offset directly, to ensure that
65         the cache does not contain invalid offsets.
66         """
67         self._fdt.CheckCache()
68         return self._offset
69
70     def Scan(self):
71         """Scan a node's properties and subnodes
72
73         This fills in the props and subnodes properties, recursively
74         searching into subnodes so that the entire tree is built.
75         """
76         self.props = self._fdt.GetProps(self, self.path)
77
78         offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.Offset())
79         while offset >= 0:
80             sep = '' if self.path[-1] == '/' else '/'
81             name = libfdt.Name(self._fdt.GetFdt(), offset)
82             path = self.path + sep + name
83             node = Node(self._fdt, offset, name, path)
84             self.subnodes.append(node)
85
86             node.Scan()
87             offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
88
89     def Refresh(self, my_offset):
90         """Fix up the _offset for each node, recursively
91
92         Note: This does not take account of property offsets - these will not
93         be updated.
94         """
95         if self._offset != my_offset:
96             #print '%s: %d -> %d\n' % (self.path, self._offset, my_offset)
97             self._offset = my_offset
98         offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self._offset)
99         for subnode in self.subnodes:
100             subnode.Refresh(offset)
101             offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset)
102
103     def DeleteProp(self, prop_name):
104         """Delete a property of a node
105
106         The property is deleted and the offset cache is invalidated.
107
108         Args:
109             prop_name: Name of the property to delete
110         Raises:
111             ValueError if the property does not exist
112         """
113         CheckErr(libfdt.fdt_delprop(self._fdt.GetFdt(), self.Offset(), prop_name),
114                  "Node '%s': delete property: '%s'" % (self.path, prop_name))
115         del self.props[prop_name]
116         self._fdt.Invalidate()
117
118 class FdtNormal(Fdt):
119     """Provides simple access to a flat device tree blob using libfdt.
120
121     Properties:
122         _fdt: Device tree contents (bytearray)
123         _cached_offsets: True if all the nodes have a valid _offset property,
124             False if something has changed to invalidate the offsets
125     """
126     def __init__(self, fname):
127         Fdt.__init__(self, fname)
128         self._cached_offsets = False
129         if self._fname:
130             self._fname = fdt_util.EnsureCompiled(self._fname)
131
132             with open(self._fname) as fd:
133                 self._fdt = bytearray(fd.read())
134
135     def GetFdt(self):
136         """Get the contents of the FDT
137
138         Returns:
139             The FDT contents as a string of bytes
140         """
141         return self._fdt
142
143     def GetProps(self, node, path):
144         """Get all properties from a node.
145
146         Args:
147             node: Full path to node name to look in.
148
149         Returns:
150             A dictionary containing all the properties, indexed by node name.
151             The entries are Prop objects.
152
153         Raises:
154             ValueError: if the node does not exist.
155         """
156         offset = libfdt.fdt_path_offset(self._fdt, path)
157         if offset < 0:
158             libfdt.Raise(offset)
159         props_dict = {}
160         poffset = libfdt.fdt_first_property_offset(self._fdt, offset)
161         while poffset >= 0:
162             dprop, plen = libfdt.fdt_get_property_by_offset(self._fdt, poffset)
163             prop = Prop(node, poffset, libfdt.String(self._fdt, dprop.nameoff),
164                         libfdt.Data(dprop))
165             props_dict[prop.name] = prop
166
167             poffset = libfdt.fdt_next_property_offset(self._fdt, poffset)
168         return props_dict
169
170     def Invalidate(self):
171         """Mark our offset cache as invalid"""
172         self._cached_offsets = False
173
174     def CheckCache(self):
175         """Refresh the offset cache if needed"""
176         if self._cached_offsets:
177             return
178         self.Refresh()
179         self._cached_offsets = True
180
181     def Refresh(self):
182         """Refresh the offset cache"""
183         self._root.Refresh(0)
184
185     @classmethod
186     def Node(self, fdt, offset, name, path):
187         """Create a new node
188
189         This is used by Fdt.Scan() to create a new node using the correct
190         class.
191
192         Args:
193             fdt: Fdt object
194             offset: Offset of node
195             name: Node name
196             path: Full path to node
197         """
198         node = Node(fdt, offset, name, path)
199         return node