blob: 114212cfe2dfb129a0b26a8fe611990187a0ec16 [file] [log] [blame]
Simon Glass9065bc92020-12-28 20:35:06 -07001#!/usr/bin/python
2# SPDX-License-Identifier: GPL-2.0+
3#
4# Copyright (C) 2017 Google, Inc
5# Written by Simon Glass <sjg@chromium.org>
6#
7
8"""Scanning of U-Boot source for drivers and structs
9
10This scans the source tree to find out things about all instances of
11U_BOOT_DRIVER(), UCLASS_DRIVER and all struct declarations in header files.
12
13See doc/driver-model/of-plat.rst for more informaiton
14"""
15
16import os
17import re
18import sys
19
20
21def conv_name_to_c(name):
22 """Convert a device-tree name to a C identifier
23
24 This uses multiple replace() calls instead of re.sub() since it is faster
25 (400ms for 1m calls versus 1000ms for the 're' version).
26
27 Args:
28 name (str): Name to convert
29 Return:
30 str: String containing the C version of this name
31 """
32 new = name.replace('@', '_at_')
33 new = new.replace('-', '_')
34 new = new.replace(',', '_')
35 new = new.replace('.', '_')
Simon Glassc14fd0c2021-02-03 06:01:11 -070036 if new == '/':
37 return 'root'
Simon Glass9065bc92020-12-28 20:35:06 -070038 return new
39
40def get_compat_name(node):
41 """Get the node's list of compatible string as a C identifiers
42
43 Args:
44 node (fdt.Node): Node object to check
45 Return:
46 list of str: List of C identifiers for all the compatible strings
47 """
48 compat = node.props['compatible'].value
49 if not isinstance(compat, list):
50 compat = [compat]
51 return [conv_name_to_c(c) for c in compat]
52
53
54class Driver:
55 """Information about a driver in U-Boot
56
57 Attributes:
58 name: Name of driver. For U_BOOT_DRIVER(x) this is 'x'
Simon Glass78933b72021-02-03 06:00:50 -070059 fname: Filename where the driver was found
60 uclass_id: Name of uclass, e.g. 'UCLASS_I2C'
61 compat: Driver data for each compatible string:
62 key: Compatible string, e.g. 'rockchip,rk3288-grf'
63 value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
64 fname: Filename where the driver was found
65 priv (str): struct name of the priv_auto member, e.g. 'serial_priv'
Simon Glass0f3b1412021-02-03 06:00:53 -070066 plat (str): struct name of the plat_auto member, e.g. 'serial_plat'
67 child_priv (str): struct name of the per_child_auto member,
68 e.g. 'pci_child_priv'
69 child_plat (str): struct name of the per_child_plat_auto member,
70 e.g. 'pci_child_plat'
Simon Glasseb3c2492021-02-03 06:01:01 -070071 used (bool): True if the driver is used by the structs being output
Simon Glassf303ee72021-02-03 06:01:02 -070072 phase (str): Which phase of U-Boot to use this driver
Simon Glassa7b1c772021-02-03 06:01:04 -070073 headers (list): List of header files needed for this driver (each a str)
74 e.g. ['<asm/cpu.h>']
Simon Glass9b2eac02021-02-03 06:01:06 -070075 dups (list): Driver objects with the same name as this one, that were
76 found after this one
77 warn_dups (bool): True if the duplicates are not distinguisble using
78 the phase
Simon Glass80d782c42021-02-03 06:01:10 -070079 uclass (Uclass): uclass for this driver
Simon Glass9065bc92020-12-28 20:35:06 -070080 """
Simon Glass78933b72021-02-03 06:00:50 -070081 def __init__(self, name, fname):
Simon Glass9065bc92020-12-28 20:35:06 -070082 self.name = name
Simon Glass78933b72021-02-03 06:00:50 -070083 self.fname = fname
84 self.uclass_id = None
85 self.compat = None
86 self.priv = ''
Simon Glass0f3b1412021-02-03 06:00:53 -070087 self.plat = ''
88 self.child_priv = ''
89 self.child_plat = ''
Simon Glasseb3c2492021-02-03 06:01:01 -070090 self.used = False
Simon Glassf303ee72021-02-03 06:01:02 -070091 self.phase = ''
Simon Glassa7b1c772021-02-03 06:01:04 -070092 self.headers = []
Simon Glass9b2eac02021-02-03 06:01:06 -070093 self.dups = []
94 self.warn_dups = False
Simon Glass80d782c42021-02-03 06:01:10 -070095 self.uclass = None
Simon Glass9065bc92020-12-28 20:35:06 -070096
97 def __eq__(self, other):
Simon Glass78933b72021-02-03 06:00:50 -070098 return (self.name == other.name and
99 self.uclass_id == other.uclass_id and
100 self.compat == other.compat and
Simon Glass0f3b1412021-02-03 06:00:53 -0700101 self.priv == other.priv and
Simon Glasseb3c2492021-02-03 06:01:01 -0700102 self.plat == other.plat and
103 self.used == other.used)
Simon Glass9065bc92020-12-28 20:35:06 -0700104
105 def __repr__(self):
Simon Glasseb3c2492021-02-03 06:01:01 -0700106 return ("Driver(name='%s', used=%s, uclass_id='%s', compat=%s, priv=%s)" %
107 (self.name, self.used, self.uclass_id, self.compat, self.priv))
Simon Glass9065bc92020-12-28 20:35:06 -0700108
109
Simon Glass2e199ab2021-02-03 06:00:54 -0700110class UclassDriver:
111 """Holds information about a uclass driver
112
113 Attributes:
114 name: Uclass name, e.g. 'i2c' if the driver is for UCLASS_I2C
115 uclass_id: Uclass ID, e.g. 'UCLASS_I2C'
116 priv: struct name of the private data, e.g. 'i2c_priv'
117 per_dev_priv (str): struct name of the priv_auto member, e.g. 'spi_info'
118 per_dev_plat (str): struct name of the plat_auto member, e.g. 'i2c_chip'
119 per_child_priv (str): struct name of the per_child_auto member,
120 e.g. 'pci_child_priv'
121 per_child_plat (str): struct name of the per_child_plat_auto member,
122 e.g. 'pci_child_plat'
Simon Glassbe88d2f2021-02-03 06:01:07 -0700123 alias_num_to_node (dict): Aliases for this uclasses (for sequence
124 numbers)
125 key (int): Alias number, e.g. 2 for "pci2"
126 value (str): Node the alias points to
127 alias_path_to_num (dict): Convert a path to an alias number
128 key (str): Full path to node (e.g. '/soc/pci')
129 seq (int): Alias number, e.g. 2 for "pci2"
Simon Glass80d782c42021-02-03 06:01:10 -0700130 devs (list): List of devices in this uclass, each a Node
131 node_refs (dict): References in the linked list of devices:
132 key (int): Sequence number (0=first, n-1=last, -1=head, n=tail)
133 value (str): Reference to the device at that position
Simon Glass2e199ab2021-02-03 06:00:54 -0700134 """
135 def __init__(self, name):
136 self.name = name
137 self.uclass_id = None
138 self.priv = ''
139 self.per_dev_priv = ''
140 self.per_dev_plat = ''
141 self.per_child_priv = ''
142 self.per_child_plat = ''
Simon Glassbe88d2f2021-02-03 06:01:07 -0700143 self.alias_num_to_node = {}
144 self.alias_path_to_num = {}
Simon Glass80d782c42021-02-03 06:01:10 -0700145 self.devs = []
146 self.node_refs = {}
Simon Glass2e199ab2021-02-03 06:00:54 -0700147
148 def __eq__(self, other):
149 return (self.name == other.name and
150 self.uclass_id == other.uclass_id and
151 self.priv == other.priv)
152
153 def __repr__(self):
154 return ("UclassDriver(name='%s', uclass_id='%s')" %
155 (self.name, self.uclass_id))
156
157 def __hash__(self):
158 # We can use the uclass ID since it is unique among uclasses
159 return hash(self.uclass_id)
160
161
Simon Glass88bd5382021-02-03 06:00:55 -0700162class Struct:
163 """Holds information about a struct definition
164
165 Attributes:
166 name: Struct name, e.g. 'fred' if the struct is 'struct fred'
167 fname: Filename containing the struct, in a format that C files can
168 include, e.g. 'asm/clk.h'
169 """
170 def __init__(self, name, fname):
171 self.name = name
172 self.fname =fname
173
174 def __repr__(self):
175 return ("Struct(name='%s', fname='%s')" % (self.name, self.fname))
176
177
Simon Glass9065bc92020-12-28 20:35:06 -0700178class Scanner:
179 """Scanning of the U-Boot source tree
180
181 Properties:
182 _basedir (str): Base directory of U-Boot source code. Defaults to the
183 grandparent of this file's directory
184 _drivers: Dict of valid driver names found in drivers/
185 key: Driver name
186 value: Driver for that driver
187 _driver_aliases: Dict that holds aliases for driver names
188 key: Driver alias declared with
189 DM_DRIVER_ALIAS(driver_alias, driver_name)
190 value: Driver name declared with U_BOOT_DRIVER(driver_name)
Simon Glassdf692c32020-12-28 20:35:07 -0700191 _warning_disabled: true to disable warnings about driver names not found
Simon Glass9065bc92020-12-28 20:35:06 -0700192 _drivers_additional (list or str): List of additional drivers to use
193 during scanning
Simon Glass78933b72021-02-03 06:00:50 -0700194 _of_match: Dict holding information about compatible strings
195 key: Name of struct udevice_id variable
196 value: Dict of compatible info in that variable:
197 key: Compatible string, e.g. 'rockchip,rk3288-grf'
198 value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
199 _compat_to_driver: Maps compatible strings to Driver
Simon Glass2e199ab2021-02-03 06:00:54 -0700200 _uclass: Dict of uclass information
201 key: uclass name, e.g. 'UCLASS_I2C'
202 value: UClassDriver
Simon Glass88bd5382021-02-03 06:00:55 -0700203 _structs: Dict of all structs found in U-Boot:
204 key: Name of struct
205 value: Struct object
Simon Glassf303ee72021-02-03 06:01:02 -0700206 _phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
207 or 'tpl'. None if not known
Simon Glass9065bc92020-12-28 20:35:06 -0700208 """
Simon Glassf303ee72021-02-03 06:01:02 -0700209 def __init__(self, basedir, warning_disabled, drivers_additional, phase=''):
Simon Glass9065bc92020-12-28 20:35:06 -0700210 """Set up a new Scanner
211 """
212 if not basedir:
213 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
214 if basedir == '':
215 basedir = './'
216 self._basedir = basedir
217 self._drivers = {}
218 self._driver_aliases = {}
219 self._drivers_additional = drivers_additional or []
220 self._warning_disabled = warning_disabled
Simon Glass78933b72021-02-03 06:00:50 -0700221 self._of_match = {}
222 self._compat_to_driver = {}
Simon Glass2e199ab2021-02-03 06:00:54 -0700223 self._uclass = {}
Simon Glass88bd5382021-02-03 06:00:55 -0700224 self._structs = {}
Simon Glassf303ee72021-02-03 06:01:02 -0700225 self._phase = phase
Simon Glass9065bc92020-12-28 20:35:06 -0700226
Simon Glass047a4802021-02-03 06:01:00 -0700227 def get_driver(self, name):
228 """Get a driver given its name
229
230 Args:
231 name (str): Driver name
232
233 Returns:
234 Driver: Driver or None if not found
235 """
236 return self._drivers.get(name)
237
Simon Glass9065bc92020-12-28 20:35:06 -0700238 def get_normalized_compat_name(self, node):
239 """Get a node's normalized compat name
240
241 Returns a valid driver name by retrieving node's list of compatible
242 string as a C identifier and performing a check against _drivers
243 and a lookup in driver_aliases printing a warning in case of failure.
244
245 Args:
246 node (Node): Node object to check
247 Return:
248 Tuple:
249 Driver name associated with the first compatible string
250 List of C identifiers for all the other compatible strings
251 (possibly empty)
252 In case of no match found, the return will be the same as
253 get_compat_name()
254 """
Simon Glassc14fd0c2021-02-03 06:01:11 -0700255 if not node.parent:
256 compat_list_c = ['root_driver']
257 else:
258 compat_list_c = get_compat_name(node)
Simon Glass9065bc92020-12-28 20:35:06 -0700259
260 for compat_c in compat_list_c:
261 if not compat_c in self._drivers.keys():
262 compat_c = self._driver_aliases.get(compat_c)
263 if not compat_c:
264 continue
265
266 aliases_c = compat_list_c
267 if compat_c in aliases_c:
268 aliases_c.remove(compat_c)
269 return compat_c, aliases_c
270
271 if not self._warning_disabled:
272 print('WARNING: the driver %s was not found in the driver list'
273 % (compat_list_c[0]))
274
275 return compat_list_c[0], compat_list_c[1:]
276
Simon Glass88bd5382021-02-03 06:00:55 -0700277 def _parse_structs(self, fname, buff):
278 """Parse a H file to extract struct definitions contained within
279
280 This parses 'struct xx {' definitions to figure out what structs this
281 header defines.
282
283 Args:
284 buff (str): Contents of file
285 fname (str): Filename (to use when printing errors)
286 """
287 structs = {}
288
289 re_struct = re.compile('^struct ([a-z0-9_]+) {$')
290 re_asm = re.compile('../arch/[a-z0-9]+/include/asm/(.*)')
291 prefix = ''
292 for line in buff.splitlines():
293 # Handle line continuation
294 if prefix:
295 line = prefix + line
296 prefix = ''
297 if line.endswith('\\'):
298 prefix = line[:-1]
299 continue
300
301 m_struct = re_struct.match(line)
302 if m_struct:
303 name = m_struct.group(1)
304 include_dir = os.path.join(self._basedir, 'include')
305 rel_fname = os.path.relpath(fname, include_dir)
306 m_asm = re_asm.match(rel_fname)
307 if m_asm:
308 rel_fname = 'asm/' + m_asm.group(1)
309 structs[name] = Struct(name, rel_fname)
310 self._structs.update(structs)
311
Simon Glass78933b72021-02-03 06:00:50 -0700312 @classmethod
313 def _get_re_for_member(cls, member):
314 """_get_re_for_member: Get a compiled regular expression
315
316 Args:
317 member (str): Struct member name, e.g. 'priv_auto'
318
319 Returns:
320 re.Pattern: Compiled regular expression that parses:
321
322 .member = sizeof(struct fred),
323
324 and returns "fred" as group 1
325 """
326 return re.compile(r'^\s*.%s\s*=\s*sizeof\(struct\s+(.*)\),$' % member)
327
Simon Glass2e199ab2021-02-03 06:00:54 -0700328 def _parse_uclass_driver(self, fname, buff):
329 """Parse a C file to extract uclass driver information contained within
330
331 This parses UCLASS_DRIVER() structs to obtain various pieces of useful
332 information.
333
334 It updates the following member:
335 _uclass: Dict of uclass information
336 key: uclass name, e.g. 'UCLASS_I2C'
337 value: UClassDriver
338
339 Args:
340 fname (str): Filename being parsed (used for warnings)
341 buff (str): Contents of file
342 """
343 uc_drivers = {}
344
345 # Collect the driver name and associated Driver
346 driver = None
Simon Glass072b9c52021-02-03 06:01:08 -0700347 re_driver = re.compile(r'^UCLASS_DRIVER\((.*)\)')
Simon Glass2e199ab2021-02-03 06:00:54 -0700348
349 # Collect the uclass ID, e.g. 'UCLASS_SPI'
350 re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)')
351
352 # Matches the header/size information for uclass-private data
353 re_priv = self._get_re_for_member('priv_auto')
354
355 # Set up parsing for the auto members
356 re_per_device_priv = self._get_re_for_member('per_device_auto')
357 re_per_device_plat = self._get_re_for_member('per_device_plat_auto')
358 re_per_child_priv = self._get_re_for_member('per_child_auto')
359 re_per_child_plat = self._get_re_for_member('per_child_plat_auto')
360
361 prefix = ''
362 for line in buff.splitlines():
363 # Handle line continuation
364 if prefix:
365 line = prefix + line
366 prefix = ''
367 if line.endswith('\\'):
368 prefix = line[:-1]
369 continue
370
371 driver_match = re_driver.search(line)
372
373 # If we have seen UCLASS_DRIVER()...
374 if driver:
375 m_id = re_id.search(line)
376 m_priv = re_priv.match(line)
377 m_per_dev_priv = re_per_device_priv.match(line)
378 m_per_dev_plat = re_per_device_plat.match(line)
379 m_per_child_priv = re_per_child_priv.match(line)
380 m_per_child_plat = re_per_child_plat.match(line)
381 if m_id:
382 driver.uclass_id = m_id.group(1)
383 elif m_priv:
384 driver.priv = m_priv.group(1)
385 elif m_per_dev_priv:
386 driver.per_dev_priv = m_per_dev_priv.group(1)
387 elif m_per_dev_plat:
388 driver.per_dev_plat = m_per_dev_plat.group(1)
389 elif m_per_child_priv:
390 driver.per_child_priv = m_per_child_priv.group(1)
391 elif m_per_child_plat:
392 driver.per_child_plat = m_per_child_plat.group(1)
393 elif '};' in line:
394 if not driver.uclass_id:
395 raise ValueError(
396 "%s: Cannot parse uclass ID in driver '%s'" %
397 (fname, driver.name))
398 uc_drivers[driver.uclass_id] = driver
399 driver = None
400
401 elif driver_match:
402 driver_name = driver_match.group(1)
403 driver = UclassDriver(driver_name)
404
405 self._uclass.update(uc_drivers)
406
Simon Glass78933b72021-02-03 06:00:50 -0700407 def _parse_driver(self, fname, buff):
408 """Parse a C file to extract driver information contained within
409
410 This parses U_BOOT_DRIVER() structs to obtain various pieces of useful
411 information.
412
413 It updates the following members:
414 _drivers - updated with new Driver records for each driver found
415 in the file
416 _of_match - updated with each compatible string found in the file
417 _compat_to_driver - Maps compatible string to Driver
Simon Glass2f4455f2021-02-03 06:01:05 -0700418 _driver_aliases - Maps alias names to driver name
Simon Glass78933b72021-02-03 06:00:50 -0700419
420 Args:
421 fname (str): Filename being parsed (used for warnings)
422 buff (str): Contents of file
423
424 Raises:
425 ValueError: Compatible variable is mentioned in .of_match in
426 U_BOOT_DRIVER() but not found in the file
427 """
428 # Dict holding information about compatible strings collected in this
429 # function so far
430 # key: Name of struct udevice_id variable
431 # value: Dict of compatible info in that variable:
432 # key: Compatible string, e.g. 'rockchip,rk3288-grf'
433 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
434 of_match = {}
435
436 # Dict holding driver information collected in this function so far
437 # key: Driver name (C name as in U_BOOT_DRIVER(xxx))
438 # value: Driver
439 drivers = {}
440
441 # Collect the driver info
442 driver = None
Simon Glass072b9c52021-02-03 06:01:08 -0700443 re_driver = re.compile(r'^U_BOOT_DRIVER\((.*)\)')
Simon Glass78933b72021-02-03 06:00:50 -0700444
445 # Collect the uclass ID, e.g. 'UCLASS_SPI'
446 re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)')
447
448 # Collect the compatible string, e.g. 'rockchip,rk3288-grf'
449 compat = None
450 re_compat = re.compile(r'{\s*.compatible\s*=\s*"(.*)"\s*'
451 r'(,\s*.data\s*=\s*(\S*))?\s*},')
452
453 # This is a dict of compatible strings that were found:
454 # key: Compatible string, e.g. 'rockchip,rk3288-grf'
455 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
456 compat_dict = {}
457
458 # Holds the var nane of the udevice_id list, e.g.
459 # 'rk3288_syscon_ids_noc' in
460 # static const struct udevice_id rk3288_syscon_ids_noc[] = {
461 ids_name = None
462 re_ids = re.compile(r'struct udevice_id (.*)\[\]\s*=')
463
464 # Matches the references to the udevice_id list
465 re_of_match = re.compile(
466 r'\.of_match\s*=\s*(of_match_ptr\()?([a-z0-9_]+)(\))?,')
467
Simon Glassf303ee72021-02-03 06:01:02 -0700468 re_phase = re.compile('^\s*DM_PHASE\((.*)\).*$')
Simon Glassa7b1c772021-02-03 06:01:04 -0700469 re_hdr = re.compile('^\s*DM_HEADER\((.*)\).*$')
Simon Glass2f4455f2021-02-03 06:01:05 -0700470 re_alias = re.compile(r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)')
Simon Glassf303ee72021-02-03 06:01:02 -0700471
Simon Glass0f3b1412021-02-03 06:00:53 -0700472 # Matches the struct name for priv, plat
Simon Glass78933b72021-02-03 06:00:50 -0700473 re_priv = self._get_re_for_member('priv_auto')
Simon Glass0f3b1412021-02-03 06:00:53 -0700474 re_plat = self._get_re_for_member('plat_auto')
475 re_child_priv = self._get_re_for_member('per_child_auto')
476 re_child_plat = self._get_re_for_member('per_child_plat_auto')
Simon Glass78933b72021-02-03 06:00:50 -0700477
478 prefix = ''
479 for line in buff.splitlines():
480 # Handle line continuation
481 if prefix:
482 line = prefix + line
483 prefix = ''
484 if line.endswith('\\'):
485 prefix = line[:-1]
486 continue
487
488 driver_match = re_driver.search(line)
489
490 # If this line contains U_BOOT_DRIVER()...
491 if driver:
492 m_id = re_id.search(line)
493 m_of_match = re_of_match.search(line)
494 m_priv = re_priv.match(line)
Simon Glass0f3b1412021-02-03 06:00:53 -0700495 m_plat = re_plat.match(line)
496 m_cplat = re_child_plat.match(line)
497 m_cpriv = re_child_priv.match(line)
Simon Glassf303ee72021-02-03 06:01:02 -0700498 m_phase = re_phase.match(line)
Simon Glassa7b1c772021-02-03 06:01:04 -0700499 m_hdr = re_hdr.match(line)
Simon Glass78933b72021-02-03 06:00:50 -0700500 if m_priv:
501 driver.priv = m_priv.group(1)
Simon Glass0f3b1412021-02-03 06:00:53 -0700502 elif m_plat:
503 driver.plat = m_plat.group(1)
504 elif m_cplat:
505 driver.child_plat = m_cplat.group(1)
506 elif m_cpriv:
507 driver.child_priv = m_cpriv.group(1)
Simon Glass78933b72021-02-03 06:00:50 -0700508 elif m_id:
509 driver.uclass_id = m_id.group(1)
510 elif m_of_match:
511 compat = m_of_match.group(2)
Simon Glassf303ee72021-02-03 06:01:02 -0700512 elif m_phase:
513 driver.phase = m_phase.group(1)
Simon Glassa7b1c772021-02-03 06:01:04 -0700514 elif m_hdr:
515 driver.headers.append(m_hdr.group(1))
Simon Glass78933b72021-02-03 06:00:50 -0700516 elif '};' in line:
Simon Glassc14fd0c2021-02-03 06:01:11 -0700517 is_root = driver.name == 'root_driver'
518 if driver.uclass_id and (compat or is_root):
519 if not is_root:
520 if compat not in of_match:
521 raise ValueError(
522 "%s: Unknown compatible var '%s' (found: %s)" %
523 (fname, compat, ','.join(of_match.keys())))
524 driver.compat = of_match[compat]
Simon Glass78933b72021-02-03 06:00:50 -0700525
Simon Glassc14fd0c2021-02-03 06:01:11 -0700526 # This needs to be deterministic, since a driver may
527 # have multiple compatible strings pointing to it.
528 # We record the one earliest in the alphabet so it
529 # will produce the same result on all machines.
530 for compat_id in of_match[compat]:
531 old = self._compat_to_driver.get(compat_id)
532 if not old or driver.name < old.name:
533 self._compat_to_driver[compat_id] = driver
Simon Glass78933b72021-02-03 06:00:50 -0700534 drivers[driver.name] = driver
535 else:
536 # The driver does not have a uclass or compat string.
537 # The first is required but the second is not, so just
538 # ignore this.
539 pass
540 driver = None
541 ids_name = None
542 compat = None
543 compat_dict = {}
544
545 elif ids_name:
546 compat_m = re_compat.search(line)
547 if compat_m:
548 compat_dict[compat_m.group(1)] = compat_m.group(3)
549 elif '};' in line:
550 of_match[ids_name] = compat_dict
551 ids_name = None
552 elif driver_match:
553 driver_name = driver_match.group(1)
554 driver = Driver(driver_name, fname)
555 else:
556 ids_m = re_ids.search(line)
Simon Glass2f4455f2021-02-03 06:01:05 -0700557 m_alias = re_alias.match(line)
Simon Glass78933b72021-02-03 06:00:50 -0700558 if ids_m:
559 ids_name = ids_m.group(1)
Simon Glass2f4455f2021-02-03 06:01:05 -0700560 elif m_alias:
561 self._driver_aliases[m_alias[2]] = m_alias[1]
Simon Glass78933b72021-02-03 06:00:50 -0700562
563 # Make the updates based on what we found
Simon Glass9b2eac02021-02-03 06:01:06 -0700564 for driver in drivers.values():
565 if driver.name in self._drivers:
566 orig = self._drivers[driver.name]
567 if self._phase:
568 # If the original driver matches our phase, use it
569 if orig.phase == self._phase:
570 orig.dups.append(driver)
571 continue
572
573 # Otherwise use the new driver, which is assumed to match
574 else:
575 # We have no way of distinguishing them
576 driver.warn_dups = True
577 driver.dups.append(orig)
578 self._drivers[driver.name] = driver
Simon Glass78933b72021-02-03 06:00:50 -0700579 self._of_match.update(of_match)
580
Simon Glass9065bc92020-12-28 20:35:06 -0700581 def scan_driver(self, fname):
582 """Scan a driver file to build a list of driver names and aliases
583
Simon Glass78933b72021-02-03 06:00:50 -0700584 It updates the following members:
585 _drivers - updated with new Driver records for each driver found
586 in the file
587 _of_match - updated with each compatible string found in the file
588 _compat_to_driver - Maps compatible string to Driver
589 _driver_aliases - Maps alias names to driver name
Simon Glass9065bc92020-12-28 20:35:06 -0700590
591 Args
592 fname: Driver filename to scan
593 """
594 with open(fname, encoding='utf-8') as inf:
595 try:
596 buff = inf.read()
597 except UnicodeDecodeError:
598 # This seems to happen on older Python versions
599 print("Skipping file '%s' due to unicode error" % fname)
600 return
601
Simon Glass78933b72021-02-03 06:00:50 -0700602 # If this file has any U_BOOT_DRIVER() declarations, process it to
603 # obtain driver information
604 if 'U_BOOT_DRIVER' in buff:
605 self._parse_driver(fname, buff)
Simon Glass2e199ab2021-02-03 06:00:54 -0700606 if 'UCLASS_DRIVER' in buff:
607 self._parse_uclass_driver(fname, buff)
Simon Glass9065bc92020-12-28 20:35:06 -0700608
Simon Glass88bd5382021-02-03 06:00:55 -0700609 def scan_header(self, fname):
610 """Scan a header file to build a list of struct definitions
611
612 It updates the following members:
613 _structs - updated with new Struct records for each struct found
614 in the file
615
616 Args
617 fname: header filename to scan
618 """
619 with open(fname, encoding='utf-8') as inf:
620 try:
621 buff = inf.read()
622 except UnicodeDecodeError:
623 # This seems to happen on older Python versions
624 print("Skipping file '%s' due to unicode error" % fname)
625 return
626
627 # If this file has any U_BOOT_DRIVER() declarations, process it to
628 # obtain driver information
629 if 'struct' in buff:
630 self._parse_structs(fname, buff)
631
Simon Glass9065bc92020-12-28 20:35:06 -0700632 def scan_drivers(self):
633 """Scan the driver folders to build a list of driver names and aliases
634
635 This procedure will populate self._drivers and self._driver_aliases
636 """
637 for (dirpath, _, filenames) in os.walk(self._basedir):
Simon Glassdc37c812021-02-03 06:00:52 -0700638 rel_path = dirpath[len(self._basedir):]
639 if rel_path.startswith('/'):
640 rel_path = rel_path[1:]
641 if rel_path.startswith('build') or rel_path.startswith('.git'):
642 continue
Simon Glass9065bc92020-12-28 20:35:06 -0700643 for fname in filenames:
Simon Glass88bd5382021-02-03 06:00:55 -0700644 pathname = dirpath + '/' + fname
645 if fname.endswith('.c'):
646 self.scan_driver(pathname)
647 elif fname.endswith('.h'):
648 self.scan_header(pathname)
Simon Glass9065bc92020-12-28 20:35:06 -0700649 for fname in self._drivers_additional:
650 if not isinstance(fname, str) or len(fname) == 0:
651 continue
652 if fname[0] == '/':
653 self.scan_driver(fname)
654 else:
655 self.scan_driver(self._basedir + '/' + fname)
Simon Glasseb3c2492021-02-03 06:01:01 -0700656
Simon Glass80d782c42021-02-03 06:01:10 -0700657 # Get the uclass for each driver
658 # TODO: Can we just get the uclass for the ones we use, e.g. in
659 # mark_used()?
660 for driver in self._drivers.values():
661 driver.uclass = self._uclass.get(driver.uclass_id)
662
Simon Glasseb3c2492021-02-03 06:01:01 -0700663 def mark_used(self, nodes):
664 """Mark the drivers associated with a list of nodes as 'used'
665
666 This takes a list of nodes, finds the driver for each one and marks it
667 as used.
668
Simon Glass9b2eac02021-02-03 06:01:06 -0700669 If two used drivers have the same name, issue a warning.
670
Simon Glasseb3c2492021-02-03 06:01:01 -0700671 Args:
672 nodes (list of None): Nodes that are in use
673 """
674 # Figure out which drivers we actually use
675 for node in nodes:
676 struct_name, _ = self.get_normalized_compat_name(node)
677 driver = self._drivers.get(struct_name)
678 if driver:
679 driver.used = True
Simon Glass9b2eac02021-02-03 06:01:06 -0700680 if driver.dups and driver.warn_dups:
681 print("Warning: Duplicate driver name '%s' (orig=%s, dups=%s)" %
682 (driver.name, driver.fname,
683 ', '.join([drv.fname for drv in driver.dups])))
Simon Glassbe88d2f2021-02-03 06:01:07 -0700684
685 def add_uclass_alias(self, name, num, node):
686 """Add an alias to a uclass
687
688 Args:
689 name: Name of uclass, e.g. 'i2c'
690 num: Alias number, e.g. 2 for alias 'i2c2'
691 node: Node the alias points to, or None if None
692
693 Returns:
694 True if the node was added
695 False if the node was not added (uclass of that name not found)
696 None if the node could not be added because it was None
697 """
698 for uclass in self._uclass.values():
699 if uclass.name == name:
700 if node is None:
701 return None
702 uclass.alias_num_to_node[int(num)] = node
703 uclass.alias_path_to_num[node.path] = int(num)
704 return True
705 return False
Simon Glass80d782c42021-02-03 06:01:10 -0700706
707 def assign_seq(self, node):
708 """Figure out the sequence number for a node
709
710 This looks in the node's uclass and assigns a sequence number if needed,
711 based on the aliases and other nodes in that uclass.
712
713 It updates the uclass alias_path_to_num and alias_num_to_node
714
715 Args:
716 node (Node): Node object to look up
717 """
718 if node.driver and node.seq == -1 and node.uclass:
719 uclass = node.uclass
720 num = uclass.alias_path_to_num.get(node.path)
721 if num is not None:
722 return num
723 else:
724 # Dynamically allocate the next available value after all
725 # existing ones
726 if uclass.alias_num_to_node:
727 start = max(uclass.alias_num_to_node.keys())
728 else:
729 start = -1
730 for seq in range(start + 1, 1000):
731 if seq not in uclass.alias_num_to_node:
732 break
733 uclass.alias_path_to_num[node.path] = seq
734 uclass.alias_num_to_node[seq] = node
735 return seq
736 return None