From a06a34b2031e0797892e188595bfb305cd9719ab Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 25 Jul 2016 18:59:04 -0600 Subject: [PATCH] dtoc: Create a base class for Fdt At present we have two separate implementations of the Fdt library, one which uses fdtget/fdtput and one which uses libfdt (via swig). Before adding more functionality it makes sense to create a base class for these. This will allow common functions to be shared, and make the Fdt API a little clearer. Create a new fdt.py file with the base class, and adjust fdt_normal.py and fdt_fallback.py to use it. Signed-off-by: Simon Glass --- tools/dtoc/fdt.py | 68 ++++++++++++++++++++++++++++++++++++++ tools/dtoc/fdt_fallback.py | 61 ++++++++++++++++++++-------------- tools/dtoc/fdt_normal.py | 58 ++++++++++++++++++++------------ tools/dtoc/fdt_select.py | 9 +++-- 4 files changed, 148 insertions(+), 48 deletions(-) create mode 100644 tools/dtoc/fdt.py diff --git a/tools/dtoc/fdt.py b/tools/dtoc/fdt.py new file mode 100644 index 0000000000..413d45daa4 --- /dev/null +++ b/tools/dtoc/fdt.py @@ -0,0 +1,68 @@ +#!/usr/bin/python +# +# Copyright (C) 2016 Google, Inc +# Written by Simon Glass +# +# SPDX-License-Identifier: GPL-2.0+ +# + +import struct +import sys + +import fdt_util + +# This deals with a device tree, presenting it as an assortment of Node and +# Prop objects, representing nodes and properties, respectively. This file +# contains the base classes and defines the high-level API. Most of the +# implementation is in the FdtFallback and FdtNormal subclasses. See +# fdt_select.py for how to create an Fdt object. + +def CheckErr(errnum, msg): + if errnum: + raise ValueError('Error %d: %s: %s' % + (errnum, libfdt.fdt_strerror(errnum), msg)) + +class PropBase: + """A device tree property + + Properties: + name: Property name (as per the device tree) + value: Property value as a string of bytes, or a list of strings of + bytes + type: Value type + """ + def __init__(self, node, offset, name): + self._node = node + self._offset = offset + self.name = name + self.value = None + +class NodeBase: + """A device tree node + + Properties: + offset: Integer offset in the device tree + name: Device tree node tname + path: Full path to node, along with the node name itself + _fdt: Device tree object + subnodes: A list of subnodes for this node, each a Node object + props: A dict of properties for this node, each a Prop object. + Keyed by property name + """ + def __init__(self, fdt, offset, name, path): + self._fdt = fdt + self._offset = offset + self.name = name + self.path = path + self.subnodes = [] + self.props = {} + +class Fdt: + """Provides simple access to a flat device tree blob. + + Properties: + fname: Filename of fdt + _root: Root of device tree (a Node object) + """ + def __init__(self, fname): + self._fname = fname diff --git a/tools/dtoc/fdt_fallback.py b/tools/dtoc/fdt_fallback.py index 9ed11e4cbf..be3b5badc9 100644 --- a/tools/dtoc/fdt_fallback.py +++ b/tools/dtoc/fdt_fallback.py @@ -7,6 +7,8 @@ # import command +import fdt +from fdt import Fdt, NodeBase, PropBase import fdt_util import sys @@ -17,7 +19,7 @@ import sys # is not very efficient for larger trees. The tool is called once for each # node and property in the tree. -class Prop: +class Prop(PropBase): """A device tree property Properties: @@ -26,14 +28,14 @@ class Prop: bytes type: Value type """ - def __init__(self, name, byte_list_str): - self.name = name - self.value = None + def __init__(self, node, name, byte_list_str): + PropBase.__init__(self, node, 0, name) if not byte_list_str.strip(): self.type = fdt_util.TYPE_BOOL return - bytes = [chr(int(byte, 16)) for byte in byte_list_str.strip().split(' ')] - self.type, self.value = fdt_util.BytesToValue(''.join(bytes)) + self.bytes = [chr(int(byte, 16)) + for byte in byte_list_str.strip().split(' ')] + self.type, self.value = fdt_util.BytesToValue(''.join(self.bytes)) def GetPhandle(self): """Get a (single) phandle value from a property @@ -77,7 +79,7 @@ class Prop: self.value.append(val) -class Node: +class Node(NodeBase): """A device tree node Properties: @@ -88,12 +90,8 @@ class Node: props: A dict of properties for this node, each a Prop object. Keyed by property name """ - def __init__(self, fdt, name, path): - self.name = name - self.path = path - self._fdt = fdt - self.subnodes = [] - self.props = {} + def __init__(self, fdt, offset, name, path): + NodeBase.__init__(self, fdt, offset, name, path) def Scan(self): """Scan a node's properties and subnodes @@ -102,35 +100,34 @@ class Node: searching into subnodes so that the entire tree is built. """ for name, byte_list_str in self._fdt.GetProps(self.path).iteritems(): - prop = Prop(name, byte_list_str) + prop = Prop(self, name, byte_list_str) self.props[name] = prop for name in self._fdt.GetSubNodes(self.path): sep = '' if self.path[-1] == '/' else '/' path = self.path + sep + name - node = Node(self._fdt, name, path) + node = Node(self._fdt, 0, name, path) self.subnodes.append(node) node.Scan() -class Fdt: - """Provides simple access to a flat device tree blob. +class FdtFallback(Fdt): + """Provides simple access to a flat device tree blob using fdtget/fdtput Properties: - fname: Filename of fdt - _root: Root of device tree (a Node object) + See superclass """ def __init__(self, fname): - self.fname = fname + Fdt.__init__(self, fname) def Scan(self): """Scan a device tree, building up a tree of Node objects This fills in the self._root property """ - self._root = Node(self, '/', '/') + self._root = Node(self, 0, '/', '/') self._root.Scan() def GetRoot(self): @@ -153,7 +150,7 @@ class Fdt: Raises: CmdError: if the node does not exist. """ - out = command.Output('fdtget', self.fname, '-l', node) + out = command.Output('fdtget', self._fname, '-l', node) return out.strip().splitlines() def GetProps(self, node, convert_dashes=False): @@ -171,7 +168,7 @@ class Fdt: Raises: CmdError: if the node does not exist. """ - out = command.Output('fdtget', self.fname, node, '-p') + out = command.Output('fdtget', self._fname, node, '-p') props = out.strip().splitlines() props_dict = {} for prop in props: @@ -204,10 +201,26 @@ class Fdt: Raises: CmdError: if the property does not exist and no default is provided. """ - args = [self.fname, node, prop, '-t', 'bx'] + args = [self._fname, node, prop, '-t', 'bx'] if default is not None: args += ['-d', str(default)] if typespec is not None: args += ['-t%s' % typespec] out = command.Output('fdtget', *args) return out.strip() + + @classmethod + def Node(self, fdt, offset, name, path): + """Create a new node + + This is used by Fdt.Scan() to create a new node using the correct + class. + + Args: + fdt: Fdt object + offset: Offset of node + name: Node name + path: Full path to node + """ + node = Node(fdt, offset, name, path) + return node diff --git a/tools/dtoc/fdt_normal.py b/tools/dtoc/fdt_normal.py index 1d913a925e..ca5335ba5b 100644 --- a/tools/dtoc/fdt_normal.py +++ b/tools/dtoc/fdt_normal.py @@ -6,9 +6,13 @@ # SPDX-License-Identifier: GPL-2.0+ # +import struct +import sys + +import fdt +from fdt import Fdt, NodeBase, PropBase import fdt_util import libfdt -import sys # This deals with a device tree, presenting it as a list of Node and Prop # objects, representing nodes and properties, respectively. @@ -16,7 +20,7 @@ import sys # This implementation uses a libfdt Python library to access the device tree, # so it is fairly efficient. -class Prop: +class Prop(PropBase): """A device tree property Properties: @@ -25,9 +29,9 @@ class Prop: bytes type: Value type """ - def __init__(self, name, bytes): - self.name = name - self.value = None + def __init__(self, node, offset, name, bytes): + PropBase.__init__(self, node, offset, name) + self.bytes = bytes if not bytes: self.type = fdt_util.TYPE_BOOL self.value = True @@ -76,7 +80,7 @@ class Prop: self.value.append(val) -class Node: +class Node(NodeBase): """A device tree node Properties: @@ -89,12 +93,7 @@ class Node: Keyed by property name """ def __init__(self, fdt, offset, name, path): - self.offset = offset - self.name = name - self.path = path - self._fdt = fdt - self.subnodes = [] - self.props = {} + NodeBase.__init__(self, fdt, offset, name, path) def Scan(self): """Scan a node's properties and subnodes @@ -104,7 +103,7 @@ class Node: """ self.props = self._fdt.GetProps(self.path) - offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self.offset) + offset = libfdt.fdt_first_subnode(self._fdt.GetFdt(), self._offset) while offset >= 0: sep = '' if self.path[-1] == '/' else '/' name = libfdt.Name(self._fdt.GetFdt(), offset) @@ -116,17 +115,17 @@ class Node: offset = libfdt.fdt_next_subnode(self._fdt.GetFdt(), offset) -class Fdt: - """Provides simple access to a flat device tree blob. +class FdtNormal(Fdt): + """Provides simple access to a flat device tree blob using libfdt. Properties: - fname: Filename of fdt - _root: Root of device tree (a Node object) + _fdt: Device tree contents (bytearray) + _cached_offsets: True if all the nodes have a valid _offset property, + False if something has changed to invalidate the offsets """ - def __init__(self, fname): - self.fname = fname - with open(fname) as fd: + Fdt.__init__(self, fname) + with open(self._fname) as fd: self._fdt = fd.read() def GetFdt(self): @@ -173,8 +172,25 @@ class Fdt: poffset = libfdt.fdt_first_property_offset(self._fdt, offset) while poffset >= 0: dprop, plen = libfdt.fdt_get_property_by_offset(self._fdt, poffset) - prop = Prop(libfdt.String(self._fdt, dprop.nameoff), libfdt.Data(dprop)) + prop = Prop(node, poffset, libfdt.String(self._fdt, dprop.nameoff), + libfdt.Data(dprop)) props_dict[prop.name] = prop poffset = libfdt.fdt_next_property_offset(self._fdt, poffset) return props_dict + + @classmethod + def Node(self, fdt, offset, name, path): + """Create a new node + + This is used by Fdt.Scan() to create a new node using the correct + class. + + Args: + fdt: Fdt object + offset: Offset of node + name: Node name + path: Full path to node + """ + node = Node(fdt, offset, name, path) + return node diff --git a/tools/dtoc/fdt_select.py b/tools/dtoc/fdt_select.py index 681dfbfda0..18a36d88a0 100644 --- a/tools/dtoc/fdt_select.py +++ b/tools/dtoc/fdt_select.py @@ -10,14 +10,17 @@ # fallback one (which uses fdtget and is slower). Both provide the same # interface for this file to use. try: - import fdt_normal as fdt + import fdt_normal have_libfdt = True except ImportError: have_libfdt = False - import fdt_fallback as fdt + import fdt_fallback def FdtScan(fname): """Returns a new Fdt object from the implementation we are using""" - dtb = fdt.Fdt(fname) + if have_libfdt: + dtb = fdt_normal.FdtNormal(fname) + else: + dtb = fdt_fallback.FdtFallback(fname) dtb.Scan() return dtb -- 2.25.1