1 // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2 /*
3  * pylibfdt - Flat Device Tree manipulation in Python
4  * Copyright (C) 2017 Google, Inc.
5  * Written by Simon Glass <sjg@chromium.org>
6  */
7 
8 %module libfdt
9 
10 %include <stdint.i>
11 
12 %{
13 #define SWIG_FILE_WITH_INIT
14 #include "libfdt.h"
15 
16 /*
17  * We rename this function here to avoid problems with swig, since we also have
18  * a struct called fdt_property. That struct causes swig to create a class in
19  * libfdt.py called fdt_property(), which confuses things.
20  */
fdt_property_stub(void * fdt,const char * name,const void * val,int len)21 static int fdt_property_stub(void *fdt, const char *name, const void *val,
22                              int len)
23 {
24     return fdt_property(fdt, name, val, len);
25 }
26 
27 %}
28 
29 %pythoncode %{
30 
31 import struct
32 
33 # Error codes, corresponding to FDT_ERR_... in libfdt.h
34 (NOTFOUND,
35         EXISTS,
36         NOSPACE,
37         BADOFFSET,
38         BADPATH,
39         BADPHANDLE,
40         BADSTATE,
41         TRUNCATED,
42         BADMAGIC,
43         BADVERSION,
44         BADSTRUCTURE,
45         BADLAYOUT,
46         INTERNAL,
47         BADNCELLS,
48         BADVALUE,
49         BADOVERLAY,
50         NOPHANDLES) = QUIET_ALL = range(1, 18)
51 # QUIET_ALL can be passed as the 'quiet' parameter to avoid exceptions
52 # altogether. All # functions passed this value will return an error instead
53 # of raising an exception.
54 
55 # Pass this as the 'quiet' parameter to return -ENOTFOUND on NOTFOUND errors,
56 # instead of raising an exception.
57 QUIET_NOTFOUND = (NOTFOUND,)
58 QUIET_NOSPACE = (NOSPACE,)
59 
60 
61 class FdtException(Exception):
62     """An exception caused by an error such as one of the codes above"""
63     def __init__(self, err):
64         self.err = err
65 
66     def __str__(self):
67         return 'pylibfdt error %d: %s' % (self.err, fdt_strerror(self.err))
68 
69 def strerror(fdt_err):
70     """Get the string for an error number
71 
72     Args:
73         fdt_err: Error number (-ve)
74 
75     Returns:
76         String containing the associated error
77     """
78     return fdt_strerror(fdt_err)
79 
80 def check_err(val, quiet=()):
81     """Raise an error if the return value is -ve
82 
83     This is used to check for errors returned by libfdt C functions.
84 
85     Args:
86         val: Return value from a libfdt function
87         quiet: Errors to ignore (empty to raise on all errors)
88 
89     Returns:
90         val if val >= 0
91 
92     Raises
93         FdtException if val < 0
94     """
95     if isinstance(val, int) and val < 0:
96         if -val not in quiet:
97             raise FdtException(val)
98     return val
99 
100 def check_err_null(val, quiet=()):
101     """Raise an error if the return value is NULL
102 
103     This is used to check for a NULL return value from certain libfdt C
104     functions
105 
106     Args:
107         val: Return value from a libfdt function
108         quiet: Errors to ignore (empty to raise on all errors)
109 
110     Returns:
111         val if val is a list, None if not
112 
113     Raises
114         FdtException if val indicates an error was reported and the error
115         is not in @quiet.
116     """
117     # Normally a list is returned which contains the data and its length.
118     # If we get just an integer error code, it means the function failed.
119     if not isinstance(val, list):
120         if -val not in quiet:
121             raise FdtException(val)
122     return val
123 
124 class FdtRo(object):
125     """Class for a read-only device-tree
126 
127     This is a base class used by FdtRw (read-write access) and FdtSw
128     (sequential-write access). It implements read-only access to the
129     device tree.
130 
131     Here are the three classes and when you should use them:
132 
133         FdtRo - read-only access to an existing FDT
134         FdtRw - read-write access to an existing FDT (most common case)
135         FdtSw - for creating a new FDT, as well as allowing read-only access
136     """
137     def __init__(self, data):
138         self._fdt = bytearray(data)
139         check_err(fdt_check_header(self._fdt));
140 
141     def as_bytearray(self):
142         """Get the device tree contents as a bytearray
143 
144         This can be passed directly to libfdt functions that access a
145         const void * for the device tree.
146 
147         Returns:
148             bytearray containing the device tree
149         """
150         return bytearray(self._fdt)
151 
152     def next_node(self, nodeoffset, depth, quiet=()):
153         """Find the next subnode
154 
155         Args:
156             nodeoffset: Node offset of previous node
157             depth: The depth of the node at nodeoffset. This is used to
158                 calculate the depth of the returned node
159             quiet: Errors to ignore (empty to raise on all errors)
160 
161         Returns:
162             Typle:
163                 Offset of the next node, if any, else a -ve error
164                 Depth of the returned node, if any, else undefined
165 
166         Raises:
167             FdtException if no more nodes found or other error occurs
168         """
169         return check_err(fdt_next_node(self._fdt, nodeoffset, depth), quiet)
170 
171     def first_subnode(self, nodeoffset, quiet=()):
172         """Find the first subnode of a parent node
173 
174         Args:
175             nodeoffset: Node offset of parent node
176             quiet: Errors to ignore (empty to raise on all errors)
177 
178         Returns:
179             The offset of the first subnode, if any
180 
181         Raises:
182             FdtException if no subnodes found or other error occurs
183         """
184         return check_err(fdt_first_subnode(self._fdt, nodeoffset), quiet)
185 
186     def next_subnode(self, nodeoffset, quiet=()):
187         """Find the next subnode
188 
189         Args:
190             nodeoffset: Node offset of previous subnode
191             quiet: Errors to ignore (empty to raise on all errors)
192 
193         Returns:
194             The offset of the next subnode, if any
195 
196         Raises:
197             FdtException if no more subnodes found or other error occurs
198         """
199         return check_err(fdt_next_subnode(self._fdt, nodeoffset), quiet)
200 
201     def magic(self):
202         """Return the magic word from the header
203 
204         Returns:
205             Magic word
206         """
207         return fdt_magic(self._fdt)
208 
209     def totalsize(self):
210         """Return the total size of the device tree
211 
212         Returns:
213             Total tree size in bytes
214         """
215         return fdt_totalsize(self._fdt)
216 
217     def off_dt_struct(self):
218         """Return the start of the device-tree struct area
219 
220         Returns:
221             Start offset of struct area
222         """
223         return fdt_off_dt_struct(self._fdt)
224 
225     def off_dt_strings(self):
226         """Return the start of the device-tree string area
227 
228         Returns:
229             Start offset of string area
230         """
231         return fdt_off_dt_strings(self._fdt)
232 
233     def off_mem_rsvmap(self):
234         """Return the start of the memory reserve map
235 
236         Returns:
237             Start offset of memory reserve map
238         """
239         return fdt_off_mem_rsvmap(self._fdt)
240 
241     def version(self):
242         """Return the version of the device tree
243 
244         Returns:
245             Version number of the device tree
246         """
247         return fdt_version(self._fdt)
248 
249     def last_comp_version(self):
250         """Return the last compatible version of the device tree
251 
252         Returns:
253             Last compatible version number of the device tree
254         """
255         return fdt_last_comp_version(self._fdt)
256 
257     def boot_cpuid_phys(self):
258         """Return the physical boot CPU ID
259 
260         Returns:
261             Physical boot CPU ID
262         """
263         return fdt_boot_cpuid_phys(self._fdt)
264 
265     def size_dt_strings(self):
266         """Return the start of the device-tree string area
267 
268         Returns:
269             Start offset of string area
270         """
271         return fdt_size_dt_strings(self._fdt)
272 
273     def size_dt_struct(self):
274         """Return the start of the device-tree struct area
275 
276         Returns:
277             Start offset of struct area
278         """
279         return fdt_size_dt_struct(self._fdt)
280 
281     def num_mem_rsv(self, quiet=()):
282         """Return the number of memory reserve-map records
283 
284         Returns:
285             Number of memory reserve-map records
286         """
287         return check_err(fdt_num_mem_rsv(self._fdt), quiet)
288 
289     def get_mem_rsv(self, index, quiet=()):
290         """Return the indexed memory reserve-map record
291 
292         Args:
293             index: Record to return (0=first)
294 
295         Returns:
296             Number of memory reserve-map records
297         """
298         return check_err(fdt_get_mem_rsv(self._fdt, index), quiet)
299 
300     def subnode_offset(self, parentoffset, name, quiet=()):
301         """Get the offset of a named subnode
302 
303         Args:
304             parentoffset: Offset of the parent node to check
305             name: Name of the required subnode, e.g. 'subnode@1'
306             quiet: Errors to ignore (empty to raise on all errors)
307 
308         Returns:
309             The node offset of the found node, if any
310 
311         Raises
312             FdtException if there is no node with that name, or other error
313         """
314         return check_err(fdt_subnode_offset(self._fdt, parentoffset, name),
315                          quiet)
316 
317     def path_offset(self, path, quiet=()):
318         """Get the offset for a given path
319 
320         Args:
321             path: Path to the required node, e.g. '/node@3/subnode@1'
322             quiet: Errors to ignore (empty to raise on all errors)
323 
324         Returns:
325             Node offset
326 
327         Raises
328             FdtException if the path is not valid or not found
329         """
330         return check_err(fdt_path_offset(self._fdt, path), quiet)
331 
332     def get_name(self, nodeoffset):
333         """Get the name of a node
334 
335         Args:
336             nodeoffset: Offset of node to check
337 
338         Returns:
339             Node name
340 
341         Raises:
342             FdtException on error (e.g. nodeoffset is invalid)
343         """
344         return check_err_null(fdt_get_name(self._fdt, nodeoffset))[0]
345 
346     def first_property_offset(self, nodeoffset, quiet=()):
347         """Get the offset of the first property in a node offset
348 
349         Args:
350             nodeoffset: Offset to the node to check
351             quiet: Errors to ignore (empty to raise on all errors)
352 
353         Returns:
354             Offset of the first property
355 
356         Raises
357             FdtException if the associated node has no properties, or some
358                 other error occurred
359         """
360         return check_err(fdt_first_property_offset(self._fdt, nodeoffset),
361                          quiet)
362 
363     def next_property_offset(self, prop_offset, quiet=()):
364         """Get the next property in a node
365 
366         Args:
367             prop_offset: Offset of the previous property
368             quiet: Errors to ignore (empty to raise on all errors)
369 
370         Returns:
371             Offset of the next property
372 
373         Raises:
374             FdtException if the associated node has no more properties, or
375                 some other error occurred
376         """
377         return check_err(fdt_next_property_offset(self._fdt, prop_offset),
378                          quiet)
379 
380     def get_property_by_offset(self, prop_offset, quiet=()):
381         """Obtains a property that can be examined
382 
383         Args:
384             prop_offset: Offset of property (e.g. from first_property_offset())
385             quiet: Errors to ignore (empty to raise on all errors)
386 
387         Returns:
388             Property object, or None if not found
389 
390         Raises:
391             FdtException on error (e.g. invalid prop_offset or device
392             tree format)
393         """
394         pdata = check_err_null(
395                 fdt_get_property_by_offset(self._fdt, prop_offset), quiet)
396         if isinstance(pdata, (int)):
397             return pdata
398         return Property(pdata[0], pdata[1])
399 
400     def getprop(self, nodeoffset, prop_name, quiet=()):
401         """Get a property from a node
402 
403         Args:
404             nodeoffset: Node offset containing property to get
405             prop_name: Name of property to get
406             quiet: Errors to ignore (empty to raise on all errors)
407 
408         Returns:
409             Value of property as a Property object (which can be used as a
410                bytearray/string), or -ve error number. On failure, returns an
411                integer error
412 
413         Raises:
414             FdtError if any error occurs (e.g. the property is not found)
415         """
416         pdata = check_err_null(fdt_getprop(self._fdt, nodeoffset, prop_name),
417                                quiet)
418         if isinstance(pdata, (int)):
419             return pdata
420         return Property(prop_name, bytearray(pdata[0]))
421 
422     def get_phandle(self, nodeoffset):
423         """Get the phandle of a node
424 
425         Args:
426             nodeoffset: Node offset to check
427 
428         Returns:
429             phandle of node, or 0 if the node has no phandle or another error
430             occurs
431         """
432         return fdt_get_phandle(self._fdt, nodeoffset)
433 
434     def get_alias(self, name):
435         """Get the full path referenced by a given alias
436 
437         Args:
438             name: name of the alias to lookup
439 
440         Returns:
441             Full path to the node for the alias named 'name', if it exists
442             None, if the given alias or the /aliases node does not exist
443         """
444         return fdt_get_alias(self._fdt, name)
445 
446     def parent_offset(self, nodeoffset, quiet=()):
447         """Get the offset of a node's parent
448 
449         Args:
450             nodeoffset: Node offset to check
451             quiet: Errors to ignore (empty to raise on all errors)
452 
453         Returns:
454             The offset of the parent node, if any
455 
456         Raises:
457             FdtException if no parent found or other error occurs
458         """
459         return check_err(fdt_parent_offset(self._fdt, nodeoffset), quiet)
460 
461     def node_offset_by_phandle(self, phandle, quiet=()):
462         """Get the offset of a node with the given phandle
463 
464         Args:
465             phandle: Phandle to search for
466             quiet: Errors to ignore (empty to raise on all errors)
467 
468         Returns:
469             The offset of node with that phandle, if any
470 
471         Raises:
472             FdtException if no node found or other error occurs
473         """
474         return check_err(fdt_node_offset_by_phandle(self._fdt, phandle), quiet)
475 
476 
477 class Fdt(FdtRo):
478     """Device tree class, supporting all operations
479 
480     The Fdt object is created is created from a device tree binary file,
481     e.g. with something like:
482 
483        fdt = Fdt(open("filename.dtb").read())
484 
485     Operations can then be performed using the methods in this class. Each
486     method xxx(args...) corresponds to a libfdt function fdt_xxx(fdt, args...).
487 
488     All methods raise an FdtException if an error occurs. To avoid this
489     behaviour a 'quiet' parameter is provided for some functions. This
490     defaults to empty, but you can pass a list of errors that you expect.
491     If one of these errors occurs, the function will return an error number
492     (e.g. -NOTFOUND).
493     """
494     def __init__(self, data):
495         FdtRo.__init__(self, data)
496 
497     @staticmethod
498     def create_empty_tree(size, quiet=()):
499         """Create an empty device tree ready for use
500 
501         Args:
502             size: Size of device tree in bytes
503 
504         Returns:
505             Fdt object containing the device tree
506         """
507         data = bytearray(size)
508         err = check_err(fdt_create_empty_tree(data, size), quiet)
509         if err:
510             return err
511         return Fdt(data)
512 
513     def resize(self, size, quiet=()):
514         """Move the device tree into a larger or smaller space
515 
516         This creates a new device tree of size @size and moves the existing
517         device tree contents over to that. It can be used to create more space
518         in a device tree. Note that the Fdt object remains the same, but it
519         now has a new bytearray holding the contents.
520 
521         Args:
522             size: Required new size of device tree in bytes
523         """
524         fdt = bytearray(size)
525         err = check_err(fdt_open_into(self._fdt, fdt, size), quiet)
526         if err:
527             return err
528         self._fdt = fdt
529 
530     def pack(self, quiet=()):
531         """Pack the device tree to remove unused space
532 
533         This adjusts the tree in place.
534 
535         Args:
536             quiet: Errors to ignore (empty to raise on all errors)
537 
538         Returns:
539             Error code, or 0 if OK
540 
541         Raises:
542             FdtException if any error occurs
543         """
544         err = check_err(fdt_pack(self._fdt), quiet)
545         if err:
546             return err
547         del self._fdt[self.totalsize():]
548         return err
549 
550     def set_name(self, nodeoffset, name, quiet=()):
551         """Set the name of a node
552 
553         Args:
554             nodeoffset: Node offset of node to update
555             name: New node name (string without \0)
556 
557         Returns:
558             Error code, or 0 if OK
559 
560         Raises:
561             FdtException if no parent found or other error occurs
562         """
563         if chr(0) in name:
564             raise ValueError('Property contains embedded nul characters')
565         return check_err(fdt_set_name(self._fdt, nodeoffset, name), quiet)
566 
567     def setprop(self, nodeoffset, prop_name, val, quiet=()):
568         """Set the value of a property
569 
570         Args:
571             nodeoffset: Node offset containing the property to create/update
572             prop_name: Name of property
573             val: Value to write (string or bytearray)
574             quiet: Errors to ignore (empty to raise on all errors)
575 
576         Returns:
577             Error code, or 0 if OK
578 
579         Raises:
580             FdtException if no parent found or other error occurs
581         """
582         return check_err(fdt_setprop(self._fdt, nodeoffset, prop_name, val,
583                                      len(val)), quiet)
584 
585     def setprop_u32(self, nodeoffset, prop_name, val, quiet=()):
586         """Set the value of a property
587 
588         Args:
589             nodeoffset: Node offset containing the property to create/update
590             prop_name: Name of property
591             val: Value to write (integer)
592             quiet: Errors to ignore (empty to raise on all errors)
593 
594         Returns:
595             Error code, or 0 if OK
596 
597         Raises:
598             FdtException if no parent found or other error occurs
599         """
600         return check_err(fdt_setprop_u32(self._fdt, nodeoffset, prop_name, val),
601                          quiet)
602 
603     def setprop_u64(self, nodeoffset, prop_name, val, quiet=()):
604         """Set the value of a property
605 
606         Args:
607             nodeoffset: Node offset containing the property to create/update
608             prop_name: Name of property
609             val: Value to write (integer)
610             quiet: Errors to ignore (empty to raise on all errors)
611 
612         Returns:
613             Error code, or 0 if OK
614 
615         Raises:
616             FdtException if no parent found or other error occurs
617         """
618         return check_err(fdt_setprop_u64(self._fdt, nodeoffset, prop_name, val),
619                          quiet)
620 
621     def setprop_str(self, nodeoffset, prop_name, val, quiet=()):
622         """Set the string value of a property
623 
624         The property is set to the string, with a nul terminator added
625 
626         Args:
627             nodeoffset: Node offset containing the property to create/update
628             prop_name: Name of property
629             val: Value to write (string without nul terminator). Unicode is
630                 supposed by encoding to UTF-8
631             quiet: Errors to ignore (empty to raise on all errors)
632 
633         Returns:
634             Error code, or 0 if OK
635 
636         Raises:
637             FdtException if no parent found or other error occurs
638         """
639         val = val.encode('utf-8') + b'\0'
640         return check_err(fdt_setprop(self._fdt, nodeoffset, prop_name,
641                                      val, len(val)), quiet)
642 
643     def delprop(self, nodeoffset, prop_name, quiet=()):
644         """Delete a property from a node
645 
646         Args:
647             nodeoffset: Node offset containing property to delete
648             prop_name: Name of property to delete
649             quiet: Errors to ignore (empty to raise on all errors)
650 
651         Returns:
652             Error code, or 0 if OK
653 
654         Raises:
655             FdtError if the property does not exist, or another error occurs
656         """
657         return check_err(fdt_delprop(self._fdt, nodeoffset, prop_name), quiet)
658 
659     def add_subnode(self, parentoffset, name, quiet=()):
660         """Add a new subnode to a node
661 
662         Args:
663             parentoffset: Parent offset to add the subnode to
664             name: Name of node to add
665 
666         Returns:
667             offset of the node created, or negative error code on failure
668 
669         Raises:
670             FdtError if there is not enough space, or another error occurs
671         """
672         return check_err(fdt_add_subnode(self._fdt, parentoffset, name), quiet)
673 
674     def del_node(self, nodeoffset, quiet=()):
675         """Delete a node
676 
677         Args:
678             nodeoffset: Offset of node to delete
679 
680         Returns:
681             Error code, or 0 if OK
682 
683         Raises:
684             FdtError if an error occurs
685         """
686         return check_err(fdt_del_node(self._fdt, nodeoffset), quiet)
687 
688 
689 class Property(bytearray):
690     """Holds a device tree property name and value.
691 
692     This holds a copy of a property taken from the device tree. It does not
693     reference the device tree, so if anything changes in the device tree,
694     a Property object will remain valid.
695 
696     Properties:
697         name: Property name
698         value: Property value as a bytearray
699     """
700     def __init__(self, name, value):
701         bytearray.__init__(self, value)
702         self.name = name
703 
704     def as_cell(self, fmt):
705         return struct.unpack('>' + fmt, self)[0]
706 
707     def as_uint32(self):
708         return self.as_cell('L')
709 
710     def as_int32(self):
711         return self.as_cell('l')
712 
713     def as_uint64(self):
714         return self.as_cell('Q')
715 
716     def as_int64(self):
717         return self.as_cell('q')
718 
719     def as_str(self):
720         """Unicode is supported by decoding from UTF-8"""
721         if self[-1] != 0:
722             raise ValueError('Property lacks nul termination')
723         if 0 in self[:-1]:
724             raise ValueError('Property contains embedded nul characters')
725         return self[:-1].decode('utf-8')
726 
727 
728 class FdtSw(FdtRo):
729     """Software interface to create a device tree from scratch
730 
731     The methods in this class work by adding to an existing 'partial' device
732     tree buffer of a fixed size created by instantiating this class. When the
733     tree is complete, call as_fdt() to obtain a device tree ready to be used.
734 
735     Similarly with nodes, a new node is started with begin_node() and finished
736     with end_node().
737 
738     The context manager functions can be used to make this a bit easier:
739 
740     # First create the device tree with a node and property:
741     sw = FdtSw()
742     sw.finish_reservemap()
743     with sw.add_node(''):
744         with sw.add_node('node'):
745             sw.property_u32('reg', 2)
746     fdt = sw.as_fdt()
747 
748     # Now we can use it as a real device tree
749     fdt.setprop_u32(0, 'reg', 3)
750 
751     The size hint provides a starting size for the space to be used by the
752     device tree. This will be increased automatically as needed as new items
753     are added to the tree.
754     """
755     INC_SIZE = 1024  # Expand size by this much when out of space
756 
757     def __init__(self, size_hint=None):
758         """Create a new FdtSw object
759 
760         Args:
761             size_hint: A hint as to the initial size to use
762 
763         Raises:
764             ValueError if size_hint is negative
765 
766         Returns:
767             FdtSw object on success, else integer error code (if not raising)
768         """
769         if not size_hint:
770             size_hint = self.INC_SIZE
771         fdtsw = bytearray(size_hint)
772         err = check_err(fdt_create(fdtsw, size_hint))
773         if err:
774             return err
775         self._fdt = fdtsw
776 
777     def as_fdt(self):
778         """Convert a FdtSw into an Fdt so it can be accessed as normal
779 
780         Creates a new Fdt object from the work-in-progress device tree. This
781         does not call fdt_finish() on the current object, so it is possible to
782         add more nodes/properties and call as_fdt() again to get an updated
783         tree.
784 
785         Returns:
786             Fdt object allowing access to the newly created device tree
787         """
788         fdtsw = bytearray(self._fdt)
789         check_err(fdt_finish(fdtsw))
790         return Fdt(fdtsw)
791 
792     def check_space(self, val):
793         """Check if we need to add more space to the FDT
794 
795         This should be called with the error code from an operation. If this is
796         -NOSPACE then the FDT will be expanded to have more space, and True will
797         be returned, indicating that the operation needs to be tried again.
798 
799         Args:
800             val: Return value from the operation that was attempted
801 
802         Returns:
803             True if the operation must be retried, else False
804         """
805         if check_err(val, QUIET_NOSPACE) < 0:
806             self.resize(len(self._fdt) + self.INC_SIZE)
807             return True
808         return False
809 
810     def resize(self, size):
811         """Resize the buffer to accommodate a larger tree
812 
813         Args:
814             size: New size of tree
815 
816         Raises:
817             FdtException on any error
818         """
819         fdt = bytearray(size)
820         err = check_err(fdt_resize(self._fdt, fdt, size))
821         self._fdt = fdt
822 
823     def add_reservemap_entry(self, addr, size):
824         """Add a new memory reserve map entry
825 
826         Once finished adding, you must call finish_reservemap().
827 
828         Args:
829             addr: 64-bit start address
830             size: 64-bit size
831 
832         Raises:
833             FdtException on any error
834         """
835         while self.check_space(fdt_add_reservemap_entry(self._fdt, addr,
836                                                         size)):
837             pass
838 
839     def finish_reservemap(self):
840         """Indicate that there are no more reserve map entries to add
841 
842         Raises:
843             FdtException on any error
844         """
845         while self.check_space(fdt_finish_reservemap(self._fdt)):
846             pass
847 
848     def begin_node(self, name):
849         """Begin a new node
850 
851         Use this before adding properties to the node. Then call end_node() to
852         finish it. You can also use the context manager as shown in the FdtSw
853         class comment.
854 
855         Args:
856             name: Name of node to begin
857 
858         Raises:
859             FdtException on any error
860         """
861         while self.check_space(fdt_begin_node(self._fdt, name)):
862             pass
863 
864     def property_string(self, name, string):
865         """Add a property with a string value
866 
867         The string will be nul-terminated when written to the device tree
868 
869         Args:
870             name: Name of property to add
871             string: String value of property
872 
873         Raises:
874             FdtException on any error
875         """
876         while self.check_space(fdt_property_string(self._fdt, name, string)):
877             pass
878 
879     def property_u32(self, name, val):
880         """Add a property with a 32-bit value
881 
882         Write a single-cell value to the device tree
883 
884         Args:
885             name: Name of property to add
886             val: Value of property
887 
888         Raises:
889             FdtException on any error
890         """
891         while self.check_space(fdt_property_u32(self._fdt, name, val)):
892             pass
893 
894     def property_u64(self, name, val):
895         """Add a property with a 64-bit value
896 
897         Write a double-cell value to the device tree in big-endian format
898 
899         Args:
900             name: Name of property to add
901             val: Value of property
902 
903         Raises:
904             FdtException on any error
905         """
906         while self.check_space(fdt_property_u64(self._fdt, name, val)):
907             pass
908 
909     def property_cell(self, name, val):
910         """Add a property with a single-cell value
911 
912         Write a single-cell value to the device tree
913 
914         Args:
915             name: Name of property to add
916             val: Value of property
917             quiet: Errors to ignore (empty to raise on all errors)
918 
919         Raises:
920             FdtException on any error
921         """
922         while self.check_space(fdt_property_cell(self._fdt, name, val)):
923             pass
924 
925     def property(self, name, val):
926         """Add a property
927 
928         Write a new property with the given value to the device tree. The value
929         is taken as is and is not nul-terminated
930 
931         Args:
932             name: Name of property to add
933             val: Value of property (bytes)
934             quiet: Errors to ignore (empty to raise on all errors)
935 
936         Raises:
937             FdtException on any error
938         """
939         while self.check_space(fdt_property_stub(self._fdt, name, val,
940                                                  len(val))):
941             pass
942 
943     def end_node(self):
944         """End a node
945 
946         Use this after adding properties to a node to close it off. You can also
947         use the context manager as shown in the FdtSw class comment.
948 
949         Args:
950             quiet: Errors to ignore (empty to raise on all errors)
951 
952         Raises:
953             FdtException on any error
954         """
955         while self.check_space(fdt_end_node(self._fdt)):
956             pass
957 
958     def add_node(self, name):
959         """Create a new context for adding a node
960 
961         When used in a 'with' clause this starts a new node and finishes it
962         afterward.
963 
964         Args:
965             name: Name of node to add
966         """
967         return NodeAdder(self, name)
968 
969 
970 class NodeAdder():
971     """Class to provide a node context
972 
973     This allows you to add nodes in a more natural way:
974 
975         with fdtsw.add_node('name'):
976             fdtsw.property_string('test', 'value')
977 
978     The node is automatically completed with a call to end_node() when the
979     context exits.
980     """
981     def __init__(self, fdtsw, name):
982         self._fdt = fdtsw
983         self._name = name
984 
985     def __enter__(self):
986         self._fdt.begin_node(self._name)
987 
988     def __exit__(self, type, value, traceback):
989         self._fdt.end_node()
990 %}
991 
992 %rename(fdt_property) fdt_property_func;
993 
994 /*
995  * fdt32_t is a big-endian 32-bit value defined to uint32_t in libfdt_env.h
996  * so use the same type here.
997  */
998 typedef uint32_t fdt32_t;
999 
1000 %include "fdt.h"
1001 
1002 %include "typemaps.i"
1003 
1004 /* Most functions don't change the device tree, so use a const void * */
1005 %typemap(in) (const void *)(const void *fdt) {
1006 	if (!PyByteArray_Check($input)) {
1007 		SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname"
1008 			"', argument " "$argnum"" of type '" "$type""'");
1009 	}
1010 	$1 = (void *)PyByteArray_AsString($input);
1011         fdt = $1;
1012         fdt = fdt; /* avoid unused variable warning */
1013 }
1014 
1015 /* Some functions do change the device tree, so use void * */
1016 %typemap(in) (void *)(const void *fdt) {
1017 	if (!PyByteArray_Check($input)) {
1018 		SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname"
1019 			"', argument " "$argnum"" of type '" "$type""'");
1020 	}
1021 	$1 = PyByteArray_AsString($input);
1022         fdt = $1;
1023         fdt = fdt; /* avoid unused variable warning */
1024 }
1025 
1026 /* typemap used for fdt_get_property_by_offset() */
1027 %typemap(out) (struct fdt_property *) {
1028 	PyObject *buff;
1029 
1030 	if ($1) {
1031 		resultobj = PyString_FromString(
1032 			fdt_string(fdt1, fdt32_to_cpu($1->nameoff)));
1033 		buff = PyByteArray_FromStringAndSize(
1034 			(const char *)($1 + 1), fdt32_to_cpu($1->len));
1035 		resultobj = SWIG_Python_AppendOutput(resultobj, buff);
1036 	}
1037 }
1038 
1039 %apply int *OUTPUT { int *lenp };
1040 
1041 /* typemap used for fdt_getprop() */
1042 %typemap(out) (const void *) {
1043 	if (!$1)
1044 		$result = Py_None;
1045 	else
1046         %#if PY_VERSION_HEX >= 0x03000000
1047             $result = Py_BuildValue("y#", $1, *arg4);
1048         %#else
1049             $result = Py_BuildValue("s#", $1, *arg4);
1050         %#endif
1051 }
1052 
1053 /* typemap used for fdt_setprop() */
1054 %typemap(in) (const void *val) {
1055     %#if PY_VERSION_HEX >= 0x03000000
1056         if (!PyBytes_Check($input)) {
1057             SWIG_exception_fail(SWIG_TypeError, "bytes expected in method '" "$symname"
1058                 "', argument " "$argnum"" of type '" "$type""'");
1059         }
1060         $1 = PyBytes_AsString($input);
1061     %#else
1062         $1 = PyString_AsString($input);   /* char *str */
1063     %#endif
1064 }
1065 
1066 /* typemaps used for fdt_next_node() */
1067 %typemap(in, numinputs=1) int *depth (int depth) {
1068    depth = (int) PyInt_AsLong($input);
1069    $1 = &depth;
1070 }
1071 
1072 %typemap(argout) int *depth {
1073         PyObject *val = Py_BuildValue("i", *arg$argnum);
1074         resultobj = SWIG_Python_AppendOutput(resultobj, val);
1075 }
1076 
1077 %apply int *depth { int *depth };
1078 
1079 /* typemaps for fdt_get_mem_rsv */
1080 %typemap(in, numinputs=0) uint64_t * (uint64_t temp) {
1081    $1 = &temp;
1082 }
1083 
1084 %typemap(argout) uint64_t * {
1085         PyObject *val = PyLong_FromUnsignedLongLong(*arg$argnum);
1086         if (!result) {
1087            if (PyTuple_GET_SIZE(resultobj) == 0)
1088               resultobj = val;
1089            else
1090               resultobj = SWIG_Python_AppendOutput(resultobj, val);
1091         }
1092 }
1093 
1094 /* We have both struct fdt_property and a function fdt_property() */
1095 %warnfilter(302) fdt_property;
1096 
1097 /* These are macros in the header so have to be redefined here */
1098 uint32_t fdt_magic(const void *fdt);
1099 uint32_t fdt_totalsize(const void *fdt);
1100 uint32_t fdt_off_dt_struct(const void *fdt);
1101 uint32_t fdt_off_dt_strings(const void *fdt);
1102 uint32_t fdt_off_mem_rsvmap(const void *fdt);
1103 uint32_t fdt_version(const void *fdt);
1104 uint32_t fdt_last_comp_version(const void *fdt);
1105 uint32_t fdt_boot_cpuid_phys(const void *fdt);
1106 uint32_t fdt_size_dt_strings(const void *fdt);
1107 uint32_t fdt_size_dt_struct(const void *fdt);
1108 
1109 int fdt_property_string(void *fdt, const char *name, const char *val);
1110 int fdt_property_cell(void *fdt, const char *name, uint32_t val);
1111 
1112 /*
1113  * This function has a stub since the name fdt_property is used for both a
1114   * function and a struct, which confuses SWIG.
1115  */
1116 int fdt_property_stub(void *fdt, const char *name, const void *val, int len);
1117 
1118 %include <libfdt.h>
1119