1b6acf807SRob Herring#!/usr/bin/env python3 2b6acf807SRob Herring# SPDX-License-Identifier: GPL-2.0-only 3b6acf807SRob Herring 48f51593cSNícolas F. R. A. Pradoimport fnmatch 5b6acf807SRob Herringimport os 6b6acf807SRob Herringimport re 7b6acf807SRob Herringimport argparse 8b6acf807SRob Herring 9b6acf807SRob Herring 10365ba0c7SNícolas F. R. A. Pradodef parse_of_declare_macros(data, include_driver_macros=True): 11b6acf807SRob Herring """ Find all compatible strings in OF_DECLARE() style macros """ 12b6acf807SRob Herring compat_list = [] 13b6acf807SRob Herring # CPU_METHOD_OF_DECLARE does not have a compatible string 14365ba0c7SNícolas F. R. A. Prado if include_driver_macros: 15365ba0c7SNícolas F. R. A. Prado re_macros = r'(?<!CPU_METHOD_)(IRQCHIP|OF)_(DECLARE|MATCH)(_DRIVER)?\(.*?\)' 16365ba0c7SNícolas F. R. A. Prado else: 17365ba0c7SNícolas F. R. A. Prado re_macros = r'(?<!CPU_METHOD_)(IRQCHIP|OF)_(DECLARE|MATCH)\(.*?\)' 18365ba0c7SNícolas F. R. A. Prado for m in re.finditer(re_macros, data): 19b6acf807SRob Herring try: 20b6acf807SRob Herring compat = re.search(r'"(.*?)"', m[0])[1] 21b6acf807SRob Herring except: 22b6acf807SRob Herring # Fails on compatible strings in #define, so just skip 23b6acf807SRob Herring continue 24b6acf807SRob Herring compat_list += [compat] 25b6acf807SRob Herring 26b6acf807SRob Herring return compat_list 27b6acf807SRob Herring 28b6acf807SRob Herring 29365ba0c7SNícolas F. R. A. Pradodef parse_of_device_id(data, match_table_list=None): 30b6acf807SRob Herring """ Find all compatible strings in of_device_id structs """ 31b6acf807SRob Herring compat_list = [] 32365ba0c7SNícolas F. R. A. Prado for m in re.finditer(r'of_device_id(\s+\S+)?\s+(\S+)\[\](\s+\S+)?\s*=\s*({.*?);', data): 33365ba0c7SNícolas F. R. A. Prado if match_table_list is not None and m[2] not in match_table_list: 34365ba0c7SNícolas F. R. A. Prado continue 35365ba0c7SNícolas F. R. A. Prado compat_list += re.findall(r'\.compatible\s+=\s+"(\S+)"', m[4]) 36b6acf807SRob Herring 37b6acf807SRob Herring return compat_list 38b6acf807SRob Herring 39b6acf807SRob Herring 40365ba0c7SNícolas F. R. A. Pradodef parse_of_match_table(data): 41365ba0c7SNícolas F. R. A. Prado """ Find all driver's of_match_table """ 42365ba0c7SNícolas F. R. A. Prado match_table_list = [] 43365ba0c7SNícolas F. R. A. Prado for m in re.finditer(r'\.of_match_table\s+=\s+(of_match_ptr\()?([a-zA-Z0-9_-]+)', data): 44365ba0c7SNícolas F. R. A. Prado match_table_list.append(m[2]) 45365ba0c7SNícolas F. R. A. Prado 46365ba0c7SNícolas F. R. A. Prado return match_table_list 47365ba0c7SNícolas F. R. A. Prado 48365ba0c7SNícolas F. R. A. Prado 49*4c727150SRob Herring (Arm)def parse_of_functions(data, func_name): 50*4c727150SRob Herring (Arm) """ Find all compatibles in the last argument of a given function """ 51*4c727150SRob Herring (Arm) compat_list = [] 52*4c727150SRob Herring (Arm) for m in re.finditer(rf'{func_name}\(([a-zA-Z0-9_>\(\)"\-]+,\s)*"([a-zA-Z0-9_,-]+)"\)', data): 53*4c727150SRob Herring (Arm) compat_list.append(m[2]) 54*4c727150SRob Herring (Arm) 55*4c727150SRob Herring (Arm) return compat_list 56*4c727150SRob Herring (Arm) 57*4c727150SRob Herring (Arm) 58365ba0c7SNícolas F. R. A. Pradodef parse_compatibles(file, compat_ignore_list): 59b6acf807SRob Herring with open(file, 'r', encoding='utf-8') as f: 60b6acf807SRob Herring data = f.read().replace('\n', '') 61b6acf807SRob Herring 62365ba0c7SNícolas F. R. A. Prado if compat_ignore_list is not None: 63365ba0c7SNícolas F. R. A. Prado # For a compatible in the DT to be matched to a driver it needs to show 64365ba0c7SNícolas F. R. A. Prado # up in a driver's of_match_table 65365ba0c7SNícolas F. R. A. Prado match_table_list = parse_of_match_table(data) 66365ba0c7SNícolas F. R. A. Prado compat_list = parse_of_device_id(data, match_table_list) 67365ba0c7SNícolas F. R. A. Prado 68365ba0c7SNícolas F. R. A. Prado compat_list = [compat for compat in compat_list if compat not in compat_ignore_list] 69365ba0c7SNícolas F. R. A. Prado else: 70b6acf807SRob Herring compat_list = parse_of_declare_macros(data) 71b6acf807SRob Herring compat_list += parse_of_device_id(data) 72*4c727150SRob Herring (Arm) compat_list += parse_of_functions(data, "_is_compatible") 73*4c727150SRob Herring (Arm) compat_list += parse_of_functions(data, "of_find_compatible_node") 74*4c727150SRob Herring (Arm) compat_list += parse_of_functions(data, "for_each_compatible_node") 75*4c727150SRob Herring (Arm) compat_list += parse_of_functions(data, "of_get_compatible_child") 76b6acf807SRob Herring 77b6acf807SRob Herring return compat_list 78b6acf807SRob Herring 79365ba0c7SNícolas F. R. A. Pradodef parse_compatibles_to_ignore(file): 80365ba0c7SNícolas F. R. A. Prado with open(file, 'r', encoding='utf-8') as f: 81365ba0c7SNícolas F. R. A. Prado data = f.read().replace('\n', '') 82365ba0c7SNícolas F. R. A. Prado 83365ba0c7SNícolas F. R. A. Prado # Compatibles that show up in OF_DECLARE macros can't be expected to 84365ba0c7SNícolas F. R. A. Prado # match a driver, except for the _DRIVER ones. 85365ba0c7SNícolas F. R. A. Prado return parse_of_declare_macros(data, include_driver_macros=False) 86365ba0c7SNícolas F. R. A. Prado 87365ba0c7SNícolas F. R. A. Prado 88b6acf807SRob Herringdef print_compat(filename, compatibles): 89b6acf807SRob Herring if not compatibles: 90b6acf807SRob Herring return 91b6acf807SRob Herring if show_filename: 92b6acf807SRob Herring compat_str = ' '.join(compatibles) 93b6acf807SRob Herring print(filename + ": compatible(s): " + compat_str) 94b6acf807SRob Herring else: 95b6acf807SRob Herring print(*compatibles, sep='\n') 96b6acf807SRob Herring 978f51593cSNícolas F. R. A. Pradodef glob_without_symlinks(root, glob): 988f51593cSNícolas F. R. A. Prado for path, dirs, files in os.walk(root): 998f51593cSNícolas F. R. A. Prado # Ignore hidden directories 1008f51593cSNícolas F. R. A. Prado for d in dirs: 1018f51593cSNícolas F. R. A. Prado if fnmatch.fnmatch(d, ".*"): 1028f51593cSNícolas F. R. A. Prado dirs.remove(d) 1038f51593cSNícolas F. R. A. Prado for f in files: 1048f51593cSNícolas F. R. A. Prado if fnmatch.fnmatch(f, glob): 1058f51593cSNícolas F. R. A. Prado yield os.path.join(path, f) 1068f51593cSNícolas F. R. A. Prado 107eb2139fcSNícolas F. R. A. Pradodef files_to_parse(path_args): 108eb2139fcSNícolas F. R. A. Prado for f in path_args: 109eb2139fcSNícolas F. R. A. Prado if os.path.isdir(f): 1108f51593cSNícolas F. R. A. Prado for filename in glob_without_symlinks(f, "*.c"): 111eb2139fcSNícolas F. R. A. Prado yield filename 112eb2139fcSNícolas F. R. A. Prado else: 113eb2139fcSNícolas F. R. A. Prado yield f 114eb2139fcSNícolas F. R. A. Prado 115b6acf807SRob Herringshow_filename = False 116b6acf807SRob Herring 117b6acf807SRob Herringif __name__ == "__main__": 118b6acf807SRob Herring ap = argparse.ArgumentParser() 119b6acf807SRob Herring ap.add_argument("cfile", type=str, nargs='*', help="C source files or directories to parse") 120b6acf807SRob Herring ap.add_argument('-H', '--with-filename', help="Print filename with compatibles", action="store_true") 121365ba0c7SNícolas F. R. A. Prado ap.add_argument('-d', '--driver-match', help="Only print compatibles that should match to a driver", action="store_true") 122b6acf807SRob Herring args = ap.parse_args() 123b6acf807SRob Herring 124b6acf807SRob Herring show_filename = args.with_filename 125365ba0c7SNícolas F. R. A. Prado compat_ignore_list = None 126365ba0c7SNícolas F. R. A. Prado 127365ba0c7SNícolas F. R. A. Prado if args.driver_match: 128365ba0c7SNícolas F. R. A. Prado compat_ignore_list = [] 129365ba0c7SNícolas F. R. A. Prado for f in files_to_parse(args.cfile): 130365ba0c7SNícolas F. R. A. Prado compat_ignore_list.extend(parse_compatibles_to_ignore(f)) 131b6acf807SRob Herring 132eb2139fcSNícolas F. R. A. Prado for f in files_to_parse(args.cfile): 133365ba0c7SNícolas F. R. A. Prado compat_list = parse_compatibles(f, compat_ignore_list) 134b6acf807SRob Herring print_compat(f, compat_list) 135