diff --git a/tool/btstack_rtos_generator.py b/tool/btstack_rtos_generator.py new file mode 100755 index 000000000..565901523 --- /dev/null +++ b/tool/btstack_rtos_generator.py @@ -0,0 +1,345 @@ +#!/usr/bin/env python +import os, sys, getopt, re, pickle + +copyright = """/* + * Copyright (C) 2016 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ +""" + +hfile_header_begin = """ + +/* + * btstack_rtos.h + * + * @brief BTstack Wrapper for use with Real-Time OS + * Wraps each public BTstack function into a thread-safe version + * + * @note Don't edit - generated by tool/btstack_rtos_generator.py + * + */ + +#ifndef __BTSTACK_RTOS_H +#define __BTSTACK_RTOS_H + +#if defined __cplusplus +extern "C" { +#endif + +#include "btstack_config.h" + +#ifndef BTSTACK_RTOS_ENTER +#error Please define BTSTACK_RTOS_ENTER that locks a recursive mutex when using the RTOS wrapper btstack_rtos.h +#endif + +#ifndef BTSTACK_RTOS_EXIT +#error Please define BTSTACK_RTOS_EXIT that releases a recursive mutex when using the RTOS wrapper btstack_rtos.h +#endif + +/* API_START */ + + +""" + +hfile_api_header = """ +#include "API_NAME" +""" + +hfile_header_end = """ + +/* API_END */ + +#if defined __cplusplus +} +#endif + +#endif // __BTSTACK_RTOS_H +""" + +class State: + SearchStartAPI = 0 + SearchEndAPI = 1 + DoneAPI = 2 + + +num_functions = 0 + +# [file_name, api_title, api_label] +apis = [ + ["src/ble/ancs_client.h", "BLE ANCS Client", "ancsClient", True], + ["src/ble/att_db_util.h", "BLE ATT Database", "attDb", True], + ["src/ble/att_server.h", "BLE ATT Server", "attServer", True], + ["src/ble/gatt_client.h", "BLE GATT Client", "gattClient", True], + ["src/ble/le_device_db.h", "BLE Device Database", "leDeviceDb", True], + ["src/ble/sm.h", "BLE Security Manager", "sm", True], + + ["src/classic/bnep.h", "BNEP", "bnep", True], + ["src/classic/btstack_link_key_db.h","Link Key DB","lkDb", True], + ["src/classic/hsp_hs.h","HSP Headset","hspHS", True], + ["src/classic/hsp_ag.h","HSP Audio Gateway","hspAG", True], + ["src/classic/hfp_hf.h","HFP Hands-Free","hfpHF", True], + ["src/classic/hfp_ag.h","HFP Audio Gateway","hfpAG", True], + ["src/classic/pan.h", "PAN", "pan", True], + ["src/classic/rfcomm.h", "RFCOMM", "rfcomm", True], + ["src/classic/sdp_client.h", "SDP Client", "sdpClient", True], + ["src/classic/sdp_client_rfcomm.h", "SDP RFCOMM Query", "sdpQueries", True], + ["src/classic/sdp_server.h", "SDP Server", "sdpSrv", True], + ["src/classic/sdp_util.h","SDP Utils", "sdpUtil", True], + + ["src/ad_parser.h", "BLE Advertisements Parser", "advParser", True], + ["src/btstack_chipset.h","BTstack Chipset","btMemory", True], + ["src/btstack_control.h","BTstack Hardware Control","btControl", True], + ["src/btstack_event.h","HCI Event Getter","btEvent", False], + ["src/btstack_memory.h","BTstack Memory Management","btMemory", True], + ["src/btstack_linked_list.h","BTstack Linked List","btList", True], + ["src/btstack_run_loop.h", "Run Loop", "runLoop", True], + ["src/btstack_util.h", "Common Utils", "btUtil", True], + ["src/gap.h", "GAP", "gap", True], + ["src/hci.h", "HCI", "hci", True], + ["src/hci_dump.h","HCI Logging","hciTrace", True], + ["src/hci_transport.h","HCI Transport","hciTransport", True], + ["src/l2cap.h", "L2CAP", "l2cap", True], +] + + +def codeReference(fname, filepath, linenr): + return fname + +def split_arguments(args_string): + args = [] + brace_level = 0 + arg = '' + for c in args_string: + if c == '(': + brace_level += 1 + if c == ')': + brace_level -= 1 + if c == ',' and brace_level == 0: + args.append(arg) + arg = '' + continue + arg = arg + c + if len(arg): + args.append(arg) + return args + +def argument_name(parameter): + function_pointer = re.match('[\w\s\*]*\(\s*\*(\w*)\s*\)\(.*\)', parameter) + if function_pointer: + return function_pointer.group(1) + parts = parameter.split(' ') + filtered_parts = [part for part in parts if part not in ['']] + arg = filtered_parts[len(filtered_parts)-1].replace('*','').replace('[]','') + # le_device_db_encryption_set(index, ediv, rand[8], ltk, key_size, authenticated, authorized); + if arg == 'rand[8]': + arg = 'rand' + return arg + +def create_wrapper(fout, type_and_name, arg_string, need_lock): + global num_functions + + parts = type_and_name.split(' ') + filtered_parts = [part for part in parts if part not in ['static','inline','']] + name = filtered_parts[len(filtered_parts)-1] + return_type = ' '.join(filtered_parts[:-1]) + # handle *function_name + if name.startswith('*'): + name = name[1:] + return_type = return_type + ' *' + rtos_name = "rtos_" + name + is_void_function = len(filtered_parts) == 2 and filtered_parts[0] == "void" + args = split_arguments(arg_string) + call = [] + is_ellipse_function = False + if len(args)!= 1 or args[0] != 'void': + for arg in args: + call_arg = argument_name(arg) + if call_arg == '...': + is_ellipse_function = True + call.append('argptr') + name += '_va_arg' + else: + call.append(argument_name(arg)) + call_args = ', '.join(call) + fout.write('static inline ' + return_type + ' ' + rtos_name + '(' + ", ".join(args) + '){\n') + orig_call = name + '(' + call_args + ')' + if need_lock: + fout.write(' BTSTACK_RTOS_ENTER();\n') + if is_ellipse_function: + fout.write(' va_list argptr;\n') + fout.write(' va_start(argptr, %s);\n' % call[-2]) + if is_void_function: + fout.write(' ' + orig_call+';\n') + else: + fout.write(' ' + return_type + ' res = ' + orig_call + ';\n') + if is_ellipse_function: + fout.write(' va_end(argptr);\n') + fout.write(' BTSTACK_RTOS_EXIT();\n') + if not is_void_function: + fout.write(' return res;\n') + else: + if is_void_function: + fout.write(' ' + orig_call+';\n') + else: + fout.write(' return ' + orig_call + ';\n') + + fout.write('}\n') + fout.write('\n') + num_functions += 1 + +def create_wrapper_file(btstackfolder, apis, wrapper_file): + with open(wrapper_file, 'w') as fout: + fout.write(copyright) + fout.write(hfile_header_begin) + + for api_tuple in apis: + api_filename = btstackfolder + "/" + api_tuple[0] + api_title = api_tuple[1] + api_lable = api_tuple[2] + need_lock = api_tuple[3] + + header_file = api_tuple[0].replace('src/','') + fout.write(hfile_api_header.replace("API_NAME", header_file)) + + with open(api_filename, 'rb') as fin: + typedefFound = 0 + multiline_function_def = 0 + multiline = '' + multiline_comment = 0 + inline_function = 0 + state = State.SearchStartAPI + + for line in fin: + if state == State.DoneAPI: + continue + + if state == State.SearchStartAPI: + parts = re.match('.*API_START.*',line) + if parts: + state = State.SearchEndAPI + continue + + if state == State.SearchEndAPI: + parts = re.match('.*API_END.*',line) + if parts: + state = State.DoneAPI + continue + + if inline_function: + function_end = re.match('.*}.*', line) + if function_end: + inline_function = 0 + continue + + if multiline_function_def: + multiline += line + function_end = re.match('.*\)', line) + if function_end: + multiline_function_def = 0 + function = re.match('([\w\s\*]*)\(([\w\s,\*]*)\).*', multiline) + if function: + type_and_name = function.group(1) + arg_string = function.group(2) + create_wrapper(fout, type_and_name, arg_string, need_lock) + continue + + if multiline_comment: + comment_end = re.match('.*\*/.*', line) + if comment_end: + multiline_comment = 0 + fout.write(line) + continue + + # search typedef struct end + if typedefFound: + typedef = re.match('}\s*(.*);\n', line) + if typedef: + typedefFound = 0 + continue + + # search comment line + comment = re.match(".*/\*.*\*/.*", line) + if comment: + fout.write(line) + continue + + # search start of multi line comment + comment = re.match(".*/\*", line) + if comment: + fout.write(line) + multiline_comment = 1 + continue + + # ignore __attribute__ for hci_dump_log in src/hci_dump.h + param = re.match(".*__attribute__", line) + if param: + continue + + # search typedef struct begin + typedef = re.match('.*typedef\s+struct.*', line) + if typedef: + typedefFound = 1 + + # complete function declaration + function = re.match('([\w\s\*]*)\((.*)\).*', line) + if function: + if "return" in line: + continue + type_and_name = function.group(1) + arg_string = function.group(2) + create_wrapper(fout, type_and_name, arg_string, need_lock) + inline_function = 'inline' in line; + continue + + # multi-line function declaration + function = re.match('([\w\s\*]*)\((.*).*', line) + if function: + multiline = line + multiline_function_def = 1 + continue + + # fout.write(hfile_header_begin) + fout.write(hfile_header_end) + +def main(argv): + btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') + gen_path = btstack_root + '/src/btstack_rtos.h' + print ('BTstack folder is: %s' % btstack_root) + print ('Generating RTOS wrapper %s' % gen_path) + create_wrapper_file(btstack_root, apis, gen_path) + print ('Number wrapped headers: %u' % len(apis)) + print ('Number wrapped functions: %u' % num_functions) +if __name__ == "__main__": + main(sys.argv[1:])