blob: 4ff7f97a9e2eb7e3d2327419cd6ef1a8a88183d4 [file] [log] [blame]
Anas Nashifed3d7c12017-04-10 08:53:23 -04001#!/usr/bin/env python3
Leandro Pereiraca42e852016-12-08 12:55:45 -08002#
3# Copyright (c) 2017 Intel Corporation
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import sys
19import pprint
20
21def read_until(line, fd, end):
22 out = [line]
23 while True:
24 idx = line.find(end)
25 if idx < 0:
26 line = clean_line(fd.readline(), fd)
27 out.append(line)
28 else:
29 out.append(line[idx + len(end):])
30 return out
31
32def remove_comment(line, fd):
33 out = []
34 while True:
35 idx = line.find('/*')
36 if idx < 0:
37 idx = line.find('//')
38 if idx < 0:
39 out.append(line)
40 else:
41 out.append(line[:idx])
42 return ' '.join(out)
43
44 out.append(line[:idx])
45 line = read_until(line[idx:], fd, '*/')[-1]
46
47def clean_line(line, fd):
48 return remove_comment(line, fd).strip()
49
50def parse_node_name(line):
51 line = line[:-1]
52
53 if '@' in line:
54 line, addr = line.split('@')
55 else:
56 addr = None
57
58 if ':' in line:
59 label, name = line.split(':')
60 else:
61 name = line
62 label = None
63
64 if addr is None:
Kumar Gala46e47552017-04-12 17:16:41 -050065 return label, name.strip(), None, None
Leandro Pereiraca42e852016-12-08 12:55:45 -080066
Kumar Gala46e47552017-04-12 17:16:41 -050067 return label, name.strip(), addr, int(addr,16)
Leandro Pereiraca42e852016-12-08 12:55:45 -080068
69def parse_values_internal(value, start, end, separator):
70 out = []
71
72 inside = False
73 accum = []
74 for ch in value:
75 if not inside:
76 if ch == start:
77 inside = True
78 accum = []
79 else:
80 if ch == end:
81 inside = False
82 out.append(''.join(accum))
83 accum = []
84 else:
85 accum.append(ch)
86
87 if separator == ' ':
88 out = [v.split() for v in out]
89
90 if len(out) == 1:
91 return parse_value(out[0])
92
93 return [parse_value(v) for v in out]
94
95def parse_values(value, start, end, separator):
96 out = parse_values_internal(value, start, end, separator)
97 if isinstance(out, list) and all(isinstance(v, str) and len(v) == 1 and not v.isalpha() for v in out):
98 return bytearray(out)
99 return out
100
101def parse_value(value):
102 if value == '':
103 return value
104
105 if isinstance(value, list):
106 out = [parse_value(v) for v in value]
107 return out[0] if len(out) == 1 else out
108
109 if value[0] == '<':
110 return parse_values(value, '<', '>', ' ')
111 if value[0] == '"':
112 return parse_values(value, '"', '"', ',')
113 if value[0] == '[':
114 return parse_values(value, '[', ']', ' ')
115
116 if value[0] == '&':
117 return {'ref': value[1:]}
118
119 if value[0].isdigit():
120 if value.startswith("0x"):
121 return int(value, 16)
122 if value[0] == '0':
123 return int(value, 8)
124 return int(value, 10)
125
126 return value
127
128def parse_property(property, fd):
129 if '=' in property:
130 key, value = property.split('=', 1)
131 value = ' '.join(read_until(value, fd, ';')).strip()
132 if not value.endswith(';'):
133 raise SyntaxError("parse_property: missing semicolon: %s" % value)
134 return key.strip(), parse_value(value[:-1])
135
136 property = property.strip()
137 if not property.endswith(';'):
138 raise SyntaxError("parse_property: missing semicolon: %s" % property)
139
140 return property[:-1].strip(), True
141
142def build_node_name(name, addr):
143 if addr is None:
144 return name
Kumar Gala46e47552017-04-12 17:16:41 -0500145 elif isinstance(addr, int):
146 return '%s@%x' % (name, addr)
147
148 return '%s@%s' % (name, addr.strip())
Leandro Pereiraca42e852016-12-08 12:55:45 -0800149
150def parse_node(line, fd):
Kumar Gala46e47552017-04-12 17:16:41 -0500151 label, name, addr, numeric_addr = parse_node_name(line)
Leandro Pereiraca42e852016-12-08 12:55:45 -0800152
153 node = {
154 'label': label,
155 'type': type,
Kumar Gala46e47552017-04-12 17:16:41 -0500156 'addr': numeric_addr,
Leandro Pereiraca42e852016-12-08 12:55:45 -0800157 'children': {},
158 'props': {},
159 'name': build_node_name(name, addr)
160 }
161 while True:
162 line = fd.readline()
163 if not line:
164 raise SyntaxError("parse_node: Missing } while parsing node")
165
166 line = clean_line(line, fd)
167 if not line:
168 continue
169
170 if line == "};":
171 break
172
173 if line.endswith('{'):
174 new_node = parse_node(line, fd)
175 node['children'][new_node['name']] = new_node
176 else:
177 key, value = parse_property(line, fd)
178 node['props'][key] = value
179
180 return node
181
182def parse_file(fd, ignore_dts_version=False):
183 nodes = {}
184 has_v1_tag = False
185 while True:
186 line = fd.readline()
187 if not line:
188 break
189
190 line = clean_line(line, fd)
191 if not line:
192 continue
193
194 if line.startswith('/include/ '):
195 tag, filename = line.split()
196 with open(filename.strip()[1:-1], "r") as new_fd:
197 nodes.update(parse_file(new_fd, True))
198 elif line == '/dts-v1/;':
199 has_v1_tag = True
200 elif line.startswith('/memreserve/ ') and line.endswith(';'):
201 tag, start, end = line.split()
202 start = int(start, 16)
203 end = int(end[:-1], 16)
204 label = "reserved_memory_0x%x_0x%x" % (start, end)
205 nodes[label] = {
206 'type': 'memory',
207 'reg': [start, end],
208 'label': label,
209 'addr': start,
210 'name': build_node_name(name, start)
211 }
212 elif line.endswith('{'):
213 if not has_v1_tag and not ignore_dts_version:
214 raise SyntaxError("parse_file: Missing /dts-v1/ tag")
215
216 new_node = parse_node(line, fd)
217 nodes[new_node['name']] = new_node
218 else:
219 raise SyntaxError("parse_file: Couldn't understand the line: %s" % line)
220 return nodes
221
222def dump_refs(name, value, indent=0):
223 spaces = ' ' * indent
224
225 out = []
226 if isinstance(value, dict) and 'ref' in value:
227 out.append('%s\"%s\" -> \"%s\";' % (spaces, name, value['ref']))
228 elif isinstance(value, list):
229 for elem in value:
230 out.extend(dump_refs(name, elem, indent))
231
232 return out
233
234def dump_all_refs(name, props, indent=0):
235 out = []
236 for key, value in props.items():
237 out.extend(dump_refs(name, value, indent))
238 return out
239
240def next_subgraph(count=[0]):
241 count[0] += 1
242 return 'subgraph cluster_%d' % count[0]
243
244def get_dot_node_name(node):
245 name = node['name']
246 return name[1:] if name[0] == '&' else name
247
248def dump_to_dot(nodes, indent=0, start_string='digraph devicetree', name=None):
249 spaces = ' ' * indent
250
251 print("%s%s {" % (spaces, start_string))
252
253 if name is not None:
254 print("%slabel = \"%s\";" % (spaces, name))
255 print("%s\"%s\";" % (spaces, name))
256
257 ref_list = []
258 for key, value in nodes.items():
259 if value.get('children'):
260 refs = dump_to_dot(value['children'], indent + 1, next_subgraph(), get_dot_node_name(value))
261 ref_list.extend(refs)
262 else:
263 print("%s\"%s\";" % (spaces, get_dot_node_name(value)))
264
265 for key, value in nodes.items():
266 refs = dump_all_refs(get_dot_node_name(value), value.get('props', {}), indent)
267 ref_list.extend(refs)
268
269 if start_string.startswith("digraph"):
270 print("%s%s" % (spaces, '\n'.join(ref_list)))
271
272 print("%s}" % spaces)
273
274 return ref_list
275
276def main(args):
277 if len(args) == 1:
278 print('Usage: %s filename.dts' % args[0])
279 return 1
280
281 if '--dot' in args:
282 formatter = dump_to_dot
283 args.remove('--dot')
284 else:
285 formatter = lambda nodes: pprint.pprint(nodes, indent=2)
286
287 with open(args[1], "r") as fd:
288 formatter(parse_file(fd))
289
290 return 0
291
292if __name__ == '__main__':
293 sys.exit(main(sys.argv))