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 <sjg@chromium.org>
This commit is contained in:
Simon Glass 2016-07-25 18:59:04 -06:00
parent 66051b1f59
commit a06a34b203
4 changed files with 148 additions and 48 deletions

68
tools/dtoc/fdt.py Normal file
View file

@ -0,0 +1,68 @@
#!/usr/bin/python
#
# Copyright (C) 2016 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# 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

View file

@ -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

View file

@ -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

View file

@ -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