blob: 206b2b375836074e457bb5dd149bbcef40a2c202 [file] [log] [blame]
Simon Glassa542a702020-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('.', '_')
36 return new
37
38def get_compat_name(node):
39 """Get the node's list of compatible string as a C identifiers
40
41 Args:
42 node (fdt.Node): Node object to check
43 Return:
44 list of str: List of C identifiers for all the compatible strings
45 """
46 compat = node.props['compatible'].value
47 if not isinstance(compat, list):
48 compat = [compat]
49 return [conv_name_to_c(c) for c in compat]
50
51
52class Driver:
53 """Information about a driver in U-Boot
54
55 Attributes:
56 name: Name of driver. For U_BOOT_DRIVER(x) this is 'x'
Simon Glassc58662f2021-02-03 06:00:50 -070057 fname: Filename where the driver was found
58 uclass_id: Name of uclass, e.g. 'UCLASS_I2C'
59 compat: Driver data for each compatible string:
60 key: Compatible string, e.g. 'rockchip,rk3288-grf'
61 value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
62 fname: Filename where the driver was found
63 priv (str): struct name of the priv_auto member, e.g. 'serial_priv'
Simon Glassc8b19b02021-02-03 06:00:53 -070064 plat (str): struct name of the plat_auto member, e.g. 'serial_plat'
65 child_priv (str): struct name of the per_child_auto member,
66 e.g. 'pci_child_priv'
67 child_plat (str): struct name of the per_child_plat_auto member,
68 e.g. 'pci_child_plat'
Simon Glassb9319c42021-02-03 06:01:01 -070069 used (bool): True if the driver is used by the structs being output
Simon Glassb00f0062021-02-03 06:01:02 -070070 phase (str): Which phase of U-Boot to use this driver
Simon Glass735ddfc2021-02-03 06:01:04 -070071 headers (list): List of header files needed for this driver (each a str)
72 e.g. ['<asm/cpu.h>']
Simon Glassa542a702020-12-28 20:35:06 -070073 """
Simon Glassc58662f2021-02-03 06:00:50 -070074 def __init__(self, name, fname):
Simon Glassa542a702020-12-28 20:35:06 -070075 self.name = name
Simon Glassc58662f2021-02-03 06:00:50 -070076 self.fname = fname
77 self.uclass_id = None
78 self.compat = None
79 self.priv = ''
Simon Glassc8b19b02021-02-03 06:00:53 -070080 self.plat = ''
81 self.child_priv = ''
82 self.child_plat = ''
Simon Glassb9319c42021-02-03 06:01:01 -070083 self.used = False
Simon Glassb00f0062021-02-03 06:01:02 -070084 self.phase = ''
Simon Glass735ddfc2021-02-03 06:01:04 -070085 self.headers = []
Simon Glassa542a702020-12-28 20:35:06 -070086
87 def __eq__(self, other):
Simon Glassc58662f2021-02-03 06:00:50 -070088 return (self.name == other.name and
89 self.uclass_id == other.uclass_id and
90 self.compat == other.compat and
Simon Glassc8b19b02021-02-03 06:00:53 -070091 self.priv == other.priv and
Simon Glassb9319c42021-02-03 06:01:01 -070092 self.plat == other.plat and
93 self.used == other.used)
Simon Glassa542a702020-12-28 20:35:06 -070094
95 def __repr__(self):
Simon Glassb9319c42021-02-03 06:01:01 -070096 return ("Driver(name='%s', used=%s, uclass_id='%s', compat=%s, priv=%s)" %
97 (self.name, self.used, self.uclass_id, self.compat, self.priv))
Simon Glassa542a702020-12-28 20:35:06 -070098
99
Simon Glass1a8b4b92021-02-03 06:00:54 -0700100class UclassDriver:
101 """Holds information about a uclass driver
102
103 Attributes:
104 name: Uclass name, e.g. 'i2c' if the driver is for UCLASS_I2C
105 uclass_id: Uclass ID, e.g. 'UCLASS_I2C'
106 priv: struct name of the private data, e.g. 'i2c_priv'
107 per_dev_priv (str): struct name of the priv_auto member, e.g. 'spi_info'
108 per_dev_plat (str): struct name of the plat_auto member, e.g. 'i2c_chip'
109 per_child_priv (str): struct name of the per_child_auto member,
110 e.g. 'pci_child_priv'
111 per_child_plat (str): struct name of the per_child_plat_auto member,
112 e.g. 'pci_child_plat'
113 """
114 def __init__(self, name):
115 self.name = name
116 self.uclass_id = None
117 self.priv = ''
118 self.per_dev_priv = ''
119 self.per_dev_plat = ''
120 self.per_child_priv = ''
121 self.per_child_plat = ''
122
123 def __eq__(self, other):
124 return (self.name == other.name and
125 self.uclass_id == other.uclass_id and
126 self.priv == other.priv)
127
128 def __repr__(self):
129 return ("UclassDriver(name='%s', uclass_id='%s')" %
130 (self.name, self.uclass_id))
131
132 def __hash__(self):
133 # We can use the uclass ID since it is unique among uclasses
134 return hash(self.uclass_id)
135
136
Simon Glassacf5cb82021-02-03 06:00:55 -0700137class Struct:
138 """Holds information about a struct definition
139
140 Attributes:
141 name: Struct name, e.g. 'fred' if the struct is 'struct fred'
142 fname: Filename containing the struct, in a format that C files can
143 include, e.g. 'asm/clk.h'
144 """
145 def __init__(self, name, fname):
146 self.name = name
147 self.fname =fname
148
149 def __repr__(self):
150 return ("Struct(name='%s', fname='%s')" % (self.name, self.fname))
151
152
Simon Glassa542a702020-12-28 20:35:06 -0700153class Scanner:
154 """Scanning of the U-Boot source tree
155
156 Properties:
157 _basedir (str): Base directory of U-Boot source code. Defaults to the
158 grandparent of this file's directory
159 _drivers: Dict of valid driver names found in drivers/
160 key: Driver name
161 value: Driver for that driver
162 _driver_aliases: Dict that holds aliases for driver names
163 key: Driver alias declared with
164 DM_DRIVER_ALIAS(driver_alias, driver_name)
165 value: Driver name declared with U_BOOT_DRIVER(driver_name)
Simon Glass10ea9c02020-12-28 20:35:07 -0700166 _warning_disabled: true to disable warnings about driver names not found
Simon Glassa542a702020-12-28 20:35:06 -0700167 _drivers_additional (list or str): List of additional drivers to use
168 during scanning
Simon Glassc58662f2021-02-03 06:00:50 -0700169 _of_match: Dict holding information about compatible strings
170 key: Name of struct udevice_id variable
171 value: Dict of compatible info in that variable:
172 key: Compatible string, e.g. 'rockchip,rk3288-grf'
173 value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
174 _compat_to_driver: Maps compatible strings to Driver
Simon Glass1a8b4b92021-02-03 06:00:54 -0700175 _uclass: Dict of uclass information
176 key: uclass name, e.g. 'UCLASS_I2C'
177 value: UClassDriver
Simon Glassacf5cb82021-02-03 06:00:55 -0700178 _structs: Dict of all structs found in U-Boot:
179 key: Name of struct
180 value: Struct object
Simon Glassb00f0062021-02-03 06:01:02 -0700181 _phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
182 or 'tpl'. None if not known
Simon Glassa542a702020-12-28 20:35:06 -0700183 """
Simon Glassb00f0062021-02-03 06:01:02 -0700184 def __init__(self, basedir, warning_disabled, drivers_additional, phase=''):
Simon Glassa542a702020-12-28 20:35:06 -0700185 """Set up a new Scanner
186 """
187 if not basedir:
188 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
189 if basedir == '':
190 basedir = './'
191 self._basedir = basedir
192 self._drivers = {}
193 self._driver_aliases = {}
194 self._drivers_additional = drivers_additional or []
195 self._warning_disabled = warning_disabled
Simon Glassc58662f2021-02-03 06:00:50 -0700196 self._of_match = {}
197 self._compat_to_driver = {}
Simon Glass1a8b4b92021-02-03 06:00:54 -0700198 self._uclass = {}
Simon Glassacf5cb82021-02-03 06:00:55 -0700199 self._structs = {}
Simon Glassb00f0062021-02-03 06:01:02 -0700200 self._phase = phase
Simon Glassa542a702020-12-28 20:35:06 -0700201
Simon Glassfd471e22021-02-03 06:01:00 -0700202 def get_driver(self, name):
203 """Get a driver given its name
204
205 Args:
206 name (str): Driver name
207
208 Returns:
209 Driver: Driver or None if not found
210 """
211 return self._drivers.get(name)
212
Simon Glassa542a702020-12-28 20:35:06 -0700213 def get_normalized_compat_name(self, node):
214 """Get a node's normalized compat name
215
216 Returns a valid driver name by retrieving node's list of compatible
217 string as a C identifier and performing a check against _drivers
218 and a lookup in driver_aliases printing a warning in case of failure.
219
220 Args:
221 node (Node): Node object to check
222 Return:
223 Tuple:
224 Driver name associated with the first compatible string
225 List of C identifiers for all the other compatible strings
226 (possibly empty)
227 In case of no match found, the return will be the same as
228 get_compat_name()
229 """
230 compat_list_c = get_compat_name(node)
231
232 for compat_c in compat_list_c:
233 if not compat_c in self._drivers.keys():
234 compat_c = self._driver_aliases.get(compat_c)
235 if not compat_c:
236 continue
237
238 aliases_c = compat_list_c
239 if compat_c in aliases_c:
240 aliases_c.remove(compat_c)
241 return compat_c, aliases_c
242
243 if not self._warning_disabled:
244 print('WARNING: the driver %s was not found in the driver list'
245 % (compat_list_c[0]))
246
247 return compat_list_c[0], compat_list_c[1:]
248
Simon Glassacf5cb82021-02-03 06:00:55 -0700249 def _parse_structs(self, fname, buff):
250 """Parse a H file to extract struct definitions contained within
251
252 This parses 'struct xx {' definitions to figure out what structs this
253 header defines.
254
255 Args:
256 buff (str): Contents of file
257 fname (str): Filename (to use when printing errors)
258 """
259 structs = {}
260
261 re_struct = re.compile('^struct ([a-z0-9_]+) {$')
262 re_asm = re.compile('../arch/[a-z0-9]+/include/asm/(.*)')
263 prefix = ''
264 for line in buff.splitlines():
265 # Handle line continuation
266 if prefix:
267 line = prefix + line
268 prefix = ''
269 if line.endswith('\\'):
270 prefix = line[:-1]
271 continue
272
273 m_struct = re_struct.match(line)
274 if m_struct:
275 name = m_struct.group(1)
276 include_dir = os.path.join(self._basedir, 'include')
277 rel_fname = os.path.relpath(fname, include_dir)
278 m_asm = re_asm.match(rel_fname)
279 if m_asm:
280 rel_fname = 'asm/' + m_asm.group(1)
281 structs[name] = Struct(name, rel_fname)
282 self._structs.update(structs)
283
Simon Glassc58662f2021-02-03 06:00:50 -0700284 @classmethod
285 def _get_re_for_member(cls, member):
286 """_get_re_for_member: Get a compiled regular expression
287
288 Args:
289 member (str): Struct member name, e.g. 'priv_auto'
290
291 Returns:
292 re.Pattern: Compiled regular expression that parses:
293
294 .member = sizeof(struct fred),
295
296 and returns "fred" as group 1
297 """
298 return re.compile(r'^\s*.%s\s*=\s*sizeof\(struct\s+(.*)\),$' % member)
299
Simon Glass1a8b4b92021-02-03 06:00:54 -0700300 def _parse_uclass_driver(self, fname, buff):
301 """Parse a C file to extract uclass driver information contained within
302
303 This parses UCLASS_DRIVER() structs to obtain various pieces of useful
304 information.
305
306 It updates the following member:
307 _uclass: Dict of uclass information
308 key: uclass name, e.g. 'UCLASS_I2C'
309 value: UClassDriver
310
311 Args:
312 fname (str): Filename being parsed (used for warnings)
313 buff (str): Contents of file
314 """
315 uc_drivers = {}
316
317 # Collect the driver name and associated Driver
318 driver = None
319 re_driver = re.compile(r'UCLASS_DRIVER\((.*)\)')
320
321 # Collect the uclass ID, e.g. 'UCLASS_SPI'
322 re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)')
323
324 # Matches the header/size information for uclass-private data
325 re_priv = self._get_re_for_member('priv_auto')
326
327 # Set up parsing for the auto members
328 re_per_device_priv = self._get_re_for_member('per_device_auto')
329 re_per_device_plat = self._get_re_for_member('per_device_plat_auto')
330 re_per_child_priv = self._get_re_for_member('per_child_auto')
331 re_per_child_plat = self._get_re_for_member('per_child_plat_auto')
332
333 prefix = ''
334 for line in buff.splitlines():
335 # Handle line continuation
336 if prefix:
337 line = prefix + line
338 prefix = ''
339 if line.endswith('\\'):
340 prefix = line[:-1]
341 continue
342
343 driver_match = re_driver.search(line)
344
345 # If we have seen UCLASS_DRIVER()...
346 if driver:
347 m_id = re_id.search(line)
348 m_priv = re_priv.match(line)
349 m_per_dev_priv = re_per_device_priv.match(line)
350 m_per_dev_plat = re_per_device_plat.match(line)
351 m_per_child_priv = re_per_child_priv.match(line)
352 m_per_child_plat = re_per_child_plat.match(line)
353 if m_id:
354 driver.uclass_id = m_id.group(1)
355 elif m_priv:
356 driver.priv = m_priv.group(1)
357 elif m_per_dev_priv:
358 driver.per_dev_priv = m_per_dev_priv.group(1)
359 elif m_per_dev_plat:
360 driver.per_dev_plat = m_per_dev_plat.group(1)
361 elif m_per_child_priv:
362 driver.per_child_priv = m_per_child_priv.group(1)
363 elif m_per_child_plat:
364 driver.per_child_plat = m_per_child_plat.group(1)
365 elif '};' in line:
366 if not driver.uclass_id:
367 raise ValueError(
368 "%s: Cannot parse uclass ID in driver '%s'" %
369 (fname, driver.name))
370 uc_drivers[driver.uclass_id] = driver
371 driver = None
372
373 elif driver_match:
374 driver_name = driver_match.group(1)
375 driver = UclassDriver(driver_name)
376
377 self._uclass.update(uc_drivers)
378
Simon Glassc58662f2021-02-03 06:00:50 -0700379 def _parse_driver(self, fname, buff):
380 """Parse a C file to extract driver information contained within
381
382 This parses U_BOOT_DRIVER() structs to obtain various pieces of useful
383 information.
384
385 It updates the following members:
386 _drivers - updated with new Driver records for each driver found
387 in the file
388 _of_match - updated with each compatible string found in the file
389 _compat_to_driver - Maps compatible string to Driver
390
391 Args:
392 fname (str): Filename being parsed (used for warnings)
393 buff (str): Contents of file
394
395 Raises:
396 ValueError: Compatible variable is mentioned in .of_match in
397 U_BOOT_DRIVER() but not found in the file
398 """
399 # Dict holding information about compatible strings collected in this
400 # function so far
401 # key: Name of struct udevice_id variable
402 # value: Dict of compatible info in that variable:
403 # key: Compatible string, e.g. 'rockchip,rk3288-grf'
404 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
405 of_match = {}
406
407 # Dict holding driver information collected in this function so far
408 # key: Driver name (C name as in U_BOOT_DRIVER(xxx))
409 # value: Driver
410 drivers = {}
411
412 # Collect the driver info
413 driver = None
414 re_driver = re.compile(r'U_BOOT_DRIVER\((.*)\)')
415
416 # Collect the uclass ID, e.g. 'UCLASS_SPI'
417 re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)')
418
419 # Collect the compatible string, e.g. 'rockchip,rk3288-grf'
420 compat = None
421 re_compat = re.compile(r'{\s*.compatible\s*=\s*"(.*)"\s*'
422 r'(,\s*.data\s*=\s*(\S*))?\s*},')
423
424 # This is a dict of compatible strings that were found:
425 # key: Compatible string, e.g. 'rockchip,rk3288-grf'
426 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
427 compat_dict = {}
428
429 # Holds the var nane of the udevice_id list, e.g.
430 # 'rk3288_syscon_ids_noc' in
431 # static const struct udevice_id rk3288_syscon_ids_noc[] = {
432 ids_name = None
433 re_ids = re.compile(r'struct udevice_id (.*)\[\]\s*=')
434
435 # Matches the references to the udevice_id list
436 re_of_match = re.compile(
437 r'\.of_match\s*=\s*(of_match_ptr\()?([a-z0-9_]+)(\))?,')
438
Simon Glassb00f0062021-02-03 06:01:02 -0700439 re_phase = re.compile('^\s*DM_PHASE\((.*)\).*$')
Simon Glass735ddfc2021-02-03 06:01:04 -0700440 re_hdr = re.compile('^\s*DM_HEADER\((.*)\).*$')
Simon Glassb00f0062021-02-03 06:01:02 -0700441
Simon Glassc8b19b02021-02-03 06:00:53 -0700442 # Matches the struct name for priv, plat
Simon Glassc58662f2021-02-03 06:00:50 -0700443 re_priv = self._get_re_for_member('priv_auto')
Simon Glassc8b19b02021-02-03 06:00:53 -0700444 re_plat = self._get_re_for_member('plat_auto')
445 re_child_priv = self._get_re_for_member('per_child_auto')
446 re_child_plat = self._get_re_for_member('per_child_plat_auto')
Simon Glassc58662f2021-02-03 06:00:50 -0700447
448 prefix = ''
449 for line in buff.splitlines():
450 # Handle line continuation
451 if prefix:
452 line = prefix + line
453 prefix = ''
454 if line.endswith('\\'):
455 prefix = line[:-1]
456 continue
457
458 driver_match = re_driver.search(line)
459
460 # If this line contains U_BOOT_DRIVER()...
461 if driver:
462 m_id = re_id.search(line)
463 m_of_match = re_of_match.search(line)
464 m_priv = re_priv.match(line)
Simon Glassc8b19b02021-02-03 06:00:53 -0700465 m_plat = re_plat.match(line)
466 m_cplat = re_child_plat.match(line)
467 m_cpriv = re_child_priv.match(line)
Simon Glassb00f0062021-02-03 06:01:02 -0700468 m_phase = re_phase.match(line)
Simon Glass735ddfc2021-02-03 06:01:04 -0700469 m_hdr = re_hdr.match(line)
Simon Glassc58662f2021-02-03 06:00:50 -0700470 if m_priv:
471 driver.priv = m_priv.group(1)
Simon Glassc8b19b02021-02-03 06:00:53 -0700472 elif m_plat:
473 driver.plat = m_plat.group(1)
474 elif m_cplat:
475 driver.child_plat = m_cplat.group(1)
476 elif m_cpriv:
477 driver.child_priv = m_cpriv.group(1)
Simon Glassc58662f2021-02-03 06:00:50 -0700478 elif m_id:
479 driver.uclass_id = m_id.group(1)
480 elif m_of_match:
481 compat = m_of_match.group(2)
Simon Glassb00f0062021-02-03 06:01:02 -0700482 elif m_phase:
483 driver.phase = m_phase.group(1)
Simon Glass735ddfc2021-02-03 06:01:04 -0700484 elif m_hdr:
485 driver.headers.append(m_hdr.group(1))
Simon Glassc58662f2021-02-03 06:00:50 -0700486 elif '};' in line:
487 if driver.uclass_id and compat:
488 if compat not in of_match:
489 raise ValueError(
490 "%s: Unknown compatible var '%s' (found: %s)" %
491 (fname, compat, ','.join(of_match.keys())))
492 driver.compat = of_match[compat]
493
494 # This needs to be deterministic, since a driver may
495 # have multiple compatible strings pointing to it.
496 # We record the one earliest in the alphabet so it
497 # will produce the same result on all machines.
498 for compat_id in of_match[compat]:
499 old = self._compat_to_driver.get(compat_id)
500 if not old or driver.name < old.name:
501 self._compat_to_driver[compat_id] = driver
502 drivers[driver.name] = driver
503 else:
504 # The driver does not have a uclass or compat string.
505 # The first is required but the second is not, so just
506 # ignore this.
507 pass
508 driver = None
509 ids_name = None
510 compat = None
511 compat_dict = {}
512
513 elif ids_name:
514 compat_m = re_compat.search(line)
515 if compat_m:
516 compat_dict[compat_m.group(1)] = compat_m.group(3)
517 elif '};' in line:
518 of_match[ids_name] = compat_dict
519 ids_name = None
520 elif driver_match:
521 driver_name = driver_match.group(1)
522 driver = Driver(driver_name, fname)
523 else:
524 ids_m = re_ids.search(line)
525 if ids_m:
526 ids_name = ids_m.group(1)
527
528 # Make the updates based on what we found
529 self._drivers.update(drivers)
530 self._of_match.update(of_match)
531
Simon Glassa542a702020-12-28 20:35:06 -0700532 def scan_driver(self, fname):
533 """Scan a driver file to build a list of driver names and aliases
534
Simon Glassc58662f2021-02-03 06:00:50 -0700535 It updates the following members:
536 _drivers - updated with new Driver records for each driver found
537 in the file
538 _of_match - updated with each compatible string found in the file
539 _compat_to_driver - Maps compatible string to Driver
540 _driver_aliases - Maps alias names to driver name
Simon Glassa542a702020-12-28 20:35:06 -0700541
542 Args
543 fname: Driver filename to scan
544 """
545 with open(fname, encoding='utf-8') as inf:
546 try:
547 buff = inf.read()
548 except UnicodeDecodeError:
549 # This seems to happen on older Python versions
550 print("Skipping file '%s' due to unicode error" % fname)
551 return
552
Simon Glassc58662f2021-02-03 06:00:50 -0700553 # If this file has any U_BOOT_DRIVER() declarations, process it to
554 # obtain driver information
555 if 'U_BOOT_DRIVER' in buff:
556 self._parse_driver(fname, buff)
Simon Glass1a8b4b92021-02-03 06:00:54 -0700557 if 'UCLASS_DRIVER' in buff:
558 self._parse_uclass_driver(fname, buff)
Simon Glassa542a702020-12-28 20:35:06 -0700559
560 # The following re will search for driver aliases declared as
561 # DM_DRIVER_ALIAS(alias, driver_name)
562 driver_aliases = re.findall(
563 r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)',
564 buff)
565
566 for alias in driver_aliases: # pragma: no cover
567 if len(alias) != 2:
568 continue
569 self._driver_aliases[alias[1]] = alias[0]
570
Simon Glassacf5cb82021-02-03 06:00:55 -0700571 def scan_header(self, fname):
572 """Scan a header file to build a list of struct definitions
573
574 It updates the following members:
575 _structs - updated with new Struct records for each struct found
576 in the file
577
578 Args
579 fname: header filename to scan
580 """
581 with open(fname, encoding='utf-8') as inf:
582 try:
583 buff = inf.read()
584 except UnicodeDecodeError:
585 # This seems to happen on older Python versions
586 print("Skipping file '%s' due to unicode error" % fname)
587 return
588
589 # If this file has any U_BOOT_DRIVER() declarations, process it to
590 # obtain driver information
591 if 'struct' in buff:
592 self._parse_structs(fname, buff)
593
Simon Glassa542a702020-12-28 20:35:06 -0700594 def scan_drivers(self):
595 """Scan the driver folders to build a list of driver names and aliases
596
597 This procedure will populate self._drivers and self._driver_aliases
598 """
599 for (dirpath, _, filenames) in os.walk(self._basedir):
Simon Glass36b22202021-02-03 06:00:52 -0700600 rel_path = dirpath[len(self._basedir):]
601 if rel_path.startswith('/'):
602 rel_path = rel_path[1:]
603 if rel_path.startswith('build') or rel_path.startswith('.git'):
604 continue
Simon Glassa542a702020-12-28 20:35:06 -0700605 for fname in filenames:
Simon Glassacf5cb82021-02-03 06:00:55 -0700606 pathname = dirpath + '/' + fname
607 if fname.endswith('.c'):
608 self.scan_driver(pathname)
609 elif fname.endswith('.h'):
610 self.scan_header(pathname)
Simon Glassa542a702020-12-28 20:35:06 -0700611
612 for fname in self._drivers_additional:
613 if not isinstance(fname, str) or len(fname) == 0:
614 continue
615 if fname[0] == '/':
616 self.scan_driver(fname)
617 else:
618 self.scan_driver(self._basedir + '/' + fname)
Simon Glassb9319c42021-02-03 06:01:01 -0700619
620 def mark_used(self, nodes):
621 """Mark the drivers associated with a list of nodes as 'used'
622
623 This takes a list of nodes, finds the driver for each one and marks it
624 as used.
625
626 Args:
627 nodes (list of None): Nodes that are in use
628 """
629 # Figure out which drivers we actually use
630 for node in nodes:
631 struct_name, _ = self.get_normalized_compat_name(node)
632 driver = self._drivers.get(struct_name)
633 if driver:
634 driver.used = True