blob: 14decf394df26734af5f1d1ae05636171087b566 [file] [log] [blame]
Simon Glassec564b42016-07-04 11:58:08 -06001#!/usr/bin/python
2#
3# Copyright (C) 2016 Google, Inc
4# Written by Simon Glass <sjg@chromium.org>
5#
6# SPDX-License-Identifier: GPL-2.0+
7#
8
9import command
10import fdt_util
11import sys
12
13# This deals with a device tree, presenting it as a list of Node and Prop
14# objects, representing nodes and properties, respectively.
15#
16# This implementation uses the fdtget tool to access the device tree, so it
17# is not very efficient for larger trees. The tool is called once for each
18# node and property in the tree.
19
20class Prop:
21 """A device tree property
22
23 Properties:
24 name: Property name (as per the device tree)
25 value: Property value as a string of bytes, or a list of strings of
26 bytes
27 type: Value type
28 """
29 def __init__(self, name, byte_list_str):
30 self.name = name
31 self.value = None
32 if not byte_list_str.strip():
33 self.type = fdt_util.TYPE_BOOL
34 return
35 bytes = [chr(int(byte, 16)) for byte in byte_list_str.strip().split(' ')]
36 self.type, self.value = fdt_util.BytesToValue(''.join(bytes))
37
38 def GetPhandle(self):
39 """Get a (single) phandle value from a property
40
41 Gets the phandle valuie from a property and returns it as an integer
42 """
43 return fdt_util.fdt32_to_cpu(self.value[:4])
44
45 def Widen(self, newprop):
46 """Figure out which property type is more general
47
48 Given a current property and a new property, this function returns the
49 one that is less specific as to type. The less specific property will
50 be ble to represent the data in the more specific property. This is
51 used for things like:
52
53 node1 {
54 compatible = "fred";
55 value = <1>;
56 };
57 node1 {
58 compatible = "fred";
59 value = <1 2>;
60 };
61
62 He we want to use an int array for 'value'. The first property
63 suggests that a single int is enough, but the second one shows that
64 it is not. Calling this function with these two propertes would
65 update the current property to be like the second, since it is less
66 specific.
67 """
68 if newprop.type < self.type:
69 self.type = newprop.type
70
71 if type(newprop.value) == list and type(self.value) != list:
72 self.value = newprop.value
73
74class Node:
75 """A device tree node
76
77 Properties:
78 name: Device tree node tname
79 path: Full path to node, along with the node name itself
80 _fdt: Device tree object
81 subnodes: A list of subnodes for this node, each a Node object
82 props: A dict of properties for this node, each a Prop object.
83 Keyed by property name
84 """
85 def __init__(self, fdt, name, path):
86 self.name = name
87 self.path = path
88 self._fdt = fdt
89 self.subnodes = []
90 self.props = {}
91
92 def Scan(self):
93 """Scan a node's properties and subnodes
94
95 This fills in the props and subnodes properties, recursively
96 searching into subnodes so that the entire tree is built.
97 """
98 for name, byte_list_str in self._fdt.GetProps(self.path).iteritems():
99 prop = Prop(name, byte_list_str)
100 self.props[name] = prop
101
102 for name in self._fdt.GetSubNodes(self.path):
103 sep = '' if self.path[-1] == '/' else '/'
104 path = self.path + sep + name
105 node = Node(self._fdt, name, path)
106 self.subnodes.append(node)
107
108 node.Scan()
109
110
111class Fdt:
112 """Provides simple access to a flat device tree blob.
113
114 Properties:
115 fname: Filename of fdt
116 _root: Root of device tree (a Node object)
117 """
118
119 def __init__(self, fname):
120 self.fname = fname
121
122 def Scan(self):
123 """Scan a device tree, building up a tree of Node objects
124
125 This fills in the self._root property
126 """
127 self._root = Node(self, '/', '/')
128 self._root.Scan()
129
130 def GetRoot(self):
131 """Get the root Node of the device tree
132
133 Returns:
134 The root Node object
135 """
136 return self._root
137
138 def GetSubNodes(self, node):
139 """Returns a list of sub-nodes of a given node
140
141 Args:
142 node: Node name to return children from
143
144 Returns:
145 List of children in the node (each a string node name)
146
147 Raises:
148 CmdError: if the node does not exist.
149 """
150 out = command.Output('fdtget', self.fname, '-l', node)
151 return out.strip().splitlines()
152
153 def GetProps(self, node, convert_dashes=False):
154 """Get all properties from a node
155
156 Args:
157 node: full path to node name to look in
158 convert_dashes: True to convert - to _ in node names
159
160 Returns:
161 A dictionary containing all the properties, indexed by node name.
162 The entries are simply strings - no decoding of lists or numbers
163 is done.
164
165 Raises:
166 CmdError: if the node does not exist.
167 """
168 out = command.Output('fdtget', self.fname, node, '-p')
169 props = out.strip().splitlines()
170 props_dict = {}
171 for prop in props:
172 name = prop
173 if convert_dashes:
174 prop = re.sub('-', '_', prop)
175 props_dict[prop] = self.GetProp(node, name)
176 return props_dict
177
178 def GetProp(self, node, prop, default=None, typespec=None):
179 """Get a property from a device tree.
180
181 This looks up the given node and property, and returns the value as a
182 string,
183
184 If the node or property does not exist, this will return the default
185 value.
186
187 Args:
188 node: Full path to node to look up.
189 prop: Property name to look up.
190 default: Default value to return if nothing is present in the fdt,
191 or None to raise in this case. This will be converted to a
192 string.
193 typespec: Type character to use (None for default, 's' for string)
194
195 Returns:
196 string containing the property value.
197
198 Raises:
199 CmdError: if the property does not exist and no default is provided.
200 """
201 args = [self.fname, node, prop, '-t', 'bx']
202 if default is not None:
203 args += ['-d', str(default)]
204 if typespec is not None:
205 args += ['-t%s' % typespec]
206 out = command.Output('fdtget', *args)
207 return out.strip()