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