1/*
2 * pylibfdt - Flat Device Tree manipulation in Python
3 * Copyright (C) 2017 Google, Inc.
4 * Written by Simon Glass <sjg@chromium.org>
5 *
6 * SPDX-License-Identifier:	GPL-2.0+ BSD-2-Clause
7 */
8
9%module libfdt
10
11%include <stdint.i>
12
13%{
14#define SWIG_FILE_WITH_INIT
15#include "libfdt.h"
16%}
17
18%pythoncode %{
19
20import struct
21
22# Error codes, corresponding to FDT_ERR_... in libfdt.h
23(NOTFOUND,
24        EXISTS,
25        NOSPACE,
26        BADOFFSET,
27        BADPATH,
28        BADPHANDLE,
29        BADSTATE,
30        TRUNCATED,
31        BADMAGIC,
32        BADVERSION,
33        BADSTRUCTURE,
34        BADLAYOUT,
35        INTERNAL,
36        BADNCELLS,
37        BADVALUE,
38        BADOVERLAY,
39        NOPHANDLES) = QUIET_ALL = range(1, 18)
40# QUIET_ALL can be passed as the 'quiet' parameter to avoid exceptions
41# altogether. All # functions passed this value will return an error instead
42# of raising an exception.
43
44# Pass this as the 'quiet' parameter to return -ENOTFOUND on NOTFOUND errors,
45# instead of raising an exception.
46QUIET_NOTFOUND = (NOTFOUND,)
47
48
49class FdtException(Exception):
50    """An exception caused by an error such as one of the codes above"""
51    def __init__(self, err):
52        self.err = err
53
54    def __str__(self):
55        return 'pylibfdt error %d: %s' % (self.err, fdt_strerror(self.err))
56
57def strerror(fdt_err):
58    """Get the string for an error number
59
60    Args:
61        fdt_err: Error number (-ve)
62
63    Returns:
64        String containing the associated error
65    """
66    return fdt_strerror(fdt_err)
67
68def check_err(val, quiet=()):
69    """Raise an error if the return value is -ve
70
71    This is used to check for errors returned by libfdt C functions.
72
73    Args:
74        val: Return value from a libfdt function
75        quiet: Errors to ignore (empty to raise on all errors)
76
77    Returns:
78        val if val >= 0
79
80    Raises
81        FdtException if val < 0
82    """
83    if val < 0:
84        if -val not in quiet:
85            raise FdtException(val)
86    return val
87
88def check_err_null(val, quiet=()):
89    """Raise an error if the return value is NULL
90
91    This is used to check for a NULL return value from certain libfdt C
92    functions
93
94    Args:
95        val: Return value from a libfdt function
96        quiet: Errors to ignore (empty to raise on all errors)
97
98    Returns:
99        val if val is a list, None if not
100
101    Raises
102        FdtException if val indicates an error was reported and the error
103        is not in @quiet.
104    """
105    # Normally a list is returned which contains the data and its length.
106    # If we get just an integer error code, it means the function failed.
107    if not isinstance(val, list):
108        if -val not in quiet:
109            raise FdtException(val)
110    return val
111
112class Fdt:
113    """Device tree class, supporting all operations
114
115    The Fdt object is created is created from a device tree binary file,
116    e.g. with something like:
117
118       fdt = Fdt(open("filename.dtb").read())
119
120    Operations can then be performed using the methods in this class. Each
121    method xxx(args...) corresponds to a libfdt function fdt_xxx(fdt, args...).
122
123    All methods raise an FdtException if an error occurs. To avoid this
124    behaviour a 'quiet' parameter is provided for some functions. This
125    defaults to empty, but you can pass a list of errors that you expect.
126    If one of these errors occurs, the function will return an error number
127    (e.g. -NOTFOUND).
128    """
129    def __init__(self, data):
130        self._fdt = bytearray(data)
131        check_err(fdt_check_header(self._fdt));
132
133    def subnode_offset(self, parentoffset, name, quiet=()):
134        """Get the offset of a named subnode
135
136        Args:
137            parentoffset: Offset of the parent node to check
138            name: Name of the required subnode, e.g. 'subnode@1'
139            quiet: Errors to ignore (empty to raise on all errors)
140
141        Returns:
142            The node offset of the found node, if any
143
144        Raises
145            FdtException if there is no node with that name, or other error
146        """
147        return check_err(fdt_subnode_offset(self._fdt, parentoffset, name),
148                         quiet)
149
150    def path_offset(self, path, quiet=()):
151        """Get the offset for a given path
152
153        Args:
154            path: Path to the required node, e.g. '/node@3/subnode@1'
155            quiet: Errors to ignore (empty to raise on all errors)
156
157        Returns:
158            Node offset
159
160        Raises
161            FdtException if the path is not valid or not found
162        """
163        return check_err(fdt_path_offset(self._fdt, path), quiet)
164
165    def first_property_offset(self, nodeoffset, quiet=()):
166        """Get the offset of the first property in a node offset
167
168        Args:
169            nodeoffset: Offset to the node to check
170            quiet: Errors to ignore (empty to raise on all errors)
171
172        Returns:
173            Offset of the first property
174
175        Raises
176            FdtException if the associated node has no properties, or some
177                other error occurred
178        """
179        return check_err(fdt_first_property_offset(self._fdt, nodeoffset),
180                         quiet)
181
182    def next_property_offset(self, prop_offset, quiet=()):
183        """Get the next property in a node
184
185        Args:
186            prop_offset: Offset of the previous property
187            quiet: Errors to ignore (empty to raise on all errors)
188
189        Returns:
190            Offset of the next property
191
192        Raises:
193            FdtException if the associated node has no more properties, or
194                some other error occurred
195        """
196        return check_err(fdt_next_property_offset(self._fdt, prop_offset),
197                         quiet)
198
199    def get_name(self, nodeoffset):
200        """Get the name of a node
201
202        Args:
203            nodeoffset: Offset of node to check
204
205        Returns:
206            Node name
207
208        Raises:
209            FdtException on error (e.g. nodeoffset is invalid)
210        """
211        return check_err_null(fdt_get_name(self._fdt, nodeoffset))[0]
212
213    def get_property_by_offset(self, prop_offset, quiet=()):
214        """Obtains a property that can be examined
215
216        Args:
217            prop_offset: Offset of property (e.g. from first_property_offset())
218            quiet: Errors to ignore (empty to raise on all errors)
219
220        Returns:
221            Property object, or None if not found
222
223        Raises:
224            FdtException on error (e.g. invalid prop_offset or device
225            tree format)
226        """
227        pdata = check_err_null(
228                fdt_get_property_by_offset(self._fdt, prop_offset), quiet)
229        if isinstance(pdata, (int)):
230            return pdata
231        return Property(pdata[0], pdata[1])
232
233    def first_subnode(self, nodeoffset, quiet=()):
234        """Find the first subnode of a parent node
235
236        Args:
237            nodeoffset: Node offset of parent node
238            quiet: Errors to ignore (empty to raise on all errors)
239
240        Returns:
241            The offset of the first subnode, if any
242
243        Raises:
244            FdtException if no subnode found or other error occurs
245        """
246        return check_err(fdt_first_subnode(self._fdt, nodeoffset), quiet)
247
248    def next_subnode(self, nodeoffset, quiet=()):
249        """Find the next subnode
250
251        Args:
252            nodeoffset: Node offset of previous subnode
253            quiet: Errors to ignore (empty to raise on all errors)
254
255        Returns:
256            The offset of the next subnode, if any
257
258        Raises:
259            FdtException if no more subnode found or other error occurs
260        """
261        return check_err(fdt_next_subnode(self._fdt, nodeoffset), quiet)
262
263    def totalsize(self):
264        """Return the total size of the device tree
265
266        Returns:
267            Total tree size in bytes
268        """
269        return check_err(fdt_totalsize(self._fdt))
270
271    def off_dt_struct(self):
272        """Return the start of the device tree struct area
273
274        Returns:
275            Start offset of struct area
276        """
277        return check_err(fdt_off_dt_struct(self._fdt))
278
279    def pack(self, quiet=()):
280        """Pack the device tree to remove unused space
281
282        This adjusts the tree in place.
283
284        Args:
285            quiet: Errors to ignore (empty to raise on all errors)
286
287        Raises:
288            FdtException if any error occurs
289        """
290        return check_err(fdt_pack(self._fdt), quiet)
291
292    def delprop(self, nodeoffset, prop_name):
293        """Delete a property from a node
294
295        Args:
296            nodeoffset: Node offset containing property to delete
297            prop_name: Name of property to delete
298
299        Raises:
300            FdtError if the property does not exist, or another error occurs
301        """
302        return check_err(fdt_delprop(self._fdt, nodeoffset, prop_name))
303
304    def getprop(self, nodeoffset, prop_name, quiet=()):
305        """Get a property from a node
306
307        Args:
308            nodeoffset: Node offset containing property to get
309            prop_name: Name of property to get
310            quiet: Errors to ignore (empty to raise on all errors)
311
312        Returns:
313            Value of property as a bytearray, or -ve error number
314
315        Raises:
316            FdtError if any error occurs (e.g. the property is not found)
317        """
318        pdata = check_err_null(fdt_getprop(self._fdt, nodeoffset, prop_name),
319                               quiet)
320        if isinstance(pdata, (int)):
321            return pdata
322        return bytearray(pdata[0])
323
324    def get_phandle(self, nodeoffset):
325        """Get the phandle of a node
326
327        Args:
328            nodeoffset: Node offset to check
329
330        Returns:
331            phandle of node, or 0 if the node has no phandle or another error
332            occurs
333        """
334        return fdt_get_phandle(self._fdt, nodeoffset)
335
336    def parent_offset(self, nodeoffset, quiet=()):
337        """Get the offset of a node's parent
338
339        Args:
340            nodeoffset: Node offset to check
341            quiet: Errors to ignore (empty to raise on all errors)
342
343        Returns:
344            The offset of the parent node, if any
345
346        Raises:
347            FdtException if no parent found or other error occurs
348        """
349        return check_err(fdt_parent_offset(self._fdt, nodeoffset), quiet)
350
351    def node_offset_by_phandle(self, phandle, quiet=()):
352        """Get the offset of a node with the given phandle
353
354        Args:
355            phandle: Phandle to search for
356            quiet: Errors to ignore (empty to raise on all errors)
357
358        Returns:
359            The offset of node with that phandle, if any
360
361        Raises:
362            FdtException if no node found or other error occurs
363        """
364        return check_err(fdt_node_offset_by_phandle(self._fdt, phandle), quiet)
365
366class Property:
367    """Holds a device tree property name and value.
368
369    This holds a copy of a property taken from the device tree. It does not
370    reference the device tree, so if anything changes in the device tree,
371    a Property object will remain valid.
372
373    Properties:
374        name: Property name
375        value: Proper value as a bytearray
376    """
377    def __init__(self, name, value):
378        self.name = name
379        self.value = value
380%}
381
382%rename(fdt_property) fdt_property_func;
383
384typedef int fdt32_t;
385
386%include "libfdt/fdt.h"
387
388%include "typemaps.i"
389
390/* Most functions don't change the device tree, so use a const void * */
391%typemap(in) (const void *)(const void *fdt) {
392	if (!PyByteArray_Check($input)) {
393		SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname"
394			"', argument " "$argnum"" of type '" "$type""'");
395	}
396	$1 = (void *)PyByteArray_AsString($input);
397        fdt = $1;
398        fdt = fdt; /* avoid unused variable warning */
399}
400
401/* Some functions do change the device tree, so use void * */
402%typemap(in) (void *)(const void *fdt) {
403	if (!PyByteArray_Check($input)) {
404		SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname"
405			"', argument " "$argnum"" of type '" "$type""'");
406	}
407	$1 = PyByteArray_AsString($input);
408        fdt = $1;
409        fdt = fdt; /* avoid unused variable warning */
410}
411
412%typemap(out) (struct fdt_property *) {
413	PyObject *buff;
414
415	if ($1) {
416		resultobj = PyString_FromString(
417			fdt_string(fdt1, fdt32_to_cpu($1->nameoff)));
418		buff = PyByteArray_FromStringAndSize(
419			(const char *)($1 + 1), fdt32_to_cpu($1->len));
420		resultobj = SWIG_Python_AppendOutput(resultobj, buff);
421	}
422}
423
424%apply int *OUTPUT { int *lenp };
425
426/* typemap used for fdt_getprop() */
427%typemap(out) (const void *) {
428	if (!$1)
429		$result = Py_None;
430	else
431		$result = Py_BuildValue("s#", $1, *arg4);
432}
433
434/* We have both struct fdt_property and a function fdt_property() */
435%warnfilter(302) fdt_property;
436
437/* These are macros in the header so have to be redefined here */
438int fdt_magic(const void *fdt);
439int fdt_totalsize(const void *fdt);
440int fdt_off_dt_struct(const void *fdt);
441int fdt_off_dt_strings(const void *fdt);
442int fdt_off_mem_rsvmap(const void *fdt);
443int fdt_version(const void *fdt);
444int fdt_last_comp_version(const void *fdt);
445int fdt_boot_cpuid_phys(const void *fdt);
446int fdt_size_dt_strings(const void *fdt);
447int fdt_size_dt_struct(const void *fdt);
448
449%include <../libfdt/libfdt.h>
450