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