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