xref: /btstack/tool/btstack_rtos_generator.py (revision dbeaa5cd7b75285b708462a3fc9fbbfda5cda24c)
1#!/usr/bin/env python3
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
42single_hfile_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
76single_hfile_api_header = """
77#include "API_NAME"
78"""
79
80single_hfile_header_end = """
81
82/* API_END */
83
84#if defined __cplusplus
85}
86#endif
87
88#endif // BTSTACK_RTOS_H
89"""
90
91multiple_header_begin = """
92
93/*
94 *  FILENAME
95 *
96 *  @brief BTstack Wrapper for use with Real-Time OS
97 *         Wraps each public BTstack function into a thread-safe version
98 *
99 *  @note  Don't edit - generated by tool/btstack_rtos_generator.py
100 *
101 */
102
103#ifndef GUARD
104#define GUARD
105
106#if defined __cplusplus
107extern "C" {
108#endif
109
110#include "btstack_config.h"
111#include "HEADER"
112
113#ifndef BTSTACK_RTOS_ENTER
114#error Please define BTSTACK_RTOS_ENTER that locks a recursive mutex when using the RTOS wrapper btstack_rtos.h
115#endif
116
117#ifndef BTSTACK_RTOS_EXIT
118#error Please define BTSTACK_RTOS_EXIT that releases a recursive mutex when using the RTOS wrapper btstack_rtos.h
119#endif
120
121/* API_START */
122
123
124"""
125
126multiple_header_end = """
127
128/* API_END */
129
130#if defined __cplusplus
131}
132#endif
133
134#endif // GUARD
135"""
136
137class State:
138    SearchStartAPI = 0
139    SearchEndAPI = 1
140    DoneAPI = 2
141
142
143num_functions = 0
144
145# [file_name, api_title, api_label]
146apis = [
147    ["src/ble/gatt-service/ancs_client.h", "BLE ANCS Client", "ancsClient", True],
148    ["src/ble/att_db_util.h", "BLE ATT Database", "attDb", True],
149    ["src/ble/att_server.h", "BLE ATT Server", "attServer", True],
150    ["src/ble/gatt_client.h", "BLE GATT Client", "gattClient", True],
151    ["src/ble/le_device_db.h", "BLE Device Database", "leDeviceDb", True],
152    ["src/ble/sm.h", "BLE Security Manager", "sm", True],
153
154    ["src/classic/bnep.h", "BNEP", "bnep", True],
155    ["src/classic/btstack_link_key_db.h","Link Key DB","lkDb", True],
156    ["src/classic/hsp_hs.h","HSP Headset","hspHS", True],
157    ["src/classic/hsp_ag.h","HSP Audio Gateway","hspAG", True],
158    ["src/classic/hfp_hf.h","HFP Hands-Free","hfpHF", True],
159    ["src/classic/hfp_ag.h","HFP Audio Gateway","hfpAG", True],
160    ["src/classic/pan.h", "PAN", "pan", True],
161    ["src/classic/rfcomm.h", "RFCOMM", "rfcomm", True],
162    ["src/classic/sdp_client.h", "SDP Client", "sdpClient", True],
163    ["src/classic/sdp_client_rfcomm.h", "SDP RFCOMM Query", "sdpQueries", True],
164    ["src/classic/sdp_server.h", "SDP Server", "sdpSrv", True],
165    ["src/classic/sdp_util.h","SDP Utils", "sdpUtil", True],
166
167    ["src/ad_parser.h", "BLE Advertisements Parser", "advParser", False],
168    # ["src/btstack_chipset.h","BTstack Chipset","btMemory", True],
169    # ["src/btstack_control.h","BTstack Hardware Control","btControl", True],
170    ["src/btstack_event.h","HCI Event Getter","btEvent", False],
171    # ["src/btstack_memory.h","BTstack Memory Management","btMemory", True],
172    ["src/btstack_linked_list.h","BTstack Linked List","btList", False],
173    ["src/btstack_run_loop.h", "Run Loop", "runLoop", True],
174    ["src/btstack_util.h", "Common Utils", "btUtil", False],
175    ["src/gap.h", "GAP", "gap", True],
176    ["src/hci.h", "HCI", "hci", True],
177    ["src/hci_dump.h","HCI Logging","hciTrace", True],
178    # ["src/hci_transport.h","HCI Transport","hciTransport", True],
179    ["src/l2cap.h", "L2CAP", "l2cap", True],
180]
181
182def split_arguments(args_string):
183    args = []
184    brace_level = 0
185    arg = ''
186    for c in args_string:
187        if c == '(':
188            brace_level += 1
189        if c == ')':
190            brace_level -= 1
191        if c == ',' and brace_level == 0:
192            args.append(arg)
193            arg = ''
194            continue
195        arg = arg + c
196    if len(arg):
197        args.append(arg)
198    return args
199
200def argument_name(parameter):
201    function_pointer = re.match(r'[\w\s\*]*\(\s*\*(\w*)\s*\)\(.*\)', parameter)
202    if function_pointer:
203        return function_pointer.group(1)
204    parts = parameter.split(' ')
205    filtered_parts = [part for part in parts if part not in ['']]
206    arg = filtered_parts[len(filtered_parts)-1].replace('*','').replace('[]','')
207    # le_device_db_encryption_set(index, ediv, rand[8], ltk, key_size, authenticated, authorized);
208    if arg == 'rand[8]':
209        arg = 'rand'
210    return arg
211
212def create_wrapper(fout, type_and_name, arg_string, need_lock):
213    global num_functions
214
215    parts = type_and_name.split(' ')
216    filtered_parts = [part for part in parts if part not in ['static','inline','']]
217    name = filtered_parts[len(filtered_parts)-1]
218    return_type = ' '.join(filtered_parts[:-1])
219    # handle *function_name
220    if name.startswith('*'):
221        name = name[1:]
222        return_type = return_type + ' *'
223    rtos_name = "rtos_" + name
224    is_void_function = len(filtered_parts) == 2 and filtered_parts[0] == "void"
225    args = split_arguments(arg_string)
226    call = []
227    is_ellipse_function = False
228    if len(args)!= 1 or args[0] != 'void':
229        for arg in args:
230            call_arg = argument_name(arg)
231            if call_arg == '...':
232                is_ellipse_function = True
233                call.append('argptr')
234                name += '_va_arg'
235            else:
236                call.append(argument_name(arg))
237    call_args = ', '.join(call)
238    fout.write('static inline ' + return_type + ' ' + rtos_name + '(' + ", ".join(args) + '){\n')
239    orig_call = name + '(' + call_args + ')'
240    if need_lock:
241        fout.write('    BTSTACK_RTOS_ENTER();\n')
242        if is_ellipse_function:
243            fout.write('    va_list argptr;\n')
244            fout.write('    va_start(argptr, %s);\n' % call[-2])
245        if is_void_function:
246            fout.write('    ' + orig_call+';\n')
247        else:
248            fout.write('    ' + return_type + ' res = ' + orig_call + ';\n')
249        if is_ellipse_function:
250            fout.write('    va_end(argptr);\n')
251        fout.write('    BTSTACK_RTOS_EXIT();\n')
252        if not is_void_function:
253            fout.write('    return res;\n')
254    else:
255        if is_void_function:
256            fout.write('    ' + orig_call+';\n')
257        else:
258            fout.write('    return ' + orig_call + ';\n')
259    fout.write('}\n')
260    fout.write('\n')
261    num_functions += 1
262
263def write_wrappers_for_file(fout, file, header_name, need_lock):
264    with open(file, 'r') as fin:
265        typedefFound = 0
266        multiline_function_def = 0
267        multiline = ''
268        multiline_comment = 0
269        inline_function = 0
270        state = State.SearchStartAPI
271
272        for line in fin:
273            if state == State.DoneAPI:
274                continue
275
276            if state == State.SearchStartAPI:
277                parts = re.match(r'.*API_START.*',line)
278                if parts:
279                    state = State.SearchEndAPI
280                continue
281
282            if state == State.SearchEndAPI:
283                parts = re.match(r'.*API_END.*',line)
284                if parts:
285                    state = State.DoneAPI
286                    continue
287
288            if inline_function:
289                function_end = re.match(r'.*}.*', line)
290                if function_end:
291                    inline_function = 0
292                continue
293
294            if multiline_function_def:
295                multiline += line
296                function_end = re.match(r'.*\)', line)
297                if function_end:
298                    multiline_function_def = 0
299                    function = re.match(r'([\w\s\*]*)\(([\w\s,\*]*)\).*', multiline)
300                    if function:
301                        type_and_name = function.group(1)
302                        arg_string    = function.group(2)
303                        create_wrapper(fout, type_and_name, arg_string, need_lock)
304                continue
305
306            if multiline_comment:
307                comment_end = re.match(r'.*\*/.*', line)
308                if comment_end:
309                    multiline_comment = 0
310                fout.write(line)
311                continue
312
313            # search typedef struct end
314            if typedefFound:
315                typedef = re.match(r'}\s*(.*);\n', line)
316                if typedef:
317                    typedefFound = 0
318                continue
319
320            # search comment line
321            comment = re.match(r'.*/\*.*\*/.*', line)
322            if comment:
323                fout.write(line)
324                continue
325
326            # search start of multi line comment
327            comment = re.match(r'.*/\*', line)
328            if comment:
329                fout.write(line)
330                multiline_comment = 1
331                continue
332
333            # ignore __attribute__ for hci_dump_log in src/hci_dump.h
334            param = re.match(r'.*__attribute__', line)
335            if param:
336                continue
337
338            # search typedef struct begin
339            typedef =  re.match(r'.*typedef\s+struct.*', line)
340            if typedef:
341                typedefFound = 1
342
343            # complete function declaration
344            function = re.match(r'([\w\s\*]*)\((.*)\).*', line)
345            if function:
346                if "return" in line:
347                    continue
348                type_and_name = function.group(1)
349                arg_string    = function.group(2)
350                create_wrapper(fout, type_and_name, arg_string, need_lock)
351                inline_function = 'inline' in line;
352                continue
353
354            # multi-line function declaration
355            function = re.match(r'([\w\s\*]*)\((.*).*', line)
356            if function:
357                multiline = line
358                multiline_function_def = 1
359                continue
360
361    # fout.write(single_hfile_header_begin)
362
363
364def create_wrapper_file(btstack_root, apis, wrapper_file):
365    with open(wrapper_file, 'w') as fout:
366        fout.write(copyright)
367        fout.write(single_hfile_header_begin)
368
369        for api_tuple in apis:
370            api_filename = btstack_root + "/" + api_tuple[0]
371            need_lock = api_tuple[3]
372            header_file = api_tuple[0].replace('src/','')
373            fout.write(single_hfile_api_header.replace("API_NAME", header_file))
374            write_wrappers_for_file(fout, api_filename, header_file, need_lock)
375            # fout.write(single_hfile_header_begin)
376        fout.write(single_hfile_header_end)
377
378def create_wrapper_files(btstack_root, rtos_folder, apis):
379    for api_tuple in apis:
380        api_filename = btstack_root + "/" + api_tuple[0]
381        need_lock = api_tuple[3]
382        header_file = api_tuple[0].replace('src/','')
383        path_parts = header_file.split('/')
384        path_parts[-1] = 'rtos_' + path_parts[-1]
385        rtos_file = '/'.join(path_parts)
386        wrapper_file = rtos_folder + '/' + rtos_file
387        # print('- %s' % wrapper_file)
388        with open(wrapper_file, 'w') as fout:
389            guard = '__' + rtos_file.replace('.','_').upper()
390            fout.write(copyright)
391            fout.write(multiple_header_begin.replace('FILENAME',rtos_file).replace('GUARD',guard).replace('HEADER',header_file))
392            write_wrappers_for_file(fout, api_filename, header_file, need_lock)
393            fout.write(multiple_header_end.replace('GUARD',guard))
394
395def assert_dir_exists(path):
396    if not os.path.exists(path):
397        os.makedirs(path)
398
399def main(argv):
400    btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
401    print ('BTstack folder is: %s' % btstack_root)
402
403    # single file
404    # gen_path = btstack_root + '/src/btstack_rtos.h'
405    # print ('Generating RTOS wrapper %s' % gen_path)
406    # create_wrapper_file(btstack_root, apis, gen_path)
407
408    # individual files in platform/rtos
409    print ('Generating RTOS wrappers...')
410    rtos_folder = btstack_root + '/platform/rtos'
411    assert_dir_exists(rtos_folder)
412    assert_dir_exists(rtos_folder+'/ble')
413    assert_dir_exists(rtos_folder+'/ble/gatt-service')
414    assert_dir_exists(rtos_folder+'/classic')
415    create_wrapper_files(btstack_root, rtos_folder, apis)
416
417    # summary
418    print ('Number wrapped headers: %u' % len(apis))
419    print ('Number wrapped functions: %u' % num_functions)
420
421if __name__ == "__main__":
422   main(sys.argv[1:])
423