xref: /btstack/tool/btstack_parser.py (revision 239f77ee6b17be4928fb4cf72db4930a172e62b6)
15c544019SMatthias Ringwald#!/usr/bin/env python3
23338b9aeSMilanka Ringwald# BlueKitchen GmbH (c) 2014
33338b9aeSMilanka Ringwald
43338b9aeSMilanka Ringwaldimport re
53338b9aeSMilanka Ringwaldimport os
63338b9aeSMilanka Ringwaldimport sys
73338b9aeSMilanka Ringwald
83338b9aeSMilanka Ringwald# paths
93338b9aeSMilanka Ringwaldbluetooth_h_path = 'src/bluetooth.h'
103338b9aeSMilanka Ringwaldbtstack_defines_h_path = 'src/btstack_defines.h'
113338b9aeSMilanka Ringwalddaemon_cmds_c_path = 'platform/daemon/src/daemon_cmds.c'
129b8ab7daSMatthias Ringwalddaemon_cmds_h_path = 'platform/daemon/src/daemon_cmds.h'
133338b9aeSMilanka Ringwaldhci_cmds_c_path = 'src/hci_cmd.c'
143338b9aeSMilanka Ringwaldhci_cmds_h_path = 'src/hci_cmd.h'
153338b9aeSMilanka Ringwaldhci_h_path = 'src/hci.h'
163338b9aeSMilanka Ringwald
1763a4cec0SDirk Helbigopen_bracket = '['
1863a4cec0SDirk Helbigclosing_bracket = ']'
1963a4cec0SDirk Helbig
203338b9aeSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
213338b9aeSMilanka Ringwaldprint ("BTstack root %s" % btstack_root)
223338b9aeSMilanka Ringwald
233338b9aeSMilanka Ringwalddef set_btstack_root(path):
243338b9aeSMilanka Ringwald    global btstack_root
253338b9aeSMilanka Ringwald    btstack_root = path
263338b9aeSMilanka Ringwald
273338b9aeSMilanka Ringwalddef assert_dir(path):
283338b9aeSMilanka Ringwald    if not os.access(path, os.R_OK):
293338b9aeSMilanka Ringwald        os.makedirs(path)
303338b9aeSMilanka Ringwald
313338b9aeSMilanka Ringwalddef cap(x):
323338b9aeSMilanka Ringwald    if x.lower() == 'btstack':
333338b9aeSMilanka Ringwald        return 'BTstack'
343338b9aeSMilanka Ringwald    acronyms = ['ATT', 'GAP', 'GATT', 'HCI', 'L2CAP', 'LE', 'RFCOMM', 'SM', 'SDP', 'UUID16', 'UUID128', 'HSP', 'HFP', 'ANCS']
353338b9aeSMilanka Ringwald    if x.upper() in acronyms:
363338b9aeSMilanka Ringwald        return x.upper()
373338b9aeSMilanka Ringwald    return x.capitalize()
383338b9aeSMilanka Ringwald
393338b9aeSMilanka Ringwalddef camel_case(name):
403338b9aeSMilanka Ringwald    return ''.join(map(cap, name.split('_')))
413338b9aeSMilanka Ringwald
423338b9aeSMilanka Ringwalddef camel_case_var(name):
433338b9aeSMilanka Ringwald    if name in ['uuid128', 'uuid16']:
443338b9aeSMilanka Ringwald        return name
453338b9aeSMilanka Ringwald    camel = camel_case(name)
463338b9aeSMilanka Ringwald    return camel[0].lower() + camel[1:]
473338b9aeSMilanka Ringwald
483338b9aeSMilanka Ringwalddef read_defines(infile):
493338b9aeSMilanka Ringwald    defines = dict()
503338b9aeSMilanka Ringwald    with open (infile, 'rt') as fin:
513338b9aeSMilanka Ringwald        for line in fin:
52*239f77eeSMatthias Ringwald            parts = re.match(r'#define\s+(\w+)\s+(\w*)[u]',line)
533338b9aeSMilanka Ringwald            if parts and len(parts.groups()) == 2:
543338b9aeSMilanka Ringwald                (key, value) = parts.groups()
55401327f3SMatthias Ringwald                defines[key] = value.lower()
563338b9aeSMilanka Ringwald    return defines
573338b9aeSMilanka Ringwald
583338b9aeSMilanka Ringwalddef parse_defines():
593338b9aeSMilanka Ringwald    global btstack_root
603338b9aeSMilanka Ringwald    defines = dict()
613338b9aeSMilanka Ringwald    defines.update(read_defines(btstack_root + '/' + hci_cmds_h_path))
623338b9aeSMilanka Ringwald    defines.update(read_defines(btstack_root + '/' + hci_h_path))
633338b9aeSMilanka Ringwald    defines.update(read_defines(btstack_root + '/' + bluetooth_h_path))
643338b9aeSMilanka Ringwald    defines.update(read_defines(btstack_root + '/' + btstack_defines_h_path))
653338b9aeSMilanka Ringwald    return defines
663338b9aeSMilanka Ringwald
673338b9aeSMilanka Ringwalddef my_parse_events(path):
683338b9aeSMilanka Ringwald    events = []
693338b9aeSMilanka Ringwald    subevents = []
703338b9aeSMilanka Ringwald    params = []
713338b9aeSMilanka Ringwald    event_types = set()
723338b9aeSMilanka Ringwald    format = None
733338b9aeSMilanka Ringwald    with open (path, 'rt') as fin:
743338b9aeSMilanka Ringwald        for line in fin:
75*239f77eeSMatthias Ringwald            parts = re.match(r'.*@format\s*([a-zA-Z0-9_' + re.escape(open_bracket) + re.escape(closing_bracket) + r']*)\s*', line)
763338b9aeSMilanka Ringwald            if parts and len(parts.groups()) == 1:
773338b9aeSMilanka Ringwald                format = parts.groups()[0]
78*239f77eeSMatthias Ringwald            parts = re.match(r'.*@param\s*(\w*)\s*', line)
793338b9aeSMilanka Ringwald            if parts and len(parts.groups()) == 1:
803338b9aeSMilanka Ringwald                param = parts.groups()[0]
813338b9aeSMilanka Ringwald                params.append(param)
82*239f77eeSMatthias Ringwald            parts = re.match(r'\s*#define\s+(\w+)\s+(\w*)[u]',line)
833338b9aeSMilanka Ringwald            if parts and len(parts.groups()) == 2:
843338b9aeSMilanka Ringwald                (key, value) = parts.groups()
853338b9aeSMilanka Ringwald                if format != None:
863338b9aeSMilanka Ringwald                    # renaming needed by Java Binding (... subevents are just enumerated with others due to event factory)
873338b9aeSMilanka Ringwald                    if "_subevent_" in key.lower():
883338b9aeSMilanka Ringwald                        subevents.append((value, key, format, params))
893338b9aeSMilanka Ringwald                    else:
903338b9aeSMilanka Ringwald                        events.append((value, key, format, params))
913338b9aeSMilanka Ringwald                    event_types.add(key)
923338b9aeSMilanka Ringwald                    params = []
933338b9aeSMilanka Ringwald                    format = None
943338b9aeSMilanka Ringwald    return (events, subevents, event_types)
953338b9aeSMilanka Ringwald
963338b9aeSMilanka Ringwalddef parse_events():
973338b9aeSMilanka Ringwald    global btstack_root
983338b9aeSMilanka Ringwald
993338b9aeSMilanka Ringwald    # parse bluetooth.h to get used events
1003338b9aeSMilanka Ringwald    (bluetooth_events, bluetooth_subevents, bluetooth_event_types) = my_parse_events(btstack_root + '/' + bluetooth_h_path)
1013338b9aeSMilanka Ringwald
1023338b9aeSMilanka Ringwald    # parse btstack_defines to get events
1033338b9aeSMilanka Ringwald    (btstack_events, btstack_subevents, btstack_event_types) = my_parse_events(btstack_root + '/' + btstack_defines_h_path)
1043338b9aeSMilanka Ringwald
1053338b9aeSMilanka Ringwald    # concat lists
1063338b9aeSMilanka Ringwald    (events, subvents, event_types) = (bluetooth_events + btstack_events, bluetooth_subevents + btstack_subevents, bluetooth_event_types | btstack_event_types)
1073338b9aeSMilanka Ringwald
1083338b9aeSMilanka Ringwald    return (events, subvents, event_types)
1099b8ab7daSMatthias Ringwalddef my_parse_opcodes(infile, convert_to_camel_case):
1109b8ab7daSMatthias Ringwald    opcodes = {}
1119b8ab7daSMatthias Ringwald    with open (infile, 'rt') as fin:
1129b8ab7daSMatthias Ringwald        for line in fin:
113*239f77eeSMatthias Ringwald            definition = re.match(r'\s*(DAEMON_OPCODE_\w+)\s*=\s*DAEMON_OPCODE\s*\(\s*(\w+)\s*\).*', line)
1149b8ab7daSMatthias Ringwald            if definition:
1159b8ab7daSMatthias Ringwald                (opcode, daemon_ocf) = definition.groups()
1169b8ab7daSMatthias Ringwald                # opcodes.append((opcode, 'OGF_BTSTACK', daemon_ocf))
1179b8ab7daSMatthias Ringwald                opcodes[opcode] = ('OGF_BTSTACK', daemon_ocf)
118*239f77eeSMatthias Ringwald            definition = re.match(r'\s*(HCI_OPCODE_\w+)\s*=\s*HCI_OPCODE\s*\(\s*(\w+)\s*,\s*(\w+)\s*\).*', line)
1199b8ab7daSMatthias Ringwald            if definition:
1209b8ab7daSMatthias Ringwald                (opcode, ogf, ocf) = definition.groups()
1219b8ab7daSMatthias Ringwald                # opcodes.append((opcode, ogf, ocf))
1229b8ab7daSMatthias Ringwald                opcodes[opcode] = (ogf, ocf)
1239b8ab7daSMatthias Ringwald    return opcodes
1243338b9aeSMilanka Ringwald
1259b8ab7daSMatthias Ringwalddef parse_opcodes(camel_case=True):
1269b8ab7daSMatthias Ringwald    global btstack_root
1279b8ab7daSMatthias Ringwald    opcodes = {}
1289b8ab7daSMatthias Ringwald    opcodes.update(my_parse_opcodes(btstack_root + '/' + hci_cmds_h_path, camel_case))
1299b8ab7daSMatthias Ringwald    opcodes.update(my_parse_opcodes(btstack_root + '/' + daemon_cmds_h_path, camel_case))
1309b8ab7daSMatthias Ringwald    return opcodes
1319b8ab7daSMatthias Ringwald
1329b8ab7daSMatthias Ringwalddef my_parse_commands(infile, opcodes, convert_to_camel_case):
1333338b9aeSMilanka Ringwald    commands = []
1343338b9aeSMilanka Ringwald    with open (infile, 'rt') as fin:
1353338b9aeSMilanka Ringwald
1363338b9aeSMilanka Ringwald        params = []
1373338b9aeSMilanka Ringwald        for line in fin:
1383338b9aeSMilanka Ringwald
139*239f77eeSMatthias Ringwald            parts = re.match(r'.*@param\s*(\w*)\s*', line)
1403338b9aeSMilanka Ringwald            if parts and len(parts.groups()) == 1:
1413338b9aeSMilanka Ringwald                param = parts.groups()[0]
1429c806232SMatthias Ringwald                if convert_to_camel_case:
1433ddaf9c7SMatthias Ringwald                    param = camel_case_var(param)
1443ddaf9c7SMatthias Ringwald                else:
1453ddaf9c7SMatthias Ringwald                    param = param.lower()
1463ddaf9c7SMatthias Ringwald                params.append(param)
1473338b9aeSMilanka Ringwald                continue
1483338b9aeSMilanka Ringwald
149*239f77eeSMatthias Ringwald            declaration = re.match(r'const\s+hci_cmd_t\s+(\w+)[\s=]+', line)
1503338b9aeSMilanka Ringwald            if declaration:
1513ddaf9c7SMatthias Ringwald                command_name = declaration.groups()[0]
1523ddaf9c7SMatthias Ringwald                # drop _cmd suffix for daemon commands
1533ddaf9c7SMatthias Ringwald                if command_name.endswith('_cmd'):
1543ddaf9c7SMatthias Ringwald                    command_name = command_name[:-len('_cmd')]
1559c806232SMatthias Ringwald                if convert_to_camel_case:
1563ddaf9c7SMatthias Ringwald                    command_name = camel_case(command_name)
1573ddaf9c7SMatthias Ringwald                else:
1583ddaf9c7SMatthias Ringwald                    command_name = command_name.lower()
1593338b9aeSMilanka Ringwald                continue
1603338b9aeSMilanka Ringwald
1619b8ab7daSMatthias Ringwald            # HCI_OPCODE or DAEMON_OPCODE definition
162*239f77eeSMatthias Ringwald            definition = re.match(r'\s*(HCI_OPCODE_\w+|DAEMON_OPCODE_\w+)\s*,\s\"(\w*)\".*', line)
1633338b9aeSMilanka Ringwald            if definition:
1649b8ab7daSMatthias Ringwald                (opcode, format) = definition.groups()
1653338b9aeSMilanka Ringwald                if len(params) != len(format):
1663338b9aeSMilanka Ringwald                    params = []
1673338b9aeSMilanka Ringwald                    arg_counter = 1
1683338b9aeSMilanka Ringwald                    for f in format:
1693338b9aeSMilanka Ringwald                        arg_name = 'arg%u' % arg_counter
1703338b9aeSMilanka Ringwald                        params.append(arg_name)
1713338b9aeSMilanka Ringwald                        arg_counter += 1
1729b8ab7daSMatthias Ringwald                (ogf, ocf) = opcodes[opcode]
1733338b9aeSMilanka Ringwald                commands.append((command_name, ogf, ocf, format, params))
1743338b9aeSMilanka Ringwald                params = []
1753338b9aeSMilanka Ringwald                continue
1769b8ab7daSMatthias Ringwald
1773338b9aeSMilanka Ringwald    return commands
1783338b9aeSMilanka Ringwald
1793ddaf9c7SMatthias Ringwalddef parse_commands(camel_case=True):
1803338b9aeSMilanka Ringwald    global btstack_root
1819b8ab7daSMatthias Ringwald    opcodes = parse_opcodes(camel_case)
1829b8ab7daSMatthias Ringwald
1833338b9aeSMilanka Ringwald    commands = []
1849b8ab7daSMatthias Ringwald    commands += my_parse_commands(btstack_root + '/' + hci_cmds_c_path, opcodes, camel_case)
1859b8ab7daSMatthias Ringwald    commands += my_parse_commands(btstack_root + '/' + daemon_cmds_c_path, opcodes, camel_case)
1863338b9aeSMilanka Ringwald    return commands
1879b8ab7daSMatthias Ringwald
1889b8ab7daSMatthias Ringwalddef parse_daemon_commands(camel_case=True):
1899b8ab7daSMatthias Ringwald    global btstack_root
1909b8ab7daSMatthias Ringwald    opcodes = parse_opcodes(camel_case)
1919b8ab7daSMatthias Ringwald    return my_parse_commands(btstack_root + '/' + daemon_cmds_c_path, opcodes, camel_case)
1929b8ab7daSMatthias Ringwald
1939b8ab7daSMatthias Ringwalddef print_opcode_enum(commands):
1949b8ab7daSMatthias Ringwald    print("typedef enum {")
1959b8ab7daSMatthias Ringwald    for command in commands:
196*239f77eeSMatthias Ringwald        (name, ogf, ocf, format, params) = command
1979b8ab7daSMatthias Ringwald        print("    DAEMON_OPCODE_%s = HCI_OPCODE (%s, %s)," % (name.upper(), ogf, ocf))
1989b8ab7daSMatthias Ringwald    print("} daemon_opcode_t;")
1999b8ab7daSMatthias Ringwald
2009b8ab7daSMatthias Ringwald
201