xref: /btstack/tool/btstack_parser.py (revision 045013fe998d7ba668a6fda194a274adcb4b68a7)
1#!/usr/bin/env python
2# BlueKitchen GmbH (c) 2014
3
4import re
5import os
6import sys
7
8# paths
9bluetooth_h_path = 'src/bluetooth.h'
10btstack_defines_h_path = 'src/btstack_defines.h'
11daemon_cmds_c_path = 'platform/daemon/src/daemon_cmds.c'
12hci_cmds_c_path = 'src/hci_cmd.c'
13hci_cmds_h_path = 'src/hci_cmd.h'
14hci_h_path = 'src/hci.h'
15
16btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
17print ("BTstack root %s" % btstack_root)
18
19def set_btstack_root(path):
20    global btstack_root
21    btstack_root = path
22
23def assert_dir(path):
24    if not os.access(path, os.R_OK):
25        os.makedirs(path)
26
27def cap(x):
28    if x.lower() == 'btstack':
29        return 'BTstack'
30    acronyms = ['ATT', 'GAP', 'GATT', 'HCI', 'L2CAP', 'LE', 'RFCOMM', 'SM', 'SDP', 'UUID16', 'UUID128', 'HSP', 'HFP', 'ANCS']
31    if x.upper() in acronyms:
32        return x.upper()
33    return x.capitalize()
34
35def camel_case(name):
36    return ''.join(map(cap, name.split('_')))
37
38def camel_case_var(name):
39    if name in ['uuid128', 'uuid16']:
40        return name
41    camel = camel_case(name)
42    return camel[0].lower() + camel[1:]
43
44def read_defines(infile):
45    defines = dict()
46    with open (infile, 'rt') as fin:
47        for line in fin:
48            parts = re.match('#define\s+(\w+)\s+(\w*)',line)
49            if parts and len(parts.groups()) == 2:
50                (key, value) = parts.groups()
51                defines[key] = value
52    return defines
53
54def parse_defines():
55    global btstack_root
56    defines = dict()
57    defines.update(read_defines(btstack_root + '/' + hci_cmds_h_path))
58    defines.update(read_defines(btstack_root + '/' + hci_h_path))
59    defines.update(read_defines(btstack_root + '/' + bluetooth_h_path))
60    defines.update(read_defines(btstack_root + '/' + btstack_defines_h_path))
61    return defines
62
63def my_parse_events(path):
64    events = []
65    subevents = []
66    params = []
67    event_types = set()
68    format = None
69    with open (path, 'rt') as fin:
70        for line in fin:
71            parts = re.match('.*@format\s*(\w*)\s*', line)
72            if parts and len(parts.groups()) == 1:
73                format = parts.groups()[0]
74            parts = re.match('.*@param\s*(\w*)\s*', line)
75            if parts and len(parts.groups()) == 1:
76                param = parts.groups()[0]
77                params.append(param)
78            parts = re.match('\s*#define\s+(\w+)\s+(\w*)',line)
79            if parts and len(parts.groups()) == 2:
80                (key, value) = parts.groups()
81                if format != None:
82                    # renaming needed by Java Binding (... subevents are just enumerated with others due to event factory)
83                    if "_subevent_" in key.lower():
84                        subevents.append((value, key, format, params))
85                    else:
86                        events.append((value, key, format, params))
87                    event_types.add(key)
88                    params = []
89                    format = None
90    return (events, subevents, event_types)
91
92def parse_events():
93    global btstack_root
94
95    # parse bluetooth.h to get used events
96    (bluetooth_events, bluetooth_subevents, bluetooth_event_types) = my_parse_events(btstack_root + '/' + bluetooth_h_path)
97
98    # parse btstack_defines to get events
99    (btstack_events, btstack_subevents, btstack_event_types) = my_parse_events(btstack_root + '/' + btstack_defines_h_path)
100
101    # concat lists
102    (events, subvents, event_types) = (bluetooth_events + btstack_events, bluetooth_subevents + btstack_subevents, bluetooth_event_types | btstack_event_types)
103
104    return (events, subvents, event_types)
105
106def my_parse_commands(infile):
107    commands = []
108    with open (infile, 'rt') as fin:
109
110        params = []
111        for line in fin:
112
113            parts = re.match('.*@param\s*(\w*)\s*', line)
114            if parts and len(parts.groups()) == 1:
115                param = parts.groups()[0]
116                params.append(camel_case_var(param))
117                continue
118
119            declaration = re.match('const\s+hci_cmd_t\s+(\w+)[\s=]+', line)
120            if declaration:
121                command_name = camel_case(declaration.groups()[0])
122                if command_name.endswith('Cmd'):
123                    command_name = command_name[:-len('Cmd')]
124                continue
125
126            definition = re.match('\s*OPCODE\\(\s*(\w+)\s*,\s+(\w+)\s*\\)\s*,\s\\"(\w*)\\".*', line)
127            if definition:
128                (ogf, ocf, format) = definition.groups()
129                if len(params) != len(format):
130                    params = []
131                    arg_counter = 1
132                    for f in format:
133                        arg_name = 'arg%u' % arg_counter
134                        params.append(arg_name)
135                        arg_counter += 1
136                commands.append((command_name, ogf, ocf, format, params))
137                params = []
138                continue
139    return commands
140
141def parse_commands():
142    global btstack_root
143    commands = []
144    commands = commands = my_parse_commands(btstack_root + '/' + hci_cmds_c_path)
145    commands = commands = my_parse_commands(btstack_root + '/' + daemon_cmds_c_path)
146    return commands