1#!/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 collections 17import os 18import re 19import sys 20 21 22def conv_name_to_c(name): 23 """Convert a device-tree name to a C identifier 24 25 This uses multiple replace() calls instead of re.sub() since it is faster 26 (400ms for 1m calls versus 1000ms for the 're' version). 27 28 Args: 29 name (str): Name to convert 30 Return: 31 str: String containing the C version of this name 32 """ 33 new = name.replace('@', '_at_') 34 new = new.replace('-', '_') 35 new = new.replace(',', '_') 36 new = new.replace('.', '_') 37 if new == '/': 38 return 'root' 39 return new 40 41def get_compat_name(node): 42 """Get the node's list of compatible string as a C identifiers 43 44 Args: 45 node (fdt.Node): Node object to check 46 Return: 47 list of str: List of C identifiers for all the compatible strings 48 """ 49 compat = node.props['compatible'].value 50 if not isinstance(compat, list): 51 compat = [compat] 52 return [conv_name_to_c(c) for c in compat] 53 54 55class Driver: 56 """Information about a driver in U-Boot 57 58 Attributes: 59 name: Name of driver. For U_BOOT_DRIVER(x) this is 'x' 60 fname: Filename where the driver was found 61 uclass_id: Name of uclass, e.g. 'UCLASS_I2C' 62 compat: Driver data for each compatible string: 63 key: Compatible string, e.g. 'rockchip,rk3288-grf' 64 value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None 65 fname: Filename where the driver was found 66 priv (str): struct name of the priv_auto member, e.g. 'serial_priv' 67 plat (str): struct name of the plat_auto member, e.g. 'serial_plat' 68 child_priv (str): struct name of the per_child_auto member, 69 e.g. 'pci_child_priv' 70 child_plat (str): struct name of the per_child_plat_auto member, 71 e.g. 'pci_child_plat' 72 used (bool): True if the driver is used by the structs being output 73 phase (str): Which phase of U-Boot to use this driver 74 headers (list): List of header files needed for this driver (each a str) 75 e.g. ['<asm/cpu.h>'] 76 dups (list): Driver objects with the same name as this one, that were 77 found after this one 78 warn_dups (bool): True if the duplicates are not distinguisble using 79 the phase 80 uclass (Uclass): uclass for this driver 81 """ 82 def __init__(self, name, fname): 83 self.name = name 84 self.fname = fname 85 self.uclass_id = None 86 self.compat = None 87 self.priv = '' 88 self.plat = '' 89 self.child_priv = '' 90 self.child_plat = '' 91 self.used = False 92 self.phase = '' 93 self.headers = [] 94 self.dups = [] 95 self.warn_dups = False 96 self.uclass = None 97 98 def __eq__(self, other): 99 return (self.name == other.name and 100 self.uclass_id == other.uclass_id and 101 self.compat == other.compat and 102 self.priv == other.priv and 103 self.plat == other.plat and 104 self.used == other.used) 105 106 def __repr__(self): 107 return ("Driver(name='%s', used=%s, uclass_id='%s', compat=%s, priv=%s)" % 108 (self.name, self.used, self.uclass_id, self.compat, self.priv)) 109 110 111class UclassDriver: 112 """Holds information about a uclass driver 113 114 Attributes: 115 name: Uclass name, e.g. 'i2c' if the driver is for UCLASS_I2C 116 uclass_id: Uclass ID, e.g. 'UCLASS_I2C' 117 priv: struct name of the private data, e.g. 'i2c_priv' 118 per_dev_priv (str): struct name of the priv_auto member, e.g. 'spi_info' 119 per_dev_plat (str): struct name of the plat_auto member, e.g. 'i2c_chip' 120 per_child_priv (str): struct name of the per_child_auto member, 121 e.g. 'pci_child_priv' 122 per_child_plat (str): struct name of the per_child_plat_auto member, 123 e.g. 'pci_child_plat' 124 alias_num_to_node (dict): Aliases for this uclasses (for sequence 125 numbers) 126 key (int): Alias number, e.g. 2 for "pci2" 127 value (str): Node the alias points to 128 alias_path_to_num (dict): Convert a path to an alias number 129 key (str): Full path to node (e.g. '/soc/pci') 130 seq (int): Alias number, e.g. 2 for "pci2" 131 devs (list): List of devices in this uclass, each a Node 132 node_refs (dict): References in the linked list of devices: 133 key (int): Sequence number (0=first, n-1=last, -1=head, n=tail) 134 value (str): Reference to the device at that position 135 """ 136 def __init__(self, name): 137 self.name = name 138 self.uclass_id = None 139 self.priv = '' 140 self.per_dev_priv = '' 141 self.per_dev_plat = '' 142 self.per_child_priv = '' 143 self.per_child_plat = '' 144 self.alias_num_to_node = {} 145 self.alias_path_to_num = {} 146 self.devs = [] 147 self.node_refs = {} 148 149 def __eq__(self, other): 150 return (self.name == other.name and 151 self.uclass_id == other.uclass_id and 152 self.priv == other.priv) 153 154 def __repr__(self): 155 return ("UclassDriver(name='%s', uclass_id='%s')" % 156 (self.name, self.uclass_id)) 157 158 def __hash__(self): 159 # We can use the uclass ID since it is unique among uclasses 160 return hash(self.uclass_id) 161 162 163class Struct: 164 """Holds information about a struct definition 165 166 Attributes: 167 name: Struct name, e.g. 'fred' if the struct is 'struct fred' 168 fname: Filename containing the struct, in a format that C files can 169 include, e.g. 'asm/clk.h' 170 """ 171 def __init__(self, name, fname): 172 self.name = name 173 self.fname =fname 174 175 def __repr__(self): 176 return ("Struct(name='%s', fname='%s')" % (self.name, self.fname)) 177 178 179class Scanner: 180 """Scanning of the U-Boot source tree 181 182 Properties: 183 _basedir (str): Base directory of U-Boot source code. Defaults to the 184 grandparent of this file's directory 185 _drivers: Dict of valid driver names found in drivers/ 186 key: Driver name 187 value: Driver for that driver 188 _driver_aliases: Dict that holds aliases for driver names 189 key: Driver alias declared with 190 DM_DRIVER_ALIAS(driver_alias, driver_name) 191 value: Driver name declared with U_BOOT_DRIVER(driver_name) 192 _drivers_additional (list or str): List of additional drivers to use 193 during scanning 194 _warnings: Dict of warnings found: 195 key: Driver name 196 value: Set of warnings 197 _of_match: Dict holding information about compatible strings 198 key: Name of struct udevice_id variable 199 value: Dict of compatible info in that variable: 200 key: Compatible string, e.g. 'rockchip,rk3288-grf' 201 value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None 202 _compat_to_driver: Maps compatible strings to Driver 203 _uclass: Dict of uclass information 204 key: uclass name, e.g. 'UCLASS_I2C' 205 value: UClassDriver 206 _structs: Dict of all structs found in U-Boot: 207 key: Name of struct 208 value: Struct object 209 _phase: The phase of U-Boot that we are generating data for, e.g. 'spl' 210 or 'tpl'. None if not known 211 """ 212 def __init__(self, basedir, drivers_additional, phase=''): 213 """Set up a new Scanner 214 """ 215 if not basedir: 216 basedir = sys.argv[0].replace('tools/dtoc/dtoc', '') 217 if basedir == '': 218 basedir = './' 219 self._basedir = basedir 220 self._drivers = {} 221 self._driver_aliases = {} 222 self._drivers_additional = drivers_additional or [] 223 self._missing_drivers = set() 224 self._warnings = collections.defaultdict(set) 225 self._of_match = {} 226 self._compat_to_driver = {} 227 self._uclass = {} 228 self._structs = {} 229 self._phase = phase 230 231 def get_driver(self, name): 232 """Get a driver given its name 233 234 Args: 235 name (str): Driver name 236 237 Returns: 238 Driver: Driver or None if not found 239 """ 240 return self._drivers.get(name) 241 242 def get_normalized_compat_name(self, node): 243 """Get a node's normalized compat name 244 245 Returns a valid driver name by retrieving node's list of compatible 246 string as a C identifier and performing a check against _drivers 247 and a lookup in driver_aliases printing a warning in case of failure. 248 249 Args: 250 node (Node): Node object to check 251 Return: 252 Tuple: 253 Driver name associated with the first compatible string 254 List of C identifiers for all the other compatible strings 255 (possibly empty) 256 In case of no match found, the return will be the same as 257 get_compat_name() 258 """ 259 if not node.parent: 260 compat_list_c = ['root_driver'] 261 else: 262 compat_list_c = get_compat_name(node) 263 264 for compat_c in compat_list_c: 265 if not compat_c in self._drivers.keys(): 266 compat_c = self._driver_aliases.get(compat_c) 267 if not compat_c: 268 continue 269 270 aliases_c = compat_list_c 271 if compat_c in aliases_c: 272 aliases_c.remove(compat_c) 273 return compat_c, aliases_c 274 275 name = compat_list_c[0] 276 self._missing_drivers.add(name) 277 self._warnings[name].add( 278 'WARNING: the driver %s was not found in the driver list' % name) 279 280 return compat_list_c[0], compat_list_c[1:] 281 282 def _parse_structs(self, fname, buff): 283 """Parse a H file to extract struct definitions contained within 284 285 This parses 'struct xx {' definitions to figure out what structs this 286 header defines. 287 288 Args: 289 buff (str): Contents of file 290 fname (str): Filename (to use when printing errors) 291 """ 292 structs = {} 293 294 re_struct = re.compile('^struct ([a-z0-9_]+) {$') 295 re_asm = re.compile('../arch/[a-z0-9]+/include/asm/(.*)') 296 prefix = '' 297 for line in buff.splitlines(): 298 # Handle line continuation 299 if prefix: 300 line = prefix + line 301 prefix = '' 302 if line.endswith('\\'): 303 prefix = line[:-1] 304 continue 305 306 m_struct = re_struct.match(line) 307 if m_struct: 308 name = m_struct.group(1) 309 include_dir = os.path.join(self._basedir, 'include') 310 rel_fname = os.path.relpath(fname, include_dir) 311 m_asm = re_asm.match(rel_fname) 312 if m_asm: 313 rel_fname = 'asm/' + m_asm.group(1) 314 structs[name] = Struct(name, rel_fname) 315 self._structs.update(structs) 316 317 @classmethod 318 def _get_re_for_member(cls, member): 319 """_get_re_for_member: Get a compiled regular expression 320 321 Args: 322 member (str): Struct member name, e.g. 'priv_auto' 323 324 Returns: 325 re.Pattern: Compiled regular expression that parses: 326 327 .member = sizeof(struct fred), 328 329 and returns "fred" as group 1 330 """ 331 return re.compile(r'^\s*.%s\s*=\s*sizeof\(struct\s+(.*)\),$' % member) 332 333 def _parse_uclass_driver(self, fname, buff): 334 """Parse a C file to extract uclass driver information contained within 335 336 This parses UCLASS_DRIVER() structs to obtain various pieces of useful 337 information. 338 339 It updates the following member: 340 _uclass: Dict of uclass information 341 key: uclass name, e.g. 'UCLASS_I2C' 342 value: UClassDriver 343 344 Args: 345 fname (str): Filename being parsed (used for warnings) 346 buff (str): Contents of file 347 """ 348 uc_drivers = {} 349 350 # Collect the driver name and associated Driver 351 driver = None 352 re_driver = re.compile(r'^UCLASS_DRIVER\((.*)\)') 353 354 # Collect the uclass ID, e.g. 'UCLASS_SPI' 355 re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)') 356 357 # Matches the header/size information for uclass-private data 358 re_priv = self._get_re_for_member('priv_auto') 359 360 # Set up parsing for the auto members 361 re_per_device_priv = self._get_re_for_member('per_device_auto') 362 re_per_device_plat = self._get_re_for_member('per_device_plat_auto') 363 re_per_child_priv = self._get_re_for_member('per_child_auto') 364 re_per_child_plat = self._get_re_for_member('per_child_plat_auto') 365 366 prefix = '' 367 for line in buff.splitlines(): 368 # Handle line continuation 369 if prefix: 370 line = prefix + line 371 prefix = '' 372 if line.endswith('\\'): 373 prefix = line[:-1] 374 continue 375 376 driver_match = re_driver.search(line) 377 378 # If we have seen UCLASS_DRIVER()... 379 if driver: 380 m_id = re_id.search(line) 381 m_priv = re_priv.match(line) 382 m_per_dev_priv = re_per_device_priv.match(line) 383 m_per_dev_plat = re_per_device_plat.match(line) 384 m_per_child_priv = re_per_child_priv.match(line) 385 m_per_child_plat = re_per_child_plat.match(line) 386 if m_id: 387 driver.uclass_id = m_id.group(1) 388 elif m_priv: 389 driver.priv = m_priv.group(1) 390 elif m_per_dev_priv: 391 driver.per_dev_priv = m_per_dev_priv.group(1) 392 elif m_per_dev_plat: 393 driver.per_dev_plat = m_per_dev_plat.group(1) 394 elif m_per_child_priv: 395 driver.per_child_priv = m_per_child_priv.group(1) 396 elif m_per_child_plat: 397 driver.per_child_plat = m_per_child_plat.group(1) 398 elif '};' in line: 399 if not driver.uclass_id: 400 raise ValueError( 401 "%s: Cannot parse uclass ID in driver '%s'" % 402 (fname, driver.name)) 403 uc_drivers[driver.uclass_id] = driver 404 driver = None 405 406 elif driver_match: 407 driver_name = driver_match.group(1) 408 driver = UclassDriver(driver_name) 409 410 self._uclass.update(uc_drivers) 411 412 def _parse_driver(self, fname, buff): 413 """Parse a C file to extract driver information contained within 414 415 This parses U_BOOT_DRIVER() structs to obtain various pieces of useful 416 information. 417 418 It updates the following members: 419 _drivers - updated with new Driver records for each driver found 420 in the file 421 _of_match - updated with each compatible string found in the file 422 _compat_to_driver - Maps compatible string to Driver 423 _driver_aliases - Maps alias names to driver name 424 425 Args: 426 fname (str): Filename being parsed (used for warnings) 427 buff (str): Contents of file 428 429 Raises: 430 ValueError: Compatible variable is mentioned in .of_match in 431 U_BOOT_DRIVER() but not found in the file 432 """ 433 # Dict holding information about compatible strings collected in this 434 # function so far 435 # key: Name of struct udevice_id variable 436 # value: Dict of compatible info in that variable: 437 # key: Compatible string, e.g. 'rockchip,rk3288-grf' 438 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None 439 of_match = {} 440 441 # Dict holding driver information collected in this function so far 442 # key: Driver name (C name as in U_BOOT_DRIVER(xxx)) 443 # value: Driver 444 drivers = {} 445 446 # Collect the driver info 447 driver = None 448 re_driver = re.compile(r'^U_BOOT_DRIVER\((.*)\)') 449 450 # Collect the uclass ID, e.g. 'UCLASS_SPI' 451 re_id = re.compile(r'\s*\.id\s*=\s*(UCLASS_[A-Z0-9_]+)') 452 453 # Collect the compatible string, e.g. 'rockchip,rk3288-grf' 454 compat = None 455 re_compat = re.compile(r'{\s*\.compatible\s*=\s*"(.*)"\s*' 456 r'(,\s*\.data\s*=\s*(\S*))?\s*},') 457 458 # This is a dict of compatible strings that were found: 459 # key: Compatible string, e.g. 'rockchip,rk3288-grf' 460 # value: Driver data, e,g, 'ROCKCHIP_SYSCON_GRF', or None 461 compat_dict = {} 462 463 # Holds the var nane of the udevice_id list, e.g. 464 # 'rk3288_syscon_ids_noc' in 465 # static const struct udevice_id rk3288_syscon_ids_noc[] = { 466 ids_name = None 467 re_ids = re.compile(r'struct udevice_id (.*)\[\]\s*=') 468 469 # Matches the references to the udevice_id list 470 re_of_match = re.compile( 471 r'\.of_match\s*=\s*(of_match_ptr\()?([a-z0-9_]+)([^,]*),') 472 473 re_phase = re.compile('^\s*DM_PHASE\((.*)\).*$') 474 re_hdr = re.compile('^\s*DM_HEADER\((.*)\).*$') 475 re_alias = re.compile(r'DM_DRIVER_ALIAS\(\s*(\w+)\s*,\s*(\w+)\s*\)') 476 477 # Matches the struct name for priv, plat 478 re_priv = self._get_re_for_member('priv_auto') 479 re_plat = self._get_re_for_member('plat_auto') 480 re_child_priv = self._get_re_for_member('per_child_auto') 481 re_child_plat = self._get_re_for_member('per_child_plat_auto') 482 483 prefix = '' 484 for line in buff.splitlines(): 485 # Handle line continuation 486 if prefix: 487 line = prefix + line 488 prefix = '' 489 if line.endswith('\\'): 490 prefix = line[:-1] 491 continue 492 493 driver_match = re_driver.search(line) 494 495 # If this line contains U_BOOT_DRIVER()... 496 if driver: 497 m_id = re_id.search(line) 498 m_of_match = re_of_match.search(line) 499 m_priv = re_priv.match(line) 500 m_plat = re_plat.match(line) 501 m_cplat = re_child_plat.match(line) 502 m_cpriv = re_child_priv.match(line) 503 m_phase = re_phase.match(line) 504 m_hdr = re_hdr.match(line) 505 if m_priv: 506 driver.priv = m_priv.group(1) 507 elif m_plat: 508 driver.plat = m_plat.group(1) 509 elif m_cplat: 510 driver.child_plat = m_cplat.group(1) 511 elif m_cpriv: 512 driver.child_priv = m_cpriv.group(1) 513 elif m_id: 514 driver.uclass_id = m_id.group(1) 515 elif m_of_match: 516 compat = m_of_match.group(2) 517 suffix = m_of_match.group(3) 518 if suffix and suffix != ')': 519 self._warnings[driver.name].add( 520 "%s: Warning: unexpected suffix '%s' on .of_match line for compat '%s'" % 521 (fname, suffix, compat)) 522 elif m_phase: 523 driver.phase = m_phase.group(1) 524 elif m_hdr: 525 driver.headers.append(m_hdr.group(1)) 526 elif '};' in line: 527 is_root = driver.name == 'root_driver' 528 if driver.uclass_id and (compat or is_root): 529 if not is_root: 530 if compat not in of_match: 531 raise ValueError( 532 "%s: Unknown compatible var '%s' (found: %s)" % 533 (fname, compat, ','.join(of_match.keys()))) 534 driver.compat = of_match[compat] 535 536 # This needs to be deterministic, since a driver may 537 # have multiple compatible strings pointing to it. 538 # We record the one earliest in the alphabet so it 539 # will produce the same result on all machines. 540 for compat_id in of_match[compat]: 541 old = self._compat_to_driver.get(compat_id) 542 if not old or driver.name < old.name: 543 self._compat_to_driver[compat_id] = driver 544 drivers[driver.name] = driver 545 else: 546 # The driver does not have a uclass or compat string. 547 # The first is required but the second is not, so just 548 # ignore this. 549 if not driver.uclass_id: 550 warn = 'Missing .uclass' 551 else: 552 warn = 'Missing .compatible' 553 self._warnings[driver.name].add('%s in %s' % 554 (warn, fname)) 555 driver = None 556 ids_name = None 557 compat = None 558 compat_dict = {} 559 560 elif ids_name: 561 compat_m = re_compat.search(line) 562 if compat_m: 563 compat_dict[compat_m.group(1)] = compat_m.group(3) 564 elif '};' in line: 565 of_match[ids_name] = compat_dict 566 ids_name = None 567 elif driver_match: 568 driver_name = driver_match.group(1) 569 driver = Driver(driver_name, fname) 570 else: 571 ids_m = re_ids.search(line) 572 m_alias = re_alias.match(line) 573 if ids_m: 574 ids_name = ids_m.group(1) 575 elif m_alias: 576 self._driver_aliases[m_alias.group(2)] = m_alias.group(1) 577 578 # Make the updates based on what we found 579 for driver in drivers.values(): 580 if driver.name in self._drivers: 581 orig = self._drivers[driver.name] 582 if self._phase: 583 # If the original driver matches our phase, use it 584 if orig.phase == self._phase: 585 orig.dups.append(driver) 586 continue 587 588 # Otherwise use the new driver, which is assumed to match 589 else: 590 # We have no way of distinguishing them 591 driver.warn_dups = True 592 driver.dups.append(orig) 593 self._drivers[driver.name] = driver 594 self._of_match.update(of_match) 595 596 def show_warnings(self): 597 """Show any warnings that have been collected""" 598 used_drivers = [drv.name for drv in self._drivers.values() if drv.used] 599 missing = self._missing_drivers.copy() 600 for name in sorted(self._warnings.keys()): 601 if name in missing or name in used_drivers: 602 warns = sorted(list(self._warnings[name])) 603 print('%s: %s' % (name, warns[0])) 604 indent = ' ' * len(name) 605 for warn in warns[1:]: 606 print('%-s: %s' % (indent, warn)) 607 if name in missing: 608 missing.remove(name) 609 print() 610 611 def scan_driver(self, fname): 612 """Scan a driver file to build a list of driver names and aliases 613 614 It updates the following members: 615 _drivers - updated with new Driver records for each driver found 616 in the file 617 _of_match - updated with each compatible string found in the file 618 _compat_to_driver - Maps compatible string to Driver 619 _driver_aliases - Maps alias names to driver name 620 621 Args 622 fname: Driver filename to scan 623 """ 624 with open(fname, encoding='utf-8') as inf: 625 try: 626 buff = inf.read() 627 except UnicodeDecodeError: 628 # This seems to happen on older Python versions 629 print("Skipping file '%s' due to unicode error" % fname) 630 return 631 632 # If this file has any U_BOOT_DRIVER() declarations, process it to 633 # obtain driver information 634 if 'U_BOOT_DRIVER' in buff: 635 self._parse_driver(fname, buff) 636 if 'UCLASS_DRIVER' in buff: 637 self._parse_uclass_driver(fname, buff) 638 639 def scan_header(self, fname): 640 """Scan a header file to build a list of struct definitions 641 642 It updates the following members: 643 _structs - updated with new Struct records for each struct found 644 in the file 645 646 Args 647 fname: header filename to scan 648 """ 649 with open(fname, encoding='utf-8') as inf: 650 try: 651 buff = inf.read() 652 except UnicodeDecodeError: 653 # This seems to happen on older Python versions 654 print("Skipping file '%s' due to unicode error" % fname) 655 return 656 657 # If this file has any U_BOOT_DRIVER() declarations, process it to 658 # obtain driver information 659 if 'struct' in buff: 660 self._parse_structs(fname, buff) 661 662 def scan_drivers(self): 663 """Scan the driver folders to build a list of driver names and aliases 664 665 This procedure will populate self._drivers and self._driver_aliases 666 """ 667 for (dirpath, _, filenames) in os.walk(self._basedir): 668 rel_path = dirpath[len(self._basedir):] 669 if rel_path.startswith('/'): 670 rel_path = rel_path[1:] 671 if rel_path.startswith('build') or rel_path.startswith('.git'): 672 continue 673 for fname in filenames: 674 pathname = dirpath + '/' + fname 675 if fname.endswith('.c'): 676 self.scan_driver(pathname) 677 elif fname.endswith('.h'): 678 self.scan_header(pathname) 679 for fname in self._drivers_additional: 680 if not isinstance(fname, str) or len(fname) == 0: 681 continue 682 if fname[0] == '/': 683 self.scan_driver(fname) 684 else: 685 self.scan_driver(self._basedir + '/' + fname) 686 687 # Get the uclass for each driver 688 # TODO: Can we just get the uclass for the ones we use, e.g. in 689 # mark_used()? 690 for driver in self._drivers.values(): 691 driver.uclass = self._uclass.get(driver.uclass_id) 692 693 def mark_used(self, nodes): 694 """Mark the drivers associated with a list of nodes as 'used' 695 696 This takes a list of nodes, finds the driver for each one and marks it 697 as used. 698 699 If two used drivers have the same name, issue a warning. 700 701 Args: 702 nodes (list of None): Nodes that are in use 703 """ 704 # Figure out which drivers we actually use 705 for node in nodes: 706 struct_name, _ = self.get_normalized_compat_name(node) 707 driver = self._drivers.get(struct_name) 708 if driver: 709 driver.used = True 710 if driver.dups and driver.warn_dups: 711 print("Warning: Duplicate driver name '%s' (orig=%s, dups=%s)" % 712 (driver.name, driver.fname, 713 ', '.join([drv.fname for drv in driver.dups]))) 714 715 def add_uclass_alias(self, name, num, node): 716 """Add an alias to a uclass 717 718 Args: 719 name: Name of uclass, e.g. 'i2c' 720 num: Alias number, e.g. 2 for alias 'i2c2' 721 node: Node the alias points to, or None if None 722 723 Returns: 724 True if the node was added 725 False if the node was not added (uclass of that name not found) 726 None if the node could not be added because it was None 727 """ 728 for uclass in self._uclass.values(): 729 if uclass.name == name: 730 if node is None: 731 return None 732 uclass.alias_num_to_node[int(num)] = node 733 uclass.alias_path_to_num[node.path] = int(num) 734 return True 735 return False 736 737 def assign_seq(self, node): 738 """Figure out the sequence number for a node 739 740 This looks in the node's uclass and assigns a sequence number if needed, 741 based on the aliases and other nodes in that uclass. 742 743 It updates the uclass alias_path_to_num and alias_num_to_node 744 745 Args: 746 node (Node): Node object to look up 747 """ 748 if node.driver and node.seq == -1 and node.uclass: 749 uclass = node.uclass 750 num = uclass.alias_path_to_num.get(node.path) 751 if num is not None: 752 return num 753 else: 754 # Dynamically allocate the next available value after all 755 # existing ones 756 if uclass.alias_num_to_node: 757 start = max(uclass.alias_num_to_node.keys()) 758 else: 759 start = -1 760 for seq in range(start + 1, 1000): 761 if seq not in uclass.alias_num_to_node: 762 break 763 uclass.alias_path_to_num[node.path] = seq 764 uclass.alias_num_to_node[seq] = node 765 return seq 766 return None 767