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 44program_info = ''' 45BTstack Event Getter Generator for BTstack 46Copyright 2016, BlueKitchen GmbH 47''' 48 49copyright = """/* 50 * Copyright (C) 2016 BlueKitchen GmbH 51 * 52 * Redistribution and use in source and binary forms, with or without 53 * modification, are permitted provided that the following conditions 54 * are met: 55 * 56 * 1. Redistributions of source code must retain the above copyright 57 * notice, this list of conditions and the following disclaimer. 58 * 2. Redistributions in binary form must reproduce the above copyright 59 * notice, this list of conditions and the following disclaimer in the 60 * documentation and/or other materials provided with the distribution. 61 * 3. Neither the name of the copyright holders nor the names of 62 * contributors may be used to endorse or promote products derived 63 * from this software without specific prior written permission. 64 * 4. Any redistribution, use, or modification is done solely for 65 * personal benefit and not for any commercial purpose or for 66 * monetary gain. 67 * 68 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 69 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 70 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 71 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 72 * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 73 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 74 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 75 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 76 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 77 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 78 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 79 * SUCH DAMAGE. 80 * 81 * Please inquire about commercial licensing options at 82 * [email protected] 83 * 84 */ 85""" 86 87hfile_header_begin = """ 88 89/** 90 * HCI Event Getter 91 * 92 * Note: Don't edit this file. It is generated by tool/btstack_event_generator.py 93 * 94 */ 95 96#ifndef BTSTACK_EVENT_H 97#define BTSTACK_EVENT_H 98 99#if defined __cplusplus 100extern "C" { 101#endif 102 103#include "btstack_util.h" 104#include <stdint.h> 105 106#ifdef ENABLE_BLE 107#include "ble/gatt_client.h" 108#endif 109 110/* API_START */ 111 112/** 113 * @brief Get event type 114 * @param event 115 * @return type of event 116 */ 117static inline uint8_t hci_event_packet_get_type(const uint8_t * event){ 118 return event[0]; 119} 120 121""" 122 123hfile_header_end = """ 124 125/* API_END */ 126 127#if defined __cplusplus 128} 129#endif 130 131#endif // BTSTACK_EVENT_H 132""" 133 134c_prototoype_simple_return = '''/** 135 * @brief {description} 136 * @param event packet 137 * @return {result_name} 138 * @note: btstack_type {format} 139 */ 140static inline {result_type} {fn_name}(const uint8_t * event){{ 141 {code} 142}} 143''' 144 145c_prototoype_array_getter = '''/** 146 * @brief {description} 147 * @param event packet 148 * @param index 149 * @return {result_name} 150 * @note: btstack_type {format} 151 */ 152static inline {result_type} {fn_name}(const uint8_t * event, uint8_t index){{ 153 {code} 154}} 155''' 156 157c_prototoype_struct_return = '''/** 158 * @brief {description} 159 * @param event packet 160 * @param Pointer to storage for {result_name} 161 * @note: btstack_type {format} 162 */ 163static inline void {fn_name}(const uint8_t * event, {result_type} {result_name}){{ 164 {code} 165}} 166''' 167 168c_prototoype_unsupported = '''/** 169 * @brief {description} 170 * @param event packet 171 * @return {result_name} 172 * @note: btstack_type {format} 173 */ 174// static inline {result_type} {fn_name}(const uint8_t * event){{ 175// not implemented yet 176// }} 177''' 178 179meta_event_template = '''/*** 180 * @brief Get subevent code for {meta_event} event 181 * @param event packet 182 * @return subevent_code 183 */ 184static inline uint8_t hci_event_{meta_event}_meta_get_subevent_code(const uint8_t * event){{ 185 return event[2]; 186}} 187''' 188 189# global variables/defines 190defines = dict() 191defines_used = set() 192 193param_read = { 194 '1' : 'return event[{offset}];', 195 'J' : 'return event[{offset}];', 196 '2' : 'return little_endian_read_16(event, {offset});', 197 'L' : 'return little_endian_read_16(event, {offset});', 198 '3' : 'return little_endian_read_24(event, {offset});', 199 '4' : 'return little_endian_read_32(event, {offset});', 200 'H' : 'return little_endian_read_16(event, {offset});', 201 'B' : 'reverse_bytes(&event[{offset}], {result_name}, 6);', 202 'R' : 'return &event[{offset}];', 203 'N' : 'return (const char *) &event[{offset}];', 204 'P' : 'return (const uint8_t *) &event[{offset}];', 205 'T' : 'return (const char *) &event[{offset}];', 206 'D' : 'return (const uint8_t *) &event[{offset}];', 207 'K' : 'reverse_bytes(&event[{offset}], {result_name}, 16);', 208 'Q' : 'reverse_bytes(&event[{offset}], {result_name}, 32);', 209 'V' : 'return &event[{offset}];', 210 'X' : 'gatt_client_deserialize_service(event, {offset}, {result_name});', 211 'Y' : 'gatt_client_deserialize_characteristic(event, {offset}, {result_name});', 212 'Z' : 'gatt_client_deserialize_characteristic_descriptor(event, {offset}, {result_name});', 213 'V' : 'return &event[{offset}];', 214 'C' : 'return little_endian_read_16(event, {offset} + (2 * (int) index));' 215} 216 217def c_type_for_btstack_type(type): 218 param_types = { '1' : 'uint8_t', '2' : 'uint16_t', '3' : 'uint32_t', '4' : 'uint32_t', 'H' : 'hci_con_handle_t', 'B' : 'bd_addr_t', 219 'D' : 'const uint8_t *', 'E' : 'const uint8_t * ', 'N' : 'const char *' , 'P' : 'const uint8_t *', 'A' : 'const uint8_t *', 220 'R' : 'const uint8_t *', 'S' : 'const uint8_t *', 221 'J' : 'uint8_t', 'L' : 'uint16_t', 'V' : 'const uint8_t *', 'U' : 'BT_UUID', 222 'Q' : 'uint8_t *', 'K' : 'uint8_t *', 223 'X' : 'gatt_client_service_t *', 'Y' : 'gatt_client_characteristic_t *', 'Z' : 'gatt_client_characteristic_descriptor_t *', 224 'T' : 'const char *', 'C' : 'uint16_t' } 225 return param_types[type] 226 227def size_for_type(type): 228 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, 229 'A' : 31, 'S' : -1, 'V': -1, 'J' : 1, 'L' : 2, 'U' : 16, 'X' : 20, 'Y' : 24, 'Z' : 18, 'T':-1, 'C':-1 } 230 return param_sizes[type] 231 232def format_function_name(event_name): 233 event_name = event_name.lower() 234 if 'event' in event_name: 235 return event_name; 236 return event_name+'_event' 237 238def template_for_type(field_type): 239 global c_prototoype_simple_return 240 global c_prototoype_struct_return 241 global c_prototoype_array_getter 242 if field_type == 'C': 243 return c_prototoype_array_getter 244 types_with_struct_return = "BKQXYZ" 245 if field_type in types_with_struct_return: 246 return c_prototoype_struct_return 247 else: 248 return c_prototoype_simple_return 249 250def all_fields_supported(format): 251 global param_read 252 for f in format: 253 if not f in param_read: 254 return False 255 return True 256 257def create_getter(event_name, field_name, field_type, offset, offset_is_number, supported): 258 global c_prototoype_unsupported 259 global param_read 260 261 if field_type == 'C': 262 description_template = 'Get element of array field %s from event %s' 263 else: 264 description_template = "Get field %s from event %s" 265 description = description_template % (field_name, event_name.upper()) 266 result_name = field_name 267 fn_name = "%s_get_%s" % (event_name, field_name) 268 result_type = c_type_for_btstack_type(field_type) 269 template = c_prototoype_unsupported 270 code = '' 271 if supported and field_type in param_read: 272 template = template_for_type(field_type) 273 read_code = param_read[field_type] 274 requires_signed = 'little_endian' in read_code or 'gatt_client_deserialize' in read_code 275 code = '' 276 if requires_signed and not offset_is_number: 277 code += 'uint8_t offset = %s;\n ' % offset 278 offset = '(int)(int8_t) offset' 279 code += read_code.format(offset=offset, result_name=result_name) 280 return template.format(description=description, fn_name=fn_name, result_name=result_name, result_type=result_type, code=code, format=field_type) 281 282def is_le_event(event_group): 283 return event_group in ['GATT', 'ANCS', 'SM'] 284 285def create_events(events): 286 global gen_path 287 global copyright 288 global hfile_header_begin 289 global hfile_header_end 290 global meta_event_template 291 292 with open(gen_path, 'wt') as fout: 293 fout.write(copyright) 294 fout.write(hfile_header_begin) 295 296 for meta_event in meta_events: 297 fout.write(meta_event_template.format(meta_event=meta_event.lower())) 298 299 for event_type, event_name, format, args in events: 300 parts = event_name.split("_") 301 event_group = parts[0] 302 if not event_group in supported_event_groups: 303 print("// %s " % event_name) 304 continue 305 # print(event_name) 306 base_name = format_function_name(event_name) 307 length_name = '' 308 offset = 2 309 offset_is_number = 1 310 offset_unknown = 0 311 supported = all_fields_supported(format) 312 last_variable_length_field_pos = "" 313 if is_le_event(event_group): 314 fout.write("#ifdef ENABLE_BLE\n") 315 if len(format) != len(args): 316 print(event_name.upper()) 317 print ("Format %s does not match params %s " % (format, args)) 318 print 319 for f, arg in zip(format, args): 320 field_name = arg 321 if field_name.lower() == 'subevent_code': 322 offset += 1 323 continue 324 if offset_unknown: 325 print("Param after variable length field without preceding 'J' length field") 326 break 327 field_type = f 328 text = create_getter(base_name, field_name, field_type, offset, offset_is_number, supported) 329 fout.write(text) 330 if field_type in 'RT': 331 break 332 if field_type in 'J': 333 if offset_is_number: 334 last_variable_length_field_pos = '%u' % offset 335 else: 336 last_variable_length_field_pos = offset 337 if field_type in 'V': 338 if last_variable_length_field_pos != '': 339 if offset_is_number: 340 # convert to string 341 offset = '%uu' % offset 342 offset_is_number = 0 343 offset = offset + ' + event[%s]' % last_variable_length_field_pos 344 else: 345 offset_unknown = 1 346 else: 347 if offset_is_number: 348 offset += size_for_type(field_type) 349 else: 350 offset = offset + ' + %uu' % size_for_type(field_type) 351 if is_le_event(event_group): 352 fout.write("#endif\n") 353 fout.write("\n") 354 355 fout.write(hfile_header_end) 356 357btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') 358gen_path = btstack_root + '/src/btstack_event.h' 359 360print(program_info) 361 362# parse events 363(events, le_events, event_types) = parser.parse_events() 364 365# create event field accesors 366create_events(events + le_events) 367 368# done 369print('Done!') 370