blob: 8619206a8d481f49834dd9f6fa30d6f3ba78e7d1 [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 Glass80d782c42021-02-03 06:01:10 -070077 uclass (Uclass): uclass for this driver
Simon Glass9065bc92020-12-28 20:35:06 -070078 """
Simon Glass78933b72021-02-03 06:00:50 -070079 def __init__(self, name, fname):
Simon Glass9065bc92020-12-28 20:35:06 -070080 self.name = name
Simon Glass78933b72021-02-03 06:00:50 -070081 self.fname = fname
82 self.uclass_id = None
83 self.compat = None
84 self.priv = ''
Simon Glass0f3b1412021-02-03 06:00:53 -070085 self.plat = ''
86 self.child_priv = ''
87 self.child_plat = ''
Simon Glasseb3c2492021-02-03 06:01:01 -070088 self.used = False
Simon Glassf303ee72021-02-03 06:01:02 -070089 self.phase = ''
Simon Glassa7b1c772021-02-03 06:01:04 -070090 self.headers = []
Simon Glass9b2eac02021-02-03 06:01:06 -070091 self.dups = []
92 self.warn_dups = False
Simon Glass80d782c42021-02-03 06:01:10 -070093 self.uclass = None
Simon Glass9065bc92020-12-28 20:35:06 -070094
95 def __eq__(self, other):
Simon Glass78933b72021-02-03 06:00:50 -070096 return (self.name == other.name and
97 self.uclass_id == other.uclass_id and
98 self.compat == other.compat and
Simon Glass0f3b1412021-02-03 06:00:53 -070099 self.priv == other.priv and
Simon Glasseb3c2492021-02-03 06:01:01 -0700100 self.plat == other.plat and
101 self.used == other.used)
Simon Glass9065bc92020-12-28 20:35:06 -0700102
103 def __repr__(self):
Simon Glasseb3c2492021-02-03 06:01:01 -0700104 return ("Driver(name='%s', used=%s, uclass_id='%s', compat=%s, priv=%s)" %
105 (self.name, self.used, self.uclass_id, self.compat, self.priv))
Simon Glass9065bc92020-12-28 20:35:06 -0700106
107
Simon Glass2e199ab2021-02-03 06:00:54 -0700108class UclassDriver:
109 """Holds information about a uclass driver
110
111 Attributes:
112 name: Uclass name, e.g. 'i2c' if the driver is for UCLASS_I2C
113 uclass_id: Uclass ID, e.g. 'UCLASS_I2C'
114 priv: struct name of the private data, e.g. 'i2c_priv'
115 per_dev_priv (str): struct name of the priv_auto member, e.g. 'spi_info'
116 per_dev_plat (str): struct name of the plat_auto member, e.g. 'i2c_chip'
117 per_child_priv (str): struct name of the per_child_auto member,
118 e.g. 'pci_child_priv'
119 per_child_plat (str): struct name of the per_child_plat_auto member,
120 e.g. 'pci_child_plat'
Simon Glassbe88d2f2021-02-03 06:01:07 -0700121 alias_num_to_node (dict): Aliases for this uclasses (for sequence
122 numbers)
123 key (int): Alias number, e.g. 2 for "pci2"
124 value (str): Node the alias points to
125 alias_path_to_num (dict): Convert a path to an alias number
126 key (str): Full path to node (e.g. '/soc/pci')
127 seq (int): Alias number, e.g. 2 for "pci2"
Simon Glass80d782c42021-02-03 06:01:10 -0700128 devs (list): List of devices in this uclass, each a Node
129 node_refs (dict): References in the linked list of devices:
130 key (int): Sequence number (0=first, n-1=last, -1=head, n=tail)
131 value (str): Reference to the device at that position
Simon Glass2e199ab2021-02-03 06:00:54 -0700132 """
133 def __init__(self, name):
134 self.name = name
135 self.uclass_id = None
136 self.priv = ''
137 self.per_dev_priv = ''
138 self.per_dev_plat = ''
139 self.per_child_priv = ''
140 self.per_child_plat = ''
Simon Glassbe88d2f2021-02-03 06:01:07 -0700141 self.alias_num_to_node = {}
142 self.alias_path_to_num = {}
Simon Glass80d782c42021-02-03 06:01:10 -0700143 self.devs = []
144 self.node_refs = {}
Simon Glass2e199ab2021-02-03 06:00:54 -0700145
146 def __eq__(self, other):
147 return (self.name == other.name and
148 self.uclass_id == other.uclass_id and
149 self.priv == other.priv)
150
151 def __repr__(self):
152 return ("UclassDriver(name='%s', uclass_id='%s')" %
153 (self.name, self.uclass_id))
154
155 def __hash__(self):
156 # We can use the uclass ID since it is unique among uclasses
157 return hash(self.uclass_id)
158
159
Simon Glass88bd5382021-02-03 06:00:55 -0700160class Struct:
161 """Holds information about a struct definition
162
163 Attributes:
164 name: Struct name, e.g. 'fred' if the struct is 'struct fred'
165 fname: Filename containing the struct, in a format that C files can
166 include, e.g. 'asm/clk.h'
167 """
168 def __init__(self, name, fname):
169 self.name = name
170 self.fname =fname
171
172 def __repr__(self):
173 return ("Struct(name='%s', fname='%s')" % (self.name, self.fname))
174
175
Simon Glass9065bc92020-12-28 20:35:06 -0700176class Scanner:
177 """Scanning of the U-Boot source tree
178
179 Properties:
180 _basedir (str): Base directory of U-Boot source code. Defaults to the
181 grandparent of this file's directory
182 _drivers: Dict of valid driver names found in drivers/
183 key: Driver name
184 value: Driver for that driver
185 _driver_aliases: Dict that holds aliases for driver names
186 key: Driver alias declared with
187 DM_DRIVER_ALIAS(driver_alias, driver_name)
188 value: Driver name declared with U_BOOT_DRIVER(driver_name)
Simon Glassdf692c32020-12-28 20:35:07 -0700189 _warning_disabled: true to disable warnings about driver names not found
Simon Glass9065bc92020-12-28 20:35:06 -0700190 _drivers_additional (list or str): List of additional drivers to use
191 during scanning
Simon Glass78933b72021-02-03 06:00:50 -0700192 _of_match: Dict holding information about compatible strings
193 key: Name of struct udevice_id variable
194 value: Dict of compatible info in that variable:
195 key: Compatible string, e.g. 'rockchip,rk3288-grf'
196 value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
197 _compat_to_driver: Maps compatible strings to Driver
Simon Glass2e199ab2021-02-03 06:00:54 -0700198 _uclass: Dict of uclass information
199 key: uclass name, e.g. 'UCLASS_I2C'
200 value: UClassDriver
Simon Glass88bd5382021-02-03 06:00:55 -0700201 _structs: Dict of all structs found in U-Boot:
202 key: Name of struct
203 value: Struct object
Simon Glassf303ee72021-02-03 06:01:02 -0700204 _phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
205 or 'tpl'. None if not known
Simon Glass9065bc92020-12-28 20:35:06 -0700206 """
Simon Glassf303ee72021-02-03 06:01:02 -0700207 def __init__(self, basedir, warning_disabled, drivers_additional, phase=''):
Simon Glass9065bc92020-12-28 20:35:06 -0700208 """Set up a new Scanner
209 """
210 if not basedir:
211 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
212 if basedir == '':
213 basedir = './'
214 self._basedir = basedir
215 self._drivers = {}
216 self._driver_aliases = {}
217 self._drivers_additional = drivers_additional or []
218 self._warning_disabled = warning_disabled
Simon Glass78933b72021-02-03 06:00:50 -0700219 self._of_match = {}
220 self._compat_to_driver = {}
Simon Glass2e199ab2021-02-03 06:00:54 -0700221 self._uclass = {}
Simon Glass88bd5382021-02-03 06:00:55 -0700222 self._structs = {}
Simon Glassf303ee72021-02-03 06:01:02 -0700223 self._phase = phase
Simon Glass9065bc92020-12-28 20:35:06 -0700224
Simon Glass047a4802021-02-03 06:01:00 -0700225 def get_driver(self, name):
226 """Get a driver given its name
227
228 Args:
229 name (str): Driver name
230
231 Returns:
232 Driver: Driver or None if not found
233 """
234 return self._drivers.get(name)
235
Simon Glass9065bc92020-12-28 20:35:06 -0700236 def get_normalized_compat_name(self, node):
237 """Get a node's normalized compat name
238
239 Returns a valid driver name by retrieving node's list of compatible
240 string as a C identifier and performing a check against _drivers
241 and a lookup in driver_aliases printing a warning in case of failure.
242
243 Args:
244 node (Node): Node object to check
245 Return:
246 Tuple:
247 Driver name associated with the first compatible string
248 List of C identifiers for all the other compatible strings
249 (possibly empty)
250 In case of no match found, the return will be the same as
251 get_compat_name()
252 """
253 compat_list_c = get_compat_name(node)
254
255 for compat_c in compat_list_c:
256 if not compat_c in self._drivers.keys():
257 compat_c = self._driver_aliases.get(compat_c)
258 if not compat_c:
259 continue
260
261 aliases_c = compat_list_c
262 if compat_c in aliases_c:
263 aliases_c.remove(compat_c)
264 return compat_c, aliases_c
265
266 if not self._warning_disabled:
267 print('WARNING: the driver %s was not found in the driver list'
268 % (compat_list_c[0]))
269
270 return compat_list_c[0], compat_list_c[1:]
271
Simon Glass88bd5382021-02-03 06:00:55 -0700272 def _parse_structs(self, fname, buff):
273 """Parse a H file to extract struct definitions contained within
274
275 This parses 'struct xx {' definitions to figure out what structs this
276 header defines.
277
278 Args:
279 buff (str): Contents of file
280 fname (str): Filename (to use when printing errors)
281 """
282 structs = {}
283
284 re_struct = re.compile('^struct ([a-z0-9_]+) {$')
285 re_asm = re.compile('../arch/[a-z0-9]+/include/asm/(.*)')
286 prefix = ''
287 for line in buff.splitlines():
288 # Handle line continuation
289 if prefix:
290 line = prefix + line
291 prefix = ''
292 if line.endswith('\\'):
293 prefix = line[:-1]
294 continue
295
296 m_struct = re_struct.match(line)
297 if m_struct:
298 name = m_struct.group(1)
299 include_dir = os.path.join(self._basedir, 'include')
300 rel_fname = os.path.relpath(fname, include_dir)
301 m_asm = re_asm.match(rel_fname)
302 if m_asm:
303 rel_fname = 'asm/' + m_asm.group(1)
304 structs[name] = Struct(name, rel_fname)
305 self._structs.update(structs)
306
Simon Glass78933b72021-02-03 06:00:50 -0700307 @classmethod
308 def _get_re_for_member(cls, member):
309 """_get_re_for_member: Get a compiled regular expression
310
311 Args:
312 member (str): Struct member name, e.g. 'priv_auto'
313
314 Returns:
315 re.Pattern: Compiled regular expression that parses:
316
317 .member = sizeof(struct fred),
318
319 and returns "fred" as group 1
320 """
321 return re.compile(r'^\s*.%s\s*=\s*sizeof\(struct\s+(.*)\),$' % member)
322
Simon Glass2e199ab2021-02-03 06:00:54 -0700323 def _parse_uclass_driver(self, fname, buff):
324 """Parse a C file to extract uclass driver information contained within
325
326 This parses UCLASS_DRIVER() structs to obtain various pieces of useful
327 information.
328
329 It updates the following member:
330 _uclass: Dict of uclass information
331 key: uclass name, e.g. 'UCLASS_I2C'
332 value: UClassDriver
333
334 Args:
335 fname (str): Filename being parsed (used for warnings)
336 buff (str): Contents of file
337 """
338 uc_drivers = {}
339
340 # Collect the driver name and associated Driver
341 driver = None
Simon Glass072b9c52021-02-03 06:01:08 -0700342 re_driver = re.compile(r'^UCLASS_DRIVER\((.*)\)')
Simon Glass2e199ab2021-02-03 06:00:54 -0700343
344 # Collect the uclass ID, e.g. 'UCLASS_SPI'
345 re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)')
346
347 # Matches the header/size information for uclass-private data
348 re_priv = self._get_re_for_member('priv_auto')
349
350 # Set up parsing for the auto members
351 re_per_device_priv = self._get_re_for_member('per_device_auto')
352 re_per_device_plat = self._get_re_for_member('per_device_plat_auto')
353 re_per_child_priv = self._get_re_for_member('per_child_auto')
354 re_per_child_plat = self._get_re_for_member('per_child_plat_auto')
355
356 prefix = ''
357 for line in buff.splitlines():
358 # Handle line continuation
359 if prefix:
360 line = prefix + line
361 prefix = ''
362 if line.endswith('\\'):
363 prefix = line[:-1]
364 continue
365
366 driver_match = re_driver.search(line)
367
368 # If we have seen UCLASS_DRIVER()...
369 if driver:
370 m_id = re_id.search(line)
371 m_priv = re_priv.match(line)
372 m_per_dev_priv = re_per_device_priv.match(line)
373 m_per_dev_plat = re_per_device_plat.match(line)
374 m_per_child_priv = re_per_child_priv.match(line)
375 m_per_child_plat = re_per_child_plat.match(line)
376 if m_id:
377 driver.uclass_id = m_id.group(1)
378 elif m_priv:
379 driver.priv = m_priv.group(1)
380 elif m_per_dev_priv:
381 driver.per_dev_priv = m_per_dev_priv.group(1)
382 elif m_per_dev_plat:
383 driver.per_dev_plat = m_per_dev_plat.group(1)
384 elif m_per_child_priv:
385 driver.per_child_priv = m_per_child_priv.group(1)
386 elif m_per_child_plat:
387 driver.per_child_plat = m_per_child_plat.group(1)
388 elif '};' in line:
389 if not driver.uclass_id:
390 raise ValueError(
391 "%s: Cannot parse uclass ID in driver '%s'" %
392 (fname, driver.name))
393 uc_drivers[driver.uclass_id] = driver
394 driver = None
395
396 elif driver_match:
397 driver_name = driver_match.group(1)
398 driver = UclassDriver(driver_name)
399
400 self._uclass.update(uc_drivers)
401
Simon Glass78933b72021-02-03 06:00:50 -0700402 def _parse_driver(self, fname, buff):
403 """Parse a C file to extract driver information contained within
404
405 This parses U_BOOT_DRIVER() structs to obtain various pieces of useful
406 information.
407
408 It updates the following members:
409 _drivers - updated with new Driver records for each driver found
410 in the file
411 _of_match - updated with each compatible string found in the file
412 _compat_to_driver - Maps compatible string to Driver
Simon Glass2f4455f2021-02-03 06:01:05 -0700413 _driver_aliases - Maps alias names to driver name
Simon Glass78933b72021-02-03 06:00:50 -0700414
415 Args:
416 fname (str): Filename being parsed (used for warnings)
417 buff (str): Contents of file
418
419 Raises:
420 ValueError: Compatible variable is mentioned in .of_match in
421 U_BOOT_DRIVER() but not found in the file
422 """
423 # Dict holding information about compatible strings collected in this
424 # function so far
425 # key: Name of struct udevice_id variable
426 # value: Dict of compatible info in that variable:
427 # key: Compatible string, e.g. 'rockchip,rk3288-grf'
428 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
429 of_match = {}
430
431 # Dict holding driver information collected in this function so far
432 # key: Driver name (C name as in U_BOOT_DRIVER(xxx))
433 # value: Driver
434 drivers = {}
435
436 # Collect the driver info
437 driver = None
Simon Glass072b9c52021-02-03 06:01:08 -0700438 re_driver = re.compile(r'^U_BOOT_DRIVER\((.*)\)')
Simon Glass78933b72021-02-03 06:00:50 -0700439
440 # Collect the uclass ID, e.g. 'UCLASS_SPI'
441 re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)')
442
443 # Collect the compatible string, e.g. 'rockchip,rk3288-grf'
444 compat = None
445 re_compat = re.compile(r'{\s*.compatible\s*=\s*"(.*)"\s*'
446 r'(,\s*.data\s*=\s*(\S*))?\s*},')
447
448 # This is a dict of compatible strings that were found:
449 # key: Compatible string, e.g. 'rockchip,rk3288-grf'
450 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
451 compat_dict = {}
452
453 # Holds the var nane of the udevice_id list, e.g.
454 # 'rk3288_syscon_ids_noc' in
455 # static const struct udevice_id rk3288_syscon_ids_noc[] = {
456 ids_name = None
457 re_ids = re.compile(r'struct udevice_id (.*)\[\]\s*=')
458
459 # Matches the references to the udevice_id list
460 re_of_match = re.compile(
461 r'\.of_match\s*=\s*(of_match_ptr\()?([a-z0-9_]+)(\))?,')
462
Simon Glassf303ee72021-02-03 06:01:02 -0700463 re_phase = re.compile('^\s*DM_PHASE\((.*)\).*$')
Simon Glassa7b1c772021-02-03 06:01:04 -0700464 re_hdr = re.compile('^\s*DM_HEADER\((.*)\).*$')
Simon Glass2f4455f2021-02-03 06:01:05 -0700465 re_alias = re.compile(r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)')
Simon Glassf303ee72021-02-03 06:01:02 -0700466
Simon Glass0f3b1412021-02-03 06:00:53 -0700467 # Matches the struct name for priv, plat
Simon Glass78933b72021-02-03 06:00:50 -0700468 re_priv = self._get_re_for_member('priv_auto')
Simon Glass0f3b1412021-02-03 06:00:53 -0700469 re_plat = self._get_re_for_member('plat_auto')
470 re_child_priv = self._get_re_for_member('per_child_auto')
471 re_child_plat = self._get_re_for_member('per_child_plat_auto')
Simon Glass78933b72021-02-03 06:00:50 -0700472
473 prefix = ''
474 for line in buff.splitlines():
475 # Handle line continuation
476 if prefix:
477 line = prefix + line
478 prefix = ''
479 if line.endswith('\\'):
480 prefix = line[:-1]
481 continue
482
483 driver_match = re_driver.search(line)
484
485 # If this line contains U_BOOT_DRIVER()...
486 if driver:
487 m_id = re_id.search(line)
488 m_of_match = re_of_match.search(line)
489 m_priv = re_priv.match(line)
Simon Glass0f3b1412021-02-03 06:00:53 -0700490 m_plat = re_plat.match(line)
491 m_cplat = re_child_plat.match(line)
492 m_cpriv = re_child_priv.match(line)
Simon Glassf303ee72021-02-03 06:01:02 -0700493 m_phase = re_phase.match(line)
Simon Glassa7b1c772021-02-03 06:01:04 -0700494 m_hdr = re_hdr.match(line)
Simon Glass78933b72021-02-03 06:00:50 -0700495 if m_priv:
496 driver.priv = m_priv.group(1)
Simon Glass0f3b1412021-02-03 06:00:53 -0700497 elif m_plat:
498 driver.plat = m_plat.group(1)
499 elif m_cplat:
500 driver.child_plat = m_cplat.group(1)
501 elif m_cpriv:
502 driver.child_priv = m_cpriv.group(1)
Simon Glass78933b72021-02-03 06:00:50 -0700503 elif m_id:
504 driver.uclass_id = m_id.group(1)
505 elif m_of_match:
506 compat = m_of_match.group(2)
Simon Glassf303ee72021-02-03 06:01:02 -0700507 elif m_phase:
508 driver.phase = m_phase.group(1)
Simon Glassa7b1c772021-02-03 06:01:04 -0700509 elif m_hdr:
510 driver.headers.append(m_hdr.group(1))
Simon Glass78933b72021-02-03 06:00:50 -0700511 elif '};' in line:
512 if driver.uclass_id and compat:
513 if compat not in of_match:
514 raise ValueError(
515 "%s: Unknown compatible var '%s' (found: %s)" %
516 (fname, compat, ','.join(of_match.keys())))
517 driver.compat = of_match[compat]
518
519 # This needs to be deterministic, since a driver may
520 # have multiple compatible strings pointing to it.
521 # We record the one earliest in the alphabet so it
522 # will produce the same result on all machines.
523 for compat_id in of_match[compat]:
524 old = self._compat_to_driver.get(compat_id)
525 if not old or driver.name < old.name:
526 self._compat_to_driver[compat_id] = driver
527 drivers[driver.name] = driver
528 else:
529 # The driver does not have a uclass or compat string.
530 # The first is required but the second is not, so just
531 # ignore this.
532 pass
533 driver = None
534 ids_name = None
535 compat = None
536 compat_dict = {}
537
538 elif ids_name:
539 compat_m = re_compat.search(line)
540 if compat_m:
541 compat_dict[compat_m.group(1)] = compat_m.group(3)
542 elif '};' in line:
543 of_match[ids_name] = compat_dict
544 ids_name = None
545 elif driver_match:
546 driver_name = driver_match.group(1)
547 driver = Driver(driver_name, fname)
548 else:
549 ids_m = re_ids.search(line)
Simon Glass2f4455f2021-02-03 06:01:05 -0700550 m_alias = re_alias.match(line)
Simon Glass78933b72021-02-03 06:00:50 -0700551 if ids_m:
552 ids_name = ids_m.group(1)
Simon Glass2f4455f2021-02-03 06:01:05 -0700553 elif m_alias:
554 self._driver_aliases[m_alias[2]] = m_alias[1]
Simon Glass78933b72021-02-03 06:00:50 -0700555
556 # Make the updates based on what we found
Simon Glass9b2eac02021-02-03 06:01:06 -0700557 for driver in drivers.values():
558 if driver.name in self._drivers:
559 orig = self._drivers[driver.name]
560 if self._phase:
561 # If the original driver matches our phase, use it
562 if orig.phase == self._phase:
563 orig.dups.append(driver)
564 continue
565
566 # Otherwise use the new driver, which is assumed to match
567 else:
568 # We have no way of distinguishing them
569 driver.warn_dups = True
570 driver.dups.append(orig)
571 self._drivers[driver.name] = driver
Simon Glass78933b72021-02-03 06:00:50 -0700572 self._of_match.update(of_match)
573
Simon Glass9065bc92020-12-28 20:35:06 -0700574 def scan_driver(self, fname):
575 """Scan a driver file to build a list of driver names and aliases
576
Simon Glass78933b72021-02-03 06:00:50 -0700577 It updates the following members:
578 _drivers - updated with new Driver records for each driver found
579 in the file
580 _of_match - updated with each compatible string found in the file
581 _compat_to_driver - Maps compatible string to Driver
582 _driver_aliases - Maps alias names to driver name
Simon Glass9065bc92020-12-28 20:35:06 -0700583
584 Args
585 fname: Driver filename to scan
586 """
587 with open(fname, encoding='utf-8') as inf:
588 try:
589 buff = inf.read()
590 except UnicodeDecodeError:
591 # This seems to happen on older Python versions
592 print("Skipping file '%s' due to unicode error" % fname)
593 return
594
Simon Glass78933b72021-02-03 06:00:50 -0700595 # If this file has any U_BOOT_DRIVER() declarations, process it to
596 # obtain driver information
597 if 'U_BOOT_DRIVER' in buff:
598 self._parse_driver(fname, buff)
Simon Glass2e199ab2021-02-03 06:00:54 -0700599 if 'UCLASS_DRIVER' in buff:
600 self._parse_uclass_driver(fname, buff)
Simon Glass9065bc92020-12-28 20:35:06 -0700601
Simon Glass88bd5382021-02-03 06:00:55 -0700602 def scan_header(self, fname):
603 """Scan a header file to build a list of struct definitions
604
605 It updates the following members:
606 _structs - updated with new Struct records for each struct found
607 in the file
608
609 Args
610 fname: header filename to scan
611 """
612 with open(fname, encoding='utf-8') as inf:
613 try:
614 buff = inf.read()
615 except UnicodeDecodeError:
616 # This seems to happen on older Python versions
617 print("Skipping file '%s' due to unicode error" % fname)
618 return
619
620 # If this file has any U_BOOT_DRIVER() declarations, process it to
621 # obtain driver information
622 if 'struct' in buff:
623 self._parse_structs(fname, buff)
624
Simon Glass9065bc92020-12-28 20:35:06 -0700625 def scan_drivers(self):
626 """Scan the driver folders to build a list of driver names and aliases
627
628 This procedure will populate self._drivers and self._driver_aliases
629 """
630 for (dirpath, _, filenames) in os.walk(self._basedir):
Simon Glassdc37c812021-02-03 06:00:52 -0700631 rel_path = dirpath[len(self._basedir):]
632 if rel_path.startswith('/'):
633 rel_path = rel_path[1:]
634 if rel_path.startswith('build') or rel_path.startswith('.git'):
635 continue
Simon Glass9065bc92020-12-28 20:35:06 -0700636 for fname in filenames:
Simon Glass88bd5382021-02-03 06:00:55 -0700637 pathname = dirpath + '/' + fname
638 if fname.endswith('.c'):
639 self.scan_driver(pathname)
640 elif fname.endswith('.h'):
641 self.scan_header(pathname)
Simon Glass9065bc92020-12-28 20:35:06 -0700642 for fname in self._drivers_additional:
643 if not isinstance(fname, str) or len(fname) == 0:
644 continue
645 if fname[0] == '/':
646 self.scan_driver(fname)
647 else:
648 self.scan_driver(self._basedir + '/' + fname)
Simon Glasseb3c2492021-02-03 06:01:01 -0700649
Simon Glass80d782c42021-02-03 06:01:10 -0700650 # Get the uclass for each driver
651 # TODO: Can we just get the uclass for the ones we use, e.g. in
652 # mark_used()?
653 for driver in self._drivers.values():
654 driver.uclass = self._uclass.get(driver.uclass_id)
655
Simon Glasseb3c2492021-02-03 06:01:01 -0700656 def mark_used(self, nodes):
657 """Mark the drivers associated with a list of nodes as 'used'
658
659 This takes a list of nodes, finds the driver for each one and marks it
660 as used.
661
Simon Glass9b2eac02021-02-03 06:01:06 -0700662 If two used drivers have the same name, issue a warning.
663
Simon Glasseb3c2492021-02-03 06:01:01 -0700664 Args:
665 nodes (list of None): Nodes that are in use
666 """
667 # Figure out which drivers we actually use
668 for node in nodes:
669 struct_name, _ = self.get_normalized_compat_name(node)
670 driver = self._drivers.get(struct_name)
671 if driver:
672 driver.used = True
Simon Glass9b2eac02021-02-03 06:01:06 -0700673 if driver.dups and driver.warn_dups:
674 print("Warning: Duplicate driver name '%s' (orig=%s, dups=%s)" %
675 (driver.name, driver.fname,
676 ', '.join([drv.fname for drv in driver.dups])))
Simon Glassbe88d2f2021-02-03 06:01:07 -0700677
678 def add_uclass_alias(self, name, num, node):
679 """Add an alias to a uclass
680
681 Args:
682 name: Name of uclass, e.g. 'i2c'
683 num: Alias number, e.g. 2 for alias 'i2c2'
684 node: Node the alias points to, or None if None
685
686 Returns:
687 True if the node was added
688 False if the node was not added (uclass of that name not found)
689 None if the node could not be added because it was None
690 """
691 for uclass in self._uclass.values():
692 if uclass.name == name:
693 if node is None:
694 return None
695 uclass.alias_num_to_node[int(num)] = node
696 uclass.alias_path_to_num[node.path] = int(num)
697 return True
698 return False
Simon Glass80d782c42021-02-03 06:01:10 -0700699
700 def assign_seq(self, node):
701 """Figure out the sequence number for a node
702
703 This looks in the node's uclass and assigns a sequence number if needed,
704 based on the aliases and other nodes in that uclass.
705
706 It updates the uclass alias_path_to_num and alias_num_to_node
707
708 Args:
709 node (Node): Node object to look up
710 """
711 if node.driver and node.seq == -1 and node.uclass:
712 uclass = node.uclass
713 num = uclass.alias_path_to_num.get(node.path)
714 if num is not None:
715 return num
716 else:
717 # Dynamically allocate the next available value after all
718 # existing ones
719 if uclass.alias_num_to_node:
720 start = max(uclass.alias_num_to_node.keys())
721 else:
722 start = -1
723 for seq in range(start + 1, 1000):
724 if seq not in uclass.alias_num_to_node:
725 break
726 uclass.alias_path_to_num[node.path] = seq
727 uclass.alias_num_to_node[seq] = node
728 return seq
729 return None