]>
Commit | Line | Data |
---|---|---|
a06a34b2 | 1 | #!/usr/bin/python |
83d290c5 | 2 | # SPDX-License-Identifier: GPL-2.0+ |
a06a34b2 SG |
3 | # |
4 | # Copyright (C) 2016 Google, Inc | |
5 | # Written by Simon Glass <sjg@chromium.org> | |
6 | # | |
a06a34b2 SG |
7 | |
8 | import struct | |
9 | import sys | |
10 | ||
11 | import fdt_util | |
7b75b448 | 12 | import libfdt |
117f57b7 | 13 | from libfdt import QUIET_NOTFOUND |
a06a34b2 SG |
14 | |
15 | # This deals with a device tree, presenting it as an assortment of Node and | |
16 | # Prop objects, representing nodes and properties, respectively. This file | |
99ed4a2e SG |
17 | # contains the base classes and defines the high-level API. You can use |
18 | # FdtScan() as a convenience function to create and scan an Fdt. | |
7b75b448 SG |
19 | |
20 | # This implementation uses a libfdt Python library to access the device tree, | |
21 | # so it is fairly efficient. | |
a06a34b2 | 22 | |
bc1dea36 | 23 | # A list of types we support |
fbdfd228 | 24 | (TYPE_BYTE, TYPE_INT, TYPE_STRING, TYPE_BOOL, TYPE_INT64) = range(5) |
bc1dea36 | 25 | |
a06a34b2 SG |
26 | def CheckErr(errnum, msg): |
27 | if errnum: | |
28 | raise ValueError('Error %d: %s: %s' % | |
29 | (errnum, libfdt.fdt_strerror(errnum), msg)) | |
30 | ||
7b75b448 | 31 | class Prop: |
a06a34b2 SG |
32 | """A device tree property |
33 | ||
34 | Properties: | |
35 | name: Property name (as per the device tree) | |
36 | value: Property value as a string of bytes, or a list of strings of | |
37 | bytes | |
38 | type: Value type | |
39 | """ | |
7b75b448 | 40 | def __init__(self, node, offset, name, bytes): |
a06a34b2 SG |
41 | self._node = node |
42 | self._offset = offset | |
43 | self.name = name | |
44 | self.value = None | |
7b75b448 | 45 | self.bytes = str(bytes) |
fa80c25c | 46 | self.dirty = False |
7b75b448 SG |
47 | if not bytes: |
48 | self.type = TYPE_BOOL | |
49 | self.value = True | |
50 | return | |
51 | self.type, self.value = self.BytesToValue(bytes) | |
a06a34b2 | 52 | |
f9b88b3a SG |
53 | def RefreshOffset(self, poffset): |
54 | self._offset = poffset | |
55 | ||
c322a850 SG |
56 | def Widen(self, newprop): |
57 | """Figure out which property type is more general | |
58 | ||
59 | Given a current property and a new property, this function returns the | |
60 | one that is less specific as to type. The less specific property will | |
61 | be ble to represent the data in the more specific property. This is | |
62 | used for things like: | |
63 | ||
64 | node1 { | |
65 | compatible = "fred"; | |
66 | value = <1>; | |
67 | }; | |
68 | node1 { | |
69 | compatible = "fred"; | |
70 | value = <1 2>; | |
71 | }; | |
72 | ||
73 | He we want to use an int array for 'value'. The first property | |
74 | suggests that a single int is enough, but the second one shows that | |
75 | it is not. Calling this function with these two propertes would | |
76 | update the current property to be like the second, since it is less | |
77 | specific. | |
78 | """ | |
79 | if newprop.type < self.type: | |
80 | self.type = newprop.type | |
81 | ||
82 | if type(newprop.value) == list and type(self.value) != list: | |
83 | self.value = [self.value] | |
84 | ||
85 | if type(self.value) == list and len(newprop.value) > len(self.value): | |
86 | val = self.GetEmpty(self.type) | |
87 | while len(self.value) < len(newprop.value): | |
88 | self.value.append(val) | |
89 | ||
bc1dea36 SG |
90 | def BytesToValue(self, bytes): |
91 | """Converts a string of bytes into a type and value | |
92 | ||
93 | Args: | |
94 | A string containing bytes | |
95 | ||
96 | Return: | |
97 | A tuple: | |
98 | Type of data | |
99 | Data, either a single element or a list of elements. Each element | |
100 | is one of: | |
101 | TYPE_STRING: string value from the property | |
102 | TYPE_INT: a byte-swapped integer stored as a 4-byte string | |
103 | TYPE_BYTE: a byte stored as a single-byte string | |
104 | """ | |
b4360206 | 105 | bytes = str(bytes) |
bc1dea36 SG |
106 | size = len(bytes) |
107 | strings = bytes.split('\0') | |
108 | is_string = True | |
109 | count = len(strings) - 1 | |
110 | if count > 0 and not strings[-1]: | |
111 | for string in strings[:-1]: | |
112 | if not string: | |
113 | is_string = False | |
114 | break | |
115 | for ch in string: | |
116 | if ch < ' ' or ch > '~': | |
117 | is_string = False | |
118 | break | |
119 | else: | |
120 | is_string = False | |
121 | if is_string: | |
122 | if count == 1: | |
123 | return TYPE_STRING, strings[0] | |
124 | else: | |
125 | return TYPE_STRING, strings[:-1] | |
126 | if size % 4: | |
127 | if size == 1: | |
128 | return TYPE_BYTE, bytes[0] | |
129 | else: | |
130 | return TYPE_BYTE, list(bytes) | |
131 | val = [] | |
132 | for i in range(0, size, 4): | |
133 | val.append(bytes[i:i + 4]) | |
134 | if size == 4: | |
135 | return TYPE_INT, val[0] | |
136 | else: | |
137 | return TYPE_INT, val | |
138 | ||
2ba98753 | 139 | @classmethod |
bc1dea36 SG |
140 | def GetEmpty(self, type): |
141 | """Get an empty / zero value of the given type | |
142 | ||
143 | Returns: | |
144 | A single value of the given type | |
145 | """ | |
146 | if type == TYPE_BYTE: | |
147 | return chr(0) | |
148 | elif type == TYPE_INT: | |
af53f5aa | 149 | return struct.pack('>I', 0); |
bc1dea36 SG |
150 | elif type == TYPE_STRING: |
151 | return '' | |
152 | else: | |
153 | return True | |
154 | ||
babdbde6 SG |
155 | def GetOffset(self): |
156 | """Get the offset of a property | |
157 | ||
babdbde6 | 158 | Returns: |
7b75b448 | 159 | The offset of the property (struct fdt_property) within the file |
babdbde6 | 160 | """ |
f9b88b3a | 161 | self._node._fdt.CheckCache() |
7b75b448 | 162 | return self._node._fdt.GetStructOffset(self._offset) |
babdbde6 | 163 | |
fa80c25c SG |
164 | def SetInt(self, val): |
165 | """Set the integer value of the property | |
166 | ||
167 | The device tree is marked dirty so that the value will be written to | |
168 | the block on the next sync. | |
169 | ||
170 | Args: | |
171 | val: Integer value (32-bit, single cell) | |
172 | """ | |
173 | self.bytes = struct.pack('>I', val); | |
41b781dd | 174 | self.value = self.bytes |
fa80c25c SG |
175 | self.type = TYPE_INT |
176 | self.dirty = True | |
177 | ||
6434961b SG |
178 | def SetData(self, bytes): |
179 | """Set the value of a property as bytes | |
180 | ||
181 | Args: | |
182 | bytes: New property value to set | |
183 | """ | |
184 | self.bytes = str(bytes) | |
185 | self.type, self.value = self.BytesToValue(bytes) | |
186 | self.dirty = True | |
187 | ||
fa80c25c SG |
188 | def Sync(self, auto_resize=False): |
189 | """Sync property changes back to the device tree | |
190 | ||
191 | This updates the device tree blob with any changes to this property | |
192 | since the last sync. | |
193 | ||
194 | Args: | |
195 | auto_resize: Resize the device tree automatically if it does not | |
196 | have enough space for the update | |
197 | ||
198 | Raises: | |
199 | FdtException if auto_resize is False and there is not enough space | |
200 | """ | |
201 | if self._offset is None or self.dirty: | |
202 | node = self._node | |
203 | fdt_obj = node._fdt._fdt_obj | |
204 | if auto_resize: | |
205 | while fdt_obj.setprop(node.Offset(), self.name, self.bytes, | |
206 | (libfdt.NOSPACE,)) == -libfdt.NOSPACE: | |
207 | fdt_obj.resize(fdt_obj.totalsize() + 1024) | |
208 | fdt_obj.setprop(node.Offset(), self.name, self.bytes) | |
209 | else: | |
210 | fdt_obj.setprop(node.Offset(), self.name, self.bytes) | |
211 | ||
212 | ||
7b75b448 | 213 | class Node: |
a06a34b2 SG |
214 | """A device tree node |
215 | ||
216 | Properties: | |
217 | offset: Integer offset in the device tree | |
218 | name: Device tree node tname | |
219 | path: Full path to node, along with the node name itself | |
220 | _fdt: Device tree object | |
221 | subnodes: A list of subnodes for this node, each a Node object | |
222 | props: A dict of properties for this node, each a Prop object. | |
223 | Keyed by property name | |
224 | """ | |
979ab024 | 225 | def __init__(self, fdt, parent, offset, name, path): |
a06a34b2 | 226 | self._fdt = fdt |
979ab024 | 227 | self.parent = parent |
a06a34b2 SG |
228 | self._offset = offset |
229 | self.name = name | |
230 | self.path = path | |
231 | self.subnodes = [] | |
232 | self.props = {} | |
233 | ||
94a7c603 SG |
234 | def GetFdt(self): |
235 | """Get the Fdt object for this node | |
236 | ||
237 | Returns: | |
238 | Fdt object | |
239 | """ | |
240 | return self._fdt | |
241 | ||
1d85888c | 242 | def FindNode(self, name): |
f7a2aeee SG |
243 | """Find a node given its name |
244 | ||
245 | Args: | |
246 | name: Node name to look for | |
247 | Returns: | |
248 | Node object if found, else None | |
249 | """ | |
250 | for subnode in self.subnodes: | |
251 | if subnode.name == name: | |
252 | return subnode | |
253 | return None | |
254 | ||
7b75b448 SG |
255 | def Offset(self): |
256 | """Returns the offset of a node, after checking the cache | |
257 | ||
258 | This should be used instead of self._offset directly, to ensure that | |
259 | the cache does not contain invalid offsets. | |
260 | """ | |
261 | self._fdt.CheckCache() | |
262 | return self._offset | |
263 | ||
f7a2aeee | 264 | def Scan(self): |
7b75b448 SG |
265 | """Scan a node's properties and subnodes |
266 | ||
267 | This fills in the props and subnodes properties, recursively | |
268 | searching into subnodes so that the entire tree is built. | |
269 | """ | |
117f57b7 | 270 | fdt_obj = self._fdt._fdt_obj |
7b75b448 | 271 | self.props = self._fdt.GetProps(self) |
117f57b7 | 272 | phandle = fdt_obj.get_phandle(self.Offset()) |
09264e04 | 273 | if phandle: |
117f57b7 | 274 | self._fdt.phandle_to_node[phandle] = self |
7b75b448 | 275 | |
117f57b7 | 276 | offset = fdt_obj.first_subnode(self.Offset(), QUIET_NOTFOUND) |
7b75b448 SG |
277 | while offset >= 0: |
278 | sep = '' if self.path[-1] == '/' else '/' | |
117f57b7 | 279 | name = fdt_obj.get_name(offset) |
7b75b448 | 280 | path = self.path + sep + name |
979ab024 | 281 | node = Node(self._fdt, self, offset, name, path) |
7b75b448 | 282 | self.subnodes.append(node) |
f7a2aeee | 283 | |
7b75b448 | 284 | node.Scan() |
117f57b7 | 285 | offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND) |
7b75b448 SG |
286 | |
287 | def Refresh(self, my_offset): | |
288 | """Fix up the _offset for each node, recursively | |
289 | ||
290 | Note: This does not take account of property offsets - these will not | |
291 | be updated. | |
f7a2aeee | 292 | """ |
96066240 | 293 | fdt_obj = self._fdt._fdt_obj |
7b75b448 | 294 | if self._offset != my_offset: |
7b75b448 | 295 | self._offset = my_offset |
96066240 | 296 | offset = fdt_obj.first_subnode(self._offset, QUIET_NOTFOUND) |
7b75b448 | 297 | for subnode in self.subnodes: |
f9b88b3a SG |
298 | if subnode.name != fdt_obj.get_name(offset): |
299 | raise ValueError('Internal error, node name mismatch %s != %s' % | |
300 | (subnode.name, fdt_obj.get_name(offset))) | |
7b75b448 | 301 | subnode.Refresh(offset) |
96066240 | 302 | offset = fdt_obj.next_subnode(offset, QUIET_NOTFOUND) |
f9b88b3a SG |
303 | if offset != -libfdt.FDT_ERR_NOTFOUND: |
304 | raise ValueError('Internal error, offset == %d' % offset) | |
305 | ||
306 | poffset = fdt_obj.first_property_offset(self._offset, QUIET_NOTFOUND) | |
307 | while poffset >= 0: | |
308 | p = fdt_obj.get_property_by_offset(poffset) | |
309 | prop = self.props.get(p.name) | |
310 | if not prop: | |
311 | raise ValueError("Internal error, property '%s' missing, " | |
312 | 'offset %d' % (p.name, poffset)) | |
313 | prop.RefreshOffset(poffset) | |
314 | poffset = fdt_obj.next_property_offset(poffset, QUIET_NOTFOUND) | |
f7a2aeee | 315 | |
2a70d897 SG |
316 | def DeleteProp(self, prop_name): |
317 | """Delete a property of a node | |
318 | ||
7b75b448 | 319 | The property is deleted and the offset cache is invalidated. |
2a70d897 SG |
320 | |
321 | Args: | |
322 | prop_name: Name of the property to delete | |
7b75b448 SG |
323 | Raises: |
324 | ValueError if the property does not exist | |
2a70d897 | 325 | """ |
96066240 | 326 | CheckErr(self._fdt._fdt_obj.delprop(self.Offset(), prop_name), |
7b75b448 SG |
327 | "Node '%s': delete property: '%s'" % (self.path, prop_name)) |
328 | del self.props[prop_name] | |
329 | self._fdt.Invalidate() | |
2a70d897 | 330 | |
116adecb SG |
331 | def AddZeroProp(self, prop_name): |
332 | """Add a new property to the device tree with an integer value of 0. | |
333 | ||
334 | Args: | |
335 | prop_name: Name of property | |
336 | """ | |
fa80c25c | 337 | self.props[prop_name] = Prop(self, None, prop_name, '\0' * 4) |
116adecb | 338 | |
6434961b SG |
339 | def AddEmptyProp(self, prop_name, len): |
340 | """Add a property with a fixed data size, for filling in later | |
341 | ||
342 | The device tree is marked dirty so that the value will be written to | |
343 | the blob on the next sync. | |
344 | ||
345 | Args: | |
346 | prop_name: Name of property | |
347 | len: Length of data in property | |
348 | """ | |
349 | value = chr(0) * len | |
350 | self.props[prop_name] = Prop(self, None, prop_name, value) | |
351 | ||
116adecb SG |
352 | def SetInt(self, prop_name, val): |
353 | """Update an integer property int the device tree. | |
354 | ||
355 | This is not allowed to change the size of the FDT. | |
356 | ||
6434961b SG |
357 | The device tree is marked dirty so that the value will be written to |
358 | the blob on the next sync. | |
359 | ||
116adecb SG |
360 | Args: |
361 | prop_name: Name of property | |
362 | val: Value to set | |
363 | """ | |
fa80c25c SG |
364 | self.props[prop_name].SetInt(val) |
365 | ||
6434961b SG |
366 | def SetData(self, prop_name, val): |
367 | """Set the data value of a property | |
368 | ||
369 | The device tree is marked dirty so that the value will be written to | |
370 | the blob on the next sync. | |
371 | ||
372 | Args: | |
373 | prop_name: Name of property to set | |
374 | val: Data value to set | |
375 | """ | |
376 | self.props[prop_name].SetData(val) | |
377 | ||
378 | def SetString(self, prop_name, val): | |
379 | """Set the string value of a property | |
380 | ||
381 | The device tree is marked dirty so that the value will be written to | |
382 | the blob on the next sync. | |
383 | ||
384 | Args: | |
385 | prop_name: Name of property to set | |
386 | val: String value to set (will be \0-terminated in DT) | |
387 | """ | |
388 | self.props[prop_name].SetData(val + chr(0)) | |
389 | ||
390 | def AddString(self, prop_name, val): | |
391 | """Add a new string property to a node | |
392 | ||
393 | The device tree is marked dirty so that the value will be written to | |
394 | the blob on the next sync. | |
395 | ||
396 | Args: | |
397 | prop_name: Name of property to add | |
398 | val: String value of property | |
399 | """ | |
400 | self.props[prop_name] = Prop(self, None, prop_name, val + chr(0)) | |
401 | ||
e21c27af | 402 | def AddSubnode(self, name): |
6434961b SG |
403 | """Add a new subnode to the node |
404 | ||
405 | Args: | |
406 | name: name of node to add | |
407 | ||
408 | Returns: | |
409 | New subnode that was created | |
410 | """ | |
e21c27af SG |
411 | path = self.path + '/' + name |
412 | subnode = Node(self._fdt, self, None, name, path) | |
413 | self.subnodes.append(subnode) | |
414 | return subnode | |
415 | ||
fa80c25c SG |
416 | def Sync(self, auto_resize=False): |
417 | """Sync node changes back to the device tree | |
418 | ||
419 | This updates the device tree blob with any changes to this node and its | |
420 | subnodes since the last sync. | |
421 | ||
422 | Args: | |
423 | auto_resize: Resize the device tree automatically if it does not | |
424 | have enough space for the update | |
425 | ||
426 | Raises: | |
427 | FdtException if auto_resize is False and there is not enough space | |
428 | """ | |
e21c27af SG |
429 | if self._offset is None: |
430 | # The subnode doesn't exist yet, so add it | |
431 | fdt_obj = self._fdt._fdt_obj | |
432 | if auto_resize: | |
433 | while True: | |
434 | offset = fdt_obj.add_subnode(self.parent._offset, self.name, | |
435 | (libfdt.NOSPACE,)) | |
436 | if offset != -libfdt.NOSPACE: | |
437 | break | |
438 | fdt_obj.resize(fdt_obj.totalsize() + 1024) | |
439 | else: | |
440 | offset = fdt_obj.add_subnode(self.parent._offset, self.name) | |
441 | self._offset = offset | |
442 | ||
fa80c25c SG |
443 | # Sync subnodes in reverse so that we don't disturb node offsets for |
444 | # nodes that are earlier in the DT. This avoids an O(n^2) rescan of | |
445 | # node offsets. | |
446 | for node in reversed(self.subnodes): | |
447 | node.Sync(auto_resize) | |
448 | ||
449 | # Sync properties now, whose offsets should not have been disturbed. | |
450 | # We do this after subnodes, since this disturbs the offsets of these | |
451 | # properties. | |
452 | prop_list = sorted(self.props.values(), key=lambda prop: prop._offset, | |
453 | reverse=True) | |
454 | for prop in prop_list: | |
455 | prop.Sync(auto_resize) | |
116adecb SG |
456 | |
457 | ||
a06a34b2 | 458 | class Fdt: |
7b75b448 | 459 | """Provides simple access to a flat device tree blob using libfdts. |
a06a34b2 SG |
460 | |
461 | Properties: | |
462 | fname: Filename of fdt | |
463 | _root: Root of device tree (a Node object) | |
464 | """ | |
465 | def __init__(self, fname): | |
466 | self._fname = fname | |
7b75b448 | 467 | self._cached_offsets = False |
09264e04 | 468 | self.phandle_to_node = {} |
7b75b448 SG |
469 | if self._fname: |
470 | self._fname = fdt_util.EnsureCompiled(self._fname) | |
471 | ||
472 | with open(self._fname) as fd: | |
96066240 | 473 | self._fdt_obj = libfdt.Fdt(fd.read()) |
f7a2aeee | 474 | |
746aee3f SG |
475 | @staticmethod |
476 | def FromData(data): | |
477 | """Create a new Fdt object from the given data | |
478 | ||
479 | Args: | |
480 | data: Device-tree data blob | |
481 | ||
482 | Returns: | |
483 | Fdt object containing the data | |
484 | """ | |
485 | fdt = Fdt(None) | |
486 | fdt._fdt_obj = libfdt.Fdt(bytearray(data)) | |
487 | return fdt | |
488 | ||
94a7c603 SG |
489 | def LookupPhandle(self, phandle): |
490 | """Look up a phandle | |
491 | ||
492 | Args: | |
493 | phandle: Phandle to look up (int) | |
494 | ||
495 | Returns: | |
496 | Node object the phandle points to | |
497 | """ | |
498 | return self.phandle_to_node.get(phandle) | |
499 | ||
f7a2aeee SG |
500 | def Scan(self, root='/'): |
501 | """Scan a device tree, building up a tree of Node objects | |
502 | ||
503 | This fills in the self._root property | |
504 | ||
505 | Args: | |
506 | root: Ignored | |
507 | ||
508 | TODO(sjg@chromium.org): Implement the 'root' parameter | |
509 | """ | |
f9b88b3a | 510 | self._cached_offsets = True |
979ab024 | 511 | self._root = self.Node(self, None, 0, '/', '/') |
f7a2aeee SG |
512 | self._root.Scan() |
513 | ||
514 | def GetRoot(self): | |
515 | """Get the root Node of the device tree | |
516 | ||
517 | Returns: | |
518 | The root Node object | |
519 | """ | |
520 | return self._root | |
521 | ||
522 | def GetNode(self, path): | |
523 | """Look up a node from its path | |
524 | ||
525 | Args: | |
526 | path: Path to look up, e.g. '/microcode/update@0' | |
527 | Returns: | |
528 | Node object, or None if not found | |
529 | """ | |
530 | node = self._root | |
b9066ffc SG |
531 | parts = path.split('/') |
532 | if len(parts) < 2: | |
533 | return None | |
534 | for part in parts[1:]: | |
1d85888c | 535 | node = node.FindNode(part) |
f7a2aeee SG |
536 | if not node: |
537 | return None | |
538 | return node | |
539 | ||
da5f7499 SG |
540 | def Flush(self): |
541 | """Flush device tree changes back to the file | |
542 | ||
543 | If the device tree has changed in memory, write it back to the file. | |
da5f7499 | 544 | """ |
7b75b448 | 545 | with open(self._fname, 'wb') as fd: |
96066240 | 546 | fd.write(self._fdt_obj.as_bytearray()) |
da5f7499 | 547 | |
fa80c25c SG |
548 | def Sync(self, auto_resize=False): |
549 | """Make sure any DT changes are written to the blob | |
550 | ||
551 | Args: | |
552 | auto_resize: Resize the device tree automatically if it does not | |
553 | have enough space for the update | |
554 | ||
555 | Raises: | |
556 | FdtException if auto_resize is False and there is not enough space | |
557 | """ | |
558 | self._root.Sync(auto_resize) | |
559 | self.Invalidate() | |
560 | ||
da5f7499 SG |
561 | def Pack(self): |
562 | """Pack the device tree down to its minimum size | |
563 | ||
564 | When nodes and properties shrink or are deleted, wasted space can | |
7b75b448 SG |
565 | build up in the device tree binary. |
566 | """ | |
117f57b7 SG |
567 | CheckErr(self._fdt_obj.pack(), 'pack') |
568 | self.Invalidate() | |
7b75b448 | 569 | |
96066240 | 570 | def GetContents(self): |
7b75b448 SG |
571 | """Get the contents of the FDT |
572 | ||
573 | Returns: | |
574 | The FDT contents as a string of bytes | |
575 | """ | |
96066240 | 576 | return self._fdt_obj.as_bytearray() |
7b75b448 | 577 | |
2ba98753 SG |
578 | def GetFdtObj(self): |
579 | """Get the contents of the FDT | |
580 | ||
581 | Returns: | |
582 | The FDT contents as a libfdt.Fdt object | |
583 | """ | |
584 | return self._fdt_obj | |
585 | ||
7b75b448 SG |
586 | def GetProps(self, node): |
587 | """Get all properties from a node. | |
588 | ||
589 | Args: | |
590 | node: Full path to node name to look in. | |
591 | ||
592 | Returns: | |
593 | A dictionary containing all the properties, indexed by node name. | |
594 | The entries are Prop objects. | |
595 | ||
596 | Raises: | |
597 | ValueError: if the node does not exist. | |
598 | """ | |
599 | props_dict = {} | |
117f57b7 SG |
600 | poffset = self._fdt_obj.first_property_offset(node._offset, |
601 | QUIET_NOTFOUND) | |
7b75b448 SG |
602 | while poffset >= 0: |
603 | p = self._fdt_obj.get_property_by_offset(poffset) | |
3def0cf2 | 604 | prop = Prop(node, poffset, p.name, p) |
7b75b448 SG |
605 | props_dict[prop.name] = prop |
606 | ||
117f57b7 SG |
607 | poffset = self._fdt_obj.next_property_offset(poffset, |
608 | QUIET_NOTFOUND) | |
7b75b448 SG |
609 | return props_dict |
610 | ||
611 | def Invalidate(self): | |
612 | """Mark our offset cache as invalid""" | |
613 | self._cached_offsets = False | |
614 | ||
615 | def CheckCache(self): | |
616 | """Refresh the offset cache if needed""" | |
617 | if self._cached_offsets: | |
618 | return | |
619 | self.Refresh() | |
620 | self._cached_offsets = True | |
621 | ||
622 | def Refresh(self): | |
623 | """Refresh the offset cache""" | |
624 | self._root.Refresh(0) | |
625 | ||
626 | def GetStructOffset(self, offset): | |
627 | """Get the file offset of a given struct offset | |
628 | ||
629 | Args: | |
630 | offset: Offset within the 'struct' region of the device tree | |
631 | Returns: | |
632 | Position of @offset within the device tree binary | |
da5f7499 | 633 | """ |
117f57b7 | 634 | return self._fdt_obj.off_dt_struct() + offset |
7b75b448 SG |
635 | |
636 | @classmethod | |
979ab024 | 637 | def Node(self, fdt, parent, offset, name, path): |
7b75b448 SG |
638 | """Create a new node |
639 | ||
640 | This is used by Fdt.Scan() to create a new node using the correct | |
641 | class. | |
642 | ||
643 | Args: | |
644 | fdt: Fdt object | |
979ab024 | 645 | parent: Parent node, or None if this is the root node |
7b75b448 SG |
646 | offset: Offset of node |
647 | name: Node name | |
648 | path: Full path to node | |
649 | """ | |
979ab024 | 650 | node = Node(fdt, parent, offset, name, path) |
7b75b448 | 651 | return node |
99ed4a2e SG |
652 | |
653 | def FdtScan(fname): | |
dfe5f5b9 | 654 | """Returns a new Fdt object""" |
99ed4a2e SG |
655 | dtb = Fdt(fname) |
656 | dtb.Scan() | |
657 | return dtb |