blob: 1dbb56712a39c7f61b54200a6eb3e7d04b3167ba [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('.', '_')
Simon Glass50aae3e2021-02-03 06:01:11 -070036 if new == '/':
37 return 'root'
Simon Glassa542a702020-12-28 20:35:06 -070038 return new
39
40def get_compat_name(node):
41 """Get the node's list of compatible string as a C identifiers
42
43 Args:
44 node (fdt.Node): Node object to check
45 Return:
46 list of str: List of C identifiers for all the compatible strings
47 """
48 compat = node.props['compatible'].value
49 if not isinstance(compat, list):
50 compat = [compat]
51 return [conv_name_to_c(c) for c in compat]
52
53
54class Driver:
55 """Information about a driver in U-Boot
56
57 Attributes:
58 name: Name of driver. For U_BOOT_DRIVER(x) this is 'x'
Simon Glassc58662f2021-02-03 06:00:50 -070059 fname: Filename where the driver was found
60 uclass_id: Name of uclass, e.g. 'UCLASS_I2C'
61 compat: Driver data for each compatible string:
62 key: Compatible string, e.g. 'rockchip,rk3288-grf'
63 value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
64 fname: Filename where the driver was found
65 priv (str): struct name of the priv_auto member, e.g. 'serial_priv'
Simon Glassc8b19b02021-02-03 06:00:53 -070066 plat (str): struct name of the plat_auto member, e.g. 'serial_plat'
67 child_priv (str): struct name of the per_child_auto member,
68 e.g. 'pci_child_priv'
69 child_plat (str): struct name of the per_child_plat_auto member,
70 e.g. 'pci_child_plat'
Simon Glassb9319c42021-02-03 06:01:01 -070071 used (bool): True if the driver is used by the structs being output
Simon Glassb00f0062021-02-03 06:01:02 -070072 phase (str): Which phase of U-Boot to use this driver
Simon Glass735ddfc2021-02-03 06:01:04 -070073 headers (list): List of header files needed for this driver (each a str)
74 e.g. ['<asm/cpu.h>']
Simon Glass1d972692021-02-03 06:01:06 -070075 dups (list): Driver objects with the same name as this one, that were
76 found after this one
77 warn_dups (bool): True if the duplicates are not distinguisble using
78 the phase
Simon Glass337d6972021-02-03 06:01:10 -070079 uclass (Uclass): uclass for this driver
Simon Glassa542a702020-12-28 20:35:06 -070080 """
Simon Glassc58662f2021-02-03 06:00:50 -070081 def __init__(self, name, fname):
Simon Glassa542a702020-12-28 20:35:06 -070082 self.name = name
Simon Glassc58662f2021-02-03 06:00:50 -070083 self.fname = fname
84 self.uclass_id = None
85 self.compat = None
86 self.priv = ''
Simon Glassc8b19b02021-02-03 06:00:53 -070087 self.plat = ''
88 self.child_priv = ''
89 self.child_plat = ''
Simon Glassb9319c42021-02-03 06:01:01 -070090 self.used = False
Simon Glassb00f0062021-02-03 06:01:02 -070091 self.phase = ''
Simon Glass735ddfc2021-02-03 06:01:04 -070092 self.headers = []
Simon Glass1d972692021-02-03 06:01:06 -070093 self.dups = []
94 self.warn_dups = False
Simon Glass337d6972021-02-03 06:01:10 -070095 self.uclass = None
Simon Glassa542a702020-12-28 20:35:06 -070096
97 def __eq__(self, other):
Simon Glassc58662f2021-02-03 06:00:50 -070098 return (self.name == other.name and
99 self.uclass_id == other.uclass_id and
100 self.compat == other.compat and
Simon Glassc8b19b02021-02-03 06:00:53 -0700101 self.priv == other.priv and
Simon Glassb9319c42021-02-03 06:01:01 -0700102 self.plat == other.plat and
103 self.used == other.used)
Simon Glassa542a702020-12-28 20:35:06 -0700104
105 def __repr__(self):
Simon Glassb9319c42021-02-03 06:01:01 -0700106 return ("Driver(name='%s', used=%s, uclass_id='%s', compat=%s, priv=%s)" %
107 (self.name, self.used, self.uclass_id, self.compat, self.priv))
Simon Glassa542a702020-12-28 20:35:06 -0700108
109
Simon Glass1a8b4b92021-02-03 06:00:54 -0700110class UclassDriver:
111 """Holds information about a uclass driver
112
113 Attributes:
114 name: Uclass name, e.g. 'i2c' if the driver is for UCLASS_I2C
115 uclass_id: Uclass ID, e.g. 'UCLASS_I2C'
116 priv: struct name of the private data, e.g. 'i2c_priv'
117 per_dev_priv (str): struct name of the priv_auto member, e.g. 'spi_info'
118 per_dev_plat (str): struct name of the plat_auto member, e.g. 'i2c_chip'
119 per_child_priv (str): struct name of the per_child_auto member,
120 e.g. 'pci_child_priv'
121 per_child_plat (str): struct name of the per_child_plat_auto member,
122 e.g. 'pci_child_plat'
Simon Glass05953522021-02-03 06:01:07 -0700123 alias_num_to_node (dict): Aliases for this uclasses (for sequence
124 numbers)
125 key (int): Alias number, e.g. 2 for "pci2"
126 value (str): Node the alias points to
127 alias_path_to_num (dict): Convert a path to an alias number
128 key (str): Full path to node (e.g. '/soc/pci')
129 seq (int): Alias number, e.g. 2 for "pci2"
Simon Glass337d6972021-02-03 06:01:10 -0700130 devs (list): List of devices in this uclass, each a Node
131 node_refs (dict): References in the linked list of devices:
132 key (int): Sequence number (0=first, n-1=last, -1=head, n=tail)
133 value (str): Reference to the device at that position
Simon Glass1a8b4b92021-02-03 06:00:54 -0700134 """
135 def __init__(self, name):
136 self.name = name
137 self.uclass_id = None
138 self.priv = ''
139 self.per_dev_priv = ''
140 self.per_dev_plat = ''
141 self.per_child_priv = ''
142 self.per_child_plat = ''
Simon Glass05953522021-02-03 06:01:07 -0700143 self.alias_num_to_node = {}
144 self.alias_path_to_num = {}
Simon Glass337d6972021-02-03 06:01:10 -0700145 self.devs = []
146 self.node_refs = {}
Simon Glass1a8b4b92021-02-03 06:00:54 -0700147
148 def __eq__(self, other):
149 return (self.name == other.name and
150 self.uclass_id == other.uclass_id and
151 self.priv == other.priv)
152
153 def __repr__(self):
154 return ("UclassDriver(name='%s', uclass_id='%s')" %
155 (self.name, self.uclass_id))
156
157 def __hash__(self):
158 # We can use the uclass ID since it is unique among uclasses
159 return hash(self.uclass_id)
160
161
Simon Glassacf5cb82021-02-03 06:00:55 -0700162class Struct:
163 """Holds information about a struct definition
164
165 Attributes:
166 name: Struct name, e.g. 'fred' if the struct is 'struct fred'
167 fname: Filename containing the struct, in a format that C files can
168 include, e.g. 'asm/clk.h'
169 """
170 def __init__(self, name, fname):
171 self.name = name
172 self.fname =fname
173
174 def __repr__(self):
175 return ("Struct(name='%s', fname='%s')" % (self.name, self.fname))
176
177
Simon Glassa542a702020-12-28 20:35:06 -0700178class Scanner:
179 """Scanning of the U-Boot source tree
180
181 Properties:
182 _basedir (str): Base directory of U-Boot source code. Defaults to the
183 grandparent of this file's directory
184 _drivers: Dict of valid driver names found in drivers/
185 key: Driver name
186 value: Driver for that driver
187 _driver_aliases: Dict that holds aliases for driver names
188 key: Driver alias declared with
189 DM_DRIVER_ALIAS(driver_alias, driver_name)
190 value: Driver name declared with U_BOOT_DRIVER(driver_name)
191 _drivers_additional (list or str): List of additional drivers to use
192 during scanning
Simon Glassc58662f2021-02-03 06:00:50 -0700193 _of_match: Dict holding information about compatible strings
194 key: Name of struct udevice_id variable
195 value: Dict of compatible info in that variable:
196 key: Compatible string, e.g. 'rockchip,rk3288-grf'
197 value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
198 _compat_to_driver: Maps compatible strings to Driver
Simon Glass1a8b4b92021-02-03 06:00:54 -0700199 _uclass: Dict of uclass information
200 key: uclass name, e.g. 'UCLASS_I2C'
201 value: UClassDriver
Simon Glassacf5cb82021-02-03 06:00:55 -0700202 _structs: Dict of all structs found in U-Boot:
203 key: Name of struct
204 value: Struct object
Simon Glassb00f0062021-02-03 06:01:02 -0700205 _phase: The phase of U-Boot that we are generating data for, e.g. 'spl'
206 or 'tpl'. None if not known
Simon Glassa542a702020-12-28 20:35:06 -0700207 """
Simon Glass0c59ace2021-03-26 16:17:25 +1300208 def __init__(self, basedir, drivers_additional, phase=''):
Simon Glassa542a702020-12-28 20:35:06 -0700209 """Set up a new Scanner
210 """
211 if not basedir:
212 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '')
213 if basedir == '':
214 basedir = './'
215 self._basedir = basedir
216 self._drivers = {}
217 self._driver_aliases = {}
218 self._drivers_additional = drivers_additional or []
Simon Glass0c59ace2021-03-26 16:17:25 +1300219 self._missing_drivers = set()
Simon Glassc58662f2021-02-03 06:00:50 -0700220 self._of_match = {}
221 self._compat_to_driver = {}
Simon Glass1a8b4b92021-02-03 06:00:54 -0700222 self._uclass = {}
Simon Glassacf5cb82021-02-03 06:00:55 -0700223 self._structs = {}
Simon Glassb00f0062021-02-03 06:01:02 -0700224 self._phase = phase
Simon Glassa542a702020-12-28 20:35:06 -0700225
Simon Glassfd471e22021-02-03 06:01:00 -0700226 def get_driver(self, name):
227 """Get a driver given its name
228
229 Args:
230 name (str): Driver name
231
232 Returns:
233 Driver: Driver or None if not found
234 """
235 return self._drivers.get(name)
236
Simon Glassa542a702020-12-28 20:35:06 -0700237 def get_normalized_compat_name(self, node):
238 """Get a node's normalized compat name
239
240 Returns a valid driver name by retrieving node's list of compatible
241 string as a C identifier and performing a check against _drivers
242 and a lookup in driver_aliases printing a warning in case of failure.
243
244 Args:
245 node (Node): Node object to check
246 Return:
247 Tuple:
248 Driver name associated with the first compatible string
249 List of C identifiers for all the other compatible strings
250 (possibly empty)
251 In case of no match found, the return will be the same as
252 get_compat_name()
253 """
Simon Glass50aae3e2021-02-03 06:01:11 -0700254 if not node.parent:
255 compat_list_c = ['root_driver']
256 else:
257 compat_list_c = get_compat_name(node)
Simon Glassa542a702020-12-28 20:35:06 -0700258
259 for compat_c in compat_list_c:
260 if not compat_c in self._drivers.keys():
261 compat_c = self._driver_aliases.get(compat_c)
262 if not compat_c:
263 continue
264
265 aliases_c = compat_list_c
266 if compat_c in aliases_c:
267 aliases_c.remove(compat_c)
268 return compat_c, aliases_c
269
Simon Glass0c59ace2021-03-26 16:17:25 +1300270 self._missing_drivers.add(compat_list_c[0])
Simon Glassa542a702020-12-28 20:35:06 -0700271
272 return compat_list_c[0], compat_list_c[1:]
273
Simon Glassacf5cb82021-02-03 06:00:55 -0700274 def _parse_structs(self, fname, buff):
275 """Parse a H file to extract struct definitions contained within
276
277 This parses 'struct xx {' definitions to figure out what structs this
278 header defines.
279
280 Args:
281 buff (str): Contents of file
282 fname (str): Filename (to use when printing errors)
283 """
284 structs = {}
285
286 re_struct = re.compile('^struct ([a-z0-9_]+) {$')
287 re_asm = re.compile('../arch/[a-z0-9]+/include/asm/(.*)')
288 prefix = ''
289 for line in buff.splitlines():
290 # Handle line continuation
291 if prefix:
292 line = prefix + line
293 prefix = ''
294 if line.endswith('\\'):
295 prefix = line[:-1]
296 continue
297
298 m_struct = re_struct.match(line)
299 if m_struct:
300 name = m_struct.group(1)
301 include_dir = os.path.join(self._basedir, 'include')
302 rel_fname = os.path.relpath(fname, include_dir)
303 m_asm = re_asm.match(rel_fname)
304 if m_asm:
305 rel_fname = 'asm/' + m_asm.group(1)
306 structs[name] = Struct(name, rel_fname)
307 self._structs.update(structs)
308
Simon Glassc58662f2021-02-03 06:00:50 -0700309 @classmethod
310 def _get_re_for_member(cls, member):
311 """_get_re_for_member: Get a compiled regular expression
312
313 Args:
314 member (str): Struct member name, e.g. 'priv_auto'
315
316 Returns:
317 re.Pattern: Compiled regular expression that parses:
318
319 .member = sizeof(struct fred),
320
321 and returns "fred" as group 1
322 """
323 return re.compile(r'^\s*.%s\s*=\s*sizeof\(struct\s+(.*)\),$' % member)
324
Simon Glass1a8b4b92021-02-03 06:00:54 -0700325 def _parse_uclass_driver(self, fname, buff):
326 """Parse a C file to extract uclass driver information contained within
327
328 This parses UCLASS_DRIVER() structs to obtain various pieces of useful
329 information.
330
331 It updates the following member:
332 _uclass: Dict of uclass information
333 key: uclass name, e.g. 'UCLASS_I2C'
334 value: UClassDriver
335
336 Args:
337 fname (str): Filename being parsed (used for warnings)
338 buff (str): Contents of file
339 """
340 uc_drivers = {}
341
342 # Collect the driver name and associated Driver
343 driver = None
Simon Glass1712f8b2021-02-03 06:01:08 -0700344 re_driver = re.compile(r'^UCLASS_DRIVER\((.*)\)')
Simon Glass1a8b4b92021-02-03 06:00:54 -0700345
346 # Collect the uclass ID, e.g. 'UCLASS_SPI'
347 re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)')
348
349 # Matches the header/size information for uclass-private data
350 re_priv = self._get_re_for_member('priv_auto')
351
352 # Set up parsing for the auto members
353 re_per_device_priv = self._get_re_for_member('per_device_auto')
354 re_per_device_plat = self._get_re_for_member('per_device_plat_auto')
355 re_per_child_priv = self._get_re_for_member('per_child_auto')
356 re_per_child_plat = self._get_re_for_member('per_child_plat_auto')
357
358 prefix = ''
359 for line in buff.splitlines():
360 # Handle line continuation
361 if prefix:
362 line = prefix + line
363 prefix = ''
364 if line.endswith('\\'):
365 prefix = line[:-1]
366 continue
367
368 driver_match = re_driver.search(line)
369
370 # If we have seen UCLASS_DRIVER()...
371 if driver:
372 m_id = re_id.search(line)
373 m_priv = re_priv.match(line)
374 m_per_dev_priv = re_per_device_priv.match(line)
375 m_per_dev_plat = re_per_device_plat.match(line)
376 m_per_child_priv = re_per_child_priv.match(line)
377 m_per_child_plat = re_per_child_plat.match(line)
378 if m_id:
379 driver.uclass_id = m_id.group(1)
380 elif m_priv:
381 driver.priv = m_priv.group(1)
382 elif m_per_dev_priv:
383 driver.per_dev_priv = m_per_dev_priv.group(1)
384 elif m_per_dev_plat:
385 driver.per_dev_plat = m_per_dev_plat.group(1)
386 elif m_per_child_priv:
387 driver.per_child_priv = m_per_child_priv.group(1)
388 elif m_per_child_plat:
389 driver.per_child_plat = m_per_child_plat.group(1)
390 elif '};' in line:
391 if not driver.uclass_id:
392 raise ValueError(
393 "%s: Cannot parse uclass ID in driver '%s'" %
394 (fname, driver.name))
395 uc_drivers[driver.uclass_id] = driver
396 driver = None
397
398 elif driver_match:
399 driver_name = driver_match.group(1)
400 driver = UclassDriver(driver_name)
401
402 self._uclass.update(uc_drivers)
403
Simon Glassc58662f2021-02-03 06:00:50 -0700404 def _parse_driver(self, fname, buff):
405 """Parse a C file to extract driver information contained within
406
407 This parses U_BOOT_DRIVER() structs to obtain various pieces of useful
408 information.
409
410 It updates the following members:
411 _drivers - updated with new Driver records for each driver found
412 in the file
413 _of_match - updated with each compatible string found in the file
414 _compat_to_driver - Maps compatible string to Driver
Simon Glass8d6f2d32021-02-03 06:01:05 -0700415 _driver_aliases - Maps alias names to driver name
Simon Glassc58662f2021-02-03 06:00:50 -0700416
417 Args:
418 fname (str): Filename being parsed (used for warnings)
419 buff (str): Contents of file
420
421 Raises:
422 ValueError: Compatible variable is mentioned in .of_match in
423 U_BOOT_DRIVER() but not found in the file
424 """
425 # Dict holding information about compatible strings collected in this
426 # function so far
427 # key: Name of struct udevice_id variable
428 # value: Dict of compatible info in that variable:
429 # key: Compatible string, e.g. 'rockchip,rk3288-grf'
430 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
431 of_match = {}
432
433 # Dict holding driver information collected in this function so far
434 # key: Driver name (C name as in U_BOOT_DRIVER(xxx))
435 # value: Driver
436 drivers = {}
437
438 # Collect the driver info
439 driver = None
Simon Glass1712f8b2021-02-03 06:01:08 -0700440 re_driver = re.compile(r'^U_BOOT_DRIVER\((.*)\)')
Simon Glassc58662f2021-02-03 06:00:50 -0700441
442 # Collect the uclass ID, e.g. 'UCLASS_SPI'
443 re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)')
444
445 # Collect the compatible string, e.g. 'rockchip,rk3288-grf'
446 compat = None
447 re_compat = re.compile(r'{\s*.compatible\s*=\s*"(.*)"\s*'
448 r'(,\s*.data\s*=\s*(\S*))?\s*},')
449
450 # This is a dict of compatible strings that were found:
451 # key: Compatible string, e.g. 'rockchip,rk3288-grf'
452 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None
453 compat_dict = {}
454
455 # Holds the var nane of the udevice_id list, e.g.
456 # 'rk3288_syscon_ids_noc' in
457 # static const struct udevice_id rk3288_syscon_ids_noc[] = {
458 ids_name = None
459 re_ids = re.compile(r'struct udevice_id (.*)\[\]\s*=')
460
461 # Matches the references to the udevice_id list
462 re_of_match = re.compile(
463 r'\.of_match\s*=\s*(of_match_ptr\()?([a-z0-9_]+)(\))?,')
464
Simon Glassb00f0062021-02-03 06:01:02 -0700465 re_phase = re.compile('^\s*DM_PHASE\((.*)\).*$')
Simon Glass735ddfc2021-02-03 06:01:04 -0700466 re_hdr = re.compile('^\s*DM_HEADER\((.*)\).*$')
Simon Glass8d6f2d32021-02-03 06:01:05 -0700467 re_alias = re.compile(r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)')
Simon Glassb00f0062021-02-03 06:01:02 -0700468
Simon Glassc8b19b02021-02-03 06:00:53 -0700469 # Matches the struct name for priv, plat
Simon Glassc58662f2021-02-03 06:00:50 -0700470 re_priv = self._get_re_for_member('priv_auto')
Simon Glassc8b19b02021-02-03 06:00:53 -0700471 re_plat = self._get_re_for_member('plat_auto')
472 re_child_priv = self._get_re_for_member('per_child_auto')
473 re_child_plat = self._get_re_for_member('per_child_plat_auto')
Simon Glassc58662f2021-02-03 06:00:50 -0700474
475 prefix = ''
476 for line in buff.splitlines():
477 # Handle line continuation
478 if prefix:
479 line = prefix + line
480 prefix = ''
481 if line.endswith('\\'):
482 prefix = line[:-1]
483 continue
484
485 driver_match = re_driver.search(line)
486
487 # If this line contains U_BOOT_DRIVER()...
488 if driver:
489 m_id = re_id.search(line)
490 m_of_match = re_of_match.search(line)
491 m_priv = re_priv.match(line)
Simon Glassc8b19b02021-02-03 06:00:53 -0700492 m_plat = re_plat.match(line)
493 m_cplat = re_child_plat.match(line)
494 m_cpriv = re_child_priv.match(line)
Simon Glassb00f0062021-02-03 06:01:02 -0700495 m_phase = re_phase.match(line)
Simon Glass735ddfc2021-02-03 06:01:04 -0700496 m_hdr = re_hdr.match(line)
Simon Glassc58662f2021-02-03 06:00:50 -0700497 if m_priv:
498 driver.priv = m_priv.group(1)
Simon Glassc8b19b02021-02-03 06:00:53 -0700499 elif m_plat:
500 driver.plat = m_plat.group(1)
501 elif m_cplat:
502 driver.child_plat = m_cplat.group(1)
503 elif m_cpriv:
504 driver.child_priv = m_cpriv.group(1)
Simon Glassc58662f2021-02-03 06:00:50 -0700505 elif m_id:
506 driver.uclass_id = m_id.group(1)
507 elif m_of_match:
508 compat = m_of_match.group(2)
Simon Glassb00f0062021-02-03 06:01:02 -0700509 elif m_phase:
510 driver.phase = m_phase.group(1)
Simon Glass735ddfc2021-02-03 06:01:04 -0700511 elif m_hdr:
512 driver.headers.append(m_hdr.group(1))
Simon Glassc58662f2021-02-03 06:00:50 -0700513 elif '};' in line:
Simon Glass50aae3e2021-02-03 06:01:11 -0700514 is_root = driver.name == 'root_driver'
515 if driver.uclass_id and (compat or is_root):
516 if not is_root:
517 if compat not in of_match:
518 raise ValueError(
519 "%s: Unknown compatible var '%s' (found: %s)" %
520 (fname, compat, ','.join(of_match.keys())))
521 driver.compat = of_match[compat]
Simon Glassc58662f2021-02-03 06:00:50 -0700522
Simon Glass50aae3e2021-02-03 06:01:11 -0700523 # This needs to be deterministic, since a driver may
524 # have multiple compatible strings pointing to it.
525 # We record the one earliest in the alphabet so it
526 # will produce the same result on all machines.
527 for compat_id in of_match[compat]:
528 old = self._compat_to_driver.get(compat_id)
529 if not old or driver.name < old.name:
530 self._compat_to_driver[compat_id] = driver
Simon Glassc58662f2021-02-03 06:00:50 -0700531 drivers[driver.name] = driver
532 else:
533 # The driver does not have a uclass or compat string.
534 # The first is required but the second is not, so just
535 # ignore this.
536 pass
537 driver = None
538 ids_name = None
539 compat = None
540 compat_dict = {}
541
542 elif ids_name:
543 compat_m = re_compat.search(line)
544 if compat_m:
545 compat_dict[compat_m.group(1)] = compat_m.group(3)
546 elif '};' in line:
547 of_match[ids_name] = compat_dict
548 ids_name = None
549 elif driver_match:
550 driver_name = driver_match.group(1)
551 driver = Driver(driver_name, fname)
552 else:
553 ids_m = re_ids.search(line)
Simon Glass8d6f2d32021-02-03 06:01:05 -0700554 m_alias = re_alias.match(line)
Simon Glassc58662f2021-02-03 06:00:50 -0700555 if ids_m:
556 ids_name = ids_m.group(1)
Simon Glass8d6f2d32021-02-03 06:01:05 -0700557 elif m_alias:
Simon Glassc7967652021-07-04 12:19:43 -0600558 self._driver_aliases[m_alias.group(2)] = m_alias.group(1)
Simon Glassc58662f2021-02-03 06:00:50 -0700559
560 # Make the updates based on what we found
Simon Glass1d972692021-02-03 06:01:06 -0700561 for driver in drivers.values():
562 if driver.name in self._drivers:
563 orig = self._drivers[driver.name]
564 if self._phase:
565 # If the original driver matches our phase, use it
566 if orig.phase == self._phase:
567 orig.dups.append(driver)
568 continue
569
570 # Otherwise use the new driver, which is assumed to match
571 else:
572 # We have no way of distinguishing them
573 driver.warn_dups = True
574 driver.dups.append(orig)
575 self._drivers[driver.name] = driver
Simon Glassc58662f2021-02-03 06:00:50 -0700576 self._of_match.update(of_match)
577
Simon Glass0c59ace2021-03-26 16:17:25 +1300578 def show_warnings(self):
579 """Show any warnings that have been collected"""
580 for name in sorted(list(self._missing_drivers)):
581 print('WARNING: the driver %s was not found in the driver list'
582 % name)
583
Simon Glassa542a702020-12-28 20:35:06 -0700584 def scan_driver(self, fname):
585 """Scan a driver file to build a list of driver names and aliases
586
Simon Glassc58662f2021-02-03 06:00:50 -0700587 It updates the following members:
588 _drivers - updated with new Driver records for each driver found
589 in the file
590 _of_match - updated with each compatible string found in the file
591 _compat_to_driver - Maps compatible string to Driver
592 _driver_aliases - Maps alias names to driver name
Simon Glassa542a702020-12-28 20:35:06 -0700593
594 Args
595 fname: Driver filename to scan
596 """
597 with open(fname, encoding='utf-8') as inf:
598 try:
599 buff = inf.read()
600 except UnicodeDecodeError:
601 # This seems to happen on older Python versions
602 print("Skipping file '%s' due to unicode error" % fname)
603 return
604
Simon Glassc58662f2021-02-03 06:00:50 -0700605 # If this file has any U_BOOT_DRIVER() declarations, process it to
606 # obtain driver information
607 if 'U_BOOT_DRIVER' in buff:
608 self._parse_driver(fname, buff)
Simon Glass1a8b4b92021-02-03 06:00:54 -0700609 if 'UCLASS_DRIVER' in buff:
610 self._parse_uclass_driver(fname, buff)
Simon Glassa542a702020-12-28 20:35:06 -0700611
Simon Glassacf5cb82021-02-03 06:00:55 -0700612 def scan_header(self, fname):
613 """Scan a header file to build a list of struct definitions
614
615 It updates the following members:
616 _structs - updated with new Struct records for each struct found
617 in the file
618
619 Args
620 fname: header filename to scan
621 """
622 with open(fname, encoding='utf-8') as inf:
623 try:
624 buff = inf.read()
625 except UnicodeDecodeError:
626 # This seems to happen on older Python versions
627 print("Skipping file '%s' due to unicode error" % fname)
628 return
629
630 # If this file has any U_BOOT_DRIVER() declarations, process it to
631 # obtain driver information
632 if 'struct' in buff:
633 self._parse_structs(fname, buff)
634
Simon Glassa542a702020-12-28 20:35:06 -0700635 def scan_drivers(self):
636 """Scan the driver folders to build a list of driver names and aliases
637
638 This procedure will populate self._drivers and self._driver_aliases
639 """
640 for (dirpath, _, filenames) in os.walk(self._basedir):
Simon Glass36b22202021-02-03 06:00:52 -0700641 rel_path = dirpath[len(self._basedir):]
642 if rel_path.startswith('/'):
643 rel_path = rel_path[1:]
644 if rel_path.startswith('build') or rel_path.startswith('.git'):
645 continue
Simon Glassa542a702020-12-28 20:35:06 -0700646 for fname in filenames:
Simon Glassacf5cb82021-02-03 06:00:55 -0700647 pathname = dirpath + '/' + fname
648 if fname.endswith('.c'):
649 self.scan_driver(pathname)
650 elif fname.endswith('.h'):
651 self.scan_header(pathname)
Simon Glassa542a702020-12-28 20:35:06 -0700652 for fname in self._drivers_additional:
653 if not isinstance(fname, str) or len(fname) == 0:
654 continue
655 if fname[0] == '/':
656 self.scan_driver(fname)
657 else:
658 self.scan_driver(self._basedir + '/' + fname)
Simon Glassb9319c42021-02-03 06:01:01 -0700659
Simon Glass337d6972021-02-03 06:01:10 -0700660 # Get the uclass for each driver
661 # TODO: Can we just get the uclass for the ones we use, e.g. in
662 # mark_used()?
663 for driver in self._drivers.values():
664 driver.uclass = self._uclass.get(driver.uclass_id)
665
Simon Glassb9319c42021-02-03 06:01:01 -0700666 def mark_used(self, nodes):
667 """Mark the drivers associated with a list of nodes as 'used'
668
669 This takes a list of nodes, finds the driver for each one and marks it
670 as used.
671
Simon Glass1d972692021-02-03 06:01:06 -0700672 If two used drivers have the same name, issue a warning.
673
Simon Glassb9319c42021-02-03 06:01:01 -0700674 Args:
675 nodes (list of None): Nodes that are in use
676 """
677 # Figure out which drivers we actually use
678 for node in nodes:
679 struct_name, _ = self.get_normalized_compat_name(node)
680 driver = self._drivers.get(struct_name)
681 if driver:
682 driver.used = True
Simon Glass1d972692021-02-03 06:01:06 -0700683 if driver.dups and driver.warn_dups:
684 print("Warning: Duplicate driver name '%s' (orig=%s, dups=%s)" %
685 (driver.name, driver.fname,
686 ', '.join([drv.fname for drv in driver.dups])))
Simon Glass05953522021-02-03 06:01:07 -0700687
688 def add_uclass_alias(self, name, num, node):
689 """Add an alias to a uclass
690
691 Args:
692 name: Name of uclass, e.g. 'i2c'
693 num: Alias number, e.g. 2 for alias 'i2c2'
694 node: Node the alias points to, or None if None
695
696 Returns:
697 True if the node was added
698 False if the node was not added (uclass of that name not found)
699 None if the node could not be added because it was None
700 """
701 for uclass in self._uclass.values():
702 if uclass.name == name:
703 if node is None:
704 return None
705 uclass.alias_num_to_node[int(num)] = node
706 uclass.alias_path_to_num[node.path] = int(num)
707 return True
708 return False
Simon Glass337d6972021-02-03 06:01:10 -0700709
710 def assign_seq(self, node):
711 """Figure out the sequence number for a node
712
713 This looks in the node's uclass and assigns a sequence number if needed,
714 based on the aliases and other nodes in that uclass.
715
716 It updates the uclass alias_path_to_num and alias_num_to_node
717
718 Args:
719 node (Node): Node object to look up
720 """
721 if node.driver and node.seq == -1 and node.uclass:
722 uclass = node.uclass
723 num = uclass.alias_path_to_num.get(node.path)
724 if num is not None:
725 return num
726 else:
727 # Dynamically allocate the next available value after all
728 # existing ones
729 if uclass.alias_num_to_node:
730 start = max(uclass.alias_num_to_node.keys())
731 else:
732 start = -1
733 for seq in range(start + 1, 1000):
734 if seq not in uclass.alias_num_to_node:
735 break
736 uclass.alias_path_to_num[node.path] = seq
737 uclass.alias_num_to_node[seq] = node
738 return seq
739 return None