#!/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 * */ """ single_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 */ """ single_hfile_api_header = """ #include "API_NAME" """ single_hfile_header_end = """ /* API_END */ #if defined __cplusplus } #endif #endif // __BTSTACK_RTOS_H """ multiple_header_begin = """ /* * FILENAME * * @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 GUARD #define GUARD #if defined __cplusplus extern "C" { #endif #include "btstack_config.h" #include "HEADER" #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 */ """ multiple_header_end = """ /* API_END */ #if defined __cplusplus } #endif #endif // GUARD """ 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", False], # ["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", False], ["src/btstack_run_loop.h", "Run Loop", "runLoop", True], ["src/btstack_util.h", "Common Utils", "btUtil", False], ["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 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 write_wrappers_for_file(fout, file, header_name, need_lock): with open(file, '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(single_hfile_header_begin) def create_wrapper_file(btstack_root, apis, wrapper_file): with open(wrapper_file, 'w') as fout: fout.write(copyright) fout.write(single_hfile_header_begin) for api_tuple in apis: api_filename = btstack_root + "/" + api_tuple[0] need_lock = api_tuple[3] header_file = api_tuple[0].replace('src/','') fout.write(single_hfile_api_header.replace("API_NAME", header_file)) write_wrappers_for_file(fout, api_filename, header_file, need_lock) # fout.write(single_hfile_header_begin) fout.write(single_hfile_header_end) def create_wrapper_files(btstack_root, rtos_folder, apis): for api_tuple in apis: api_filename = btstack_root + "/" + api_tuple[0] need_lock = api_tuple[3] header_file = api_tuple[0].replace('src/','') path_parts = header_file.split('/') path_parts[-1] = 'rtos_' + path_parts[-1] rtos_file = '/'.join(path_parts) wrapper_file = rtos_folder + '/' + rtos_file # print('- %s' % wrapper_file) with open(wrapper_file, 'w') as fout: guard = '__' + rtos_file.replace('.','_').upper() fout.write(copyright) fout.write(multiple_header_begin.replace('FILENAME',rtos_file).replace('GUARD',guard).replace('HEADER',header_file)) write_wrappers_for_file(fout, api_filename, header_file, need_lock) fout.write(multiple_header_end.replace('GUARD',guard)) def assert_dir_exists(path): if not os.path.exists(path): os.makedirs(path) def main(argv): btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') print ('BTstack folder is: %s' % btstack_root) # single file # gen_path = btstack_root + '/src/btstack_rtos.h' # print ('Generating RTOS wrapper %s' % gen_path) # create_wrapper_file(btstack_root, apis, gen_path) # individual files in platform/rtos print ('Generating RTOS wrappers...') rtos_folder = btstack_root + '/platform/rtos' assert_dir_exists(rtos_folder) assert_dir_exists(rtos_folder+'/ble') assert_dir_exists(rtos_folder+'/classic') create_wrapper_files(btstack_root, rtos_folder, apis) # summary print ('Number wrapped headers: %u' % len(apis)) print ('Number wrapped functions: %u' % num_functions) if __name__ == "__main__": main(sys.argv[1:])