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