1b3fcedb9SMatthias Ringwald#!/usr/bin/env python 2b3fcedb9SMatthias Ringwald# 3*dbb3997aSMilanka Ringwald# BLE GATT configuration generator for use with BTstack 4*dbb3997aSMilanka Ringwald# Copyright 2018 BlueKitchen GmbH 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 11b165f97bSMatthias Ringwaldimport csv 12b165f97bSMatthias Ringwaldimport io 13b165f97bSMatthias Ringwaldimport os 14b165f97bSMatthias Ringwaldimport re 15b165f97bSMatthias Ringwaldimport string 1660b51a4cSMatthias Ringwaldimport sys 17*dbb3997aSMilanka Ringwaldimport argparse 18b3fcedb9SMatthias Ringwald 19b3fcedb9SMatthias Ringwaldheader = ''' 20b3fcedb9SMatthias Ringwald// {0} generated from {1} for BTstack 21fd1be25dSMatthias Ringwald// att db format version 1 22b3fcedb9SMatthias Ringwald 23fd1be25dSMatthias Ringwald// binary attribute representation: 24fd1be25dSMatthias Ringwald// - size in bytes (16), flags(16), handle (16), uuid (16/128), value(...) 25b3fcedb9SMatthias Ringwald 26b3fcedb9SMatthias Ringwald#include <stdint.h> 27b3fcedb9SMatthias Ringwald 28b3fcedb9SMatthias Ringwaldconst uint8_t profile_data[] = 29b3fcedb9SMatthias Ringwald''' 30b3fcedb9SMatthias Ringwald 31b3fcedb9SMatthias Ringwaldprint(''' 32*dbb3997aSMilanka RingwaldBLE configuration generator for use with BTstack 33*dbb3997aSMilanka RingwaldCopyright 2018 BlueKitchen GmbH 34b3fcedb9SMatthias Ringwald''') 35b3fcedb9SMatthias Ringwald 36b3fcedb9SMatthias Ringwaldassigned_uuids = { 37b3fcedb9SMatthias Ringwald 'GAP_SERVICE' : 0x1800, 38b3fcedb9SMatthias Ringwald 'GATT_SERVICE' : 0x1801, 39b3fcedb9SMatthias Ringwald 'GAP_DEVICE_NAME' : 0x2a00, 40b3fcedb9SMatthias Ringwald 'GAP_APPEARANCE' : 0x2a01, 41b3fcedb9SMatthias Ringwald 'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02, 42b3fcedb9SMatthias Ringwald 'GAP_RECONNECTION_ADDRESS' : 0x2A03, 43b3fcedb9SMatthias Ringwald 'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04, 44b3fcedb9SMatthias Ringwald 'GATT_SERVICE_CHANGED' : 0x2a05, 45b3fcedb9SMatthias Ringwald} 46b3fcedb9SMatthias Ringwald 47d7ec1d24SMatthias Ringwaldsecurity_permsission = ['ANYBODY','ENCRYPTED', 'AUTHENTICATED', 'AUTHORIZED'] 48d7ec1d24SMatthias Ringwald 49b3fcedb9SMatthias Ringwaldproperty_flags = { 50eb6072adSMatthias Ringwald # GATT Characteristic Properties 51b3fcedb9SMatthias Ringwald 'BROADCAST' : 0x01, 52b3fcedb9SMatthias Ringwald 'READ' : 0x02, 53b3fcedb9SMatthias Ringwald 'WRITE_WITHOUT_RESPONSE' : 0x04, 54b3fcedb9SMatthias Ringwald 'WRITE' : 0x08, 55b3fcedb9SMatthias Ringwald 'NOTIFY': 0x10, 56b3fcedb9SMatthias Ringwald 'INDICATE' : 0x20, 57b3fcedb9SMatthias Ringwald 'AUTHENTICATED_SIGNED_WRITE' : 0x40, 58b3fcedb9SMatthias Ringwald 'EXTENDED_PROPERTIES' : 0x80, 59b3fcedb9SMatthias Ringwald # custom BTstack extension 60b3fcedb9SMatthias Ringwald 'DYNAMIC': 0x100, 61b3fcedb9SMatthias Ringwald 'LONG_UUID': 0x200, 62e22a2612SMatthias Ringwald 63e22a2612SMatthias Ringwald # read permissions 64e22a2612SMatthias Ringwald 'READ_PERMISSION_BIT_0': 0x400, 65e22a2612SMatthias Ringwald 'READ_PERMISSION_BIT_1': 0x800, 66e22a2612SMatthias Ringwald 67e22a2612SMatthias Ringwald # 68b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_7': 0x6000, 69b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_8': 0x7000, 70b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_9': 0x8000, 71b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_10': 0x9000, 72b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_11': 0xa000, 73b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_12': 0xb000, 74b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_13': 0xc000, 75b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_14': 0xd000, 76b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_15': 0xe000, 77b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_16': 0xf000, 78e22a2612SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_MASK': 0xf000, 79eb6072adSMatthias Ringwald 80b3fcedb9SMatthias Ringwald # only used by gatt compiler >= 0xffff 81b3fcedb9SMatthias Ringwald # Extended Properties 82e22a2612SMatthias Ringwald 'RELIABLE_WRITE': 0x0010000, 83e22a2612SMatthias Ringwald 'AUTHENTICATION_REQUIRED': 0x0020000, 84e22a2612SMatthias Ringwald 'AUTHORIZATION_REQUIRED': 0x0040000, 85e22a2612SMatthias Ringwald 'READ_ANYBODY': 0x0080000, 86e22a2612SMatthias Ringwald 'READ_ENCRYPTED': 0x0100000, 87e22a2612SMatthias Ringwald 'READ_AUTHENTICATED': 0x0200000, 88e22a2612SMatthias Ringwald 'READ_AUTHORIZED': 0x0400000, 89e22a2612SMatthias Ringwald 'WRITE_ANYBODY': 0x0800000, 90e22a2612SMatthias Ringwald 'WRITE_ENCRYPTED': 0x1000000, 91e22a2612SMatthias Ringwald 'WRITE_AUTHENTICATED': 0x2000000, 92e22a2612SMatthias Ringwald 'WRITE_AUTHORIZED': 0x4000000, 93eb6072adSMatthias Ringwald 94eb6072adSMatthias Ringwald # Broadcast, Notify, Indicate, Extended Properties are only used to describe a GATT Characteristic, but are free to use with att_db 95e22a2612SMatthias Ringwald 96e22a2612SMatthias Ringwald # write permissions 97e22a2612SMatthias Ringwald 'WRITE_PERMISSION_BIT_0': 0x01, 98e22a2612SMatthias Ringwald 'WRITE_PERMISSION_BIT_1': 0x10, 99eb6072adSMatthias Ringwald # 0x20 100eb6072adSMatthias Ringwald # 0x80 101b3fcedb9SMatthias Ringwald} 102b3fcedb9SMatthias Ringwald 103b3fcedb9SMatthias Ringwaldservices = dict() 104b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict() 105b3fcedb9SMatthias Ringwaldpresentation_formats = dict() 106b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = "" 107b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0 108b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = "" 109729074c4SMatthias Ringwalddefines_for_characteristics = [] 110729074c4SMatthias Ringwalddefines_for_services = [] 111b3fcedb9SMatthias Ringwald 112b3fcedb9SMatthias Ringwaldhandle = 1 113b3fcedb9SMatthias Ringwaldtotal_size = 0 114b3fcedb9SMatthias Ringwald 115b165f97bSMatthias Ringwalddef read_defines(infile): 116b165f97bSMatthias Ringwald defines = dict() 117b165f97bSMatthias Ringwald with open (infile, 'rt') as fin: 118b165f97bSMatthias Ringwald for line in fin: 119b165f97bSMatthias Ringwald parts = re.match('#define\s+(\w+)\s+(\w+)',line) 120b165f97bSMatthias Ringwald if parts and len(parts.groups()) == 2: 121b165f97bSMatthias Ringwald (key, value) = parts.groups() 122b165f97bSMatthias Ringwald defines[key] = int(value, 16) 123b165f97bSMatthias Ringwald return defines 124b165f97bSMatthias Ringwald 125b3fcedb9SMatthias Ringwalddef keyForUUID(uuid): 126b3fcedb9SMatthias Ringwald keyUUID = "" 127b3fcedb9SMatthias Ringwald for i in uuid: 128b3fcedb9SMatthias Ringwald keyUUID += "%02x" % i 129b3fcedb9SMatthias Ringwald return keyUUID 130b3fcedb9SMatthias Ringwald 131b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid): 132b3fcedb9SMatthias Ringwald return uuid.replace('-', '_') 133b3fcedb9SMatthias Ringwald 134b3fcedb9SMatthias Ringwalddef twoByteLEFor(value): 135b3fcedb9SMatthias Ringwald return [ (value & 0xff), (value >> 8)] 136b3fcedb9SMatthias Ringwald 137b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text): 138b3fcedb9SMatthias 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): 139b3fcedb9SMatthias Ringwald return True 140b3fcedb9SMatthias Ringwald return False 141b3fcedb9SMatthias Ringwald 142b3fcedb9SMatthias Ringwalddef parseUUID128(uuid): 143b3fcedb9SMatthias 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) 144b3fcedb9SMatthias Ringwald uuid_bytes = [] 145b3fcedb9SMatthias Ringwald for i in range(8, 0, -1): 146b3fcedb9SMatthias Ringwald uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16)) 147b3fcedb9SMatthias Ringwald return uuid_bytes 148b3fcedb9SMatthias Ringwald 149b3fcedb9SMatthias Ringwalddef parseUUID(uuid): 150b3fcedb9SMatthias Ringwald if uuid in assigned_uuids: 151b3fcedb9SMatthias Ringwald return twoByteLEFor(assigned_uuids[uuid]) 152b165f97bSMatthias Ringwald uuid_upper = uuid.upper().replace('.','_') 153b165f97bSMatthias Ringwald if uuid_upper in bluetooth_gatt: 154b165f97bSMatthias Ringwald return twoByteLEFor(bluetooth_gatt[uuid_upper]) 155b3fcedb9SMatthias Ringwald if is_128bit_uuid(uuid): 156b3fcedb9SMatthias Ringwald return parseUUID128(uuid) 157b3fcedb9SMatthias Ringwald uuidInt = int(uuid, 16) 158b3fcedb9SMatthias Ringwald return twoByteLEFor(uuidInt) 159b3fcedb9SMatthias Ringwald 160b3fcedb9SMatthias Ringwalddef parseProperties(properties): 161b3fcedb9SMatthias Ringwald value = 0 162b3fcedb9SMatthias Ringwald parts = properties.split("|") 163b3fcedb9SMatthias Ringwald for property in parts: 164b3fcedb9SMatthias Ringwald property = property.strip() 165b3fcedb9SMatthias Ringwald if property in property_flags: 166b3fcedb9SMatthias Ringwald value |= property_flags[property] 167b3fcedb9SMatthias Ringwald else: 168b3fcedb9SMatthias Ringwald print("WARNING: property %s undefined" % (property)) 169e22a2612SMatthias Ringwald 170e22a2612SMatthias Ringwald return value; 171e22a2612SMatthias Ringwald 172e22a2612SMatthias Ringwalddef gatt_characteristic_properties(properties): 173e22a2612SMatthias Ringwald return properties & 0xff 174e22a2612SMatthias Ringwald 175e22a2612SMatthias Ringwalddef att_flags(properties): 176e22a2612SMatthias Ringwald # drop Broadcast (0x01), Notify (0x10), Indicate (0x20) - not used for flags 177d7ec1d24SMatthias Ringwald properties &= 0xffffffce 178e22a2612SMatthias Ringwald 179e22a2612SMatthias Ringwald # rw permissions distinct 180e22a2612SMatthias Ringwald distinct_permissions_used = properties & ( 181e22a2612SMatthias Ringwald property_flags['READ_AUTHORIZED'] | 182e22a2612SMatthias Ringwald property_flags['READ_AUTHENTICATED'] | 183e22a2612SMatthias Ringwald property_flags['READ_ENCRYPTED'] | 184e22a2612SMatthias Ringwald property_flags['READ_ANYBODY'] | 185e22a2612SMatthias Ringwald property_flags['WRITE_AUTHORIZED'] | 186e22a2612SMatthias Ringwald property_flags['WRITE_AUTHENTICATED'] | 187e22a2612SMatthias Ringwald property_flags['WRITE_ENCRYPTED'] | 188e22a2612SMatthias Ringwald property_flags['WRITE_ANYBODY'] 189e22a2612SMatthias Ringwald ) != 0 190e22a2612SMatthias Ringwald 191e22a2612SMatthias Ringwald # post process properties 192e22a2612SMatthias Ringwald encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0 193e22a2612SMatthias Ringwald 194d7ec1d24SMatthias Ringwald # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted 195e22a2612SMatthias Ringwald if encryption_key_size_specified and not distinct_permissions_used: 196e22a2612SMatthias Ringwald properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED'] 197e22a2612SMatthias Ringwald 198d7ec1d24SMatthias Ringwald # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated 199d7ec1d24SMatthias Ringwald if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used: 200d7ec1d24SMatthias Ringwald properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED'] 201d7ec1d24SMatthias Ringwald 202d7ec1d24SMatthias Ringwald # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized 203d7ec1d24SMatthias Ringwald if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used: 204d7ec1d24SMatthias Ringwald properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED'] 205d7ec1d24SMatthias Ringwald 206d7ec1d24SMatthias Ringwald # determine read/write security requirements 207d7ec1d24SMatthias Ringwald read_security_level = 0 208d7ec1d24SMatthias Ringwald write_security_level = 0 209e22a2612SMatthias Ringwald if properties & property_flags['READ_AUTHORIZED']: 210d7ec1d24SMatthias Ringwald read_security_level = 3 211e22a2612SMatthias Ringwald elif properties & property_flags['READ_AUTHENTICATED']: 212d7ec1d24SMatthias Ringwald read_security_level = 2 213e22a2612SMatthias Ringwald elif properties & property_flags['READ_ENCRYPTED']: 214d7ec1d24SMatthias Ringwald read_security_level = 1 215e22a2612SMatthias Ringwald if properties & property_flags['WRITE_AUTHORIZED']: 216d7ec1d24SMatthias Ringwald write_security_level = 3 217e22a2612SMatthias Ringwald elif properties & property_flags['WRITE_AUTHENTICATED']: 218d7ec1d24SMatthias Ringwald write_security_level = 2 219e22a2612SMatthias Ringwald elif properties & property_flags['WRITE_ENCRYPTED']: 220d7ec1d24SMatthias Ringwald write_security_level = 1 221d7ec1d24SMatthias Ringwald 222d7ec1d24SMatthias Ringwald # map security requirements to flags 223d7ec1d24SMatthias Ringwald if read_security_level & 2: 224d7ec1d24SMatthias Ringwald properties |= property_flags['READ_PERMISSION_BIT_1'] 225d7ec1d24SMatthias Ringwald if read_security_level & 1: 226d7ec1d24SMatthias Ringwald properties |= property_flags['READ_PERMISSION_BIT_0'] 227d7ec1d24SMatthias Ringwald if write_security_level & 2: 228d7ec1d24SMatthias Ringwald properties |= property_flags['WRITE_PERMISSION_BIT_1'] 229d7ec1d24SMatthias Ringwald if write_security_level & 1: 230e22a2612SMatthias Ringwald properties |= property_flags['WRITE_PERMISSION_BIT_0'] 231e22a2612SMatthias Ringwald 232e22a2612SMatthias Ringwald return properties 233e22a2612SMatthias Ringwald 234d7ec1d24SMatthias Ringwalddef write_permissions_and_key_size_flags_from_properties(properties): 235e22a2612SMatthias Ringwald return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1']) 236b3fcedb9SMatthias Ringwald 237b3fcedb9SMatthias Ringwalddef write_8(fout, value): 238b3fcedb9SMatthias Ringwald fout.write( "0x%02x, " % (value & 0xff)) 239b3fcedb9SMatthias Ringwald 240b3fcedb9SMatthias Ringwalddef write_16(fout, value): 241b3fcedb9SMatthias Ringwald fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff)) 242b3fcedb9SMatthias Ringwald 243b3fcedb9SMatthias Ringwalddef write_uuid(uuid): 244b3fcedb9SMatthias Ringwald for byte in uuid: 245b3fcedb9SMatthias Ringwald fout.write( "0x%02x, " % byte) 246b3fcedb9SMatthias Ringwald 247b3fcedb9SMatthias Ringwalddef write_string(fout, text): 248b3fcedb9SMatthias Ringwald for l in text.lstrip('"').rstrip('"'): 249b3fcedb9SMatthias Ringwald write_8(fout, ord(l)) 250b3fcedb9SMatthias Ringwald 251b3fcedb9SMatthias Ringwalddef write_sequence(fout, text): 252b3fcedb9SMatthias Ringwald parts = text.split() 253b3fcedb9SMatthias Ringwald for part in parts: 254b3fcedb9SMatthias Ringwald fout.write("0x%s, " % (part.strip())) 255b3fcedb9SMatthias Ringwald 256b3fcedb9SMatthias Ringwalddef write_indent(fout): 257b3fcedb9SMatthias Ringwald fout.write(" ") 258b3fcedb9SMatthias Ringwald 259d7ec1d24SMatthias Ringwalddef read_permissions_from_flags(flags): 260d7ec1d24SMatthias Ringwald permissions = 0 261d7ec1d24SMatthias Ringwald if flags & property_flags['READ_PERMISSION_BIT_0']: 262d7ec1d24SMatthias Ringwald permissions |= 1 263d7ec1d24SMatthias Ringwald if flags & property_flags['READ_PERMISSION_BIT_1']: 264d7ec1d24SMatthias Ringwald permissions |= 2 265d7ec1d24SMatthias Ringwald return permissions 266d7ec1d24SMatthias Ringwald 267d7ec1d24SMatthias Ringwalddef write_permissions_from_flags(flags): 268d7ec1d24SMatthias Ringwald permissions = 0 269d7ec1d24SMatthias Ringwald if flags & property_flags['WRITE_PERMISSION_BIT_0']: 270d7ec1d24SMatthias Ringwald permissions |= 1 271d7ec1d24SMatthias Ringwald if flags & property_flags['WRITE_PERMISSION_BIT_1']: 272d7ec1d24SMatthias Ringwald permissions |= 2 273d7ec1d24SMatthias Ringwald return permissions 274d7ec1d24SMatthias Ringwald 275d7ec1d24SMatthias Ringwalddef encryption_key_size_from_flags(flags): 276d7ec1d24SMatthias Ringwald encryption_key_size = (flags & 0xf000) >> 12 277d7ec1d24SMatthias Ringwald if encryption_key_size > 0: 278d7ec1d24SMatthias Ringwald encryption_key_size += 1 279d7ec1d24SMatthias Ringwald return encryption_key_size 280d7ec1d24SMatthias Ringwald 281b3fcedb9SMatthias Ringwalddef is_string(text): 282b3fcedb9SMatthias Ringwald for item in text.split(" "): 283b3fcedb9SMatthias Ringwald if not all(c in string.hexdigits for c in item): 284b3fcedb9SMatthias Ringwald return True 285b3fcedb9SMatthias Ringwald return False 286b3fcedb9SMatthias Ringwald 287b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties): 288b3fcedb9SMatthias Ringwald return properties & (property_flags['NOTIFY'] | property_flags['INDICATE']) 289b3fcedb9SMatthias Ringwald 290729074c4SMatthias Ringwalddef serviceDefinitionComplete(fout): 291729074c4SMatthias Ringwald global services 292729074c4SMatthias Ringwald if current_service_uuid_string: 293729074c4SMatthias Ringwald fout.write("\n") 294729074c4SMatthias Ringwald # print("append service %s = [%d, %d]" % (current_characteristic_uuid_string, current_service_start_handle, handle-1)) 295729074c4SMatthias Ringwald defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle)) 296729074c4SMatthias Ringwald defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1)) 297729074c4SMatthias Ringwald services[current_service_uuid_string] = [current_service_start_handle, handle-1] 298729074c4SMatthias Ringwald 299d7ec1d24SMatthias Ringwalddef dump_flags(fout, flags): 300d7ec1d24SMatthias Ringwald global security_permsission 301d7ec1d24SMatthias Ringwald encryption_key_size = encryption_key_size_from_flags(flags) 302d7ec1d24SMatthias Ringwald read_permissions = security_permsission[read_permissions_from_flags(flags)] 303d7ec1d24SMatthias Ringwald write_permissions = security_permsission[write_permissions_from_flags(flags)] 304d7ec1d24SMatthias Ringwald write_indent(fout) 305d7ec1d24SMatthias Ringwald fout.write('// ') 306d7ec1d24SMatthias Ringwald first = 1 307d7ec1d24SMatthias Ringwald if flags & property_flags['READ']: 308d7ec1d24SMatthias Ringwald fout.write('READ_%s' % read_permissions) 309d7ec1d24SMatthias Ringwald first = 0 310d7ec1d24SMatthias Ringwald if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']): 311d7ec1d24SMatthias Ringwald if not first: 312d7ec1d24SMatthias Ringwald fout.write(', ') 313d7ec1d24SMatthias Ringwald first = 0 314d7ec1d24SMatthias Ringwald fout.write('WRITE_%s' % write_permissions) 315d7ec1d24SMatthias Ringwald if encryption_key_size > 0: 316d7ec1d24SMatthias Ringwald if not first: 317d7ec1d24SMatthias Ringwald fout.write(', ') 318d7ec1d24SMatthias Ringwald first = 0 319d7ec1d24SMatthias Ringwald fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size) 320d7ec1d24SMatthias Ringwald fout.write('\n') 321d7ec1d24SMatthias Ringwald 322b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type): 323b3fcedb9SMatthias Ringwald global handle 324b3fcedb9SMatthias Ringwald global total_size 325b3fcedb9SMatthias Ringwald global current_service_uuid_string 326b3fcedb9SMatthias Ringwald global current_service_start_handle 327b3fcedb9SMatthias Ringwald 328729074c4SMatthias Ringwald serviceDefinitionComplete(fout) 329b3fcedb9SMatthias Ringwald 330d7ec1d24SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 331b3fcedb9SMatthias Ringwald 332b3fcedb9SMatthias Ringwald write_indent(fout) 333b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 334b3fcedb9SMatthias Ringwald 335b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 336b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 337b3fcedb9SMatthias Ringwald 338b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + uuid_size + 2 339b3fcedb9SMatthias Ringwald 340b3fcedb9SMatthias Ringwald if service_type == 0x2802: 341b3fcedb9SMatthias Ringwald size += 4 342b3fcedb9SMatthias Ringwald 343b3fcedb9SMatthias Ringwald write_indent(fout) 344b3fcedb9SMatthias Ringwald write_16(fout, size) 345d7ec1d24SMatthias Ringwald write_16(fout, read_only_anybody_flags) 346b3fcedb9SMatthias Ringwald write_16(fout, handle) 347b3fcedb9SMatthias Ringwald write_16(fout, service_type) 348b3fcedb9SMatthias Ringwald write_uuid(uuid) 349b3fcedb9SMatthias Ringwald fout.write("\n") 350b3fcedb9SMatthias Ringwald 351729074c4SMatthias Ringwald current_service_uuid_string = c_string_for_uuid(parts[1]) 352b3fcedb9SMatthias Ringwald current_service_start_handle = handle 353b3fcedb9SMatthias Ringwald handle = handle + 1 354b3fcedb9SMatthias Ringwald total_size = total_size + size 355b3fcedb9SMatthias Ringwald 356b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts): 357b3fcedb9SMatthias Ringwald parseService(fout, parts, 0x2800) 358b3fcedb9SMatthias Ringwald 359b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts): 360b3fcedb9SMatthias Ringwald parseService(fout, parts, 0x2801) 361b3fcedb9SMatthias Ringwald 362b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts): 363b3fcedb9SMatthias Ringwald global handle 364b3fcedb9SMatthias Ringwald global total_size 365b3fcedb9SMatthias Ringwald 366e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 367b3fcedb9SMatthias Ringwald 368b3fcedb9SMatthias Ringwald write_indent(fout) 369b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 370b3fcedb9SMatthias Ringwald 371b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 372b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 373b3fcedb9SMatthias Ringwald if uuid_size > 2: 374b3fcedb9SMatthias Ringwald uuid_size = 0 375729074c4SMatthias Ringwald # print("Include Service ", c_string_for_uuid(uuid)) 376b3fcedb9SMatthias Ringwald 377b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 4 + uuid_size 378b3fcedb9SMatthias Ringwald 379729074c4SMatthias Ringwald keyUUID = c_string_for_uuid(parts[1]) 380b3fcedb9SMatthias Ringwald 381b3fcedb9SMatthias Ringwald write_indent(fout) 382b3fcedb9SMatthias Ringwald write_16(fout, size) 383e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 384b3fcedb9SMatthias Ringwald write_16(fout, handle) 385b3fcedb9SMatthias Ringwald write_16(fout, 0x2802) 386b3fcedb9SMatthias Ringwald write_16(fout, services[keyUUID][0]) 387b3fcedb9SMatthias Ringwald write_16(fout, services[keyUUID][1]) 388b3fcedb9SMatthias Ringwald if uuid_size > 0: 389b3fcedb9SMatthias Ringwald write_uuid(uuid) 390b3fcedb9SMatthias Ringwald fout.write("\n") 391b3fcedb9SMatthias Ringwald 392b3fcedb9SMatthias Ringwald handle = handle + 1 393b3fcedb9SMatthias Ringwald total_size = total_size + size 394b3fcedb9SMatthias Ringwald 395b3fcedb9SMatthias Ringwald 396b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts): 397b3fcedb9SMatthias Ringwald global handle 398b3fcedb9SMatthias Ringwald global total_size 399b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 400b3fcedb9SMatthias Ringwald global characteristic_indices 401b3fcedb9SMatthias Ringwald 402e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 403b3fcedb9SMatthias Ringwald 404b3fcedb9SMatthias Ringwald # enumerate characteristics with same UUID, using optional name tag if available 405b3fcedb9SMatthias Ringwald current_characteristic_uuid_string = c_string_for_uuid(parts[1]); 406b3fcedb9SMatthias Ringwald index = 1 407b3fcedb9SMatthias Ringwald if current_characteristic_uuid_string in characteristic_indices: 408b3fcedb9SMatthias Ringwald index = characteristic_indices[current_characteristic_uuid_string] + 1 409b3fcedb9SMatthias Ringwald characteristic_indices[current_characteristic_uuid_string] = index 410b3fcedb9SMatthias Ringwald if len(parts) > 4: 411b3fcedb9SMatthias Ringwald current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_') 412b3fcedb9SMatthias Ringwald else: 413b3fcedb9SMatthias Ringwald current_characteristic_uuid_string += ('_%02x' % index) 414b3fcedb9SMatthias Ringwald 415b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 416b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 417b3fcedb9SMatthias Ringwald properties = parseProperties(parts[2]) 418b3fcedb9SMatthias Ringwald value = ', '.join([str(x) for x in parts[3:]]) 419b3fcedb9SMatthias Ringwald 420b3fcedb9SMatthias Ringwald # reliable writes is defined in an extended properties 421b3fcedb9SMatthias Ringwald if (properties & property_flags['RELIABLE_WRITE']): 422b3fcedb9SMatthias Ringwald properties = properties | property_flags['EXTENDED_PROPERTIES'] 423b3fcedb9SMatthias Ringwald 424b3fcedb9SMatthias Ringwald write_indent(fout) 425b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3]))) 426b3fcedb9SMatthias Ringwald 427e22a2612SMatthias Ringwald 428e22a2612SMatthias Ringwald characteristic_properties = gatt_characteristic_properties(properties) 429b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + (1+2+uuid_size) 430b3fcedb9SMatthias Ringwald write_indent(fout) 431b3fcedb9SMatthias Ringwald write_16(fout, size) 432e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 433b3fcedb9SMatthias Ringwald write_16(fout, handle) 434b3fcedb9SMatthias Ringwald write_16(fout, 0x2803) 435e22a2612SMatthias Ringwald write_8(fout, characteristic_properties) 436b3fcedb9SMatthias Ringwald write_16(fout, handle+1) 437b3fcedb9SMatthias Ringwald write_uuid(uuid) 438b3fcedb9SMatthias Ringwald fout.write("\n") 439b3fcedb9SMatthias Ringwald handle = handle + 1 440b3fcedb9SMatthias Ringwald total_size = total_size + size 441b3fcedb9SMatthias Ringwald 442b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + uuid_size 443b3fcedb9SMatthias Ringwald if is_string(value): 444b3fcedb9SMatthias Ringwald size = size + len(value) 445b3fcedb9SMatthias Ringwald else: 446b3fcedb9SMatthias Ringwald size = size + len(value.split()) 447b3fcedb9SMatthias Ringwald 448e22a2612SMatthias Ringwald value_flags = att_flags(properties) 4498ea3236cSMatthias Ringwald 4508ea3236cSMatthias Ringwald # add UUID128 flag for value handle 451b3fcedb9SMatthias Ringwald if uuid_size == 16: 452e22a2612SMatthias Ringwald value_flags = value_flags | property_flags['LONG_UUID']; 453b3fcedb9SMatthias Ringwald 454b3fcedb9SMatthias Ringwald write_indent(fout) 455b3fcedb9SMatthias Ringwald fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value)) 456d7ec1d24SMatthias Ringwald 457d7ec1d24SMatthias Ringwald dump_flags(fout, value_flags) 458d7ec1d24SMatthias Ringwald 459b3fcedb9SMatthias Ringwald write_indent(fout) 460b3fcedb9SMatthias Ringwald write_16(fout, size) 461e22a2612SMatthias Ringwald write_16(fout, value_flags) 462b3fcedb9SMatthias Ringwald write_16(fout, handle) 463b3fcedb9SMatthias Ringwald write_uuid(uuid) 464b3fcedb9SMatthias Ringwald if is_string(value): 465b3fcedb9SMatthias Ringwald write_string(fout, value) 466b3fcedb9SMatthias Ringwald else: 467b3fcedb9SMatthias Ringwald write_sequence(fout,value) 468b3fcedb9SMatthias Ringwald 469b3fcedb9SMatthias Ringwald fout.write("\n") 470729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 471b3fcedb9SMatthias Ringwald handle = handle + 1 472b3fcedb9SMatthias Ringwald 473b3fcedb9SMatthias Ringwald if add_client_characteristic_configuration(properties): 474e22a2612SMatthias Ringwald # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC 475d7ec1d24SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 476e22a2612SMatthias Ringwald flags |= property_flags['READ'] 477e22a2612SMatthias Ringwald flags |= property_flags['WRITE'] 478e22a2612SMatthias Ringwald flags |= property_flags['DYNAMIC'] 479b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 2 480d7ec1d24SMatthias Ringwald 481b3fcedb9SMatthias Ringwald write_indent(fout) 482b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle)) 483d7ec1d24SMatthias Ringwald 484d7ec1d24SMatthias Ringwald dump_flags(fout, flags) 485d7ec1d24SMatthias Ringwald 486b3fcedb9SMatthias Ringwald write_indent(fout) 487b3fcedb9SMatthias Ringwald write_16(fout, size) 488e22a2612SMatthias Ringwald write_16(fout, flags) 489b3fcedb9SMatthias Ringwald write_16(fout, handle) 490b3fcedb9SMatthias Ringwald write_16(fout, 0x2902) 491b3fcedb9SMatthias Ringwald write_16(fout, 0) 492b3fcedb9SMatthias Ringwald fout.write("\n") 493729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 494b3fcedb9SMatthias Ringwald handle = handle + 1 495b3fcedb9SMatthias Ringwald 496b3fcedb9SMatthias Ringwald if properties & property_flags['RELIABLE_WRITE']: 497b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 2 498b3fcedb9SMatthias Ringwald write_indent(fout) 499b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle)) 500b3fcedb9SMatthias Ringwald write_indent(fout) 501b3fcedb9SMatthias Ringwald write_16(fout, size) 502e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 503b3fcedb9SMatthias Ringwald write_16(fout, handle) 504b3fcedb9SMatthias Ringwald write_16(fout, 0x2900) 505b3fcedb9SMatthias Ringwald write_16(fout, 1) # Reliable Write 506b3fcedb9SMatthias Ringwald fout.write("\n") 507b3fcedb9SMatthias Ringwald handle = handle + 1 508b3fcedb9SMatthias Ringwald 509b3fcedb9SMatthias Ringwalddef parseCharacteristicUserDescription(fout, parts): 510b3fcedb9SMatthias Ringwald global handle 511b3fcedb9SMatthias Ringwald global total_size 512b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 513b3fcedb9SMatthias Ringwald 514b3fcedb9SMatthias Ringwald properties = parseProperties(parts[1]) 515b3fcedb9SMatthias Ringwald value = parts[2] 516b3fcedb9SMatthias Ringwald 517b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 518b3fcedb9SMatthias Ringwald if is_string(value): 519b7647eb6SMatthias Ringwald size = size + len(value) 520b3fcedb9SMatthias Ringwald else: 521b3fcedb9SMatthias Ringwald size = size + len(value.split()) 522b3fcedb9SMatthias Ringwald 523e22a2612SMatthias Ringwald # use write, write permissions and encryption key size from attribute value and set READ_ANYBODY 524d7ec1d24SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 525e22a2612SMatthias Ringwald flags |= properties & property_flags['WRITE'] 526e22a2612SMatthias Ringwald flags |= property_flags['READ'] 527e22a2612SMatthias Ringwald 528b3fcedb9SMatthias Ringwald write_indent(fout) 529b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:]))) 530d7ec1d24SMatthias Ringwald 531d7ec1d24SMatthias Ringwald dump_flags(fout, flags) 532d7ec1d24SMatthias Ringwald 533b3fcedb9SMatthias Ringwald write_indent(fout) 534b3fcedb9SMatthias Ringwald write_16(fout, size) 535e22a2612SMatthias Ringwald write_16(fout, flags) 536b3fcedb9SMatthias Ringwald write_16(fout, handle) 537b3fcedb9SMatthias Ringwald write_16(fout, 0x2901) 538b3fcedb9SMatthias Ringwald if is_string(value): 539b3fcedb9SMatthias Ringwald write_string(fout, value) 540b3fcedb9SMatthias Ringwald else: 541b3fcedb9SMatthias Ringwald write_sequence(fout,value) 542b3fcedb9SMatthias Ringwald fout.write("\n") 543729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 544b3fcedb9SMatthias Ringwald handle = handle + 1 545b3fcedb9SMatthias Ringwald 546b3fcedb9SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts): 547b3fcedb9SMatthias Ringwald global handle 548b3fcedb9SMatthias Ringwald global total_size 549b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 550b3fcedb9SMatthias Ringwald 551b3fcedb9SMatthias Ringwald properties = parseProperties(parts[1]) 552b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 553b3fcedb9SMatthias Ringwald 554e22a2612SMatthias Ringwald # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY 555d7ec1d24SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 556e22a2612SMatthias Ringwald flags |= property_flags['READ'] 557e22a2612SMatthias Ringwald flags |= property_flags['WRITE'] 558e22a2612SMatthias Ringwald flags |= property_flags['DYNAMIC'] 559e22a2612SMatthias Ringwald 560b3fcedb9SMatthias Ringwald write_indent(fout) 561b3fcedb9SMatthias Ringwald fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:]))) 562d7ec1d24SMatthias Ringwald 563d7ec1d24SMatthias Ringwald dump_flags(fout, flags) 564d7ec1d24SMatthias Ringwald 565b3fcedb9SMatthias Ringwald write_indent(fout) 566b3fcedb9SMatthias Ringwald write_16(fout, size) 567e22a2612SMatthias Ringwald write_16(fout, flags) 568b3fcedb9SMatthias Ringwald write_16(fout, handle) 569b3fcedb9SMatthias Ringwald write_16(fout, 0x2903) 570b3fcedb9SMatthias Ringwald fout.write("\n") 571729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 572b3fcedb9SMatthias Ringwald handle = handle + 1 573b3fcedb9SMatthias Ringwald 574b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts): 575b3fcedb9SMatthias Ringwald global handle 576b3fcedb9SMatthias Ringwald global total_size 577b3fcedb9SMatthias Ringwald 578e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 579b3fcedb9SMatthias Ringwald 580b3fcedb9SMatthias Ringwald identifier = parts[1] 581b3fcedb9SMatthias Ringwald presentation_formats[identifier] = handle 582b3fcedb9SMatthias Ringwald # print("format '%s' with handle %d\n" % (identifier, handle)) 583b3fcedb9SMatthias Ringwald 584b3fcedb9SMatthias Ringwald format = parts[2] 585b3fcedb9SMatthias Ringwald exponent = parts[3] 586b3fcedb9SMatthias Ringwald unit = parseUUID(parts[4]) 587b3fcedb9SMatthias Ringwald name_space = parts[5] 588b3fcedb9SMatthias Ringwald description = parseUUID(parts[6]) 589b3fcedb9SMatthias Ringwald 590b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 7 591b3fcedb9SMatthias Ringwald 592b3fcedb9SMatthias Ringwald write_indent(fout) 593b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 594b3fcedb9SMatthias Ringwald write_indent(fout) 595b3fcedb9SMatthias Ringwald write_16(fout, size) 596e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 597b3fcedb9SMatthias Ringwald write_16(fout, handle) 598b3fcedb9SMatthias Ringwald write_16(fout, 0x2904) 599b3fcedb9SMatthias Ringwald write_sequence(fout, format) 600b3fcedb9SMatthias Ringwald write_sequence(fout, exponent) 601b3fcedb9SMatthias Ringwald write_uuid(unit) 602b3fcedb9SMatthias Ringwald write_sequence(fout, name_space) 603b3fcedb9SMatthias Ringwald write_uuid(description) 604b3fcedb9SMatthias Ringwald fout.write("\n") 605b3fcedb9SMatthias Ringwald handle = handle + 1 606b3fcedb9SMatthias Ringwald 607b3fcedb9SMatthias Ringwald 608b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts): 609b3fcedb9SMatthias Ringwald global handle 610b3fcedb9SMatthias Ringwald global total_size 611b3fcedb9SMatthias Ringwald 612e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 613b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2 614b3fcedb9SMatthias Ringwald 615b3fcedb9SMatthias Ringwald write_indent(fout) 616b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 617b3fcedb9SMatthias Ringwald write_indent(fout) 618b3fcedb9SMatthias Ringwald write_16(fout, size) 619e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 620b3fcedb9SMatthias Ringwald write_16(fout, handle) 621b3fcedb9SMatthias Ringwald write_16(fout, 0x2905) 622b3fcedb9SMatthias Ringwald for identifier in parts[1:]: 623b3fcedb9SMatthias Ringwald format_handle = presentation_formats[identifier] 624b3fcedb9SMatthias Ringwald if format == 0: 625b3fcedb9SMatthias Ringwald print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier) 626b3fcedb9SMatthias Ringwald sys.exit(1) 627b3fcedb9SMatthias Ringwald write_16(fout, format_handle) 628b3fcedb9SMatthias Ringwald fout.write("\n") 629b3fcedb9SMatthias Ringwald handle = handle + 1 630b3fcedb9SMatthias Ringwald 631b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts): 632b3fcedb9SMatthias Ringwald global handle 633b3fcedb9SMatthias Ringwald global total_size 634b3fcedb9SMatthias Ringwald 635e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 636b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 1 + 1 637b3fcedb9SMatthias Ringwald 638231a3c5dSMatthias Ringwald report_id = parts[2] 639231a3c5dSMatthias Ringwald report_type = parts[3] 640b3fcedb9SMatthias Ringwald 641b3fcedb9SMatthias Ringwald write_indent(fout) 642b3fcedb9SMatthias Ringwald fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:]))) 643b3fcedb9SMatthias Ringwald write_indent(fout) 644b3fcedb9SMatthias Ringwald write_16(fout, size) 645e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 646b3fcedb9SMatthias Ringwald write_16(fout, handle) 647b3fcedb9SMatthias Ringwald write_16(fout, 0x2908) 648b3fcedb9SMatthias Ringwald write_sequence(fout, report_id) 649b3fcedb9SMatthias Ringwald write_sequence(fout, report_type) 650b3fcedb9SMatthias Ringwald fout.write("\n") 651b3fcedb9SMatthias Ringwald handle = handle + 1 652b3fcedb9SMatthias Ringwald 653b3fcedb9SMatthias Ringwald 654b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts): 655b3fcedb9SMatthias Ringwald global handle 656b3fcedb9SMatthias Ringwald global total_size 657b3fcedb9SMatthias Ringwald 658e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 659b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 1 660b3fcedb9SMatthias Ringwald 661b3fcedb9SMatthias Ringwald no_of_digitals = parts[1] 662b3fcedb9SMatthias Ringwald 663b3fcedb9SMatthias Ringwald write_indent(fout) 664b3fcedb9SMatthias Ringwald fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:]))) 665b3fcedb9SMatthias Ringwald write_indent(fout) 666b3fcedb9SMatthias Ringwald write_16(fout, size) 667e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 668b3fcedb9SMatthias Ringwald write_16(fout, handle) 669b3fcedb9SMatthias Ringwald write_16(fout, 0x2909) 670b3fcedb9SMatthias Ringwald write_sequence(fout, no_of_digitals) 671b3fcedb9SMatthias Ringwald fout.write("\n") 672b3fcedb9SMatthias Ringwald handle = handle + 1 673b3fcedb9SMatthias Ringwald 67460b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout): 675b3fcedb9SMatthias Ringwald global handle 676b3fcedb9SMatthias Ringwald global total_size 677b3fcedb9SMatthias Ringwald 678b165f97bSMatthias Ringwald line_count = 0; 679b3fcedb9SMatthias Ringwald for line in fin: 680b3fcedb9SMatthias Ringwald line = line.strip("\n\r ") 681b165f97bSMatthias Ringwald line_count += 1 682b3fcedb9SMatthias Ringwald 683b165f97bSMatthias Ringwald if line.startswith("//"): 684b165f97bSMatthias Ringwald fout.write(" //" + line.lstrip('/') + '\n') 685b165f97bSMatthias Ringwald continue 686b165f97bSMatthias Ringwald 68760b51a4cSMatthias Ringwald if line.startswith("#import"): 68860b51a4cSMatthias Ringwald imported_file = '' 68960b51a4cSMatthias Ringwald parts = re.match('#import\s+<(.*)>\w*',line) 69060b51a4cSMatthias Ringwald if parts and len(parts.groups()) == 1: 691*dbb3997aSMilanka Ringwald imported_file = parts.groups()[0] 69260b51a4cSMatthias Ringwald parts = re.match('#import\s+"(.*)"\w*',line) 69360b51a4cSMatthias Ringwald if parts and len(parts.groups()) == 1: 694*dbb3997aSMilanka Ringwald imported_file = parts.groups()[0] 69560b51a4cSMatthias Ringwald if len(imported_file) == 0: 69660b51a4cSMatthias Ringwald print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count)) 69760b51a4cSMatthias Ringwald continue 69860b51a4cSMatthias Ringwald 699*dbb3997aSMilanka Ringwald imported_file = getFile( imported_file ) 70060b51a4cSMatthias Ringwald print("Importing %s" % imported_file) 70160b51a4cSMatthias Ringwald try: 70260b51a4cSMatthias Ringwald imported_fin = codecs.open (imported_file, encoding='utf-8') 70360b51a4cSMatthias Ringwald fout.write(' // ' + line + ' -- BEGIN\n') 70460b51a4cSMatthias Ringwald parseLines(imported_file, imported_fin, fout) 70560b51a4cSMatthias Ringwald fout.write(' // ' + line + ' -- END\n') 70660b51a4cSMatthias Ringwald except IOError as e: 70760b51a4cSMatthias Ringwald print('ERROR: Import failed. Please check path.') 70860b51a4cSMatthias Ringwald 70960b51a4cSMatthias Ringwald continue 71060b51a4cSMatthias Ringwald 71160b51a4cSMatthias Ringwald if line.startswith("#TODO"): 71260b51a4cSMatthias Ringwald print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count)) 713b165f97bSMatthias Ringwald print ("'%s'" % line) 714b165f97bSMatthias Ringwald fout.write("// " + line + '\n') 715b3fcedb9SMatthias Ringwald continue 716b3fcedb9SMatthias Ringwald 717b3fcedb9SMatthias Ringwald if len(line) == 0: 718b3fcedb9SMatthias Ringwald continue 719b3fcedb9SMatthias Ringwald 720b3fcedb9SMatthias Ringwald f = io.StringIO(line) 721b3fcedb9SMatthias Ringwald parts_list = csv.reader(f, delimiter=',', quotechar='"') 722b3fcedb9SMatthias Ringwald 723b3fcedb9SMatthias Ringwald for parts in parts_list: 724b3fcedb9SMatthias Ringwald for index, object in enumerate(parts): 725b3fcedb9SMatthias Ringwald parts[index] = object.strip().lstrip('"').rstrip('"') 726b3fcedb9SMatthias Ringwald 727b3fcedb9SMatthias Ringwald if parts[0] == 'PRIMARY_SERVICE': 728b3fcedb9SMatthias Ringwald parsePrimaryService(fout, parts) 729b3fcedb9SMatthias Ringwald continue 730b3fcedb9SMatthias Ringwald 731b3fcedb9SMatthias Ringwald if parts[0] == 'SECONDARY_SERVICE': 732b3fcedb9SMatthias Ringwald parseSecondaryService(fout, parts) 733b3fcedb9SMatthias Ringwald continue 734b3fcedb9SMatthias Ringwald 735b3fcedb9SMatthias Ringwald if parts[0] == 'INCLUDE_SERVICE': 736b3fcedb9SMatthias Ringwald parseIncludeService(fout, parts) 737b3fcedb9SMatthias Ringwald continue 738b3fcedb9SMatthias Ringwald 739b3fcedb9SMatthias Ringwald # 2803 740b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC': 741b3fcedb9SMatthias Ringwald parseCharacteristic(fout, parts) 742b3fcedb9SMatthias Ringwald continue 743b3fcedb9SMatthias Ringwald 744b3fcedb9SMatthias Ringwald # 2900 Characteristic Extended Properties 745b3fcedb9SMatthias Ringwald 746b3fcedb9SMatthias Ringwald # 2901 747b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION': 748b3fcedb9SMatthias Ringwald parseCharacteristicUserDescription(fout, parts) 749b3fcedb9SMatthias Ringwald continue 750b3fcedb9SMatthias Ringwald 751b165f97bSMatthias Ringwald 752b165f97bSMatthias Ringwald # 2902 Client Characteristic Configuration - automatically included in Characteristic if 753b3fcedb9SMatthias Ringwald # notification / indication is supported 754231a3c5dSMatthias Ringwald if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION': 755b165f97bSMatthias Ringwald continue 756b3fcedb9SMatthias Ringwald 757b3fcedb9SMatthias Ringwald # 2903 758b3fcedb9SMatthias Ringwald if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION': 759b3fcedb9SMatthias Ringwald parseServerCharacteristicConfiguration(fout, parts) 760b3fcedb9SMatthias Ringwald continue 761b3fcedb9SMatthias Ringwald 762b3fcedb9SMatthias Ringwald # 2904 763b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_FORMAT': 764b3fcedb9SMatthias Ringwald parseCharacteristicFormat(fout, parts) 765b3fcedb9SMatthias Ringwald continue 766b3fcedb9SMatthias Ringwald 767b3fcedb9SMatthias Ringwald # 2905 768b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT': 769b3fcedb9SMatthias Ringwald parseCharacteristicAggregateFormat(fout, parts) 770b3fcedb9SMatthias Ringwald continue 771b3fcedb9SMatthias Ringwald 772b3fcedb9SMatthias Ringwald # 2906 773b3fcedb9SMatthias Ringwald if parts[0] == 'VALID_RANGE': 774b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 775b3fcedb9SMatthias Ringwald continue 776b3fcedb9SMatthias Ringwald 777b3fcedb9SMatthias Ringwald # 2907 778b3fcedb9SMatthias Ringwald if parts[0] == 'EXTERNAL_REPORT_REFERENCE': 779b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 780b3fcedb9SMatthias Ringwald continue 781b3fcedb9SMatthias Ringwald 782b3fcedb9SMatthias Ringwald # 2908 783b3fcedb9SMatthias Ringwald if parts[0] == 'REPORT_REFERENCE': 784b3fcedb9SMatthias Ringwald parseReportReference(fout, parts) 785b3fcedb9SMatthias Ringwald continue 786b3fcedb9SMatthias Ringwald 787b3fcedb9SMatthias Ringwald # 2909 788b3fcedb9SMatthias Ringwald if parts[0] == 'NUMBER_OF_DIGITALS': 789b3fcedb9SMatthias Ringwald parseNumberOfDigitals(fout, parts) 790b3fcedb9SMatthias Ringwald continue 791b3fcedb9SMatthias Ringwald 792b3fcedb9SMatthias Ringwald # 290A 793b3fcedb9SMatthias Ringwald if parts[0] == 'VALUE_TRIGGER_SETTING': 794b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 795b3fcedb9SMatthias Ringwald continue 796b3fcedb9SMatthias Ringwald 797b3fcedb9SMatthias Ringwald # 290B 798b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION': 799b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 800b3fcedb9SMatthias Ringwald continue 801b3fcedb9SMatthias Ringwald 802b3fcedb9SMatthias Ringwald # 290C 803b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT': 804b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 805b3fcedb9SMatthias Ringwald continue 806b3fcedb9SMatthias Ringwald 807b3fcedb9SMatthias Ringwald # 290D 808b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING': 809b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 810b3fcedb9SMatthias Ringwald continue 811b3fcedb9SMatthias Ringwald 812b3fcedb9SMatthias Ringwald # 2906 813b3fcedb9SMatthias Ringwald if parts[0] == 'VALID_RANGE': 814b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 815b3fcedb9SMatthias Ringwald continue 816b3fcedb9SMatthias Ringwald 817b3fcedb9SMatthias Ringwald print("WARNING: unknown token: %s\n" % (parts[0])) 818b3fcedb9SMatthias Ringwald 81960b51a4cSMatthias Ringwalddef parse(fname_in, fin, fname_out, fout): 82060b51a4cSMatthias Ringwald global handle 82160b51a4cSMatthias Ringwald global total_size 82260b51a4cSMatthias Ringwald 82360b51a4cSMatthias Ringwald fout.write(header.format(fname_out, fname_in)) 82460b51a4cSMatthias Ringwald fout.write('{\n') 825fd1be25dSMatthias Ringwald write_indent(fout) 826fd1be25dSMatthias Ringwald fout.write('// ATT DB Version\n') 827fd1be25dSMatthias Ringwald write_indent(fout) 828fd1be25dSMatthias Ringwald fout.write('1,\n') 829fd1be25dSMatthias Ringwald fout.write("\n") 83060b51a4cSMatthias Ringwald 83160b51a4cSMatthias Ringwald parseLines(fname_in, fin, fout) 83260b51a4cSMatthias Ringwald 833729074c4SMatthias Ringwald serviceDefinitionComplete(fout) 834b3fcedb9SMatthias Ringwald write_indent(fout) 835b3fcedb9SMatthias Ringwald fout.write("// END\n"); 836b3fcedb9SMatthias Ringwald write_indent(fout) 837b3fcedb9SMatthias Ringwald write_16(fout,0) 838b3fcedb9SMatthias Ringwald fout.write("\n") 839b3fcedb9SMatthias Ringwald total_size = total_size + 2 840b3fcedb9SMatthias Ringwald 841b3fcedb9SMatthias Ringwald fout.write("}; // total size %u bytes \n" % total_size); 842b3fcedb9SMatthias Ringwald 843b3fcedb9SMatthias Ringwalddef listHandles(fout): 844b3fcedb9SMatthias Ringwald fout.write('\n\n') 845b3fcedb9SMatthias Ringwald fout.write('//\n') 846729074c4SMatthias Ringwald fout.write('// list service handle ranges\n') 847729074c4SMatthias Ringwald fout.write('//\n') 848729074c4SMatthias Ringwald for define in defines_for_services: 849729074c4SMatthias Ringwald fout.write(define) 850729074c4SMatthias Ringwald fout.write('\n') 851729074c4SMatthias Ringwald fout.write('\n') 852729074c4SMatthias Ringwald fout.write('//\n') 853b3fcedb9SMatthias Ringwald fout.write('// list mapping between characteristics and handles\n') 854b3fcedb9SMatthias Ringwald fout.write('//\n') 855729074c4SMatthias Ringwald for define in defines_for_characteristics: 856b3fcedb9SMatthias Ringwald fout.write(define) 857b3fcedb9SMatthias Ringwald fout.write('\n') 858b3fcedb9SMatthias Ringwald 859*dbb3997aSMilanka Ringwalddef getFile( fileName ): 860*dbb3997aSMilanka Ringwald inc = args.I 861*dbb3997aSMilanka Ringwald for d in inc: 862*dbb3997aSMilanka Ringwald fullFile = d[0] + fileName 863*dbb3997aSMilanka Ringwald print("test %s" % fullFile) 864*dbb3997aSMilanka Ringwald if os.path.isfile( fullFile ) == True: 865*dbb3997aSMilanka Ringwald return fullFile 866*dbb3997aSMilanka Ringwald print ("'{0}' not found".format( fileName )) 867*dbb3997aSMilanka Ringwald print ("Include paths: %s" % ", ".join(inc)) 868*dbb3997aSMilanka Ringwald exit(-1) 869*dbb3997aSMilanka Ringwald 870*dbb3997aSMilanka Ringwald 871*dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') 872*dbb3997aSMilanka Ringwalddefault_includes = [ btstack_root + '/src/', btstack_root + '/src/ble/gatt-service/'] 873*dbb3997aSMilanka Ringwald 874*dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack') 875*dbb3997aSMilanka Ringwald 876*dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes', 877*dbb3997aSMilanka Ringwald help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes)) 878*dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str, 879*dbb3997aSMilanka Ringwald help='gatt file to be compiled') 880*dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str, 881*dbb3997aSMilanka Ringwald help='header file to be generated') 882*dbb3997aSMilanka Ringwald 883*dbb3997aSMilanka Ringwaldargs = parser.parse_args() 884*dbb3997aSMilanka Ringwald 885*dbb3997aSMilanka Ringwald# append default include paths 886*dbb3997aSMilanka Ringwaldif args.I == None: 887*dbb3997aSMilanka Ringwald args.I = [] 888*dbb3997aSMilanka Ringwaldfor d in default_includes: 889*dbb3997aSMilanka Ringwald args.I.append([d]) 890*dbb3997aSMilanka Ringwald 891b3fcedb9SMatthias Ringwaldtry: 892b165f97bSMatthias Ringwald # read defines from bluetooth_gatt.h 893*dbb3997aSMilanka Ringwald gen_path = getFile( 'bluetooth_gatt.h' ) 894b165f97bSMatthias Ringwald bluetooth_gatt = read_defines(gen_path) 895b165f97bSMatthias Ringwald 896*dbb3997aSMilanka Ringwald filename = args.hfile 897*dbb3997aSMilanka Ringwald fin = codecs.open (args.gattfile, encoding='utf-8') 898b3fcedb9SMatthias Ringwald fout = open (filename, 'w') 899*dbb3997aSMilanka Ringwald parse(args.gattfile, fin, filename, fout) 900b3fcedb9SMatthias Ringwald listHandles(fout) 901b3fcedb9SMatthias Ringwald fout.close() 902b165f97bSMatthias Ringwald print('Created %s' % filename) 903b3fcedb9SMatthias Ringwald 904b3fcedb9SMatthias Ringwaldexcept IOError as e: 905e22a2612SMatthias Ringwald 906b3fcedb9SMatthias Ringwald print(usage) 907b3fcedb9SMatthias Ringwald sys.exit(1) 908b3fcedb9SMatthias Ringwald 909b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n') 910