blob: 9d161a2cbc743dc6f8ae47a0d2cacc05cbdf1ba3 [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
Simon Glass8d6f2d32021-02-03 06:01:05 -0700390 _driver_aliases - Maps alias names to driver name
Simon Glassc58662f2021-02-03 06:00:50 -0700391
392 Args:
393 fname (str): Filename being parsed (used for warnings)
394 buff (str): Contents of file
395
396 Raises:
397 ValueError: Compatible variable is mentioned in .of_match in
398 U_BOOT_DRIVER() but not found in the file
399 """
400 # Dict holding information about compatible strings collected in this
401 # function so far
402 # key: Name of struct udevice_id variable
403 # value: Dict of compatible info in that variable:
404 # key: Compatible string, e.g. 'rockchip,rk3288-grf'
405 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
406 of_match = {}
407
408 # Dict holding driver information collected in this function so far
409 # key: Driver name (C name as in U_BOOT_DRIVER(xxx))
410 # value: Driver
411 drivers = {}
412
413 # Collect the driver info
414 driver = None
415 re_driver = re.compile(r'U_BOOT_DRIVER\((.*)\)')
416
417 # Collect the uclass ID, e.g. 'UCLASS_SPI'
418 re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)')
419
420 # Collect the compatible string, e.g. 'rockchip,rk3288-grf'
421 compat = None
422 re_compat = re.compile(r'{\s*.compatible\s*=\s*"(.*)"\s*'
423 r'(,\s*.data\s*=\s*(\S*))?\s*},')
424
425 # This is a dict of compatible strings that were found:
426 # key: Compatible string, e.g. 'rockchip,rk3288-grf'
427 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
428 compat_dict = {}
429
430 # Holds the var nane of the udevice_id list, e.g.
431 # 'rk3288_syscon_ids_noc' in
432 # static const struct udevice_id rk3288_syscon_ids_noc[] = {
433 ids_name = None
434 re_ids = re.compile(r'struct udevice_id (.*)\[\]\s*=')
435
436 # Matches the references to the udevice_id list
437 re_of_match = re.compile(
438 r'\.of_match\s*=\s*(of_match_ptr\()?([a-z0-9_]+)(\))?,')
439
Simon Glassb00f0062021-02-03 06:01:02 -0700440 re_phase = re.compile('^\s*DM_PHASE\((.*)\).*$')
Simon Glass735ddfc2021-02-03 06:01:04 -0700441 re_hdr = re.compile('^\s*DM_HEADER\((.*)\).*$')
Simon Glass8d6f2d32021-02-03 06:01:05 -0700442 re_alias = re.compile(r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)')
Simon Glassb00f0062021-02-03 06:01:02 -0700443
Simon Glassc8b19b02021-02-03 06:00:53 -0700444 # Matches the struct name for priv, plat
Simon Glassc58662f2021-02-03 06:00:50 -0700445 re_priv = self._get_re_for_member('priv_auto')
Simon Glassc8b19b02021-02-03 06:00:53 -0700446 re_plat = self._get_re_for_member('plat_auto')
447 re_child_priv = self._get_re_for_member('per_child_auto')
448 re_child_plat = self._get_re_for_member('per_child_plat_auto')
Simon Glassc58662f2021-02-03 06:00:50 -0700449
450 prefix = ''
451 for line in buff.splitlines():
452 # Handle line continuation
453 if prefix:
454 line = prefix + line
455 prefix = ''
456 if line.endswith('\\'):
457 prefix = line[:-1]
458 continue
459
460 driver_match = re_driver.search(line)
461
462 # If this line contains U_BOOT_DRIVER()...
463 if driver:
464 m_id = re_id.search(line)
465 m_of_match = re_of_match.search(line)
466 m_priv = re_priv.match(line)
Simon Glassc8b19b02021-02-03 06:00:53 -0700467 m_plat = re_plat.match(line)
468 m_cplat = re_child_plat.match(line)
469 m_cpriv = re_child_priv.match(line)
Simon Glassb00f0062021-02-03 06:01:02 -0700470 m_phase = re_phase.match(line)
Simon Glass735ddfc2021-02-03 06:01:04 -0700471 m_hdr = re_hdr.match(line)
Simon Glassc58662f2021-02-03 06:00:50 -0700472 if m_priv:
473 driver.priv = m_priv.group(1)
Simon Glassc8b19b02021-02-03 06:00:53 -0700474 elif m_plat:
475 driver.plat = m_plat.group(1)
476 elif m_cplat:
477 driver.child_plat = m_cplat.group(1)
478 elif m_cpriv:
479 driver.child_priv = m_cpriv.group(1)
Simon Glassc58662f2021-02-03 06:00:50 -0700480 elif m_id:
481 driver.uclass_id = m_id.group(1)
482 elif m_of_match:
483 compat = m_of_match.group(2)
Simon Glassb00f0062021-02-03 06:01:02 -0700484 elif m_phase:
485 driver.phase = m_phase.group(1)
Simon Glass735ddfc2021-02-03 06:01:04 -0700486 elif m_hdr:
487 driver.headers.append(m_hdr.group(1))
Simon Glassc58662f2021-02-03 06:00:50 -0700488 elif '};' in line:
489 if driver.uclass_id and compat:
490 if compat not in of_match:
491 raise ValueError(
492 "%s: Unknown compatible var '%s' (found: %s)" %
493 (fname, compat, ','.join(of_match.keys())))
494 driver.compat = of_match[compat]
495
496 # This needs to be deterministic, since a driver may
497 # have multiple compatible strings pointing to it.
498 # We record the one earliest in the alphabet so it
499 # will produce the same result on all machines.
500 for compat_id in of_match[compat]:
501 old = self._compat_to_driver.get(compat_id)
502 if not old or driver.name < old.name:
503 self._compat_to_driver[compat_id] = driver
504 drivers[driver.name] = driver
505 else:
506 # The driver does not have a uclass or compat string.
507 # The first is required but the second is not, so just
508 # ignore this.
509 pass
510 driver = None
511 ids_name = None
512 compat = None
513 compat_dict = {}
514
515 elif ids_name:
516 compat_m = re_compat.search(line)
517 if compat_m:
518 compat_dict[compat_m.group(1)] = compat_m.group(3)
519 elif '};' in line:
520 of_match[ids_name] = compat_dict
521 ids_name = None
522 elif driver_match:
523 driver_name = driver_match.group(1)
524 driver = Driver(driver_name, fname)
525 else:
526 ids_m = re_ids.search(line)
Simon Glass8d6f2d32021-02-03 06:01:05 -0700527 m_alias = re_alias.match(line)
Simon Glassc58662f2021-02-03 06:00:50 -0700528 if ids_m:
529 ids_name = ids_m.group(1)
Simon Glass8d6f2d32021-02-03 06:01:05 -0700530 elif m_alias:
531 self._driver_aliases[m_alias[2]] = m_alias[1]
Simon Glassc58662f2021-02-03 06:00:50 -0700532
533 # Make the updates based on what we found
534 self._drivers.update(drivers)
535 self._of_match.update(of_match)
536
Simon Glassa542a702020-12-28 20:35:06 -0700537 def scan_driver(self, fname):
538 """Scan a driver file to build a list of driver names and aliases
539
Simon Glassc58662f2021-02-03 06:00:50 -0700540 It updates the following members:
541 _drivers - updated with new Driver records for each driver found
542 in the file
543 _of_match - updated with each compatible string found in the file
544 _compat_to_driver - Maps compatible string to Driver
545 _driver_aliases - Maps alias names to driver name
Simon Glassa542a702020-12-28 20:35:06 -0700546
547 Args
548 fname: Driver filename to scan
549 """
550 with open(fname, encoding='utf-8') as inf:
551 try:
552 buff = inf.read()
553 except UnicodeDecodeError:
554 # This seems to happen on older Python versions
555 print("Skipping file '%s' due to unicode error" % fname)
556 return
557
Simon Glassc58662f2021-02-03 06:00:50 -0700558 # If this file has any U_BOOT_DRIVER() declarations, process it to
559 # obtain driver information
560 if 'U_BOOT_DRIVER' in buff:
561 self._parse_driver(fname, buff)
Simon Glass1a8b4b92021-02-03 06:00:54 -0700562 if 'UCLASS_DRIVER' in buff:
563 self._parse_uclass_driver(fname, buff)
Simon Glassa542a702020-12-28 20:35:06 -0700564
Simon Glassacf5cb82021-02-03 06:00:55 -0700565 def scan_header(self, fname):
566 """Scan a header file to build a list of struct definitions
567
568 It updates the following members:
569 _structs - updated with new Struct records for each struct found
570 in the file
571
572 Args
573 fname: header filename to scan
574 """
575 with open(fname, encoding='utf-8') as inf:
576 try:
577 buff = inf.read()
578 except UnicodeDecodeError:
579 # This seems to happen on older Python versions
580 print("Skipping file '%s' due to unicode error" % fname)
581 return
582
583 # If this file has any U_BOOT_DRIVER() declarations, process it to
584 # obtain driver information
585 if 'struct' in buff:
586 self._parse_structs(fname, buff)
587
Simon Glassa542a702020-12-28 20:35:06 -0700588 def scan_drivers(self):
589 """Scan the driver folders to build a list of driver names and aliases
590
591 This procedure will populate self._drivers and self._driver_aliases
592 """
593 for (dirpath, _, filenames) in os.walk(self._basedir):
Simon Glass36b22202021-02-03 06:00:52 -0700594 rel_path = dirpath[len(self._basedir):]
595 if rel_path.startswith('/'):
596 rel_path = rel_path[1:]
597 if rel_path.startswith('build') or rel_path.startswith('.git'):
598 continue
Simon Glassa542a702020-12-28 20:35:06 -0700599 for fname in filenames:
Simon Glassacf5cb82021-02-03 06:00:55 -0700600 pathname = dirpath + '/' + fname
601 if fname.endswith('.c'):
602 self.scan_driver(pathname)
603 elif fname.endswith('.h'):
604 self.scan_header(pathname)
Simon Glassa542a702020-12-28 20:35:06 -0700605
606 for fname in self._drivers_additional:
607 if not isinstance(fname, str) or len(fname) == 0:
608 continue
609 if fname[0] == '/':
610 self.scan_driver(fname)
611 else:
612 self.scan_driver(self._basedir + '/' + fname)
Simon Glassb9319c42021-02-03 06:01:01 -0700613
614 def mark_used(self, nodes):
615 """Mark the drivers associated with a list of nodes as 'used'
616
617 This takes a list of nodes, finds the driver for each one and marks it
618 as used.
619
620 Args:
621 nodes (list of None): Nodes that are in use
622 """
623 # Figure out which drivers we actually use
624 for node in nodes:
625 struct_name, _ = self.get_normalized_compat_name(node)
626 driver = self._drivers.get(struct_name)
627 if driver:
628 driver.used = True