]> git.sur5r.net Git - u-boot/blob - tools/dtoc/fdt.py
dtoc: Move a few more common functions into fdt.py
[u-boot] / tools / dtoc / fdt.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_util
13
14 # This deals with a device tree, presenting it as an assortment of Node and
15 # Prop objects, representing nodes and properties, respectively. This file
16 # contains the base classes and defines the high-level API. Most of the
17 # implementation is in the FdtFallback and FdtNormal subclasses. See
18 # fdt_select.py for how to create an Fdt object.
19
20 # A list of types we support
21 (TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL) = range(4)
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 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):
38         self._node = node
39         self._offset = offset
40         self.name = name
41         self.value = None
42
43     def GetPhandle(self):
44         """Get a (single) phandle value from a property
45
46         Gets the phandle valuie from a property and returns it as an integer
47         """
48         return fdt_util.fdt32_to_cpu(self.value[:4])
49
50     def Widen(self, newprop):
51         """Figure out which property type is more general
52
53         Given a current property and a new property, this function returns the
54         one that is less specific as to type. The less specific property will
55         be ble to represent the data in the more specific property. This is
56         used for things like:
57
58             node1 {
59                 compatible = "fred";
60                 value = <1>;
61             };
62             node1 {
63                 compatible = "fred";
64                 value = <1 2>;
65             };
66
67         He we want to use an int array for 'value'. The first property
68         suggests that a single int is enough, but the second one shows that
69         it is not. Calling this function with these two propertes would
70         update the current property to be like the second, since it is less
71         specific.
72         """
73         if newprop.type < self.type:
74             self.type = newprop.type
75
76         if type(newprop.value) == list and type(self.value) != list:
77             self.value = [self.value]
78
79         if type(self.value) == list and len(newprop.value) > len(self.value):
80             val = self.GetEmpty(self.type)
81             while len(self.value) < len(newprop.value):
82                 self.value.append(val)
83
84     def BytesToValue(self, bytes):
85         """Converts a string of bytes into a type and value
86
87         Args:
88             A string containing bytes
89
90         Return:
91             A tuple:
92                 Type of data
93                 Data, either a single element or a list of elements. Each element
94                 is one of:
95                     TYPE_STRING: string value from the property
96                     TYPE_INT: a byte-swapped integer stored as a 4-byte string
97                     TYPE_BYTE: a byte stored as a single-byte string
98         """
99         size = len(bytes)
100         strings = bytes.split('\0')
101         is_string = True
102         count = len(strings) - 1
103         if count > 0 and not strings[-1]:
104             for string in strings[:-1]:
105                 if not string:
106                     is_string = False
107                     break
108                 for ch in string:
109                     if ch < ' ' or ch > '~':
110                         is_string = False
111                         break
112         else:
113             is_string = False
114         if is_string:
115             if count == 1:
116                 return TYPE_STRING, strings[0]
117             else:
118                 return TYPE_STRING, strings[:-1]
119         if size % 4:
120             if size == 1:
121                 return TYPE_BYTE, bytes[0]
122             else:
123                 return TYPE_BYTE, list(bytes)
124         val = []
125         for i in range(0, size, 4):
126             val.append(bytes[i:i + 4])
127         if size == 4:
128             return TYPE_INT, val[0]
129         else:
130             return TYPE_INT, val
131
132     def GetEmpty(self, type):
133         """Get an empty / zero value of the given type
134
135         Returns:
136             A single value of the given type
137         """
138         if type == TYPE_BYTE:
139             return chr(0)
140         elif type == TYPE_INT:
141             return struct.pack('<I', 0);
142         elif type == TYPE_STRING:
143             return ''
144         else:
145             return True
146
147 class NodeBase:
148     """A device tree node
149
150     Properties:
151         offset: Integer offset in the device tree
152         name: Device tree node tname
153         path: Full path to node, along with the node name itself
154         _fdt: Device tree object
155         subnodes: A list of subnodes for this node, each a Node object
156         props: A dict of properties for this node, each a Prop object.
157             Keyed by property name
158     """
159     def __init__(self, fdt, offset, name, path):
160         self._fdt = fdt
161         self._offset = offset
162         self.name = name
163         self.path = path
164         self.subnodes = []
165         self.props = {}
166
167     def _FindNode(self, name):
168         """Find a node given its name
169
170         Args:
171             name: Node name to look for
172         Returns:
173             Node object if found, else None
174         """
175         for subnode in self.subnodes:
176             if subnode.name == name:
177                 return subnode
178         return None
179
180     def Scan(self):
181         """Scan the subnodes of a node
182
183         This should be implemented by subclasses
184         """
185         raise NotImplementedError()
186
187 class Fdt:
188     """Provides simple access to a flat device tree blob.
189
190     Properties:
191       fname: Filename of fdt
192       _root: Root of device tree (a Node object)
193     """
194     def __init__(self, fname):
195         self._fname = fname
196
197     def Scan(self, root='/'):
198         """Scan a device tree, building up a tree of Node objects
199
200         This fills in the self._root property
201
202         Args:
203             root: Ignored
204
205         TODO(sjg@chromium.org): Implement the 'root' parameter
206         """
207         self._root = self.Node(self, 0, '/', '/')
208         self._root.Scan()
209
210     def GetRoot(self):
211         """Get the root Node of the device tree
212
213         Returns:
214             The root Node object
215         """
216         return self._root
217
218     def GetNode(self, path):
219         """Look up a node from its path
220
221         Args:
222             path: Path to look up, e.g. '/microcode/update@0'
223         Returns:
224             Node object, or None if not found
225         """
226         node = self._root
227         for part in path.split('/')[1:]:
228             node = node._FindNode(part)
229             if not node:
230                 return None
231         return node
232