blob: fb78536e0030d4c600c70ce3a3ede724ca44c951 [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 Glass1d972692021-02-03 06:01:06 -070073 dups (list): Driver objects with the same name as this one, that were
74 found after this one
75 warn_dups (bool): True if the duplicates are not distinguisble using
76 the phase
Simon Glassa542a702020-12-28 20:35:06 -070077 """
Simon Glassc58662f2021-02-03 06:00:50 -070078 def __init__(self, name, fname):
Simon Glassa542a702020-12-28 20:35:06 -070079 self.name = name
Simon Glassc58662f2021-02-03 06:00:50 -070080 self.fname = fname
81 self.uclass_id = None
82 self.compat = None
83 self.priv = ''
Simon Glassc8b19b02021-02-03 06:00:53 -070084 self.plat = ''
85 self.child_priv = ''
86 self.child_plat = ''
Simon Glassb9319c42021-02-03 06:01:01 -070087 self.used = False
Simon Glassb00f0062021-02-03 06:01:02 -070088 self.phase = ''
Simon Glass735ddfc2021-02-03 06:01:04 -070089 self.headers = []
Simon Glass1d972692021-02-03 06:01:06 -070090 self.dups = []
91 self.warn_dups = False
Simon Glassa542a702020-12-28 20:35:06 -070092
93 def __eq__(self, other):
Simon Glassc58662f2021-02-03 06:00:50 -070094 return (self.name == other.name and
95 self.uclass_id == other.uclass_id and
96 self.compat == other.compat and
Simon Glassc8b19b02021-02-03 06:00:53 -070097 self.priv == other.priv and
Simon Glassb9319c42021-02-03 06:01:01 -070098 self.plat == other.plat and
99 self.used == other.used)
Simon Glassa542a702020-12-28 20:35:06 -0700100
101 def __repr__(self):
Simon Glassb9319c42021-02-03 06:01:01 -0700102 return ("Driver(name='%s', used=%s, uclass_id='%s', compat=%s, priv=%s)" %
103 (self.name, self.used, self.uclass_id, self.compat, self.priv))
Simon Glassa542a702020-12-28 20:35:06 -0700104
105
Simon Glass1a8b4b92021-02-03 06:00:54 -0700106class UclassDriver:
107 """Holds information about a uclass driver
108
109 Attributes:
110 name: Uclass name, e.g. 'i2c' if the driver is for UCLASS_I2C
111 uclass_id: Uclass ID, e.g. 'UCLASS_I2C'
112 priv: struct name of the private data, e.g. 'i2c_priv'
113 per_dev_priv (str): struct name of the priv_auto member, e.g. 'spi_info'
114 per_dev_plat (str): struct name of the plat_auto member, e.g. 'i2c_chip'
115 per_child_priv (str): struct name of the per_child_auto member,
116 e.g. 'pci_child_priv'
117 per_child_plat (str): struct name of the per_child_plat_auto member,
118 e.g. 'pci_child_plat'
119 """
120 def __init__(self, name):
121 self.name = name
122 self.uclass_id = None
123 self.priv = ''
124 self.per_dev_priv = ''
125 self.per_dev_plat = ''
126 self.per_child_priv = ''
127 self.per_child_plat = ''
128
129 def __eq__(self, other):
130 return (self.name == other.name and
131 self.uclass_id == other.uclass_id and
132 self.priv == other.priv)
133
134 def __repr__(self):
135 return ("UclassDriver(name='%s', uclass_id='%s')" %
136 (self.name, self.uclass_id))
137
138 def __hash__(self):
139 # We can use the uclass ID since it is unique among uclasses
140 return hash(self.uclass_id)
141
142
Simon Glassacf5cb82021-02-03 06:00:55 -0700143class Struct:
144 """Holds information about a struct definition
145
146 Attributes:
147 name: Struct name, e.g. 'fred' if the struct is 'struct fred'
148 fname: Filename containing the struct, in a format that C files can
149 include, e.g. 'asm/clk.h'
150 """
151 def __init__(self, name, fname):
152 self.name = name
153 self.fname =fname
154
155 def __repr__(self):
156 return ("Struct(name='%s', fname='%s')" % (self.name, self.fname))
157
158
Simon Glassa542a702020-12-28 20:35:06 -0700159class Scanner:
160 """Scanning of the U-Boot source tree
161
162 Properties:
163 _basedir (str): Base directory of U-Boot source code. Defaults to the
164 grandparent of this file's directory
165 _drivers: Dict of valid driver names found in drivers/
166 key: Driver name
167 value: Driver for that driver
168 _driver_aliases: Dict that holds aliases for driver names
169 key: Driver alias declared with
170 DM_DRIVER_ALIAS(driver_alias, driver_name)
171 value: Driver name declared with U_BOOT_DRIVER(driver_name)
Simon Glass10ea9c02020-12-28 20:35:07 -0700172 _warning_disabled: true to disable warnings about driver names not found
Simon Glassa542a702020-12-28 20:35:06 -0700173 _drivers_additional (list or str): List of additional drivers to use
174 during scanning
Simon Glassc58662f2021-02-03 06:00:50 -0700175 _of_match: Dict holding information about compatible strings
176 key: Name of struct udevice_id variable
177 value: Dict of compatible info in that variable:
178 key: Compatible string, e.g. 'rockchip,rk3288-grf'
179 value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
180 _compat_to_driver: Maps compatible strings to Driver
Simon Glass1a8b4b92021-02-03 06:00:54 -0700181 _uclass: Dict of uclass information
182 key: uclass name, e.g. 'UCLASS_I2C'
183 value: UClassDriver
Simon Glassacf5cb82021-02-03 06:00:55 -0700184 _structs: Dict of all structs found in U-Boot:
185 key: Name of struct
186 value: Struct object
Simon Glassb00f0062021-02-03 06:01:02 -0700187 _phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
188 or 'tpl'. None if not known
Simon Glassa542a702020-12-28 20:35:06 -0700189 """
Simon Glassb00f0062021-02-03 06:01:02 -0700190 def __init__(self, basedir, warning_disabled, drivers_additional, phase=''):
Simon Glassa542a702020-12-28 20:35:06 -0700191 """Set up a new Scanner
192 """
193 if not basedir:
194 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
195 if basedir == '':
196 basedir = './'
197 self._basedir = basedir
198 self._drivers = {}
199 self._driver_aliases = {}
200 self._drivers_additional = drivers_additional or []
201 self._warning_disabled = warning_disabled
Simon Glassc58662f2021-02-03 06:00:50 -0700202 self._of_match = {}
203 self._compat_to_driver = {}
Simon Glass1a8b4b92021-02-03 06:00:54 -0700204 self._uclass = {}
Simon Glassacf5cb82021-02-03 06:00:55 -0700205 self._structs = {}
Simon Glassb00f0062021-02-03 06:01:02 -0700206 self._phase = phase
Simon Glassa542a702020-12-28 20:35:06 -0700207
Simon Glassfd471e22021-02-03 06:01:00 -0700208 def get_driver(self, name):
209 """Get a driver given its name
210
211 Args:
212 name (str): Driver name
213
214 Returns:
215 Driver: Driver or None if not found
216 """
217 return self._drivers.get(name)
218
Simon Glassa542a702020-12-28 20:35:06 -0700219 def get_normalized_compat_name(self, node):
220 """Get a node's normalized compat name
221
222 Returns a valid driver name by retrieving node's list of compatible
223 string as a C identifier and performing a check against _drivers
224 and a lookup in driver_aliases printing a warning in case of failure.
225
226 Args:
227 node (Node): Node object to check
228 Return:
229 Tuple:
230 Driver name associated with the first compatible string
231 List of C identifiers for all the other compatible strings
232 (possibly empty)
233 In case of no match found, the return will be the same as
234 get_compat_name()
235 """
236 compat_list_c = get_compat_name(node)
237
238 for compat_c in compat_list_c:
239 if not compat_c in self._drivers.keys():
240 compat_c = self._driver_aliases.get(compat_c)
241 if not compat_c:
242 continue
243
244 aliases_c = compat_list_c
245 if compat_c in aliases_c:
246 aliases_c.remove(compat_c)
247 return compat_c, aliases_c
248
249 if not self._warning_disabled:
250 print('WARNING: the driver %s was not found in the driver list'
251 % (compat_list_c[0]))
252
253 return compat_list_c[0], compat_list_c[1:]
254
Simon Glassacf5cb82021-02-03 06:00:55 -0700255 def _parse_structs(self, fname, buff):
256 """Parse a H file to extract struct definitions contained within
257
258 This parses 'struct xx {' definitions to figure out what structs this
259 header defines.
260
261 Args:
262 buff (str): Contents of file
263 fname (str): Filename (to use when printing errors)
264 """
265 structs = {}
266
267 re_struct = re.compile('^struct ([a-z0-9_]+) {$')
268 re_asm = re.compile('../arch/[a-z0-9]+/include/asm/(.*)')
269 prefix = ''
270 for line in buff.splitlines():
271 # Handle line continuation
272 if prefix:
273 line = prefix + line
274 prefix = ''
275 if line.endswith('\\'):
276 prefix = line[:-1]
277 continue
278
279 m_struct = re_struct.match(line)
280 if m_struct:
281 name = m_struct.group(1)
282 include_dir = os.path.join(self._basedir, 'include')
283 rel_fname = os.path.relpath(fname, include_dir)
284 m_asm = re_asm.match(rel_fname)
285 if m_asm:
286 rel_fname = 'asm/' + m_asm.group(1)
287 structs[name] = Struct(name, rel_fname)
288 self._structs.update(structs)
289
Simon Glassc58662f2021-02-03 06:00:50 -0700290 @classmethod
291 def _get_re_for_member(cls, member):
292 """_get_re_for_member: Get a compiled regular expression
293
294 Args:
295 member (str): Struct member name, e.g. 'priv_auto'
296
297 Returns:
298 re.Pattern: Compiled regular expression that parses:
299
300 .member = sizeof(struct fred),
301
302 and returns "fred" as group 1
303 """
304 return re.compile(r'^\s*.%s\s*=\s*sizeof\(struct\s+(.*)\),$' % member)
305
Simon Glass1a8b4b92021-02-03 06:00:54 -0700306 def _parse_uclass_driver(self, fname, buff):
307 """Parse a C file to extract uclass driver information contained within
308
309 This parses UCLASS_DRIVER() structs to obtain various pieces of useful
310 information.
311
312 It updates the following member:
313 _uclass: Dict of uclass information
314 key: uclass name, e.g. 'UCLASS_I2C'
315 value: UClassDriver
316
317 Args:
318 fname (str): Filename being parsed (used for warnings)
319 buff (str): Contents of file
320 """
321 uc_drivers = {}
322
323 # Collect the driver name and associated Driver
324 driver = None
325 re_driver = re.compile(r'UCLASS_DRIVER\((.*)\)')
326
327 # Collect the uclass ID, e.g. 'UCLASS_SPI'
328 re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)')
329
330 # Matches the header/size information for uclass-private data
331 re_priv = self._get_re_for_member('priv_auto')
332
333 # Set up parsing for the auto members
334 re_per_device_priv = self._get_re_for_member('per_device_auto')
335 re_per_device_plat = self._get_re_for_member('per_device_plat_auto')
336 re_per_child_priv = self._get_re_for_member('per_child_auto')
337 re_per_child_plat = self._get_re_for_member('per_child_plat_auto')
338
339 prefix = ''
340 for line in buff.splitlines():
341 # Handle line continuation
342 if prefix:
343 line = prefix + line
344 prefix = ''
345 if line.endswith('\\'):
346 prefix = line[:-1]
347 continue
348
349 driver_match = re_driver.search(line)
350
351 # If we have seen UCLASS_DRIVER()...
352 if driver:
353 m_id = re_id.search(line)
354 m_priv = re_priv.match(line)
355 m_per_dev_priv = re_per_device_priv.match(line)
356 m_per_dev_plat = re_per_device_plat.match(line)
357 m_per_child_priv = re_per_child_priv.match(line)
358 m_per_child_plat = re_per_child_plat.match(line)
359 if m_id:
360 driver.uclass_id = m_id.group(1)
361 elif m_priv:
362 driver.priv = m_priv.group(1)
363 elif m_per_dev_priv:
364 driver.per_dev_priv = m_per_dev_priv.group(1)
365 elif m_per_dev_plat:
366 driver.per_dev_plat = m_per_dev_plat.group(1)
367 elif m_per_child_priv:
368 driver.per_child_priv = m_per_child_priv.group(1)
369 elif m_per_child_plat:
370 driver.per_child_plat = m_per_child_plat.group(1)
371 elif '};' in line:
372 if not driver.uclass_id:
373 raise ValueError(
374 "%s: Cannot parse uclass ID in driver '%s'" %
375 (fname, driver.name))
376 uc_drivers[driver.uclass_id] = driver
377 driver = None
378
379 elif driver_match:
380 driver_name = driver_match.group(1)
381 driver = UclassDriver(driver_name)
382
383 self._uclass.update(uc_drivers)
384
Simon Glassc58662f2021-02-03 06:00:50 -0700385 def _parse_driver(self, fname, buff):
386 """Parse a C file to extract driver information contained within
387
388 This parses U_BOOT_DRIVER() structs to obtain various pieces of useful
389 information.
390
391 It updates the following members:
392 _drivers - updated with new Driver records for each driver found
393 in the file
394 _of_match - updated with each compatible string found in the file
395 _compat_to_driver - Maps compatible string to Driver
Simon Glass8d6f2d32021-02-03 06:01:05 -0700396 _driver_aliases - Maps alias names to driver name
Simon Glassc58662f2021-02-03 06:00:50 -0700397
398 Args:
399 fname (str): Filename being parsed (used for warnings)
400 buff (str): Contents of file
401
402 Raises:
403 ValueError: Compatible variable is mentioned in .of_match in
404 U_BOOT_DRIVER() but not found in the file
405 """
406 # Dict holding information about compatible strings collected in this
407 # function so far
408 # key: Name of struct udevice_id variable
409 # value: Dict of compatible info in that variable:
410 # key: Compatible string, e.g. 'rockchip,rk3288-grf'
411 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
412 of_match = {}
413
414 # Dict holding driver information collected in this function so far
415 # key: Driver name (C name as in U_BOOT_DRIVER(xxx))
416 # value: Driver
417 drivers = {}
418
419 # Collect the driver info
420 driver = None
421 re_driver = re.compile(r'U_BOOT_DRIVER\((.*)\)')
422
423 # Collect the uclass ID, e.g. 'UCLASS_SPI'
424 re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)')
425
426 # Collect the compatible string, e.g. 'rockchip,rk3288-grf'
427 compat = None
428 re_compat = re.compile(r'{\s*.compatible\s*=\s*"(.*)"\s*'
429 r'(,\s*.data\s*=\s*(\S*))?\s*},')
430
431 # This is a dict of compatible strings that were found:
432 # key: Compatible string, e.g. 'rockchip,rk3288-grf'
433 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
434 compat_dict = {}
435
436 # Holds the var nane of the udevice_id list, e.g.
437 # 'rk3288_syscon_ids_noc' in
438 # static const struct udevice_id rk3288_syscon_ids_noc[] = {
439 ids_name = None
440 re_ids = re.compile(r'struct udevice_id (.*)\[\]\s*=')
441
442 # Matches the references to the udevice_id list
443 re_of_match = re.compile(
444 r'\.of_match\s*=\s*(of_match_ptr\()?([a-z0-9_]+)(\))?,')
445
Simon Glassb00f0062021-02-03 06:01:02 -0700446 re_phase = re.compile('^\s*DM_PHASE\((.*)\).*$')
Simon Glass735ddfc2021-02-03 06:01:04 -0700447 re_hdr = re.compile('^\s*DM_HEADER\((.*)\).*$')
Simon Glass8d6f2d32021-02-03 06:01:05 -0700448 re_alias = re.compile(r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)')
Simon Glassb00f0062021-02-03 06:01:02 -0700449
Simon Glassc8b19b02021-02-03 06:00:53 -0700450 # Matches the struct name for priv, plat
Simon Glassc58662f2021-02-03 06:00:50 -0700451 re_priv = self._get_re_for_member('priv_auto')
Simon Glassc8b19b02021-02-03 06:00:53 -0700452 re_plat = self._get_re_for_member('plat_auto')
453 re_child_priv = self._get_re_for_member('per_child_auto')
454 re_child_plat = self._get_re_for_member('per_child_plat_auto')
Simon Glassc58662f2021-02-03 06:00:50 -0700455
456 prefix = ''
457 for line in buff.splitlines():
458 # Handle line continuation
459 if prefix:
460 line = prefix + line
461 prefix = ''
462 if line.endswith('\\'):
463 prefix = line[:-1]
464 continue
465
466 driver_match = re_driver.search(line)
467
468 # If this line contains U_BOOT_DRIVER()...
469 if driver:
470 m_id = re_id.search(line)
471 m_of_match = re_of_match.search(line)
472 m_priv = re_priv.match(line)
Simon Glassc8b19b02021-02-03 06:00:53 -0700473 m_plat = re_plat.match(line)
474 m_cplat = re_child_plat.match(line)
475 m_cpriv = re_child_priv.match(line)
Simon Glassb00f0062021-02-03 06:01:02 -0700476 m_phase = re_phase.match(line)
Simon Glass735ddfc2021-02-03 06:01:04 -0700477 m_hdr = re_hdr.match(line)
Simon Glassc58662f2021-02-03 06:00:50 -0700478 if m_priv:
479 driver.priv = m_priv.group(1)
Simon Glassc8b19b02021-02-03 06:00:53 -0700480 elif m_plat:
481 driver.plat = m_plat.group(1)
482 elif m_cplat:
483 driver.child_plat = m_cplat.group(1)
484 elif m_cpriv:
485 driver.child_priv = m_cpriv.group(1)
Simon Glassc58662f2021-02-03 06:00:50 -0700486 elif m_id:
487 driver.uclass_id = m_id.group(1)
488 elif m_of_match:
489 compat = m_of_match.group(2)
Simon Glassb00f0062021-02-03 06:01:02 -0700490 elif m_phase:
491 driver.phase = m_phase.group(1)
Simon Glass735ddfc2021-02-03 06:01:04 -0700492 elif m_hdr:
493 driver.headers.append(m_hdr.group(1))
Simon Glassc58662f2021-02-03 06:00:50 -0700494 elif '};' in line:
495 if driver.uclass_id and compat:
496 if compat not in of_match:
497 raise ValueError(
498 "%s: Unknown compatible var '%s' (found: %s)" %
499 (fname, compat, ','.join(of_match.keys())))
500 driver.compat = of_match[compat]
501
502 # This needs to be deterministic, since a driver may
503 # have multiple compatible strings pointing to it.
504 # We record the one earliest in the alphabet so it
505 # will produce the same result on all machines.
506 for compat_id in of_match[compat]:
507 old = self._compat_to_driver.get(compat_id)
508 if not old or driver.name < old.name:
509 self._compat_to_driver[compat_id] = driver
510 drivers[driver.name] = driver
511 else:
512 # The driver does not have a uclass or compat string.
513 # The first is required but the second is not, so just
514 # ignore this.
515 pass
516 driver = None
517 ids_name = None
518 compat = None
519 compat_dict = {}
520
521 elif ids_name:
522 compat_m = re_compat.search(line)
523 if compat_m:
524 compat_dict[compat_m.group(1)] = compat_m.group(3)
525 elif '};' in line:
526 of_match[ids_name] = compat_dict
527 ids_name = None
528 elif driver_match:
529 driver_name = driver_match.group(1)
530 driver = Driver(driver_name, fname)
531 else:
532 ids_m = re_ids.search(line)
Simon Glass8d6f2d32021-02-03 06:01:05 -0700533 m_alias = re_alias.match(line)
Simon Glassc58662f2021-02-03 06:00:50 -0700534 if ids_m:
535 ids_name = ids_m.group(1)
Simon Glass8d6f2d32021-02-03 06:01:05 -0700536 elif m_alias:
537 self._driver_aliases[m_alias[2]] = m_alias[1]
Simon Glassc58662f2021-02-03 06:00:50 -0700538
539 # Make the updates based on what we found
Simon Glass1d972692021-02-03 06:01:06 -0700540 for driver in drivers.values():
541 if driver.name in self._drivers:
542 orig = self._drivers[driver.name]
543 if self._phase:
544 # If the original driver matches our phase, use it
545 if orig.phase == self._phase:
546 orig.dups.append(driver)
547 continue
548
549 # Otherwise use the new driver, which is assumed to match
550 else:
551 # We have no way of distinguishing them
552 driver.warn_dups = True
553 driver.dups.append(orig)
554 self._drivers[driver.name] = driver
Simon Glassc58662f2021-02-03 06:00:50 -0700555 self._of_match.update(of_match)
556
Simon Glassa542a702020-12-28 20:35:06 -0700557 def scan_driver(self, fname):
558 """Scan a driver file to build a list of driver names and aliases
559
Simon Glassc58662f2021-02-03 06:00:50 -0700560 It updates the following members:
561 _drivers - updated with new Driver records for each driver found
562 in the file
563 _of_match - updated with each compatible string found in the file
564 _compat_to_driver - Maps compatible string to Driver
565 _driver_aliases - Maps alias names to driver name
Simon Glassa542a702020-12-28 20:35:06 -0700566
567 Args
568 fname: Driver filename to scan
569 """
570 with open(fname, encoding='utf-8') as inf:
571 try:
572 buff = inf.read()
573 except UnicodeDecodeError:
574 # This seems to happen on older Python versions
575 print("Skipping file '%s' due to unicode error" % fname)
576 return
577
Simon Glassc58662f2021-02-03 06:00:50 -0700578 # If this file has any U_BOOT_DRIVER() declarations, process it to
579 # obtain driver information
580 if 'U_BOOT_DRIVER' in buff:
581 self._parse_driver(fname, buff)
Simon Glass1a8b4b92021-02-03 06:00:54 -0700582 if 'UCLASS_DRIVER' in buff:
583 self._parse_uclass_driver(fname, buff)
Simon Glassa542a702020-12-28 20:35:06 -0700584
Simon Glassacf5cb82021-02-03 06:00:55 -0700585 def scan_header(self, fname):
586 """Scan a header file to build a list of struct definitions
587
588 It updates the following members:
589 _structs - updated with new Struct records for each struct found
590 in the file
591
592 Args
593 fname: header filename to scan
594 """
595 with open(fname, encoding='utf-8') as inf:
596 try:
597 buff = inf.read()
598 except UnicodeDecodeError:
599 # This seems to happen on older Python versions
600 print("Skipping file '%s' due to unicode error" % fname)
601 return
602
603 # If this file has any U_BOOT_DRIVER() declarations, process it to
604 # obtain driver information
605 if 'struct' in buff:
606 self._parse_structs(fname, buff)
607
Simon Glassa542a702020-12-28 20:35:06 -0700608 def scan_drivers(self):
609 """Scan the driver folders to build a list of driver names and aliases
610
611 This procedure will populate self._drivers and self._driver_aliases
612 """
613 for (dirpath, _, filenames) in os.walk(self._basedir):
Simon Glass36b22202021-02-03 06:00:52 -0700614 rel_path = dirpath[len(self._basedir):]
615 if rel_path.startswith('/'):
616 rel_path = rel_path[1:]
617 if rel_path.startswith('build') or rel_path.startswith('.git'):
618 continue
Simon Glassa542a702020-12-28 20:35:06 -0700619 for fname in filenames:
Simon Glassacf5cb82021-02-03 06:00:55 -0700620 pathname = dirpath + '/' + fname
621 if fname.endswith('.c'):
622 self.scan_driver(pathname)
623 elif fname.endswith('.h'):
624 self.scan_header(pathname)
Simon Glassa542a702020-12-28 20:35:06 -0700625
626 for fname in self._drivers_additional:
627 if not isinstance(fname, str) or len(fname) == 0:
628 continue
629 if fname[0] == '/':
630 self.scan_driver(fname)
631 else:
632 self.scan_driver(self._basedir + '/' + fname)
Simon Glassb9319c42021-02-03 06:01:01 -0700633
634 def mark_used(self, nodes):
635 """Mark the drivers associated with a list of nodes as 'used'
636
637 This takes a list of nodes, finds the driver for each one and marks it
638 as used.
639
Simon Glass1d972692021-02-03 06:01:06 -0700640 If two used drivers have the same name, issue a warning.
641
Simon Glassb9319c42021-02-03 06:01:01 -0700642 Args:
643 nodes (list of None): Nodes that are in use
644 """
645 # Figure out which drivers we actually use
646 for node in nodes:
647 struct_name, _ = self.get_normalized_compat_name(node)
648 driver = self._drivers.get(struct_name)
649 if driver:
650 driver.used = True
Simon Glass1d972692021-02-03 06:01:06 -0700651 if driver.dups and driver.warn_dups:
652 print("Warning: Duplicate driver name '%s' (orig=%s, dups=%s)" %
653 (driver.name, driver.fname,
654 ', '.join([drv.fname for drv in driver.dups])))