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