src_scan.py 29 KB

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