src_scan.py 28 KB

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