|
@@ -0,0 +1,389 @@
|
|
|
+/*
|
|
|
+ * pylibfdt - Flat Device Tree manipulation in Python
|
|
|
+ * Copyright (C) 2017 Google, Inc.
|
|
|
+ * Written by Simon Glass <sjg@chromium.org>
|
|
|
+ *
|
|
|
+ * SPDX-License-Identifier: GPL-2.0+ BSD-2-Clause
|
|
|
+ */
|
|
|
+
|
|
|
+%module libfdt
|
|
|
+
|
|
|
+%{
|
|
|
+#define SWIG_FILE_WITH_INIT
|
|
|
+#include "libfdt.h"
|
|
|
+%}
|
|
|
+
|
|
|
+%pythoncode %{
|
|
|
+
|
|
|
+import struct
|
|
|
+
|
|
|
+# Error codes, corresponding to FDT_ERR_... in libfdt.h
|
|
|
+(NOTFOUND,
|
|
|
+ EXISTS,
|
|
|
+ NOSPACE,
|
|
|
+ BADOFFSET,
|
|
|
+ BADPATH,
|
|
|
+ BADPHANDLE,
|
|
|
+ BADSTATE,
|
|
|
+ TRUNCATED,
|
|
|
+ BADMAGIC,
|
|
|
+ BADVERSION,
|
|
|
+ BADSTRUCTURE,
|
|
|
+ BADLAYOUT,
|
|
|
+ INTERNAL,
|
|
|
+ BADNCELLS,
|
|
|
+ BADVALUE,
|
|
|
+ BADOVERLAY,
|
|
|
+ NOPHANDLES) = QUIET_ALL = range(1, 18)
|
|
|
+# QUIET_ALL can be passed as the 'quiet' parameter to avoid exceptions
|
|
|
+# altogether. All # functions passed this value will return an error instead
|
|
|
+# of raising an exception.
|
|
|
+
|
|
|
+# Pass this as the 'quiet' parameter to return -ENOTFOUND on NOTFOUND errors,
|
|
|
+# instead of raising an exception.
|
|
|
+QUIET_NOTFOUND = (NOTFOUND,)
|
|
|
+
|
|
|
+
|
|
|
+class FdtException(Exception):
|
|
|
+ """An exception caused by an error such as one of the codes above"""
|
|
|
+ def __init__(self, err):
|
|
|
+ self.err = err
|
|
|
+
|
|
|
+ def __str__(self):
|
|
|
+ return 'pylibfdt error %d: %s' % (self.err, fdt_strerror(self.err))
|
|
|
+
|
|
|
+def strerror(fdt_err):
|
|
|
+ """Get the string for an error number
|
|
|
+
|
|
|
+ Args:
|
|
|
+ fdt_err: Error number (-ve)
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ String containing the associated error
|
|
|
+ """
|
|
|
+ return fdt_strerror(fdt_err)
|
|
|
+
|
|
|
+def check_err(val, quiet=()):
|
|
|
+ """Raise an error if the return value is -ve
|
|
|
+
|
|
|
+ This is used to check for errors returned by libfdt C functions.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ val: Return value from a libfdt function
|
|
|
+ quiet: Errors to ignore (empty to raise on all errors)
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ val if val >= 0
|
|
|
+
|
|
|
+ Raises
|
|
|
+ FdtException if val < 0
|
|
|
+ """
|
|
|
+ if val < 0:
|
|
|
+ if -val not in quiet:
|
|
|
+ raise FdtException(val)
|
|
|
+ return val
|
|
|
+
|
|
|
+def check_err_null(val, quiet=()):
|
|
|
+ """Raise an error if the return value is NULL
|
|
|
+
|
|
|
+ This is used to check for a NULL return value from certain libfdt C
|
|
|
+ functions
|
|
|
+
|
|
|
+ Args:
|
|
|
+ val: Return value from a libfdt function
|
|
|
+ quiet: Errors to ignore (empty to raise on all errors)
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ val if val is a list, None if not
|
|
|
+
|
|
|
+ Raises
|
|
|
+ FdtException if val indicates an error was reported and the error
|
|
|
+ is not in @quiet.
|
|
|
+ """
|
|
|
+ # Normally a list is returned which contains the data and its length.
|
|
|
+ # If we get just an integer error code, it means the function failed.
|
|
|
+ if not isinstance(val, list):
|
|
|
+ if -val not in quiet:
|
|
|
+ raise FdtException(val)
|
|
|
+ return val
|
|
|
+
|
|
|
+class Fdt:
|
|
|
+ """Device tree class, supporting all operations
|
|
|
+
|
|
|
+ The Fdt object is created is created from a device tree binary file,
|
|
|
+ e.g. with something like:
|
|
|
+
|
|
|
+ fdt = Fdt(open("filename.dtb").read())
|
|
|
+
|
|
|
+ Operations can then be performed using the methods in this class. Each
|
|
|
+ method xxx(args...) corresponds to a libfdt function fdt_xxx(fdt, args...).
|
|
|
+
|
|
|
+ All methods raise an FdtException if an error occurs. To avoid this
|
|
|
+ behaviour a 'quiet' parameter is provided for some functions. This
|
|
|
+ defaults to empty, but you can pass a list of errors that you expect.
|
|
|
+ If one of these errors occurs, the function will return an error number
|
|
|
+ (e.g. -NOTFOUND).
|
|
|
+ """
|
|
|
+ def __init__(self, data):
|
|
|
+ self._fdt = bytearray(data)
|
|
|
+ check_err(fdt_check_header(self._fdt));
|
|
|
+
|
|
|
+ def path_offset(self, path, quiet=()):
|
|
|
+ """Get the offset for a given path
|
|
|
+
|
|
|
+ Args:
|
|
|
+ path: Path to the required node, e.g. '/node@3/subnode@1'
|
|
|
+ quiet: Errors to ignore (empty to raise on all errors)
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Node offset
|
|
|
+
|
|
|
+ Raises
|
|
|
+ FdtException if the path is not valid or not found
|
|
|
+ """
|
|
|
+ return check_err(fdt_path_offset(self._fdt, path), quiet)
|
|
|
+
|
|
|
+ def first_property_offset(self, nodeoffset, quiet=()):
|
|
|
+ """Get the offset of the first property in a node offset
|
|
|
+
|
|
|
+ Args:
|
|
|
+ nodeoffset: Offset to the node to check
|
|
|
+ quiet: Errors to ignore (empty to raise on all errors)
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Offset of the first property
|
|
|
+
|
|
|
+ Raises
|
|
|
+ FdtException if the associated node has no properties, or some
|
|
|
+ other error occurred
|
|
|
+ """
|
|
|
+ return check_err(fdt_first_property_offset(self._fdt, nodeoffset),
|
|
|
+ quiet)
|
|
|
+
|
|
|
+ def next_property_offset(self, prop_offset, quiet=()):
|
|
|
+ """Get the next property in a node
|
|
|
+
|
|
|
+ Args:
|
|
|
+ prop_offset: Offset of the previous property
|
|
|
+ quiet: Errors to ignore (empty to raise on all errors)
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Offset of the next property
|
|
|
+
|
|
|
+ Raises:
|
|
|
+ FdtException if the associated node has no more properties, or
|
|
|
+ some other error occurred
|
|
|
+ """
|
|
|
+ return check_err(fdt_next_property_offset(self._fdt, prop_offset),
|
|
|
+ quiet)
|
|
|
+
|
|
|
+ def get_name(self, nodeoffset):
|
|
|
+ """Get the name of a node
|
|
|
+
|
|
|
+ Args:
|
|
|
+ nodeoffset: Offset of node to check
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Node name
|
|
|
+
|
|
|
+ Raises:
|
|
|
+ FdtException on error (e.g. nodeoffset is invalid)
|
|
|
+ """
|
|
|
+ return check_err_null(fdt_get_name(self._fdt, nodeoffset))[0]
|
|
|
+
|
|
|
+ def get_property_by_offset(self, prop_offset, quiet=()):
|
|
|
+ """Obtains a property that can be examined
|
|
|
+
|
|
|
+ Args:
|
|
|
+ prop_offset: Offset of property (e.g. from first_property_offset())
|
|
|
+ quiet: Errors to ignore (empty to raise on all errors)
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Property object, or None if not found
|
|
|
+
|
|
|
+ Raises:
|
|
|
+ FdtException on error (e.g. invalid prop_offset or device
|
|
|
+ tree format)
|
|
|
+ """
|
|
|
+ pdata = check_err_null(
|
|
|
+ fdt_get_property_by_offset(self._fdt, prop_offset), quiet)
|
|
|
+ if isinstance(pdata, (int)):
|
|
|
+ return pdata
|
|
|
+ return Property(pdata[0], pdata[1])
|
|
|
+
|
|
|
+ def first_subnode(self, nodeoffset, quiet=()):
|
|
|
+ """Find the first subnode of a parent node
|
|
|
+
|
|
|
+ Args:
|
|
|
+ nodeoffset: Node offset of parent node
|
|
|
+ quiet: Errors to ignore (empty to raise on all errors)
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ The offset of the first subnode, if any
|
|
|
+
|
|
|
+ Raises:
|
|
|
+ FdtException if no subnode found or other error occurs
|
|
|
+ """
|
|
|
+ return check_err(fdt_first_subnode(self._fdt, nodeoffset), quiet)
|
|
|
+
|
|
|
+ def next_subnode(self, nodeoffset, quiet=()):
|
|
|
+ """Find the next subnode
|
|
|
+
|
|
|
+ Args:
|
|
|
+ nodeoffset: Node offset of previous subnode
|
|
|
+ quiet: Errors to ignore (empty to raise on all errors)
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ The offset of the next subnode, if any
|
|
|
+
|
|
|
+ Raises:
|
|
|
+ FdtException if no more subnode found or other error occurs
|
|
|
+ """
|
|
|
+ return check_err(fdt_next_subnode(self._fdt, nodeoffset), quiet)
|
|
|
+
|
|
|
+ def totalsize(self):
|
|
|
+ """Return the total size of the device tree
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Total tree size in bytes
|
|
|
+ """
|
|
|
+ return check_err(fdt_totalsize(self._fdt))
|
|
|
+
|
|
|
+ def off_dt_struct(self):
|
|
|
+ """Return the start of the device tree struct area
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Start offset of struct area
|
|
|
+ """
|
|
|
+ return check_err(fdt_off_dt_struct(self._fdt))
|
|
|
+
|
|
|
+ def pack(self, quiet=()):
|
|
|
+ """Pack the device tree to remove unused space
|
|
|
+
|
|
|
+ This adjusts the tree in place.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ quiet: Errors to ignore (empty to raise on all errors)
|
|
|
+
|
|
|
+ Raises:
|
|
|
+ FdtException if any error occurs
|
|
|
+ """
|
|
|
+ return check_err(fdt_pack(self._fdt), quiet)
|
|
|
+
|
|
|
+ def delprop(self, nodeoffset, prop_name):
|
|
|
+ """Delete a property from a node
|
|
|
+
|
|
|
+ Args:
|
|
|
+ nodeoffset: Node offset containing property to delete
|
|
|
+ prop_name: Name of property to delete
|
|
|
+
|
|
|
+ Raises:
|
|
|
+ FdtError if the property does not exist, or another error occurs
|
|
|
+ """
|
|
|
+ return check_err(fdt_delprop(self._fdt, nodeoffset, prop_name))
|
|
|
+
|
|
|
+ def getprop(self, nodeoffset, prop_name, quiet=()):
|
|
|
+ """Get a property from a node
|
|
|
+
|
|
|
+ Args:
|
|
|
+ nodeoffset: Node offset containing property to get
|
|
|
+ prop_name: Name of property to get
|
|
|
+ quiet: Errors to ignore (empty to raise on all errors)
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ Value of property as a bytearray, or -ve error number
|
|
|
+
|
|
|
+ Raises:
|
|
|
+ FdtError if any error occurs (e.g. the property is not found)
|
|
|
+ """
|
|
|
+ pdata = check_err_null(fdt_getprop(self._fdt, nodeoffset, prop_name),
|
|
|
+ quiet)
|
|
|
+ if isinstance(pdata, (int)):
|
|
|
+ return pdata
|
|
|
+ return bytearray(pdata[0])
|
|
|
+
|
|
|
+
|
|
|
+class Property:
|
|
|
+ """Holds a device tree property name and value.
|
|
|
+
|
|
|
+ This holds a copy of a property taken from the device tree. It does not
|
|
|
+ reference the device tree, so if anything changes in the device tree,
|
|
|
+ a Property object will remain valid.
|
|
|
+
|
|
|
+ Properties:
|
|
|
+ name: Property name
|
|
|
+ value: Proper value as a bytearray
|
|
|
+ """
|
|
|
+ def __init__(self, name, value):
|
|
|
+ self.name = name
|
|
|
+ self.value = value
|
|
|
+%}
|
|
|
+
|
|
|
+%rename(fdt_property) fdt_property_func;
|
|
|
+
|
|
|
+typedef int fdt32_t;
|
|
|
+
|
|
|
+%include "libfdt/fdt.h"
|
|
|
+
|
|
|
+%include "typemaps.i"
|
|
|
+
|
|
|
+/* Most functions don't change the device tree, so use a const void * */
|
|
|
+%typemap(in) (const void *)(const void *fdt) {
|
|
|
+ if (!PyByteArray_Check($input)) {
|
|
|
+ SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname"
|
|
|
+ "', argument " "$argnum"" of type '" "$type""'");
|
|
|
+ }
|
|
|
+ $1 = (void *)PyByteArray_AsString($input);
|
|
|
+ fdt = $1;
|
|
|
+ fdt = fdt; /* avoid unused variable warning */
|
|
|
+}
|
|
|
+
|
|
|
+/* Some functions do change the device tree, so use void * */
|
|
|
+%typemap(in) (void *)(const void *fdt) {
|
|
|
+ if (!PyByteArray_Check($input)) {
|
|
|
+ SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname"
|
|
|
+ "', argument " "$argnum"" of type '" "$type""'");
|
|
|
+ }
|
|
|
+ $1 = PyByteArray_AsString($input);
|
|
|
+ fdt = $1;
|
|
|
+ fdt = fdt; /* avoid unused variable warning */
|
|
|
+}
|
|
|
+
|
|
|
+%typemap(out) (struct fdt_property *) {
|
|
|
+ PyObject *buff;
|
|
|
+
|
|
|
+ if ($1) {
|
|
|
+ resultobj = PyString_FromString(
|
|
|
+ fdt_string(fdt1, fdt32_to_cpu($1->nameoff)));
|
|
|
+ buff = PyByteArray_FromStringAndSize(
|
|
|
+ (const char *)($1 + 1), fdt32_to_cpu($1->len));
|
|
|
+ resultobj = SWIG_Python_AppendOutput(resultobj, buff);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+%apply int *OUTPUT { int *lenp };
|
|
|
+
|
|
|
+/* typemap used for fdt_getprop() */
|
|
|
+%typemap(out) (const void *) {
|
|
|
+ if (!$1)
|
|
|
+ $result = Py_None;
|
|
|
+ else
|
|
|
+ $result = Py_BuildValue("s#", $1, *arg4);
|
|
|
+}
|
|
|
+
|
|
|
+/* We have both struct fdt_property and a function fdt_property() */
|
|
|
+%warnfilter(302) fdt_property;
|
|
|
+
|
|
|
+/* These are macros in the header so have to be redefined here */
|
|
|
+int fdt_magic(const void *fdt);
|
|
|
+int fdt_totalsize(const void *fdt);
|
|
|
+int fdt_off_dt_struct(const void *fdt);
|
|
|
+int fdt_off_dt_strings(const void *fdt);
|
|
|
+int fdt_off_mem_rsvmap(const void *fdt);
|
|
|
+int fdt_version(const void *fdt);
|
|
|
+int fdt_last_comp_version(const void *fdt);
|
|
|
+int fdt_boot_cpuid_phys(const void *fdt);
|
|
|
+int fdt_size_dt_strings(const void *fdt);
|
|
|
+int fdt_size_dt_struct(const void *fdt);
|
|
|
+
|
|
|
+%include <../libfdt/libfdt.h>
|