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/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('[\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('.*API_START.*',line) 278 if parts: 279 state = State.SearchEndAPI 280 continue 281 282 if state == State.SearchEndAPI: 283 parts = re.match('.*API_END.*',line) 284 if parts: 285 state = State.DoneAPI 286 continue 287 288 if inline_function: 289 function_end = re.match('.*}.*', 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('.*\)', line) 297 if function_end: 298 multiline_function_def = 0 299 function = re.match('([\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('.*\*/.*', 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('}\s*(.*);\n', line) 316 if typedef: 317 typedefFound = 0 318 continue 319 320 # search comment line 321 comment = re.match(".*/\*.*\*/.*", line) 322 if comment: 323 fout.write(line) 324 continue 325 326 # search start of multi line comment 327 comment = re.match(".*/\*", 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(".*__attribute__", line) 335 if param: 336 continue 337 338 # search typedef struct begin 339 typedef = re.match('.*typedef\s+struct.*', line) 340 if typedef: 341 typedefFound = 1 342 343 # complete function declaration 344 function = re.match('([\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('([\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+'/classic') 414 create_wrapper_files(btstack_root, rtos_folder, apis) 415 416 # summary 417 print ('Number wrapped headers: %u' % len(apis)) 418 print ('Number wrapped functions: %u' % num_functions) 419 420if __name__ == "__main__": 421 main(sys.argv[1:]) 422