1b3fcedb9SMatthias Ringwald#!/usr/bin/env python 2b3fcedb9SMatthias Ringwald# 3b3fcedb9SMatthias Ringwald# BLE GATT configuration generator for use with BTstack, v0.1 4b3fcedb9SMatthias Ringwald# Copyright 2011 Matthias Ringwald 5b3fcedb9SMatthias Ringwald# 6b3fcedb9SMatthias Ringwald# Format of input file: 7b3fcedb9SMatthias Ringwald# PRIMARY_SERVICE, SERVICE_UUID 8b3fcedb9SMatthias Ringwald# CHARACTERISTIC, ATTRIBUTE_TYPE_UUID, [READ | WRITE | DYNAMIC], VALUE 9b3fcedb9SMatthias Ringwald 10b3fcedb9SMatthias Ringwaldimport codecs 11*b165f97bSMatthias Ringwaldimport csv 12*b165f97bSMatthias Ringwaldimport io 13*b165f97bSMatthias Ringwaldimport os 14*b165f97bSMatthias Ringwaldimport re 15*b165f97bSMatthias Ringwaldimport string 16b3fcedb9SMatthias Ringwald 17b3fcedb9SMatthias Ringwaldheader = ''' 18b3fcedb9SMatthias Ringwald// {0} generated from {1} for BTstack 19b3fcedb9SMatthias Ringwald 20b3fcedb9SMatthias Ringwald// binary representation 21b3fcedb9SMatthias Ringwald// attribute size in bytes (16), flags(16), handle (16), uuid (16/128), value(...) 22b3fcedb9SMatthias Ringwald 23b3fcedb9SMatthias Ringwald#include <stdint.h> 24b3fcedb9SMatthias Ringwald 25b3fcedb9SMatthias Ringwaldconst uint8_t profile_data[] = 26b3fcedb9SMatthias Ringwald''' 27b3fcedb9SMatthias Ringwald 28b3fcedb9SMatthias Ringwaldusage = ''' 29b3fcedb9SMatthias RingwaldUsage: ./compile_gatt.py profile.gatt profile.h 30b3fcedb9SMatthias Ringwald''' 31b3fcedb9SMatthias Ringwald 32b3fcedb9SMatthias Ringwaldimport re 33b3fcedb9SMatthias Ringwaldimport sys 34b3fcedb9SMatthias Ringwald 35b3fcedb9SMatthias Ringwaldprint(''' 36b3fcedb9SMatthias RingwaldBLE configuration generator for use with BTstack, v0.1 37b3fcedb9SMatthias RingwaldCopyright 2011 Matthias Ringwald 38b3fcedb9SMatthias Ringwald''') 39b3fcedb9SMatthias Ringwald 40b3fcedb9SMatthias Ringwaldassigned_uuids = { 41b3fcedb9SMatthias Ringwald 'GAP_SERVICE' : 0x1800, 42b3fcedb9SMatthias Ringwald 'GATT_SERVICE' : 0x1801, 43b3fcedb9SMatthias Ringwald 'GAP_DEVICE_NAME' : 0x2a00, 44b3fcedb9SMatthias Ringwald 'GAP_APPEARANCE' : 0x2a01, 45b3fcedb9SMatthias Ringwald 'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02, 46b3fcedb9SMatthias Ringwald 'GAP_RECONNECTION_ADDRESS' : 0x2A03, 47b3fcedb9SMatthias Ringwald 'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04, 48b3fcedb9SMatthias Ringwald 'GATT_SERVICE_CHANGED' : 0x2a05, 49b3fcedb9SMatthias Ringwald} 50b3fcedb9SMatthias Ringwald 51b3fcedb9SMatthias Ringwaldproperty_flags = { 52b3fcedb9SMatthias Ringwald 'BROADCAST' : 0x01, 53b3fcedb9SMatthias Ringwald 'READ' : 0x02, 54b3fcedb9SMatthias Ringwald 'WRITE_WITHOUT_RESPONSE' : 0x04, 55b3fcedb9SMatthias Ringwald 'WRITE' : 0x08, 56b3fcedb9SMatthias Ringwald 'NOTIFY': 0x10, 57b3fcedb9SMatthias Ringwald 'INDICATE' : 0x20, 58b3fcedb9SMatthias Ringwald 'AUTHENTICATED_SIGNED_WRITE' : 0x40, 59b3fcedb9SMatthias Ringwald 'EXTENDED_PROPERTIES' : 0x80, 60b3fcedb9SMatthias Ringwald # custom BTstack extension 61b3fcedb9SMatthias Ringwald 'DYNAMIC': 0x100, 62b3fcedb9SMatthias Ringwald 'LONG_UUID': 0x200, 63b3fcedb9SMatthias Ringwald 'AUTHENTICATION_REQUIRED': 0x400, 64b3fcedb9SMatthias Ringwald 'AUTHORIZATION_REQUIRED': 0x800, 65b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_7': 0x6000, 66b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_8': 0x7000, 67b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_9': 0x8000, 68b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_10': 0x9000, 69b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_11': 0xa000, 70b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_12': 0xb000, 71b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_13': 0xc000, 72b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_14': 0xd000, 73b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_15': 0xe000, 74b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_16': 0xf000, 75b3fcedb9SMatthias Ringwald # only used by gatt compiler >= 0xffff 76b3fcedb9SMatthias Ringwald # Extended Properties 77b3fcedb9SMatthias Ringwald 'RELIABLE_WRITE': 0x10000, 78b3fcedb9SMatthias Ringwald} 79b3fcedb9SMatthias Ringwald 80b3fcedb9SMatthias Ringwaldservices = dict() 81b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict() 82b3fcedb9SMatthias Ringwaldpresentation_formats = dict() 83b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = "" 84b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0 85b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = "" 86b3fcedb9SMatthias Ringwalddefines = [] 87b3fcedb9SMatthias Ringwald 88b3fcedb9SMatthias Ringwaldhandle = 1 89b3fcedb9SMatthias Ringwaldtotal_size = 0 90b3fcedb9SMatthias Ringwald 91*b165f97bSMatthias Ringwalddef read_defines(infile): 92*b165f97bSMatthias Ringwald defines = dict() 93*b165f97bSMatthias Ringwald with open (infile, 'rt') as fin: 94*b165f97bSMatthias Ringwald for line in fin: 95*b165f97bSMatthias Ringwald parts = re.match('#define\s+(\w+)\s+(\w+)',line) 96*b165f97bSMatthias Ringwald if parts and len(parts.groups()) == 2: 97*b165f97bSMatthias Ringwald (key, value) = parts.groups() 98*b165f97bSMatthias Ringwald defines[key] = int(value, 16) 99*b165f97bSMatthias Ringwald return defines 100*b165f97bSMatthias Ringwald 101b3fcedb9SMatthias Ringwalddef keyForUUID(uuid): 102b3fcedb9SMatthias Ringwald keyUUID = "" 103b3fcedb9SMatthias Ringwald for i in uuid: 104b3fcedb9SMatthias Ringwald keyUUID += "%02x" % i 105b3fcedb9SMatthias Ringwald return keyUUID 106b3fcedb9SMatthias Ringwald 107b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid): 108b3fcedb9SMatthias Ringwald return uuid.replace('-', '_') 109b3fcedb9SMatthias Ringwald 110b3fcedb9SMatthias Ringwalddef twoByteLEFor(value): 111b3fcedb9SMatthias Ringwald return [ (value & 0xff), (value >> 8)] 112b3fcedb9SMatthias Ringwald 113b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text): 114b3fcedb9SMatthias Ringwald if re.match("[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}", text): 115b3fcedb9SMatthias Ringwald return True 116b3fcedb9SMatthias Ringwald return False 117b3fcedb9SMatthias Ringwald 118b3fcedb9SMatthias Ringwalddef parseUUID128(uuid): 119b3fcedb9SMatthias Ringwald parts = re.match("([0-9A-Fa-f]{4})([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})-([0-9A-Fa-f]{4})([0-9A-Fa-f]{4})([0-9A-Fa-f]{4})", uuid) 120b3fcedb9SMatthias Ringwald uuid_bytes = [] 121b3fcedb9SMatthias Ringwald for i in range(8, 0, -1): 122b3fcedb9SMatthias Ringwald uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16)) 123b3fcedb9SMatthias Ringwald return uuid_bytes 124b3fcedb9SMatthias Ringwald 125b3fcedb9SMatthias Ringwalddef parseUUID(uuid): 126b3fcedb9SMatthias Ringwald if uuid in assigned_uuids: 127b3fcedb9SMatthias Ringwald return twoByteLEFor(assigned_uuids[uuid]) 128*b165f97bSMatthias Ringwald uuid_upper = uuid.upper().replace('.','_') 129*b165f97bSMatthias Ringwald if uuid_upper in bluetooth_gatt: 130*b165f97bSMatthias Ringwald return twoByteLEFor(bluetooth_gatt[uuid_upper]) 131b3fcedb9SMatthias Ringwald if is_128bit_uuid(uuid): 132b3fcedb9SMatthias Ringwald return parseUUID128(uuid) 133b3fcedb9SMatthias Ringwald uuidInt = int(uuid, 16) 134b3fcedb9SMatthias Ringwald return twoByteLEFor(uuidInt) 135b3fcedb9SMatthias Ringwald 136b3fcedb9SMatthias Ringwalddef parseProperties(properties): 137b3fcedb9SMatthias Ringwald value = 0 138b3fcedb9SMatthias Ringwald parts = properties.split("|") 139b3fcedb9SMatthias Ringwald for property in parts: 140b3fcedb9SMatthias Ringwald property = property.strip() 141b3fcedb9SMatthias Ringwald if property in property_flags: 142b3fcedb9SMatthias Ringwald value |= property_flags[property] 143b3fcedb9SMatthias Ringwald else: 144b3fcedb9SMatthias Ringwald print("WARNING: property %s undefined" % (property)) 145b3fcedb9SMatthias Ringwald return value 146b3fcedb9SMatthias Ringwald 147b3fcedb9SMatthias Ringwalddef write_8(fout, value): 148b3fcedb9SMatthias Ringwald fout.write( "0x%02x, " % (value & 0xff)) 149b3fcedb9SMatthias Ringwald 150b3fcedb9SMatthias Ringwalddef write_16(fout, value): 151b3fcedb9SMatthias Ringwald fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff)) 152b3fcedb9SMatthias Ringwald 153b3fcedb9SMatthias Ringwalddef write_uuid(uuid): 154b3fcedb9SMatthias Ringwald for byte in uuid: 155b3fcedb9SMatthias Ringwald fout.write( "0x%02x, " % byte) 156b3fcedb9SMatthias Ringwald 157b3fcedb9SMatthias Ringwalddef write_string(fout, text): 158b3fcedb9SMatthias Ringwald for l in text.lstrip('"').rstrip('"'): 159b3fcedb9SMatthias Ringwald write_8(fout, ord(l)) 160b3fcedb9SMatthias Ringwald 161b3fcedb9SMatthias Ringwalddef write_sequence(fout, text): 162b3fcedb9SMatthias Ringwald parts = text.split() 163b3fcedb9SMatthias Ringwald for part in parts: 164b3fcedb9SMatthias Ringwald fout.write("0x%s, " % (part.strip())) 165b3fcedb9SMatthias Ringwald 166b3fcedb9SMatthias Ringwalddef write_indent(fout): 167b3fcedb9SMatthias Ringwald fout.write(" ") 168b3fcedb9SMatthias Ringwald 169b3fcedb9SMatthias Ringwalddef is_string(text): 170b3fcedb9SMatthias Ringwald for item in text.split(" "): 171b3fcedb9SMatthias Ringwald if not all(c in string.hexdigits for c in item): 172b3fcedb9SMatthias Ringwald return True 173b3fcedb9SMatthias Ringwald return False 174b3fcedb9SMatthias Ringwald 175b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties): 176b3fcedb9SMatthias Ringwald return properties & (property_flags['NOTIFY'] | property_flags['INDICATE']) 177b3fcedb9SMatthias Ringwald 178b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type): 179b3fcedb9SMatthias Ringwald global handle 180b3fcedb9SMatthias Ringwald global total_size 181b3fcedb9SMatthias Ringwald global current_service_uuid_string 182b3fcedb9SMatthias Ringwald global current_service_start_handle 183b3fcedb9SMatthias Ringwald global services 184b3fcedb9SMatthias Ringwald 185b3fcedb9SMatthias Ringwald if current_service_uuid_string: 186b3fcedb9SMatthias Ringwald fout.write("\n") 187b3fcedb9SMatthias Ringwald # print("append service %s = [%d, %d]\n" % (current_characteristic_uuid_string, current_service_start_handle, handle-1)) 188b3fcedb9SMatthias Ringwald services[current_service_uuid_string] = [current_service_start_handle, handle-1] 189b3fcedb9SMatthias Ringwald 190b3fcedb9SMatthias Ringwald property = property_flags['READ']; 191b3fcedb9SMatthias Ringwald 192b3fcedb9SMatthias Ringwald write_indent(fout) 193b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 194b3fcedb9SMatthias Ringwald 195b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 196b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 197b3fcedb9SMatthias Ringwald 198b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + uuid_size + 2 199b3fcedb9SMatthias Ringwald 200b3fcedb9SMatthias Ringwald if service_type == 0x2802: 201b3fcedb9SMatthias Ringwald size += 4 202b3fcedb9SMatthias Ringwald 203b3fcedb9SMatthias Ringwald write_indent(fout) 204b3fcedb9SMatthias Ringwald write_16(fout, size) 205b3fcedb9SMatthias Ringwald write_16(fout, property) 206b3fcedb9SMatthias Ringwald write_16(fout, handle) 207b3fcedb9SMatthias Ringwald write_16(fout, service_type) 208b3fcedb9SMatthias Ringwald write_uuid(uuid) 209b3fcedb9SMatthias Ringwald fout.write("\n") 210b3fcedb9SMatthias Ringwald 211b3fcedb9SMatthias Ringwald current_service_uuid_string = keyForUUID(uuid) 212b3fcedb9SMatthias Ringwald current_service_start_handle = handle 213b3fcedb9SMatthias Ringwald handle = handle + 1 214b3fcedb9SMatthias Ringwald total_size = total_size + size 215b3fcedb9SMatthias Ringwald 216b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts): 217b3fcedb9SMatthias Ringwald parseService(fout, parts, 0x2800) 218b3fcedb9SMatthias Ringwald 219b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts): 220b3fcedb9SMatthias Ringwald parseService(fout, parts, 0x2801) 221b3fcedb9SMatthias Ringwald 222b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts): 223b3fcedb9SMatthias Ringwald global handle 224b3fcedb9SMatthias Ringwald global total_size 225b3fcedb9SMatthias Ringwald 226b3fcedb9SMatthias Ringwald property = property_flags['READ']; 227b3fcedb9SMatthias Ringwald 228b3fcedb9SMatthias Ringwald write_indent(fout) 229b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 230b3fcedb9SMatthias Ringwald 231b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 232b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 233b3fcedb9SMatthias Ringwald if uuid_size > 2: 234b3fcedb9SMatthias Ringwald uuid_size = 0 235b3fcedb9SMatthias Ringwald # print("Include Service ", keyForUUID(uuid)) 236b3fcedb9SMatthias Ringwald 237b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 4 + uuid_size 238b3fcedb9SMatthias Ringwald 239b3fcedb9SMatthias Ringwald keyUUID = keyForUUID(uuid) 240b3fcedb9SMatthias Ringwald 241b3fcedb9SMatthias Ringwald write_indent(fout) 242b3fcedb9SMatthias Ringwald write_16(fout, size) 243b3fcedb9SMatthias Ringwald write_16(fout, property) 244b3fcedb9SMatthias Ringwald write_16(fout, handle) 245b3fcedb9SMatthias Ringwald write_16(fout, 0x2802) 246b3fcedb9SMatthias Ringwald write_16(fout, services[keyUUID][0]) 247b3fcedb9SMatthias Ringwald write_16(fout, services[keyUUID][1]) 248b3fcedb9SMatthias Ringwald if uuid_size > 0: 249b3fcedb9SMatthias Ringwald write_uuid(uuid) 250b3fcedb9SMatthias Ringwald fout.write("\n") 251b3fcedb9SMatthias Ringwald 252b3fcedb9SMatthias Ringwald handle = handle + 1 253b3fcedb9SMatthias Ringwald total_size = total_size + size 254b3fcedb9SMatthias Ringwald 255b3fcedb9SMatthias Ringwald 256b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts): 257b3fcedb9SMatthias Ringwald global handle 258b3fcedb9SMatthias Ringwald global total_size 259b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 260b3fcedb9SMatthias Ringwald global characteristic_indices 261b3fcedb9SMatthias Ringwald 262b3fcedb9SMatthias Ringwald property_read = property_flags['READ']; 263b3fcedb9SMatthias Ringwald 264b3fcedb9SMatthias Ringwald # enumerate characteristics with same UUID, using optional name tag if available 265b3fcedb9SMatthias Ringwald current_characteristic_uuid_string = c_string_for_uuid(parts[1]); 266b3fcedb9SMatthias Ringwald index = 1 267b3fcedb9SMatthias Ringwald if current_characteristic_uuid_string in characteristic_indices: 268b3fcedb9SMatthias Ringwald index = characteristic_indices[current_characteristic_uuid_string] + 1 269b3fcedb9SMatthias Ringwald characteristic_indices[current_characteristic_uuid_string] = index 270b3fcedb9SMatthias Ringwald if len(parts) > 4: 271b3fcedb9SMatthias Ringwald current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_') 272b3fcedb9SMatthias Ringwald else: 273b3fcedb9SMatthias Ringwald current_characteristic_uuid_string += ('_%02x' % index) 274b3fcedb9SMatthias Ringwald 275b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 276b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 277b3fcedb9SMatthias Ringwald properties = parseProperties(parts[2]) 278b3fcedb9SMatthias Ringwald value = ', '.join([str(x) for x in parts[3:]]) 279b3fcedb9SMatthias Ringwald 280b3fcedb9SMatthias Ringwald # reliable writes is defined in an extended properties 281b3fcedb9SMatthias Ringwald if (properties & property_flags['RELIABLE_WRITE']): 282b3fcedb9SMatthias Ringwald properties = properties | property_flags['EXTENDED_PROPERTIES'] 283b3fcedb9SMatthias Ringwald 284b3fcedb9SMatthias Ringwald write_indent(fout) 285b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3]))) 286b3fcedb9SMatthias Ringwald 287b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + (1+2+uuid_size) 288b3fcedb9SMatthias Ringwald write_indent(fout) 289b3fcedb9SMatthias Ringwald write_16(fout, size) 290b3fcedb9SMatthias Ringwald write_16(fout, property_read) 291b3fcedb9SMatthias Ringwald write_16(fout, handle) 292b3fcedb9SMatthias Ringwald write_16(fout, 0x2803) 293b3fcedb9SMatthias Ringwald write_8(fout, properties) 294b3fcedb9SMatthias Ringwald write_16(fout, handle+1) 295b3fcedb9SMatthias Ringwald write_uuid(uuid) 296b3fcedb9SMatthias Ringwald fout.write("\n") 297b3fcedb9SMatthias Ringwald handle = handle + 1 298b3fcedb9SMatthias Ringwald total_size = total_size + size 299b3fcedb9SMatthias Ringwald 300b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + uuid_size 301b3fcedb9SMatthias Ringwald if is_string(value): 302b3fcedb9SMatthias Ringwald size = size + len(value) 303b3fcedb9SMatthias Ringwald else: 304b3fcedb9SMatthias Ringwald size = size + len(value.split()) 305b3fcedb9SMatthias Ringwald 306b3fcedb9SMatthias Ringwald if uuid_size == 16: 307b3fcedb9SMatthias Ringwald properties = properties | property_flags['LONG_UUID']; 308b3fcedb9SMatthias Ringwald 309b3fcedb9SMatthias Ringwald write_indent(fout) 310b3fcedb9SMatthias Ringwald fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value)) 311b3fcedb9SMatthias Ringwald write_indent(fout) 312b3fcedb9SMatthias Ringwald write_16(fout, size) 313b3fcedb9SMatthias Ringwald write_16(fout, properties) 314b3fcedb9SMatthias Ringwald write_16(fout, handle) 315b3fcedb9SMatthias Ringwald write_uuid(uuid) 316b3fcedb9SMatthias Ringwald if is_string(value): 317b3fcedb9SMatthias Ringwald write_string(fout, value) 318b3fcedb9SMatthias Ringwald else: 319b3fcedb9SMatthias Ringwald write_sequence(fout,value) 320b3fcedb9SMatthias Ringwald 321b3fcedb9SMatthias Ringwald fout.write("\n") 322b3fcedb9SMatthias Ringwald defines.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 323b3fcedb9SMatthias Ringwald handle = handle + 1 324b3fcedb9SMatthias Ringwald 325b3fcedb9SMatthias Ringwald if add_client_characteristic_configuration(properties): 326b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 2 327b3fcedb9SMatthias Ringwald write_indent(fout) 328b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle)) 329b3fcedb9SMatthias Ringwald write_indent(fout) 330b3fcedb9SMatthias Ringwald write_16(fout, size) 331b3fcedb9SMatthias Ringwald write_16(fout, property_flags['READ'] | property_flags['WRITE'] | property_flags['DYNAMIC']) 332b3fcedb9SMatthias Ringwald write_16(fout, handle) 333b3fcedb9SMatthias Ringwald write_16(fout, 0x2902) 334b3fcedb9SMatthias Ringwald write_16(fout, 0) 335b3fcedb9SMatthias Ringwald fout.write("\n") 336b3fcedb9SMatthias Ringwald defines.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 337b3fcedb9SMatthias Ringwald handle = handle + 1 338b3fcedb9SMatthias Ringwald 339b3fcedb9SMatthias Ringwald if properties & property_flags['RELIABLE_WRITE']: 340b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 2 341b3fcedb9SMatthias Ringwald write_indent(fout) 342b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle)) 343b3fcedb9SMatthias Ringwald write_indent(fout) 344b3fcedb9SMatthias Ringwald write_16(fout, size) 345b3fcedb9SMatthias Ringwald write_16(fout, property_flags['READ']) 346b3fcedb9SMatthias Ringwald write_16(fout, handle) 347b3fcedb9SMatthias Ringwald write_16(fout, 0x2900) 348b3fcedb9SMatthias Ringwald write_16(fout, 1) # Reliable Write 349b3fcedb9SMatthias Ringwald fout.write("\n") 350b3fcedb9SMatthias Ringwald handle = handle + 1 351b3fcedb9SMatthias Ringwald 352b3fcedb9SMatthias Ringwalddef parseCharacteristicUserDescription(fout, parts): 353b3fcedb9SMatthias Ringwald global handle 354b3fcedb9SMatthias Ringwald global total_size 355b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 356b3fcedb9SMatthias Ringwald 357b3fcedb9SMatthias Ringwald properties = parseProperties(parts[1]) 358b3fcedb9SMatthias Ringwald value = parts[2] 359b3fcedb9SMatthias Ringwald 360b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 361b3fcedb9SMatthias Ringwald if is_string(value): 362b3fcedb9SMatthias Ringwald size = size + len(value) - 2 363b3fcedb9SMatthias Ringwald else: 364b3fcedb9SMatthias Ringwald size = size + len(value.split()) 365b3fcedb9SMatthias Ringwald 366b3fcedb9SMatthias Ringwald write_indent(fout) 367b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:]))) 368b3fcedb9SMatthias Ringwald write_indent(fout) 369b3fcedb9SMatthias Ringwald write_16(fout, size) 370b3fcedb9SMatthias Ringwald write_16(fout, properties) 371b3fcedb9SMatthias Ringwald write_16(fout, handle) 372b3fcedb9SMatthias Ringwald write_16(fout, 0x2901) 373b3fcedb9SMatthias Ringwald if is_string(value): 374b3fcedb9SMatthias Ringwald write_string(fout, value) 375b3fcedb9SMatthias Ringwald else: 376b3fcedb9SMatthias Ringwald write_sequence(fout,value) 377b3fcedb9SMatthias Ringwald fout.write("\n") 378b3fcedb9SMatthias Ringwald defines.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 379b3fcedb9SMatthias Ringwald handle = handle + 1 380b3fcedb9SMatthias Ringwald 381b3fcedb9SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts): 382b3fcedb9SMatthias Ringwald global handle 383b3fcedb9SMatthias Ringwald global total_size 384b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 385b3fcedb9SMatthias Ringwald 386b3fcedb9SMatthias Ringwald properties = parseProperties(parts[1]) 387b3fcedb9SMatthias Ringwald properties = properties | property_flags['DYNAMIC'] 388b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 389b3fcedb9SMatthias Ringwald 390b3fcedb9SMatthias Ringwald write_indent(fout) 391b3fcedb9SMatthias Ringwald fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:]))) 392b3fcedb9SMatthias Ringwald write_indent(fout) 393b3fcedb9SMatthias Ringwald write_16(fout, size) 394b3fcedb9SMatthias Ringwald write_16(fout, properties) 395b3fcedb9SMatthias Ringwald write_16(fout, handle) 396b3fcedb9SMatthias Ringwald write_16(fout, 0x2903) 397b3fcedb9SMatthias Ringwald fout.write("\n") 398b3fcedb9SMatthias Ringwald defines.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 399b3fcedb9SMatthias Ringwald handle = handle + 1 400b3fcedb9SMatthias Ringwald 401b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts): 402b3fcedb9SMatthias Ringwald global handle 403b3fcedb9SMatthias Ringwald global total_size 404b3fcedb9SMatthias Ringwald 405b3fcedb9SMatthias Ringwald property_read = property_flags['READ']; 406b3fcedb9SMatthias Ringwald 407b3fcedb9SMatthias Ringwald identifier = parts[1] 408b3fcedb9SMatthias Ringwald presentation_formats[identifier] = handle 409b3fcedb9SMatthias Ringwald # print("format '%s' with handle %d\n" % (identifier, handle)) 410b3fcedb9SMatthias Ringwald 411b3fcedb9SMatthias Ringwald format = parts[2] 412b3fcedb9SMatthias Ringwald exponent = parts[3] 413b3fcedb9SMatthias Ringwald unit = parseUUID(parts[4]) 414b3fcedb9SMatthias Ringwald name_space = parts[5] 415b3fcedb9SMatthias Ringwald description = parseUUID(parts[6]) 416b3fcedb9SMatthias Ringwald 417b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 7 418b3fcedb9SMatthias Ringwald 419b3fcedb9SMatthias Ringwald write_indent(fout) 420b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 421b3fcedb9SMatthias Ringwald write_indent(fout) 422b3fcedb9SMatthias Ringwald write_16(fout, size) 423b3fcedb9SMatthias Ringwald write_16(fout, property_read) 424b3fcedb9SMatthias Ringwald write_16(fout, handle) 425b3fcedb9SMatthias Ringwald write_16(fout, 0x2904) 426b3fcedb9SMatthias Ringwald write_sequence(fout, format) 427b3fcedb9SMatthias Ringwald write_sequence(fout, exponent) 428b3fcedb9SMatthias Ringwald write_uuid(unit) 429b3fcedb9SMatthias Ringwald write_sequence(fout, name_space) 430b3fcedb9SMatthias Ringwald write_uuid(description) 431b3fcedb9SMatthias Ringwald fout.write("\n") 432b3fcedb9SMatthias Ringwald handle = handle + 1 433b3fcedb9SMatthias Ringwald 434b3fcedb9SMatthias Ringwald 435b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts): 436b3fcedb9SMatthias Ringwald global handle 437b3fcedb9SMatthias Ringwald global total_size 438b3fcedb9SMatthias Ringwald 439b3fcedb9SMatthias Ringwald property_read = property_flags['READ']; 440b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2 441b3fcedb9SMatthias Ringwald 442b3fcedb9SMatthias Ringwald write_indent(fout) 443b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 444b3fcedb9SMatthias Ringwald write_indent(fout) 445b3fcedb9SMatthias Ringwald write_16(fout, size) 446b3fcedb9SMatthias Ringwald write_16(fout, property_read) 447b3fcedb9SMatthias Ringwald write_16(fout, handle) 448b3fcedb9SMatthias Ringwald write_16(fout, 0x2905) 449b3fcedb9SMatthias Ringwald for identifier in parts[1:]: 450b3fcedb9SMatthias Ringwald format_handle = presentation_formats[identifier] 451b3fcedb9SMatthias Ringwald if format == 0: 452b3fcedb9SMatthias Ringwald print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier) 453b3fcedb9SMatthias Ringwald sys.exit(1) 454b3fcedb9SMatthias Ringwald write_16(fout, format_handle) 455b3fcedb9SMatthias Ringwald fout.write("\n") 456b3fcedb9SMatthias Ringwald handle = handle + 1 457b3fcedb9SMatthias Ringwald 458b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts): 459b3fcedb9SMatthias Ringwald global handle 460b3fcedb9SMatthias Ringwald global total_size 461b3fcedb9SMatthias Ringwald 462b3fcedb9SMatthias Ringwald property_read = property_flags['READ']; 463b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 1 + 1 464b3fcedb9SMatthias Ringwald 465b3fcedb9SMatthias Ringwald report_id = parts[1] 466b3fcedb9SMatthias Ringwald report_type = parts[2] 467b3fcedb9SMatthias Ringwald 468b3fcedb9SMatthias Ringwald write_indent(fout) 469b3fcedb9SMatthias Ringwald fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:]))) 470b3fcedb9SMatthias Ringwald write_indent(fout) 471b3fcedb9SMatthias Ringwald write_16(fout, size) 472b3fcedb9SMatthias Ringwald write_16(fout, property_read) 473b3fcedb9SMatthias Ringwald write_16(fout, handle) 474b3fcedb9SMatthias Ringwald write_16(fout, 0x2908) 475b3fcedb9SMatthias Ringwald write_sequence(fout, report_id) 476b3fcedb9SMatthias Ringwald write_sequence(fout, report_type) 477b3fcedb9SMatthias Ringwald fout.write("\n") 478b3fcedb9SMatthias Ringwald handle = handle + 1 479b3fcedb9SMatthias Ringwald 480b3fcedb9SMatthias Ringwald 481b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts): 482b3fcedb9SMatthias Ringwald global handle 483b3fcedb9SMatthias Ringwald global total_size 484b3fcedb9SMatthias Ringwald 485b3fcedb9SMatthias Ringwald property_read = property_flags['READ']; 486b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 1 487b3fcedb9SMatthias Ringwald 488b3fcedb9SMatthias Ringwald no_of_digitals = parts[1] 489b3fcedb9SMatthias Ringwald 490b3fcedb9SMatthias Ringwald write_indent(fout) 491b3fcedb9SMatthias Ringwald fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:]))) 492b3fcedb9SMatthias Ringwald write_indent(fout) 493b3fcedb9SMatthias Ringwald write_16(fout, size) 494b3fcedb9SMatthias Ringwald write_16(fout, property_read) 495b3fcedb9SMatthias Ringwald write_16(fout, handle) 496b3fcedb9SMatthias Ringwald write_16(fout, 0x2909) 497b3fcedb9SMatthias Ringwald write_sequence(fout, no_of_digitals) 498b3fcedb9SMatthias Ringwald fout.write("\n") 499b3fcedb9SMatthias Ringwald handle = handle + 1 500b3fcedb9SMatthias Ringwald 501b3fcedb9SMatthias Ringwald 502b3fcedb9SMatthias Ringwalddef parse(fname_in, fin, fname_out, fout): 503b3fcedb9SMatthias Ringwald global handle 504b3fcedb9SMatthias Ringwald global total_size 505b3fcedb9SMatthias Ringwald 506b3fcedb9SMatthias Ringwald fout.write(header.format(fname_out, fname_in)) 507b3fcedb9SMatthias Ringwald fout.write('{\n') 508b3fcedb9SMatthias Ringwald 509*b165f97bSMatthias Ringwald line_count = 0; 510b3fcedb9SMatthias Ringwald for line in fin: 511b3fcedb9SMatthias Ringwald line = line.strip("\n\r ") 512*b165f97bSMatthias Ringwald line_count += 1 513b3fcedb9SMatthias Ringwald 514*b165f97bSMatthias Ringwald if line.startswith("//"): 515*b165f97bSMatthias Ringwald fout.write("//" + line.lstrip('/') + '\n') 516*b165f97bSMatthias Ringwald continue 517*b165f97bSMatthias Ringwald 518*b165f97bSMatthias Ringwald if line.startswith("#"): 519*b165f97bSMatthias Ringwald print ("WARNING: #TODO in line %u not handled, skipping declaration:" % line_count) 520*b165f97bSMatthias Ringwald print ("'%s'" % line) 521*b165f97bSMatthias Ringwald fout.write("// " + line + '\n') 522b3fcedb9SMatthias Ringwald continue 523b3fcedb9SMatthias Ringwald 524b3fcedb9SMatthias Ringwald if len(line) == 0: 525b3fcedb9SMatthias Ringwald continue 526b3fcedb9SMatthias Ringwald 527b3fcedb9SMatthias Ringwald f = io.StringIO(line) 528b3fcedb9SMatthias Ringwald parts_list = csv.reader(f, delimiter=',', quotechar='"') 529b3fcedb9SMatthias Ringwald 530b3fcedb9SMatthias Ringwald for parts in parts_list: 531b3fcedb9SMatthias Ringwald for index, object in enumerate(parts): 532b3fcedb9SMatthias Ringwald parts[index] = object.strip().lstrip('"').rstrip('"') 533b3fcedb9SMatthias Ringwald 534b3fcedb9SMatthias Ringwald if parts[0] == 'PRIMARY_SERVICE': 535b3fcedb9SMatthias Ringwald parsePrimaryService(fout, parts) 536b3fcedb9SMatthias Ringwald continue 537b3fcedb9SMatthias Ringwald 538b3fcedb9SMatthias Ringwald if parts[0] == 'SECONDARY_SERVICE': 539b3fcedb9SMatthias Ringwald parseSecondaryService(fout, parts) 540b3fcedb9SMatthias Ringwald continue 541b3fcedb9SMatthias Ringwald 542b3fcedb9SMatthias Ringwald if parts[0] == 'INCLUDE_SERVICE': 543b3fcedb9SMatthias Ringwald parseIncludeService(fout, parts) 544b3fcedb9SMatthias Ringwald continue 545b3fcedb9SMatthias Ringwald 546b3fcedb9SMatthias Ringwald # 2803 547b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC': 548b3fcedb9SMatthias Ringwald parseCharacteristic(fout, parts) 549b3fcedb9SMatthias Ringwald continue 550b3fcedb9SMatthias Ringwald 551b3fcedb9SMatthias Ringwald # 2900 Characteristic Extended Properties 552b3fcedb9SMatthias Ringwald 553b3fcedb9SMatthias Ringwald # 2901 554b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION': 555b3fcedb9SMatthias Ringwald parseCharacteristicUserDescription(fout, parts) 556b3fcedb9SMatthias Ringwald continue 557b3fcedb9SMatthias Ringwald 558*b165f97bSMatthias Ringwald 559*b165f97bSMatthias Ringwald # 2902 Client Characteristic Configuration - automatically included in Characteristic if 560b3fcedb9SMatthias Ringwald # notification / indication is supported 561*b165f97bSMatthias Ringwald if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION': 562*b165f97bSMatthias Ringwald continue 563b3fcedb9SMatthias Ringwald 564b3fcedb9SMatthias Ringwald # 2903 565b3fcedb9SMatthias Ringwald if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION': 566b3fcedb9SMatthias Ringwald parseServerCharacteristicConfiguration(fout, parts) 567b3fcedb9SMatthias Ringwald continue 568b3fcedb9SMatthias Ringwald 569b3fcedb9SMatthias Ringwald # 2904 570b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_FORMAT': 571b3fcedb9SMatthias Ringwald parseCharacteristicFormat(fout, parts) 572b3fcedb9SMatthias Ringwald continue 573b3fcedb9SMatthias Ringwald 574b3fcedb9SMatthias Ringwald # 2905 575b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT': 576b3fcedb9SMatthias Ringwald parseCharacteristicAggregateFormat(fout, parts) 577b3fcedb9SMatthias Ringwald continue 578b3fcedb9SMatthias Ringwald 579b3fcedb9SMatthias Ringwald # 2906 580b3fcedb9SMatthias Ringwald if parts[0] == 'VALID_RANGE': 581b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 582b3fcedb9SMatthias Ringwald continue 583b3fcedb9SMatthias Ringwald 584b3fcedb9SMatthias Ringwald # 2907 585b3fcedb9SMatthias Ringwald if parts[0] == 'EXTERNAL_REPORT_REFERENCE': 586b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 587b3fcedb9SMatthias Ringwald continue 588b3fcedb9SMatthias Ringwald 589b3fcedb9SMatthias Ringwald # 2908 590b3fcedb9SMatthias Ringwald if parts[0] == 'REPORT_REFERENCE': 591b3fcedb9SMatthias Ringwald parseReportReference(fout, parts) 592b3fcedb9SMatthias Ringwald continue 593b3fcedb9SMatthias Ringwald 594b3fcedb9SMatthias Ringwald # 2909 595b3fcedb9SMatthias Ringwald if parts[0] == 'NUMBER_OF_DIGITALS': 596b3fcedb9SMatthias Ringwald parseNumberOfDigitals(fout, parts) 597b3fcedb9SMatthias Ringwald continue 598b3fcedb9SMatthias Ringwald 599b3fcedb9SMatthias Ringwald # 290A 600b3fcedb9SMatthias Ringwald if parts[0] == 'VALUE_TRIGGER_SETTING': 601b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 602b3fcedb9SMatthias Ringwald continue 603b3fcedb9SMatthias Ringwald 604b3fcedb9SMatthias Ringwald # 290B 605b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION': 606b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 607b3fcedb9SMatthias Ringwald continue 608b3fcedb9SMatthias Ringwald 609b3fcedb9SMatthias Ringwald # 290C 610b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT': 611b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 612b3fcedb9SMatthias Ringwald continue 613b3fcedb9SMatthias Ringwald 614b3fcedb9SMatthias Ringwald # 290D 615b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING': 616b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 617b3fcedb9SMatthias Ringwald continue 618b3fcedb9SMatthias Ringwald 619b3fcedb9SMatthias Ringwald # 2906 620b3fcedb9SMatthias Ringwald if parts[0] == 'VALID_RANGE': 621b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 622b3fcedb9SMatthias Ringwald continue 623b3fcedb9SMatthias Ringwald 624b3fcedb9SMatthias Ringwald print("WARNING: unknown token: %s\n" % (parts[0])) 625b3fcedb9SMatthias Ringwald 626b3fcedb9SMatthias Ringwald write_indent(fout) 627b3fcedb9SMatthias Ringwald fout.write("// END\n"); 628b3fcedb9SMatthias Ringwald write_indent(fout) 629b3fcedb9SMatthias Ringwald write_16(fout,0) 630b3fcedb9SMatthias Ringwald fout.write("\n") 631b3fcedb9SMatthias Ringwald total_size = total_size + 2 632b3fcedb9SMatthias Ringwald 633b3fcedb9SMatthias Ringwald fout.write("}; // total size %u bytes \n" % total_size); 634b3fcedb9SMatthias Ringwald 635b3fcedb9SMatthias Ringwalddef listHandles(fout): 636b3fcedb9SMatthias Ringwald fout.write('\n\n') 637b3fcedb9SMatthias Ringwald fout.write('//\n') 638b3fcedb9SMatthias Ringwald fout.write('// list mapping between characteristics and handles\n') 639b3fcedb9SMatthias Ringwald fout.write('//\n') 640b3fcedb9SMatthias Ringwald for define in defines: 641b3fcedb9SMatthias Ringwald fout.write(define) 642b3fcedb9SMatthias Ringwald fout.write('\n') 643b3fcedb9SMatthias Ringwald 644b3fcedb9SMatthias Ringwaldif (len(sys.argv) < 3): 645b3fcedb9SMatthias Ringwald print(usage) 646b3fcedb9SMatthias Ringwald sys.exit(1) 647b3fcedb9SMatthias Ringwaldtry: 648*b165f97bSMatthias Ringwald # read defines from bluetooth_gatt.h 649*b165f97bSMatthias Ringwald btstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') 650*b165f97bSMatthias Ringwald gen_path = btstack_root + '/src/bluetooth_gatt.h' 651*b165f97bSMatthias Ringwald bluetooth_gatt = read_defines(gen_path) 652*b165f97bSMatthias Ringwald 653b3fcedb9SMatthias Ringwald filename = sys.argv[2] 654b3fcedb9SMatthias Ringwald fin = codecs.open (sys.argv[1], encoding='utf-8') 655b3fcedb9SMatthias Ringwald fout = open (filename, 'w') 656b3fcedb9SMatthias Ringwald parse(sys.argv[1], fin, filename, fout) 657b3fcedb9SMatthias Ringwald listHandles(fout) 658b3fcedb9SMatthias Ringwald fout.close() 659*b165f97bSMatthias Ringwald print('Created %s' % filename) 660b3fcedb9SMatthias Ringwald 661b3fcedb9SMatthias Ringwaldexcept IOError as e: 662b3fcedb9SMatthias Ringwald print(usage) 663b3fcedb9SMatthias Ringwald sys.exit(1) 664b3fcedb9SMatthias Ringwald 665b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n') 666