xref: /btstack/tool/btstack_rtos_generator.py (revision 1dd2ed97dc83220636e5a39c9b81638e62ff2141)
1#!/usr/bin/env python
2import os, sys, getopt, re, pickle
3
4copyright = """/*
5 * Copyright (C) 2016 BlueKitchen GmbH
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the copyright holders nor the names of
17 *    contributors may be used to endorse or promote products derived
18 *    from this software without specific prior written permission.
19 * 4. Any redistribution, use, or modification is done solely for
20 *    personal benefit and not for any commercial purpose or for
21 *    monetary gain.
22 *
23 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
27 * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
30 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
33 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * Please inquire about commercial licensing options at
37 * [email protected]
38 *
39 */
40"""
41
42hfile_header_begin = """
43
44/*
45 *  btstack_rtos.h
46 *
47 *  @brief BTstack Wrapper for use with Real-Time OS
48 *         Wraps each public BTstack function into a thread-safe version
49 *
50 *  @note  Don't edit - generated by tool/btstack_rtos_generator.py
51 *
52 */
53
54#ifndef __BTSTACK_RTOS_H
55#define __BTSTACK_RTOS_H
56
57#if defined __cplusplus
58extern "C" {
59#endif
60
61#include "btstack_config.h"
62
63#ifndef BTSTACK_RTOS_ENTER
64#error Please define BTSTACK_RTOS_ENTER that locks a recursive mutex when using the RTOS wrapper btstack_rtos.h
65#endif
66
67#ifndef BTSTACK_RTOS_EXIT
68#error Please define BTSTACK_RTOS_EXIT that releases a recursive mutex when using the RTOS wrapper btstack_rtos.h
69#endif
70
71/* API_START */
72
73
74"""
75
76hfile_api_header = """
77#include "API_NAME"
78"""
79
80hfile_header_end = """
81
82/* API_END */
83
84#if defined __cplusplus
85}
86#endif
87
88#endif // __BTSTACK_RTOS_H
89"""
90
91class State:
92    SearchStartAPI = 0
93    SearchEndAPI = 1
94    DoneAPI = 2
95
96
97num_functions = 0
98
99# [file_name, api_title, api_label]
100apis = [
101    ["src/ble/ancs_client.h", "BLE ANCS Client", "ancsClient", True],
102    ["src/ble/att_db_util.h", "BLE ATT Database", "attDb", True],
103    ["src/ble/att_server.h", "BLE ATT Server", "attServer", True],
104    ["src/ble/gatt_client.h", "BLE GATT Client", "gattClient", True],
105    ["src/ble/le_device_db.h", "BLE Device Database", "leDeviceDb", True],
106    ["src/ble/sm.h", "BLE Security Manager", "sm", True],
107
108    ["src/classic/bnep.h", "BNEP", "bnep", True],
109    ["src/classic/btstack_link_key_db.h","Link Key DB","lkDb", True],
110    ["src/classic/hsp_hs.h","HSP Headset","hspHS", True],
111    ["src/classic/hsp_ag.h","HSP Audio Gateway","hspAG", True],
112    ["src/classic/hfp_hf.h","HFP Hands-Free","hfpHF", True],
113    ["src/classic/hfp_ag.h","HFP Audio Gateway","hfpAG", True],
114    ["src/classic/pan.h", "PAN", "pan", True],
115    ["src/classic/rfcomm.h", "RFCOMM", "rfcomm", True],
116    ["src/classic/sdp_client.h", "SDP Client", "sdpClient", True],
117    ["src/classic/sdp_client_rfcomm.h", "SDP RFCOMM Query", "sdpQueries", True],
118    ["src/classic/sdp_server.h", "SDP Server", "sdpSrv", True],
119    ["src/classic/sdp_util.h","SDP Utils", "sdpUtil", True],
120
121    ["src/ad_parser.h", "BLE Advertisements Parser", "advParser", True],
122    ["src/btstack_chipset.h","BTstack Chipset","btMemory", True],
123    ["src/btstack_control.h","BTstack Hardware Control","btControl", True],
124    ["src/btstack_event.h","HCI Event Getter","btEvent", False],
125    ["src/btstack_memory.h","BTstack Memory Management","btMemory", True],
126    ["src/btstack_linked_list.h","BTstack Linked List","btList", True],
127    ["src/btstack_run_loop.h", "Run Loop", "runLoop", True],
128    ["src/btstack_util.h", "Common Utils", "btUtil", True],
129    ["src/gap.h", "GAP", "gap", True],
130    ["src/hci.h", "HCI", "hci", True],
131    ["src/hci_dump.h","HCI Logging","hciTrace", True],
132    ["src/hci_transport.h","HCI Transport","hciTransport", True],
133    ["src/l2cap.h", "L2CAP", "l2cap", True],
134]
135
136
137def codeReference(fname, filepath, linenr):
138    return fname
139
140def split_arguments(args_string):
141    args = []
142    brace_level = 0
143    arg = ''
144    for c in args_string:
145        if c == '(':
146            brace_level += 1
147        if c == ')':
148            brace_level -= 1
149        if c == ',' and brace_level == 0:
150            args.append(arg)
151            arg = ''
152            continue
153        arg = arg + c
154    if len(arg):
155        args.append(arg)
156    return args
157
158def argument_name(parameter):
159    function_pointer = re.match('[\w\s\*]*\(\s*\*(\w*)\s*\)\(.*\)', parameter)
160    if function_pointer:
161        return function_pointer.group(1)
162    parts = parameter.split(' ')
163    filtered_parts = [part for part in parts if part not in ['']]
164    arg = filtered_parts[len(filtered_parts)-1].replace('*','').replace('[]','')
165    # le_device_db_encryption_set(index, ediv, rand[8], ltk, key_size, authenticated, authorized);
166    if arg == 'rand[8]':
167        arg = 'rand'
168    return arg
169
170def create_wrapper(fout, type_and_name, arg_string, need_lock):
171    global num_functions
172
173    parts = type_and_name.split(' ')
174    filtered_parts = [part for part in parts if part not in ['static','inline','']]
175    name = filtered_parts[len(filtered_parts)-1]
176    return_type = ' '.join(filtered_parts[:-1])
177    # handle *function_name
178    if name.startswith('*'):
179        name = name[1:]
180        return_type = return_type + ' *'
181    rtos_name = "rtos_" + name
182    is_void_function = len(filtered_parts) == 2 and filtered_parts[0] == "void"
183    args = split_arguments(arg_string)
184    call = []
185    is_ellipse_function = False
186    if len(args)!= 1 or args[0] != 'void':
187        for arg in args:
188            call_arg = argument_name(arg)
189            if call_arg == '...':
190                is_ellipse_function = True
191                call.append('argptr')
192                name += '_va_arg'
193            else:
194                call.append(argument_name(arg))
195    call_args = ', '.join(call)
196    fout.write('static inline ' + return_type + ' ' + rtos_name + '(' + ", ".join(args) + '){\n')
197    orig_call = name + '(' + call_args + ')'
198    if need_lock:
199        fout.write('    BTSTACK_RTOS_ENTER();\n')
200        if is_ellipse_function:
201            fout.write('    va_list argptr;\n')
202            fout.write('    va_start(argptr, %s);\n' % call[-2])
203        if is_void_function:
204            fout.write('    ' + orig_call+';\n')
205        else:
206            fout.write('    ' + return_type + ' res = ' + orig_call + ';\n')
207        if is_ellipse_function:
208            fout.write('    va_end(argptr);\n')
209        fout.write('    BTSTACK_RTOS_EXIT();\n')
210        if not is_void_function:
211            fout.write('    return res;\n')
212    else:
213        if is_void_function:
214            fout.write('    ' + orig_call+';\n')
215        else:
216            fout.write('    return ' + orig_call + ';\n')
217
218    fout.write('}\n')
219    fout.write('\n')
220    num_functions += 1
221
222def create_wrapper_file(btstackfolder, apis, wrapper_file):
223    with open(wrapper_file, 'w') as fout:
224        fout.write(copyright)
225        fout.write(hfile_header_begin)
226
227        for api_tuple in apis:
228            api_filename = btstackfolder + "/" + api_tuple[0]
229            api_title = api_tuple[1]
230            api_lable = api_tuple[2]
231            need_lock = api_tuple[3]
232
233            header_file = api_tuple[0].replace('src/','')
234            fout.write(hfile_api_header.replace("API_NAME", header_file))
235
236            with open(api_filename, 'rb') as fin:
237                typedefFound = 0
238                multiline_function_def = 0
239                multiline = ''
240                multiline_comment = 0
241                inline_function = 0
242                state = State.SearchStartAPI
243
244                for line in fin:
245                    if state == State.DoneAPI:
246                        continue
247
248                    if state == State.SearchStartAPI:
249                        parts = re.match('.*API_START.*',line)
250                        if parts:
251                            state = State.SearchEndAPI
252                        continue
253
254                    if state == State.SearchEndAPI:
255                        parts = re.match('.*API_END.*',line)
256                        if parts:
257                            state = State.DoneAPI
258                            continue
259
260                    if inline_function:
261                        function_end = re.match('.*}.*', line)
262                        if function_end:
263                            inline_function = 0
264                        continue
265
266                    if multiline_function_def:
267                        multiline += line
268                        function_end = re.match('.*\)', line)
269                        if function_end:
270                            multiline_function_def = 0
271                            function = re.match('([\w\s\*]*)\(([\w\s,\*]*)\).*', multiline)
272                            if function:
273                                type_and_name = function.group(1)
274                                arg_string    = function.group(2)
275                                create_wrapper(fout, type_and_name, arg_string, need_lock)
276                        continue
277
278                    if multiline_comment:
279                        comment_end = re.match('.*\*/.*', line)
280                        if comment_end:
281                            multiline_comment = 0
282                        fout.write(line)
283                        continue
284
285                    # search typedef struct end
286                    if typedefFound:
287                        typedef = re.match('}\s*(.*);\n', line)
288                        if typedef:
289                            typedefFound = 0
290                        continue
291
292                    # search comment line
293                    comment = re.match(".*/\*.*\*/.*", line)
294                    if comment:
295                        fout.write(line)
296                        continue
297
298                    # search start of multi line comment
299                    comment = re.match(".*/\*", line)
300                    if comment:
301                        fout.write(line)
302                        multiline_comment = 1
303                        continue
304
305                    # ignore __attribute__ for hci_dump_log in src/hci_dump.h
306                    param = re.match(".*__attribute__", line)
307                    if param:
308                        continue
309
310                    # search typedef struct begin
311                    typedef =  re.match('.*typedef\s+struct.*', line)
312                    if typedef:
313                        typedefFound = 1
314
315                    # complete function declaration
316                    function = re.match('([\w\s\*]*)\((.*)\).*', line)
317                    if function:
318                        if "return" in line:
319                            continue
320                        type_and_name = function.group(1)
321                        arg_string    = function.group(2)
322                        create_wrapper(fout, type_and_name, arg_string, need_lock)
323                        inline_function = 'inline' in line;
324                        continue
325
326                    # multi-line function declaration
327                    function = re.match('([\w\s\*]*)\((.*).*', line)
328                    if function:
329                        multiline = line
330                        multiline_function_def = 1
331                        continue
332
333            # fout.write(hfile_header_begin)
334        fout.write(hfile_header_end)
335
336def main(argv):
337    btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
338    gen_path = btstack_root + '/src/btstack_rtos.h'
339    print ('BTstack folder is: %s' % btstack_root)
340    print ('Generating RTOS wrapper %s' % gen_path)
341    create_wrapper_file(btstack_root, apis, gen_path)
342    print ('Number wrapped headers: %u' % len(apis))
343    print ('Number wrapped functions: %u' % num_functions)
344if __name__ == "__main__":
345   main(sys.argv[1:])
346