1#!/usr/bin/env python3 2 3import glob 4import re 5import sys 6import os 7 8import btstack_parser as parser 9 10meta_events = [ 11 'A2DP', 12 'ANCS', 13 'AVDTP', 14 'AVRCP', 15 'GAP', 16 'GATTSERVICE', 17 'GOEP', 18 'HFP', 19 'HID', 20 'HIDS', 21 'HSP', 22 'LE', 23 'LEAUDIO', 24 'MAP', 25 'MESH', 26 'PBAP', 27 'OPP' 28] 29 30supported_event_groups = meta_events + [ 31 'ATT', 32 'BNEP', 33 'BTSTACK', 34 'GAP', 35 'GATT', 36 'HCI', 37 'HID', 38 'L2CAP', 39 'RFCOMM', 40 'SDP', 41 'SM' 42] 43 44open_bracket = parser.open_bracket 45closing_bracket = parser.closing_bracket 46 47program_info = ''' 48BTstack Event Getter Generator for BTstack 49Copyright 2016, BlueKitchen GmbH 50''' 51 52copyright = """/* 53 * Copyright (C) 2016 BlueKitchen GmbH 54 * 55 * Redistribution and use in source and binary forms, with or without 56 * modification, are permitted provided that the following conditions 57 * are met: 58 * 59 * 1. Redistributions of source code must retain the above copyright 60 * notice, this list of conditions and the following disclaimer. 61 * 2. Redistributions in binary form must reproduce the above copyright 62 * notice, this list of conditions and the following disclaimer in the 63 * documentation and/or other materials provided with the distribution. 64 * 3. Neither the name of the copyright holders nor the names of 65 * contributors may be used to endorse or promote products derived 66 * from this software without specific prior written permission. 67 * 4. Any redistribution, use, or modification is done solely for 68 * personal benefit and not for any commercial purpose or for 69 * monetary gain. 70 * 71 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 72 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 73 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 74 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 75 * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 76 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 77 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 78 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 79 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 80 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 81 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 82 * SUCH DAMAGE. 83 * 84 * Please inquire about commercial licensing options at 85 * [email protected] 86 * 87 */ 88""" 89 90hfile_header_begin = """ 91 92/** 93 * HCI Event Getter 94 * 95 * Note: Don't edit this file. It is generated by tool/btstack_event_generator.py 96 * 97 */ 98 99#ifndef BTSTACK_EVENT_H 100#define BTSTACK_EVENT_H 101 102#if defined __cplusplus 103extern "C" { 104#endif 105 106#include "btstack_util.h" 107#include <stdint.h> 108 109#ifdef ENABLE_BLE 110#include "ble/gatt_client.h" 111#endif 112 113/* API_START */ 114 115/** 116 * @brief Get event type 117 * @param event 118 * @return type of event 119 */ 120static inline uint8_t hci_event_packet_get_type(const uint8_t * event){ 121 return event[0]; 122} 123 124typedef uint8_t* btstack_event_iterator_t; 125 126""" 127 128hfile_header_end = """ 129 130/* API_END */ 131 132#if defined __cplusplus 133} 134#endif 135 136#endif // BTSTACK_EVENT_H 137""" 138 139c_prototoype_simple_return = '''/** 140 * @brief {description} 141 * @param event packet 142 * @return {result_name} 143 * @note: btstack_type {format} 144 */ 145static inline {result_type} {event}_get_{field}(const uint8_t * event){{ 146 {code} 147}} 148''' 149 150c_prototype_iterator_return = '''/** 151 * @brief {description} 152 * @param event packet 153 * @return {result_name} 154 * @note: btstack_type {format} 155 */ 156static inline {result_type} {event}_get_{scope}_item_{field}(btstack_event_iterator_t * iter){{ 157 {code} 158}} 159''' 160 161c_prototype_iterator_init = '''/** 162 * @brief Initialize iterator for list {scope} of {event} 163 * @param iterator 164 * @param event packet 165 * @note: btstack_type {format} 166 */ 167static inline void {event}_{field}_init(btstack_event_iterator_t * iter, const uint8_t * event){{ 168 *iter = (btstack_event_iterator_t)&event[{list_field}]; 169}} 170 171''' 172 173c_prototype_iterator_has_next = '''/** 174 * @brief Returns true if iterator of list {scope} of {event} has more elements, false otherwise. 175 * @param iterator 176 * @param event packet 177 * @return 178 * @note: btstack_type {format} 179 */ 180static inline bool {event}_{field}_has_next(btstack_event_iterator_t * iter, const uint8_t * event){{ 181 uint8_t length = event[{length_field}]; 182 const uint8_t *begin = &event[{list_field}]; 183 const uint8_t *end = begin+length; 184 return *iter<end; 185}} 186 187''' 188 189c_prototype_iterator_next = '''/** 190 * @brief Advances the iterator to the next element 191 * @param event packet 192 * @note: btstack_type {format} 193 */ 194static inline void {event}_{scope}_next(btstack_event_iterator_t * iter){{ 195 uint8_t length = {code} 196 *iter = *iter+length; 197}} 198 199''' 200 201c_prototoype_array_getter = '''/** 202 * @brief {description} 203 * @param event packet 204 * @param index 205 * @return {result_name} 206 * @note: btstack_type {format} 207 */ 208static inline {result_type} {event}_get_{field}(const uint8_t * event, uint8_t index){{ 209 {code} 210}} 211''' 212 213c_prototoype_struct_return = '''/** 214 * @brief {description} 215 * @param event packet 216 * @param Pointer to storage for {result_name} 217 * @note: btstack_type {format} 218 */ 219static inline void {event}_get_{field}(const uint8_t * event, {result_type} {result_name}){{ 220 {code} 221}} 222''' 223 224c_prototoype_unsupported = '''/** 225 * @brief {description} 226 * @param event packet 227 * @return {result_name} 228 * @note: btstack_type {format} 229 */ 230// static inline {result_type} {event}_get_{field}(const uint8_t * event){{ 231// not implemented yet 232// }} 233''' 234 235meta_event_template = '''/*** 236 * @brief Get subevent code for {meta_event} event 237 * @param event packet 238 * @return subevent_code 239 */ 240static inline uint8_t hci_event_{meta_event}_meta_get_subevent_code(const uint8_t * event){{ 241 return event[2]; 242}} 243''' 244 245 246# global variables/defines 247defines = dict() 248defines_used = set() 249 250param_read = { 251 '1' : 'return event[{offset}];', 252 'J' : 'return event[{offset}];', 253 '2' : 'return little_endian_read_16(event, {offset});', 254 'L' : 'return little_endian_read_16(event, {offset});', 255 '3' : 'return little_endian_read_24(event, {offset});', 256 '4' : 'return little_endian_read_32(event, {offset});', 257 'H' : 'return little_endian_read_16(event, {offset});', 258 'B' : 'reverse_bytes(&event[{offset}], {result_name}, 6);', 259 'R' : 'return &event[{offset}];', 260 'N' : 'return (const char *) &event[{offset}];', 261 'P' : 'return (const uint8_t *) &event[{offset}];', 262 'T' : 'return (const char *) &event[{offset}];', 263 'D' : 'return (const uint8_t *) &event[{offset}];', 264 'K' : 'reverse_bytes(&event[{offset}], {result_name}, 16);', 265 'Q' : 'reverse_bytes(&event[{offset}], {result_name}, 32);', 266 'V' : 'return &event[{offset}];', 267 'X' : 'gatt_client_deserialize_service(event, {offset}, {result_name});', 268 'Y' : 'gatt_client_deserialize_characteristic(event, {offset}, {result_name});', 269 'Z' : 'gatt_client_deserialize_characteristic_descriptor(event, {offset}, {result_name});', 270 'V' : 'return &event[{offset}];', 271 'C' : 'return little_endian_read_16(event, {offset} + (2 * (int) index));', 272 open_bracket : 'dummy', 273 closing_bracket : 'dummy', 274} 275 276param_iterator_read = { 277 '1' : 'return (*iter)[{offset}];', 278 '2' : 'return little_endian_read_16(*iter, {offset});', 279 'J' : 'return (*iter)[{offset}] + 1;', 280 'V' : 'return &((*iter)[{offset}]);', 281 open_bracket : '*iter = &event[{offset}];', 282 closing_bracket : '' 283} 284 285listScope = list() 286 287def read_template_for_type(type): 288 if listScope: 289 return param_iterator_read[type] 290 return param_read[type] 291 292def description_for_type(type): 293 if type == 'C': 294 return 'Get element of array field {0} from event {1}' 295 if listScope: 296 return 'Get element of list field {0} from event {1}' 297 return 'Get field {0} from event {1}' 298 299def c_type_for_btstack_type(type): 300 param_types = { '1' : 'uint8_t', '2' : 'uint16_t', '3' : 'uint32_t', '4' : 'uint32_t', 'H' : 'hci_con_handle_t', 'B' : 'bd_addr_t', 301 'D' : 'const uint8_t *', 'E' : 'const uint8_t * ', 'N' : 'const char *' , 'P' : 'const uint8_t *', 'A' : 'const uint8_t *', 302 'R' : 'const uint8_t *', 'S' : 'const uint8_t *', 303 'J' : 'uint8_t', 'L' : 'uint16_t', 'V' : 'const uint8_t *', 'U' : 'BT_UUID', 304 'Q' : 'uint8_t *', 'K' : 'uint8_t *', 305 'X' : 'gatt_client_service_t *', 'Y' : 'gatt_client_characteristic_t *', 'Z' : 'gatt_client_characteristic_descriptor_t *', 306 'T' : 'const char *', 'C' : 'uint16_t', 307 open_bracket : 'void', closing_bracket : ''} 308 return param_types[type] 309 310def size_for_type(type): 311 param_sizes = { '1' : 1, '2' : 2, '3' : 3, '4' : 4, 'H' : 2, 'B' : 6, 'D' : 8, 'E' : 240, 'N' : 248, 'P' : 16, 'Q':32, 'K':16, 312 'A' : 31, 'S' : -1, 'V': -1, 'J' : 1, 'L' : 2, 'U' : 16, 'X' : 20, 'Y' : 24, 'Z' : 18, 'T':-1, 'C':-1, 313 open_bracket : 0, closing_bracket : 0 } 314 return param_sizes[type] 315 316def format_function_name(event_name): 317 event_name = event_name.lower() 318 if 'event' in event_name: 319 return event_name; 320 return event_name+'_event' 321 322def template_for_type(field_type): 323 global c_prototoype_simple_return 324 global c_prototoype_struct_return 325 global c_prototoype_array_getter 326 if field_type == 'C': 327 return c_prototoype_array_getter 328# if field_type == open_bracket: 329# return c_prototype_iterator_init 330 types_with_struct_return = "BKQXYZ" 331 if field_type in types_with_struct_return: 332 return c_prototoype_struct_return 333 if listScope: 334 return c_prototype_iterator_return; 335 return c_prototoype_simple_return 336 337def all_fields_supported(format): 338 global param_read 339 for f in format: 340 if not f in param_read: 341 return False 342 return True 343 344def create_iterator( event_name, field_name, field_type, offset, offset_is_number, last_length_field_offset, supported): 345 if field_type == open_bracket: 346 # list field name, list field start, length field of list ( in bytes ), list elemet size if static 347 listScope.append( (field_name, offset, last_length_field_offset, 0) ) 348 list_name_scope = '' 349 list_base = 0 350 if listScope: 351 list_name_scope = listScope[-1][0] 352 list_base = listScope[-1][1] 353 list_length_field_offset = listScope[-1][2] 354 list_static_size = listScope[-1][3] 355 356 generated = '' 357 if field_type == open_bracket: 358 generated_init = c_prototype_iterator_init.format( list_field=offset, scope=list_name_scope, 359 event=event_name, field=field_name, format=field_type) 360 generated_has_next = c_prototype_iterator_has_next.format( list_field=offset, length_field=last_length_field_offset, 361 format=field_type, scope=list_name_scope, event=event_name, 362 field=field_name ) 363 generated = generated_init + generated_has_next; 364 else: 365 # the item length is either determiend statically, format "12" 366 # or dynamically by an list element, format "J" 367 code = '' 368 if list_length_field_offset < last_length_field_offset: 369 #dynamic element size 370 code = '*iter[{0}] + 1;'.format( last_length_field_offset-list_base ) 371 else: 372 code = '{0};'.format( list_static_size ) 373 generated = c_prototype_iterator_next.format( event=event_name, scope=list_name_scope, format=field_type, code=code ); 374 375 if field_type == closing_bracket: 376 listScope.pop() 377 return generated 378 379def create_getter(event_name, field_name, field_type, offset, offset_is_number, last_length_field_offset, supported): 380 global c_prototoype_unsupported 381 global param_read 382 383 description_template = description_for_type(field_type) 384 list_name_scope = '' 385 if listScope: 386 list_name_scope = listScope[-1][0] 387 list_base = listScope[-1][1] 388 list_length_field_offset = listScope[-1][2] 389 list_static_size = listScope[-1][3] 390 if offset_is_number: 391 offset = offset - list_base; 392 listScope[-1] = (list_name_scope, list_base, list_length_field_offset, list_static_size+size_for_type(field_type)) 393 description = description_template.format(field_name, event_name.upper()) 394 result_name = field_name 395 result_type = c_type_for_btstack_type(field_type) 396 template = c_prototoype_unsupported 397 code = '' 398 if supported and field_type in param_read: 399 template = template_for_type(field_type) 400 read_code = read_template_for_type(field_type) 401 requires_signed = 'little_endian' in read_code or 'gatt_client_deserialize' in read_code 402 code = '' 403 if requires_signed and not offset_is_number: 404 code += 'uint8_t offset = %s;\n ' % offset 405 offset = '(int)(int8_t) offset' 406 code += read_code.format(offset=offset, result_name=result_name) 407 return template.format(description=description, scope=list_name_scope, event=event_name, field=field_name, result_name=result_name, result_type=result_type, code=code, format=field_type) 408 409def is_le_event(event_group): 410 return event_group in ['GATT', 'ANCS', 'SM'] 411 412def create_events(events): 413 global gen_path 414 global copyright 415 global hfile_header_begin 416 global hfile_header_end 417 global meta_event_template 418 419 with open(gen_path, 'wt') as fout: 420 fout.write(copyright) 421 fout.write(hfile_header_begin) 422 423 for meta_event in meta_events: 424 fout.write(meta_event_template.format(meta_event=meta_event.lower())) 425 426 for event_type, event_name, format, args in events: 427 parts = event_name.split("_") 428 event_group = parts[0] 429 if not event_group in supported_event_groups: 430 print("// %s " % event_name) 431 continue 432 # print(event_name) 433 base_name = format_function_name(event_name) 434 length_name = '' 435 offset = 2 436 offset_is_number = 1 437 offset_unknown = 0 438 supported = all_fields_supported(format) 439 last_variable_length_field_pos = "" 440 last_length_field_offset = 0 441 if is_le_event(event_group): 442 fout.write("#ifdef ENABLE_BLE\n") 443 if len(format) != len(args): 444 print(event_name.upper()) 445 print ("Format %s does not match params %s " % (format, args)) 446 print 447 for f, arg in zip(format, args): 448 field_name = arg 449 if field_name.lower() == 'subevent_code': 450 offset += 1 451 continue 452 if offset_unknown: 453 print("Param after variable length field without preceding 'J' length field") 454 break 455 field_type = f 456 if field_type in open_bracket + closing_bracket: 457 text = create_iterator( base_name, field_name, field_type, offset, offset_is_number, last_length_field_offset, supported ) 458 else: 459 text = create_getter(base_name, field_name, field_type, offset, offset_is_number, last_length_field_offset, supported) 460 fout.write(text) 461 if field_type in 'RT': 462 break 463 if field_type in 'J': 464 if offset_is_number: 465 last_length_field_offset = offset 466 last_variable_length_field_pos = '%u' % offset 467 else: 468 last_variable_length_field_pos = offset 469 if field_type in 'V': 470 if last_variable_length_field_pos != '': 471 if offset_is_number: 472 # convert to string 473 offset = '%uu' % offset 474 offset_is_number = 0 475 offset = offset + ' + event[%s]' % last_variable_length_field_pos 476 else: 477 offset_unknown = 1 478 else: 479 if offset_is_number: 480 offset += size_for_type(field_type) 481 else: 482 offset = offset + ' + %uu' % size_for_type(field_type) 483 if is_le_event(event_group): 484 fout.write("#endif\n") 485 fout.write("\n") 486 487 fout.write(hfile_header_end) 488 489btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') 490gen_path = btstack_root + '/src/btstack_event.h' 491 492print(program_info) 493 494# parse events 495(events, le_events, event_types) = parser.parse_events() 496# create event field accesors 497create_events(events + le_events) 498 499# done 500print('Done!') 501