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