blob: bb22b0b64ff84b0934415d38f425e97a1cc895af [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('.', '_')
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 Glass78933b72021-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 Glass0f3b1412021-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 Glasseb3c2492021-02-03 06:01:01 -070069 used (bool): True if the driver is used by the structs being output
Simon Glassf303ee72021-02-03 06:01:02 -070070 phase (str): Which phase of U-Boot to use this driver
Simon Glassa7b1c772021-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 Glass9b2eac02021-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 Glass9065bc92020-12-28 20:35:06 -070077 """
Simon Glass78933b72021-02-03 06:00:50 -070078 def __init__(self, name, fname):
Simon Glass9065bc92020-12-28 20:35:06 -070079 self.name = name
Simon Glass78933b72021-02-03 06:00:50 -070080 self.fname = fname
81 self.uclass_id = None
82 self.compat = None
83 self.priv = ''
Simon Glass0f3b1412021-02-03 06:00:53 -070084 self.plat = ''
85 self.child_priv = ''
86 self.child_plat = ''
Simon Glasseb3c2492021-02-03 06:01:01 -070087 self.used = False
Simon Glassf303ee72021-02-03 06:01:02 -070088 self.phase = ''
Simon Glassa7b1c772021-02-03 06:01:04 -070089 self.headers = []
Simon Glass9b2eac02021-02-03 06:01:06 -070090 self.dups = []
91 self.warn_dups = False
Simon Glass9065bc92020-12-28 20:35:06 -070092
93 def __eq__(self, other):
Simon Glass78933b72021-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 Glass0f3b1412021-02-03 06:00:53 -070097 self.priv == other.priv and
Simon Glasseb3c2492021-02-03 06:01:01 -070098 self.plat == other.plat and
99 self.used == other.used)
Simon Glass9065bc92020-12-28 20:35:06 -0700100
101 def __repr__(self):
Simon Glasseb3c2492021-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 Glass9065bc92020-12-28 20:35:06 -0700104
105
Simon Glass2e199ab2021-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'
Simon Glassbe88d2f2021-02-03 06:01:07 -0700119 alias_num_to_node (dict): Aliases for this uclasses (for sequence
120 numbers)
121 key (int): Alias number, e.g. 2 for "pci2"
122 value (str): Node the alias points to
123 alias_path_to_num (dict): Convert a path to an alias number
124 key (str): Full path to node (e.g. '/soc/pci')
125 seq (int): Alias number, e.g. 2 for "pci2"
Simon Glass2e199ab2021-02-03 06:00:54 -0700126 """
127 def __init__(self, name):
128 self.name = name
129 self.uclass_id = None
130 self.priv = ''
131 self.per_dev_priv = ''
132 self.per_dev_plat = ''
133 self.per_child_priv = ''
134 self.per_child_plat = ''
Simon Glassbe88d2f2021-02-03 06:01:07 -0700135 self.alias_num_to_node = {}
136 self.alias_path_to_num = {}
Simon Glass2e199ab2021-02-03 06:00:54 -0700137
138 def __eq__(self, other):
139 return (self.name == other.name and
140 self.uclass_id == other.uclass_id and
141 self.priv == other.priv)
142
143 def __repr__(self):
144 return ("UclassDriver(name='%s', uclass_id='%s')" %
145 (self.name, self.uclass_id))
146
147 def __hash__(self):
148 # We can use the uclass ID since it is unique among uclasses
149 return hash(self.uclass_id)
150
151
Simon Glass88bd5382021-02-03 06:00:55 -0700152class Struct:
153 """Holds information about a struct definition
154
155 Attributes:
156 name: Struct name, e.g. 'fred' if the struct is 'struct fred'
157 fname: Filename containing the struct, in a format that C files can
158 include, e.g. 'asm/clk.h'
159 """
160 def __init__(self, name, fname):
161 self.name = name
162 self.fname =fname
163
164 def __repr__(self):
165 return ("Struct(name='%s', fname='%s')" % (self.name, self.fname))
166
167
Simon Glass9065bc92020-12-28 20:35:06 -0700168class Scanner:
169 """Scanning of the U-Boot source tree
170
171 Properties:
172 _basedir (str): Base directory of U-Boot source code. Defaults to the
173 grandparent of this file's directory
174 _drivers: Dict of valid driver names found in drivers/
175 key: Driver name
176 value: Driver for that driver
177 _driver_aliases: Dict that holds aliases for driver names
178 key: Driver alias declared with
179 DM_DRIVER_ALIAS(driver_alias, driver_name)
180 value: Driver name declared with U_BOOT_DRIVER(driver_name)
Simon Glassdf692c32020-12-28 20:35:07 -0700181 _warning_disabled: true to disable warnings about driver names not found
Simon Glass9065bc92020-12-28 20:35:06 -0700182 _drivers_additional (list or str): List of additional drivers to use
183 during scanning
Simon Glass78933b72021-02-03 06:00:50 -0700184 _of_match: Dict holding information about compatible strings
185 key: Name of struct udevice_id variable
186 value: Dict of compatible info in that variable:
187 key: Compatible string, e.g. 'rockchip,rk3288-grf'
188 value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
189 _compat_to_driver: Maps compatible strings to Driver
Simon Glass2e199ab2021-02-03 06:00:54 -0700190 _uclass: Dict of uclass information
191 key: uclass name, e.g. 'UCLASS_I2C'
192 value: UClassDriver
Simon Glass88bd5382021-02-03 06:00:55 -0700193 _structs: Dict of all structs found in U-Boot:
194 key: Name of struct
195 value: Struct object
Simon Glassf303ee72021-02-03 06:01:02 -0700196 _phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
197 or 'tpl'. None if not known
Simon Glass9065bc92020-12-28 20:35:06 -0700198 """
Simon Glassf303ee72021-02-03 06:01:02 -0700199 def __init__(self, basedir, warning_disabled, drivers_additional, phase=''):
Simon Glass9065bc92020-12-28 20:35:06 -0700200 """Set up a new Scanner
201 """
202 if not basedir:
203 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
204 if basedir == '':
205 basedir = './'
206 self._basedir = basedir
207 self._drivers = {}
208 self._driver_aliases = {}
209 self._drivers_additional = drivers_additional or []
210 self._warning_disabled = warning_disabled
Simon Glass78933b72021-02-03 06:00:50 -0700211 self._of_match = {}
212 self._compat_to_driver = {}
Simon Glass2e199ab2021-02-03 06:00:54 -0700213 self._uclass = {}
Simon Glass88bd5382021-02-03 06:00:55 -0700214 self._structs = {}
Simon Glassf303ee72021-02-03 06:01:02 -0700215 self._phase = phase
Simon Glass9065bc92020-12-28 20:35:06 -0700216
Simon Glass047a4802021-02-03 06:01:00 -0700217 def get_driver(self, name):
218 """Get a driver given its name
219
220 Args:
221 name (str): Driver name
222
223 Returns:
224 Driver: Driver or None if not found
225 """
226 return self._drivers.get(name)
227
Simon Glass9065bc92020-12-28 20:35:06 -0700228 def get_normalized_compat_name(self, node):
229 """Get a node's normalized compat name
230
231 Returns a valid driver name by retrieving node's list of compatible
232 string as a C identifier and performing a check against _drivers
233 and a lookup in driver_aliases printing a warning in case of failure.
234
235 Args:
236 node (Node): Node object to check
237 Return:
238 Tuple:
239 Driver name associated with the first compatible string
240 List of C identifiers for all the other compatible strings
241 (possibly empty)
242 In case of no match found, the return will be the same as
243 get_compat_name()
244 """
245 compat_list_c = get_compat_name(node)
246
247 for compat_c in compat_list_c:
248 if not compat_c in self._drivers.keys():
249 compat_c = self._driver_aliases.get(compat_c)
250 if not compat_c:
251 continue
252
253 aliases_c = compat_list_c
254 if compat_c in aliases_c:
255 aliases_c.remove(compat_c)
256 return compat_c, aliases_c
257
258 if not self._warning_disabled:
259 print('WARNING: the driver %s was not found in the driver list'
260 % (compat_list_c[0]))
261
262 return compat_list_c[0], compat_list_c[1:]
263
Simon Glass88bd5382021-02-03 06:00:55 -0700264 def _parse_structs(self, fname, buff):
265 """Parse a H file to extract struct definitions contained within
266
267 This parses 'struct xx {' definitions to figure out what structs this
268 header defines.
269
270 Args:
271 buff (str): Contents of file
272 fname (str): Filename (to use when printing errors)
273 """
274 structs = {}
275
276 re_struct = re.compile('^struct ([a-z0-9_]+) {$')
277 re_asm = re.compile('../arch/[a-z0-9]+/include/asm/(.*)')
278 prefix = ''
279 for line in buff.splitlines():
280 # Handle line continuation
281 if prefix:
282 line = prefix + line
283 prefix = ''
284 if line.endswith('\\'):
285 prefix = line[:-1]
286 continue
287
288 m_struct = re_struct.match(line)
289 if m_struct:
290 name = m_struct.group(1)
291 include_dir = os.path.join(self._basedir, 'include')
292 rel_fname = os.path.relpath(fname, include_dir)
293 m_asm = re_asm.match(rel_fname)
294 if m_asm:
295 rel_fname = 'asm/' + m_asm.group(1)
296 structs[name] = Struct(name, rel_fname)
297 self._structs.update(structs)
298
Simon Glass78933b72021-02-03 06:00:50 -0700299 @classmethod
300 def _get_re_for_member(cls, member):
301 """_get_re_for_member: Get a compiled regular expression
302
303 Args:
304 member (str): Struct member name, e.g. 'priv_auto'
305
306 Returns:
307 re.Pattern: Compiled regular expression that parses:
308
309 .member = sizeof(struct fred),
310
311 and returns "fred" as group 1
312 """
313 return re.compile(r'^\s*.%s\s*=\s*sizeof\(struct\s+(.*)\),$' % member)
314
Simon Glass2e199ab2021-02-03 06:00:54 -0700315 def _parse_uclass_driver(self, fname, buff):
316 """Parse a C file to extract uclass driver information contained within
317
318 This parses UCLASS_DRIVER() structs to obtain various pieces of useful
319 information.
320
321 It updates the following member:
322 _uclass: Dict of uclass information
323 key: uclass name, e.g. 'UCLASS_I2C'
324 value: UClassDriver
325
326 Args:
327 fname (str): Filename being parsed (used for warnings)
328 buff (str): Contents of file
329 """
330 uc_drivers = {}
331
332 # Collect the driver name and associated Driver
333 driver = None
Simon Glass072b9c52021-02-03 06:01:08 -0700334 re_driver = re.compile(r'^UCLASS_DRIVER\((.*)\)')
Simon Glass2e199ab2021-02-03 06:00:54 -0700335
336 # Collect the uclass ID, e.g. 'UCLASS_SPI'
337 re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)')
338
339 # Matches the header/size information for uclass-private data
340 re_priv = self._get_re_for_member('priv_auto')
341
342 # Set up parsing for the auto members
343 re_per_device_priv = self._get_re_for_member('per_device_auto')
344 re_per_device_plat = self._get_re_for_member('per_device_plat_auto')
345 re_per_child_priv = self._get_re_for_member('per_child_auto')
346 re_per_child_plat = self._get_re_for_member('per_child_plat_auto')
347
348 prefix = ''
349 for line in buff.splitlines():
350 # Handle line continuation
351 if prefix:
352 line = prefix + line
353 prefix = ''
354 if line.endswith('\\'):
355 prefix = line[:-1]
356 continue
357
358 driver_match = re_driver.search(line)
359
360 # If we have seen UCLASS_DRIVER()...
361 if driver:
362 m_id = re_id.search(line)
363 m_priv = re_priv.match(line)
364 m_per_dev_priv = re_per_device_priv.match(line)
365 m_per_dev_plat = re_per_device_plat.match(line)
366 m_per_child_priv = re_per_child_priv.match(line)
367 m_per_child_plat = re_per_child_plat.match(line)
368 if m_id:
369 driver.uclass_id = m_id.group(1)
370 elif m_priv:
371 driver.priv = m_priv.group(1)
372 elif m_per_dev_priv:
373 driver.per_dev_priv = m_per_dev_priv.group(1)
374 elif m_per_dev_plat:
375 driver.per_dev_plat = m_per_dev_plat.group(1)
376 elif m_per_child_priv:
377 driver.per_child_priv = m_per_child_priv.group(1)
378 elif m_per_child_plat:
379 driver.per_child_plat = m_per_child_plat.group(1)
380 elif '};' in line:
381 if not driver.uclass_id:
382 raise ValueError(
383 "%s: Cannot parse uclass ID in driver '%s'" %
384 (fname, driver.name))
385 uc_drivers[driver.uclass_id] = driver
386 driver = None
387
388 elif driver_match:
389 driver_name = driver_match.group(1)
390 driver = UclassDriver(driver_name)
391
392 self._uclass.update(uc_drivers)
393
Simon Glass78933b72021-02-03 06:00:50 -0700394 def _parse_driver(self, fname, buff):
395 """Parse a C file to extract driver information contained within
396
397 This parses U_BOOT_DRIVER() structs to obtain various pieces of useful
398 information.
399
400 It updates the following members:
401 _drivers - updated with new Driver records for each driver found
402 in the file
403 _of_match - updated with each compatible string found in the file
404 _compat_to_driver - Maps compatible string to Driver
Simon Glass2f4455f2021-02-03 06:01:05 -0700405 _driver_aliases - Maps alias names to driver name
Simon Glass78933b72021-02-03 06:00:50 -0700406
407 Args:
408 fname (str): Filename being parsed (used for warnings)
409 buff (str): Contents of file
410
411 Raises:
412 ValueError: Compatible variable is mentioned in .of_match in
413 U_BOOT_DRIVER() but not found in the file
414 """
415 # Dict holding information about compatible strings collected in this
416 # function so far
417 # key: Name of struct udevice_id variable
418 # value: Dict of compatible info in that variable:
419 # key: Compatible string, e.g. 'rockchip,rk3288-grf'
420 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
421 of_match = {}
422
423 # Dict holding driver information collected in this function so far
424 # key: Driver name (C name as in U_BOOT_DRIVER(xxx))
425 # value: Driver
426 drivers = {}
427
428 # Collect the driver info
429 driver = None
Simon Glass072b9c52021-02-03 06:01:08 -0700430 re_driver = re.compile(r'^U_BOOT_DRIVER\((.*)\)')
Simon Glass78933b72021-02-03 06:00:50 -0700431
432 # Collect the uclass ID, e.g. 'UCLASS_SPI'
433 re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)')
434
435 # Collect the compatible string, e.g. 'rockchip,rk3288-grf'
436 compat = None
437 re_compat = re.compile(r'{\s*.compatible\s*=\s*"(.*)"\s*'
438 r'(,\s*.data\s*=\s*(\S*))?\s*},')
439
440 # This is a dict of compatible strings that were found:
441 # key: Compatible string, e.g. 'rockchip,rk3288-grf'
442 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
443 compat_dict = {}
444
445 # Holds the var nane of the udevice_id list, e.g.
446 # 'rk3288_syscon_ids_noc' in
447 # static const struct udevice_id rk3288_syscon_ids_noc[] = {
448 ids_name = None
449 re_ids = re.compile(r'struct udevice_id (.*)\[\]\s*=')
450
451 # Matches the references to the udevice_id list
452 re_of_match = re.compile(
453 r'\.of_match\s*=\s*(of_match_ptr\()?([a-z0-9_]+)(\))?,')
454
Simon Glassf303ee72021-02-03 06:01:02 -0700455 re_phase = re.compile('^\s*DM_PHASE\((.*)\).*$')
Simon Glassa7b1c772021-02-03 06:01:04 -0700456 re_hdr = re.compile('^\s*DM_HEADER\((.*)\).*$')
Simon Glass2f4455f2021-02-03 06:01:05 -0700457 re_alias = re.compile(r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)')
Simon Glassf303ee72021-02-03 06:01:02 -0700458
Simon Glass0f3b1412021-02-03 06:00:53 -0700459 # Matches the struct name for priv, plat
Simon Glass78933b72021-02-03 06:00:50 -0700460 re_priv = self._get_re_for_member('priv_auto')
Simon Glass0f3b1412021-02-03 06:00:53 -0700461 re_plat = self._get_re_for_member('plat_auto')
462 re_child_priv = self._get_re_for_member('per_child_auto')
463 re_child_plat = self._get_re_for_member('per_child_plat_auto')
Simon Glass78933b72021-02-03 06:00:50 -0700464
465 prefix = ''
466 for line in buff.splitlines():
467 # Handle line continuation
468 if prefix:
469 line = prefix + line
470 prefix = ''
471 if line.endswith('\\'):
472 prefix = line[:-1]
473 continue
474
475 driver_match = re_driver.search(line)
476
477 # If this line contains U_BOOT_DRIVER()...
478 if driver:
479 m_id = re_id.search(line)
480 m_of_match = re_of_match.search(line)
481 m_priv = re_priv.match(line)
Simon Glass0f3b1412021-02-03 06:00:53 -0700482 m_plat = re_plat.match(line)
483 m_cplat = re_child_plat.match(line)
484 m_cpriv = re_child_priv.match(line)
Simon Glassf303ee72021-02-03 06:01:02 -0700485 m_phase = re_phase.match(line)
Simon Glassa7b1c772021-02-03 06:01:04 -0700486 m_hdr = re_hdr.match(line)
Simon Glass78933b72021-02-03 06:00:50 -0700487 if m_priv:
488 driver.priv = m_priv.group(1)
Simon Glass0f3b1412021-02-03 06:00:53 -0700489 elif m_plat:
490 driver.plat = m_plat.group(1)
491 elif m_cplat:
492 driver.child_plat = m_cplat.group(1)
493 elif m_cpriv:
494 driver.child_priv = m_cpriv.group(1)
Simon Glass78933b72021-02-03 06:00:50 -0700495 elif m_id:
496 driver.uclass_id = m_id.group(1)
497 elif m_of_match:
498 compat = m_of_match.group(2)
Simon Glassf303ee72021-02-03 06:01:02 -0700499 elif m_phase:
500 driver.phase = m_phase.group(1)
Simon Glassa7b1c772021-02-03 06:01:04 -0700501 elif m_hdr:
502 driver.headers.append(m_hdr.group(1))
Simon Glass78933b72021-02-03 06:00:50 -0700503 elif '};' in line:
504 if driver.uclass_id and compat:
505 if compat not in of_match:
506 raise ValueError(
507 "%s: Unknown compatible var '%s' (found: %s)" %
508 (fname, compat, ','.join(of_match.keys())))
509 driver.compat = of_match[compat]
510
511 # This needs to be deterministic, since a driver may
512 # have multiple compatible strings pointing to it.
513 # We record the one earliest in the alphabet so it
514 # will produce the same result on all machines.
515 for compat_id in of_match[compat]:
516 old = self._compat_to_driver.get(compat_id)
517 if not old or driver.name < old.name:
518 self._compat_to_driver[compat_id] = driver
519 drivers[driver.name] = driver
520 else:
521 # The driver does not have a uclass or compat string.
522 # The first is required but the second is not, so just
523 # ignore this.
524 pass
525 driver = None
526 ids_name = None
527 compat = None
528 compat_dict = {}
529
530 elif ids_name:
531 compat_m = re_compat.search(line)
532 if compat_m:
533 compat_dict[compat_m.group(1)] = compat_m.group(3)
534 elif '};' in line:
535 of_match[ids_name] = compat_dict
536 ids_name = None
537 elif driver_match:
538 driver_name = driver_match.group(1)
539 driver = Driver(driver_name, fname)
540 else:
541 ids_m = re_ids.search(line)
Simon Glass2f4455f2021-02-03 06:01:05 -0700542 m_alias = re_alias.match(line)
Simon Glass78933b72021-02-03 06:00:50 -0700543 if ids_m:
544 ids_name = ids_m.group(1)
Simon Glass2f4455f2021-02-03 06:01:05 -0700545 elif m_alias:
546 self._driver_aliases[m_alias[2]] = m_alias[1]
Simon Glass78933b72021-02-03 06:00:50 -0700547
548 # Make the updates based on what we found
Simon Glass9b2eac02021-02-03 06:01:06 -0700549 for driver in drivers.values():
550 if driver.name in self._drivers:
551 orig = self._drivers[driver.name]
552 if self._phase:
553 # If the original driver matches our phase, use it
554 if orig.phase == self._phase:
555 orig.dups.append(driver)
556 continue
557
558 # Otherwise use the new driver, which is assumed to match
559 else:
560 # We have no way of distinguishing them
561 driver.warn_dups = True
562 driver.dups.append(orig)
563 self._drivers[driver.name] = driver
Simon Glass78933b72021-02-03 06:00:50 -0700564 self._of_match.update(of_match)
565
Simon Glass9065bc92020-12-28 20:35:06 -0700566 def scan_driver(self, fname):
567 """Scan a driver file to build a list of driver names and aliases
568
Simon Glass78933b72021-02-03 06:00:50 -0700569 It updates the following members:
570 _drivers - updated with new Driver records for each driver found
571 in the file
572 _of_match - updated with each compatible string found in the file
573 _compat_to_driver - Maps compatible string to Driver
574 _driver_aliases - Maps alias names to driver name
Simon Glass9065bc92020-12-28 20:35:06 -0700575
576 Args
577 fname: Driver filename to scan
578 """
579 with open(fname, encoding='utf-8') as inf:
580 try:
581 buff = inf.read()
582 except UnicodeDecodeError:
583 # This seems to happen on older Python versions
584 print("Skipping file '%s' due to unicode error" % fname)
585 return
586
Simon Glass78933b72021-02-03 06:00:50 -0700587 # If this file has any U_BOOT_DRIVER() declarations, process it to
588 # obtain driver information
589 if 'U_BOOT_DRIVER' in buff:
590 self._parse_driver(fname, buff)
Simon Glass2e199ab2021-02-03 06:00:54 -0700591 if 'UCLASS_DRIVER' in buff:
592 self._parse_uclass_driver(fname, buff)
Simon Glass9065bc92020-12-28 20:35:06 -0700593
Simon Glass88bd5382021-02-03 06:00:55 -0700594 def scan_header(self, fname):
595 """Scan a header file to build a list of struct definitions
596
597 It updates the following members:
598 _structs - updated with new Struct records for each struct found
599 in the file
600
601 Args
602 fname: header filename to scan
603 """
604 with open(fname, encoding='utf-8') as inf:
605 try:
606 buff = inf.read()
607 except UnicodeDecodeError:
608 # This seems to happen on older Python versions
609 print("Skipping file '%s' due to unicode error" % fname)
610 return
611
612 # If this file has any U_BOOT_DRIVER() declarations, process it to
613 # obtain driver information
614 if 'struct' in buff:
615 self._parse_structs(fname, buff)
616
Simon Glass9065bc92020-12-28 20:35:06 -0700617 def scan_drivers(self):
618 """Scan the driver folders to build a list of driver names and aliases
619
620 This procedure will populate self._drivers and self._driver_aliases
621 """
622 for (dirpath, _, filenames) in os.walk(self._basedir):
Simon Glassdc37c812021-02-03 06:00:52 -0700623 rel_path = dirpath[len(self._basedir):]
624 if rel_path.startswith('/'):
625 rel_path = rel_path[1:]
626 if rel_path.startswith('build') or rel_path.startswith('.git'):
627 continue
Simon Glass9065bc92020-12-28 20:35:06 -0700628 for fname in filenames:
Simon Glass88bd5382021-02-03 06:00:55 -0700629 pathname = dirpath + '/' + fname
630 if fname.endswith('.c'):
631 self.scan_driver(pathname)
632 elif fname.endswith('.h'):
633 self.scan_header(pathname)
Simon Glass9065bc92020-12-28 20:35:06 -0700634 for fname in self._drivers_additional:
635 if not isinstance(fname, str) or len(fname) == 0:
636 continue
637 if fname[0] == '/':
638 self.scan_driver(fname)
639 else:
640 self.scan_driver(self._basedir + '/' + fname)
Simon Glasseb3c2492021-02-03 06:01:01 -0700641
642 def mark_used(self, nodes):
643 """Mark the drivers associated with a list of nodes as 'used'
644
645 This takes a list of nodes, finds the driver for each one and marks it
646 as used.
647
Simon Glass9b2eac02021-02-03 06:01:06 -0700648 If two used drivers have the same name, issue a warning.
649
Simon Glasseb3c2492021-02-03 06:01:01 -0700650 Args:
651 nodes (list of None): Nodes that are in use
652 """
653 # Figure out which drivers we actually use
654 for node in nodes:
655 struct_name, _ = self.get_normalized_compat_name(node)
656 driver = self._drivers.get(struct_name)
657 if driver:
658 driver.used = True
Simon Glass9b2eac02021-02-03 06:01:06 -0700659 if driver.dups and driver.warn_dups:
660 print("Warning: Duplicate driver name '%s' (orig=%s, dups=%s)" %
661 (driver.name, driver.fname,
662 ', '.join([drv.fname for drv in driver.dups])))
Simon Glassbe88d2f2021-02-03 06:01:07 -0700663
664 def add_uclass_alias(self, name, num, node):
665 """Add an alias to a uclass
666
667 Args:
668 name: Name of uclass, e.g. 'i2c'
669 num: Alias number, e.g. 2 for alias 'i2c2'
670 node: Node the alias points to, or None if None
671
672 Returns:
673 True if the node was added
674 False if the node was not added (uclass of that name not found)
675 None if the node could not be added because it was None
676 """
677 for uclass in self._uclass.values():
678 if uclass.name == name:
679 if node is None:
680 return None
681 uclass.alias_num_to_node[int(num)] = node
682 uclass.alias_path_to_num[node.path] = int(num)
683 return True
684 return False