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