xref: /btstack/tool/btstack_parser.py (revision eda0480f018f79db7599dc8db1ce251f24acf9dd)
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/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']
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    le_events = []
65    hfp_events = []
66    hsp_events = []
67    params = []
68    event_types = set()
69    format = None
70    with open (path, 'rt') as fin:
71        for line in fin:
72            parts = re.match('.*@format\s*(\w*)\s*', line)
73            if parts and len(parts.groups()) == 1:
74                format = parts.groups()[0]
75            parts = re.match('.*@param\s*(\w*)\s*', line)
76            if parts and len(parts.groups()) == 1:
77                param = parts.groups()[0]
78                params.append(param)
79            parts = re.match('\s*#define\s+(\w+)\s+(\w*)',line)
80            if parts and len(parts.groups()) == 2:
81                (key, value) = parts.groups()
82                if format != None:
83                    if key.lower().startswith('hci_subevent_'):
84                        le_events.append((value, key.lower().replace('hci_subevent_', 'hci_event_'), format, params))
85                    elif key.lower().startswith('hsp_subevent_'):
86                        hsp_events.append((value, key.lower().replace('hsp_subevent_', 'hsp_event_'), format, params))
87                    elif key.lower().startswith('hfp_subevent_'):
88                        hfp.append((value, key.lower().replace('hfp_subevent_', 'hfp_event_'), format, params))
89                    else:
90                        events.append((value, key, format, params))
91                    event_types.add(key)
92                params = []
93                format = None
94    return (events, le_events, event_types)
95
96def parse_events():
97    global btstack_root
98
99    # parse bluetooth.h to get used events
100    (bluetooth_events, bluetooth_le_events, bluetooth_event_types) = my_parse_events(btstack_root + '/' + bluetooth_h_path)
101
102    # parse btstack_defines to get events
103    (btstack_events, btstack_le_events, btstack_event_types) = my_parse_events(btstack_root + '/' + btstack_defines_h_path)
104
105    # concat lists
106    (events, le_events, event_types) = (bluetooth_events + btstack_events, bluetooth_le_events + btstack_le_events, bluetooth_event_types | btstack_event_types)
107
108    return (events, le_events, event_types)
109
110def my_parse_commands(infile):
111    commands = []
112    with open (infile, 'rt') as fin:
113
114        params = []
115        for line in fin:
116
117            parts = re.match('.*@param\s*(\w*)\s*', line)
118            if parts and len(parts.groups()) == 1:
119                param = parts.groups()[0]
120                params.append(camel_case_var(param))
121                continue
122
123            declaration = re.match('const\s+hci_cmd_t\s+(\w+)[\s=]+', line)
124            if declaration:
125                command_name = camel_case(declaration.groups()[0])
126                if command_name.endswith('Cmd'):
127                    command_name = command_name[:-len('Cmd')]
128                continue
129
130            definition = re.match('\s*OPCODE\\(\s*(\w+)\s*,\s+(\w+)\s*\\)\s*,\s\\"(\w*)\\".*', line)
131            if definition:
132                (ogf, ocf, format) = definition.groups()
133                if len(params) != len(format):
134                    params = []
135                    arg_counter = 1
136                    for f in format:
137                        arg_name = 'arg%u' % arg_counter
138                        params.append(arg_name)
139                        arg_counter += 1
140                commands.append((command_name, ogf, ocf, format, params))
141                params = []
142                continue
143    return commands
144
145def parse_commands():
146    global btstack_root
147    commands = []
148    commands = commands = my_parse_commands(btstack_root + '/' + hci_cmds_c_path)
149    commands = commands = my_parse_commands(btstack_root + '/' + daemon_cmds_c_path)
150    return commands