1*4783d256SMatheus Garbelini#!/usr/bin/env python3 2b3fcedb9SMatthias Ringwald# 3dbb3997aSMilanka Ringwald# BLE GATT configuration generator for use with BTstack 4043f8832SMatthias Ringwald# Copyright 2019 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 10043f8832SMatthias Ringwald# dependencies: 11043f8832SMatthias Ringwald# - pip3 install pycryptodomex 12043f8832SMatthias Ringwald 13b3fcedb9SMatthias Ringwaldimport codecs 14b165f97bSMatthias Ringwaldimport csv 15b165f97bSMatthias Ringwaldimport io 16b165f97bSMatthias Ringwaldimport os 17b165f97bSMatthias Ringwaldimport re 18b165f97bSMatthias Ringwaldimport string 1960b51a4cSMatthias Ringwaldimport sys 20dbb3997aSMilanka Ringwaldimport argparse 21285653b2SMatthias Ringwaldimport tempfile 22b3fcedb9SMatthias Ringwald 23043f8832SMatthias Ringwald# try to import Cryptodome 24043f8832SMatthias Ringwaldtry: 25379d3aceSMatthias Ringwald from Cryptodome.Cipher import AES 26043f8832SMatthias Ringwald from Cryptodome.Hash import CMAC 27043f8832SMatthias Ringwald have_crypto = True 28043f8832SMatthias Ringwaldexcept ImportError: 29043f8832SMatthias Ringwald have_crypto = False 307490175eSMatthias Ringwald print("\n[!] PyCryptodome required to calculate GATT Database Hash but not installed (using random value instead)") 31043f8832SMatthias Ringwald print("[!] Please install PyCryptodome, e.g. 'pip install pycryptodomex'\n") 32043f8832SMatthias Ringwald 33b3fcedb9SMatthias Ringwaldheader = ''' 34b3fcedb9SMatthias Ringwald// {0} generated from {1} for BTstack 357050bf34SMatthias Ringwald// it needs to be regenerated when the .gatt file is updated. 367050bf34SMatthias Ringwald 377050bf34SMatthias Ringwald// To generate {0}: 387050bf34SMatthias Ringwald// {2} {1} {0} 397050bf34SMatthias Ringwald 40fd1be25dSMatthias Ringwald// att db format version 1 41b3fcedb9SMatthias Ringwald 42fd1be25dSMatthias Ringwald// binary attribute representation: 43fd1be25dSMatthias Ringwald// - size in bytes (16), flags(16), handle (16), uuid (16/128), value(...) 44b3fcedb9SMatthias Ringwald 45b3fcedb9SMatthias Ringwald#include <stdint.h> 46b3fcedb9SMatthias Ringwald 47b3fcedb9SMatthias Ringwaldconst uint8_t profile_data[] = 48b3fcedb9SMatthias Ringwald''' 49b3fcedb9SMatthias Ringwald 50b3fcedb9SMatthias Ringwaldprint(''' 51dbb3997aSMilanka RingwaldBLE configuration generator for use with BTstack 52dbb3997aSMilanka RingwaldCopyright 2018 BlueKitchen GmbH 53b3fcedb9SMatthias Ringwald''') 54b3fcedb9SMatthias Ringwald 55b3fcedb9SMatthias Ringwaldassigned_uuids = { 56b3fcedb9SMatthias Ringwald 'GAP_SERVICE' : 0x1800, 57b3fcedb9SMatthias Ringwald 'GATT_SERVICE' : 0x1801, 58b3fcedb9SMatthias Ringwald 'GAP_DEVICE_NAME' : 0x2a00, 59b3fcedb9SMatthias Ringwald 'GAP_APPEARANCE' : 0x2a01, 60b3fcedb9SMatthias Ringwald 'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02, 61b3fcedb9SMatthias Ringwald 'GAP_RECONNECTION_ADDRESS' : 0x2A03, 62b3fcedb9SMatthias Ringwald 'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04, 63b3fcedb9SMatthias Ringwald 'GATT_SERVICE_CHANGED' : 0x2a05, 64043f8832SMatthias Ringwald 'GATT_DATABASE_HASH' : 0x2b2a 65b3fcedb9SMatthias Ringwald} 66b3fcedb9SMatthias Ringwald 67e72176f8SMatthias Ringwaldsecurity_permsission = ['ANYBODY','ENCRYPTED', 'AUTHENTICATED', 'AUTHORIZED', 'AUTHENTICATED_SC'] 68d7ec1d24SMatthias Ringwald 69b3fcedb9SMatthias Ringwaldproperty_flags = { 70eb6072adSMatthias Ringwald # GATT Characteristic Properties 71b3fcedb9SMatthias Ringwald 'BROADCAST' : 0x01, 72b3fcedb9SMatthias Ringwald 'READ' : 0x02, 73b3fcedb9SMatthias Ringwald 'WRITE_WITHOUT_RESPONSE' : 0x04, 74b3fcedb9SMatthias Ringwald 'WRITE' : 0x08, 75b3fcedb9SMatthias Ringwald 'NOTIFY': 0x10, 76b3fcedb9SMatthias Ringwald 'INDICATE' : 0x20, 77b3fcedb9SMatthias Ringwald 'AUTHENTICATED_SIGNED_WRITE' : 0x40, 78b3fcedb9SMatthias Ringwald 'EXTENDED_PROPERTIES' : 0x80, 79b3fcedb9SMatthias Ringwald # custom BTstack extension 80b3fcedb9SMatthias Ringwald 'DYNAMIC': 0x100, 81b3fcedb9SMatthias Ringwald 'LONG_UUID': 0x200, 82e22a2612SMatthias Ringwald 83e22a2612SMatthias Ringwald # read permissions 84e22a2612SMatthias Ringwald 'READ_PERMISSION_BIT_0': 0x400, 85e22a2612SMatthias Ringwald 'READ_PERMISSION_BIT_1': 0x800, 86e22a2612SMatthias Ringwald 87e22a2612SMatthias Ringwald # 88b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_7': 0x6000, 89b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_8': 0x7000, 90b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_9': 0x8000, 91b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_10': 0x9000, 92b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_11': 0xa000, 93b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_12': 0xb000, 94b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_13': 0xc000, 95b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_14': 0xd000, 96b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_15': 0xe000, 97b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_16': 0xf000, 98e22a2612SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_MASK': 0xf000, 99eb6072adSMatthias Ringwald 100b3fcedb9SMatthias Ringwald # only used by gatt compiler >= 0xffff 101b3fcedb9SMatthias Ringwald # Extended Properties 102e72176f8SMatthias Ringwald 'RELIABLE_WRITE': 0x00010000, 103e72176f8SMatthias Ringwald 'AUTHENTICATION_REQUIRED': 0x00020000, 104e72176f8SMatthias Ringwald 'AUTHORIZATION_REQUIRED': 0x00040000, 105e72176f8SMatthias Ringwald 'READ_ANYBODY': 0x00080000, 106e72176f8SMatthias Ringwald 'READ_ENCRYPTED': 0x00100000, 107e72176f8SMatthias Ringwald 'READ_AUTHENTICATED': 0x00200000, 108e72176f8SMatthias Ringwald 'READ_AUTHENTICATED_SC': 0x00400000, 109e72176f8SMatthias Ringwald 'READ_AUTHORIZED': 0x00800000, 110e72176f8SMatthias Ringwald 'WRITE_ANYBODY': 0x01000000, 111e72176f8SMatthias Ringwald 'WRITE_ENCRYPTED': 0x02000000, 112e72176f8SMatthias Ringwald 'WRITE_AUTHENTICATED': 0x04000000, 113e72176f8SMatthias Ringwald 'WRITE_AUTHENTICATED_SC': 0x08000000, 114e72176f8SMatthias Ringwald 'WRITE_AUTHORIZED': 0x10000000, 115eb6072adSMatthias Ringwald 116eb6072adSMatthias Ringwald # Broadcast, Notify, Indicate, Extended Properties are only used to describe a GATT Characteristic, but are free to use with att_db 117e72176f8SMatthias Ringwald # - write permissions 118e22a2612SMatthias Ringwald 'WRITE_PERMISSION_BIT_0': 0x01, 119e22a2612SMatthias Ringwald 'WRITE_PERMISSION_BIT_1': 0x10, 120e72176f8SMatthias Ringwald # - SC required 121e72176f8SMatthias Ringwald 'READ_PERMISSION_SC': 0x20, 122e72176f8SMatthias Ringwald 'WRITE_PERMISSION_SC': 0x80, 123b3fcedb9SMatthias Ringwald} 124b3fcedb9SMatthias Ringwald 125b3fcedb9SMatthias Ringwaldservices = dict() 126b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict() 127b3fcedb9SMatthias Ringwaldpresentation_formats = dict() 128b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = "" 129b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0 130b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = "" 131729074c4SMatthias Ringwalddefines_for_characteristics = [] 132729074c4SMatthias Ringwalddefines_for_services = [] 13378b65b0aSMatthias Ringwaldinclude_paths = [] 134043f8832SMatthias Ringwalddatabase_hash_message = bytearray() 135b3fcedb9SMatthias Ringwald 136b3fcedb9SMatthias Ringwaldhandle = 1 137b3fcedb9SMatthias Ringwaldtotal_size = 0 138b3fcedb9SMatthias Ringwald 139043f8832SMatthias Ringwalddef aes_cmac(key, n): 140043f8832SMatthias Ringwald if have_crypto: 141043f8832SMatthias Ringwald cobj = CMAC.new(key, ciphermod=AES) 142043f8832SMatthias Ringwald cobj.update(n) 143043f8832SMatthias Ringwald return cobj.digest() 144043f8832SMatthias Ringwald else: 1457490175eSMatthias Ringwald # return random value 1467490175eSMatthias Ringwald return os.urandom(16) 147043f8832SMatthias Ringwald 148b165f97bSMatthias Ringwalddef read_defines(infile): 149b165f97bSMatthias Ringwald defines = dict() 150b165f97bSMatthias Ringwald with open (infile, 'rt') as fin: 151b165f97bSMatthias Ringwald for line in fin: 152b165f97bSMatthias Ringwald parts = re.match('#define\s+(\w+)\s+(\w+)',line) 153b165f97bSMatthias Ringwald if parts and len(parts.groups()) == 2: 154b165f97bSMatthias Ringwald (key, value) = parts.groups() 155b165f97bSMatthias Ringwald defines[key] = int(value, 16) 156b165f97bSMatthias Ringwald return defines 157b165f97bSMatthias Ringwald 158b3fcedb9SMatthias Ringwalddef keyForUUID(uuid): 159b3fcedb9SMatthias Ringwald keyUUID = "" 160b3fcedb9SMatthias Ringwald for i in uuid: 161b3fcedb9SMatthias Ringwald keyUUID += "%02x" % i 162b3fcedb9SMatthias Ringwald return keyUUID 163b3fcedb9SMatthias Ringwald 164b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid): 165b3fcedb9SMatthias Ringwald return uuid.replace('-', '_') 166b3fcedb9SMatthias Ringwald 167b3fcedb9SMatthias Ringwalddef twoByteLEFor(value): 168b3fcedb9SMatthias Ringwald return [ (value & 0xff), (value >> 8)] 169b3fcedb9SMatthias Ringwald 170b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text): 171b3fcedb9SMatthias 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): 172b3fcedb9SMatthias Ringwald return True 173b3fcedb9SMatthias Ringwald return False 174b3fcedb9SMatthias Ringwald 175b3fcedb9SMatthias Ringwalddef parseUUID128(uuid): 176b3fcedb9SMatthias 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) 177b3fcedb9SMatthias Ringwald uuid_bytes = [] 178b3fcedb9SMatthias Ringwald for i in range(8, 0, -1): 179b3fcedb9SMatthias Ringwald uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16)) 180b3fcedb9SMatthias Ringwald return uuid_bytes 181b3fcedb9SMatthias Ringwald 182b3fcedb9SMatthias Ringwalddef parseUUID(uuid): 183b3fcedb9SMatthias Ringwald if uuid in assigned_uuids: 184b3fcedb9SMatthias Ringwald return twoByteLEFor(assigned_uuids[uuid]) 185b165f97bSMatthias Ringwald uuid_upper = uuid.upper().replace('.','_') 186b165f97bSMatthias Ringwald if uuid_upper in bluetooth_gatt: 187b165f97bSMatthias Ringwald return twoByteLEFor(bluetooth_gatt[uuid_upper]) 188b3fcedb9SMatthias Ringwald if is_128bit_uuid(uuid): 189b3fcedb9SMatthias Ringwald return parseUUID128(uuid) 190b3fcedb9SMatthias Ringwald uuidInt = int(uuid, 16) 191b3fcedb9SMatthias Ringwald return twoByteLEFor(uuidInt) 192b3fcedb9SMatthias Ringwald 193b3fcedb9SMatthias Ringwalddef parseProperties(properties): 194b3fcedb9SMatthias Ringwald value = 0 195b3fcedb9SMatthias Ringwald parts = properties.split("|") 196b3fcedb9SMatthias Ringwald for property in parts: 197b3fcedb9SMatthias Ringwald property = property.strip() 198b3fcedb9SMatthias Ringwald if property in property_flags: 199b3fcedb9SMatthias Ringwald value |= property_flags[property] 200b3fcedb9SMatthias Ringwald else: 201b3fcedb9SMatthias Ringwald print("WARNING: property %s undefined" % (property)) 202e22a2612SMatthias Ringwald 203e22a2612SMatthias Ringwald return value; 204e22a2612SMatthias Ringwald 205e22a2612SMatthias Ringwalddef gatt_characteristic_properties(properties): 206e22a2612SMatthias Ringwald return properties & 0xff 207e22a2612SMatthias Ringwald 208e22a2612SMatthias Ringwalddef att_flags(properties): 209e72176f8SMatthias Ringwald # drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Properties (0x80) - not used for flags 210e72176f8SMatthias Ringwald properties &= 0xffffff4e 211e22a2612SMatthias Ringwald 212e22a2612SMatthias Ringwald # rw permissions distinct 213e22a2612SMatthias Ringwald distinct_permissions_used = properties & ( 214e22a2612SMatthias Ringwald property_flags['READ_AUTHORIZED'] | 215e72176f8SMatthias Ringwald property_flags['READ_AUTHENTICATED_SC'] | 216e22a2612SMatthias Ringwald property_flags['READ_AUTHENTICATED'] | 217e22a2612SMatthias Ringwald property_flags['READ_ENCRYPTED'] | 218e22a2612SMatthias Ringwald property_flags['READ_ANYBODY'] | 219e22a2612SMatthias Ringwald property_flags['WRITE_AUTHORIZED'] | 220e22a2612SMatthias Ringwald property_flags['WRITE_AUTHENTICATED'] | 221e72176f8SMatthias Ringwald property_flags['WRITE_AUTHENTICATED_SC'] | 222e22a2612SMatthias Ringwald property_flags['WRITE_ENCRYPTED'] | 223e22a2612SMatthias Ringwald property_flags['WRITE_ANYBODY'] 224e22a2612SMatthias Ringwald ) != 0 225e22a2612SMatthias Ringwald 226e22a2612SMatthias Ringwald # post process properties 227e22a2612SMatthias Ringwald encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0 228e22a2612SMatthias Ringwald 229d7ec1d24SMatthias Ringwald # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted 230e22a2612SMatthias Ringwald if encryption_key_size_specified and not distinct_permissions_used: 231e22a2612SMatthias Ringwald properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED'] 232e22a2612SMatthias Ringwald 233d7ec1d24SMatthias Ringwald # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated 234d7ec1d24SMatthias Ringwald if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used: 235d7ec1d24SMatthias Ringwald properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED'] 236d7ec1d24SMatthias Ringwald 237d7ec1d24SMatthias Ringwald # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized 238d7ec1d24SMatthias Ringwald if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used: 239d7ec1d24SMatthias Ringwald properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED'] 240d7ec1d24SMatthias Ringwald 241d7ec1d24SMatthias Ringwald # determine read/write security requirements 242d7ec1d24SMatthias Ringwald read_security_level = 0 243d7ec1d24SMatthias Ringwald write_security_level = 0 244e72176f8SMatthias Ringwald read_requires_sc = False 245e72176f8SMatthias Ringwald write_requires_sc = False 246e22a2612SMatthias Ringwald if properties & property_flags['READ_AUTHORIZED']: 247d7ec1d24SMatthias Ringwald read_security_level = 3 248e22a2612SMatthias Ringwald elif properties & property_flags['READ_AUTHENTICATED']: 249d7ec1d24SMatthias Ringwald read_security_level = 2 250e72176f8SMatthias Ringwald elif properties & property_flags['READ_AUTHENTICATED_SC']: 251e72176f8SMatthias Ringwald read_security_level = 2 252e72176f8SMatthias Ringwald read_requires_sc = True 253e22a2612SMatthias Ringwald elif properties & property_flags['READ_ENCRYPTED']: 254d7ec1d24SMatthias Ringwald read_security_level = 1 255e22a2612SMatthias Ringwald if properties & property_flags['WRITE_AUTHORIZED']: 256d7ec1d24SMatthias Ringwald write_security_level = 3 257e22a2612SMatthias Ringwald elif properties & property_flags['WRITE_AUTHENTICATED']: 258d7ec1d24SMatthias Ringwald write_security_level = 2 259e72176f8SMatthias Ringwald elif properties & property_flags['WRITE_AUTHENTICATED_SC']: 260e72176f8SMatthias Ringwald write_security_level = 2 261e72176f8SMatthias Ringwald write_requires_sc = True 262e22a2612SMatthias Ringwald elif properties & property_flags['WRITE_ENCRYPTED']: 263d7ec1d24SMatthias Ringwald write_security_level = 1 264d7ec1d24SMatthias Ringwald 265d7ec1d24SMatthias Ringwald # map security requirements to flags 266d7ec1d24SMatthias Ringwald if read_security_level & 2: 267d7ec1d24SMatthias Ringwald properties |= property_flags['READ_PERMISSION_BIT_1'] 268d7ec1d24SMatthias Ringwald if read_security_level & 1: 269d7ec1d24SMatthias Ringwald properties |= property_flags['READ_PERMISSION_BIT_0'] 270e72176f8SMatthias Ringwald if read_requires_sc: 271e72176f8SMatthias Ringwald properties |= property_flags['READ_PERMISSION_SC'] 272d7ec1d24SMatthias Ringwald if write_security_level & 2: 273d7ec1d24SMatthias Ringwald properties |= property_flags['WRITE_PERMISSION_BIT_1'] 274d7ec1d24SMatthias Ringwald if write_security_level & 1: 275e22a2612SMatthias Ringwald properties |= property_flags['WRITE_PERMISSION_BIT_0'] 276e72176f8SMatthias Ringwald if write_requires_sc: 277e72176f8SMatthias Ringwald properties |= property_flags['WRITE_PERMISSION_SC'] 278e22a2612SMatthias Ringwald 279e22a2612SMatthias Ringwald return properties 280e22a2612SMatthias Ringwald 281d7ec1d24SMatthias Ringwalddef write_permissions_and_key_size_flags_from_properties(properties): 282e22a2612SMatthias Ringwald return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1']) 283b3fcedb9SMatthias Ringwald 284b3fcedb9SMatthias Ringwalddef write_8(fout, value): 285b3fcedb9SMatthias Ringwald fout.write( "0x%02x, " % (value & 0xff)) 286b3fcedb9SMatthias Ringwald 287b3fcedb9SMatthias Ringwalddef write_16(fout, value): 288b3fcedb9SMatthias Ringwald fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff)) 289b3fcedb9SMatthias Ringwald 290285653b2SMatthias Ringwalddef write_uuid(fout, uuid): 291b3fcedb9SMatthias Ringwald for byte in uuid: 292b3fcedb9SMatthias Ringwald fout.write( "0x%02x, " % byte) 293b3fcedb9SMatthias Ringwald 294b3fcedb9SMatthias Ringwalddef write_string(fout, text): 295b3fcedb9SMatthias Ringwald for l in text.lstrip('"').rstrip('"'): 296b3fcedb9SMatthias Ringwald write_8(fout, ord(l)) 297b3fcedb9SMatthias Ringwald 298b3fcedb9SMatthias Ringwalddef write_sequence(fout, text): 299b3fcedb9SMatthias Ringwald parts = text.split() 300b3fcedb9SMatthias Ringwald for part in parts: 301b3fcedb9SMatthias Ringwald fout.write("0x%s, " % (part.strip())) 302b3fcedb9SMatthias Ringwald 303043f8832SMatthias Ringwalddef write_database_hash(fout): 304043f8832SMatthias Ringwald fout.write("THE-DATABASE-HASH") 305043f8832SMatthias Ringwald 306b3fcedb9SMatthias Ringwalddef write_indent(fout): 307b3fcedb9SMatthias Ringwald fout.write(" ") 308b3fcedb9SMatthias Ringwald 309d7ec1d24SMatthias Ringwalddef read_permissions_from_flags(flags): 310d7ec1d24SMatthias Ringwald permissions = 0 311d7ec1d24SMatthias Ringwald if flags & property_flags['READ_PERMISSION_BIT_0']: 312d7ec1d24SMatthias Ringwald permissions |= 1 313d7ec1d24SMatthias Ringwald if flags & property_flags['READ_PERMISSION_BIT_1']: 314d7ec1d24SMatthias Ringwald permissions |= 2 315e72176f8SMatthias Ringwald if flags & property_flags['READ_PERMISSION_SC'] and permissions == 2: 316e72176f8SMatthias Ringwald permissions = 4 317d7ec1d24SMatthias Ringwald return permissions 318d7ec1d24SMatthias Ringwald 319d7ec1d24SMatthias Ringwalddef write_permissions_from_flags(flags): 320d7ec1d24SMatthias Ringwald permissions = 0 321d7ec1d24SMatthias Ringwald if flags & property_flags['WRITE_PERMISSION_BIT_0']: 322d7ec1d24SMatthias Ringwald permissions |= 1 323d7ec1d24SMatthias Ringwald if flags & property_flags['WRITE_PERMISSION_BIT_1']: 324d7ec1d24SMatthias Ringwald permissions |= 2 325e72176f8SMatthias Ringwald if flags & property_flags['WRITE_PERMISSION_SC'] and permissions == 2: 326e72176f8SMatthias Ringwald permissions = 4 327d7ec1d24SMatthias Ringwald return permissions 328d7ec1d24SMatthias Ringwald 329d7ec1d24SMatthias Ringwalddef encryption_key_size_from_flags(flags): 330d7ec1d24SMatthias Ringwald encryption_key_size = (flags & 0xf000) >> 12 331d7ec1d24SMatthias Ringwald if encryption_key_size > 0: 332d7ec1d24SMatthias Ringwald encryption_key_size += 1 333d7ec1d24SMatthias Ringwald return encryption_key_size 334d7ec1d24SMatthias Ringwald 335b3fcedb9SMatthias Ringwalddef is_string(text): 336b3fcedb9SMatthias Ringwald for item in text.split(" "): 337b3fcedb9SMatthias Ringwald if not all(c in string.hexdigits for c in item): 338b3fcedb9SMatthias Ringwald return True 339b3fcedb9SMatthias Ringwald return False 340b3fcedb9SMatthias Ringwald 341b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties): 342b3fcedb9SMatthias Ringwald return properties & (property_flags['NOTIFY'] | property_flags['INDICATE']) 343b3fcedb9SMatthias Ringwald 344729074c4SMatthias Ringwalddef serviceDefinitionComplete(fout): 345729074c4SMatthias Ringwald global services 346729074c4SMatthias Ringwald if current_service_uuid_string: 347729074c4SMatthias Ringwald fout.write("\n") 348729074c4SMatthias Ringwald # print("append service %s = [%d, %d]" % (current_characteristic_uuid_string, current_service_start_handle, handle-1)) 349729074c4SMatthias Ringwald defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle)) 350729074c4SMatthias Ringwald defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1)) 351729074c4SMatthias Ringwald services[current_service_uuid_string] = [current_service_start_handle, handle-1] 352729074c4SMatthias Ringwald 353d7ec1d24SMatthias Ringwalddef dump_flags(fout, flags): 354d7ec1d24SMatthias Ringwald global security_permsission 355d7ec1d24SMatthias Ringwald encryption_key_size = encryption_key_size_from_flags(flags) 356d7ec1d24SMatthias Ringwald read_permissions = security_permsission[read_permissions_from_flags(flags)] 357d7ec1d24SMatthias Ringwald write_permissions = security_permsission[write_permissions_from_flags(flags)] 358d7ec1d24SMatthias Ringwald write_indent(fout) 359d7ec1d24SMatthias Ringwald fout.write('// ') 360d7ec1d24SMatthias Ringwald first = 1 361d7ec1d24SMatthias Ringwald if flags & property_flags['READ']: 362d7ec1d24SMatthias Ringwald fout.write('READ_%s' % read_permissions) 363d7ec1d24SMatthias Ringwald first = 0 364d7ec1d24SMatthias Ringwald if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']): 365d7ec1d24SMatthias Ringwald if not first: 366d7ec1d24SMatthias Ringwald fout.write(', ') 367d7ec1d24SMatthias Ringwald first = 0 368d7ec1d24SMatthias Ringwald fout.write('WRITE_%s' % write_permissions) 369d7ec1d24SMatthias Ringwald if encryption_key_size > 0: 370d7ec1d24SMatthias Ringwald if not first: 371d7ec1d24SMatthias Ringwald fout.write(', ') 372d7ec1d24SMatthias Ringwald first = 0 373d7ec1d24SMatthias Ringwald fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size) 374d7ec1d24SMatthias Ringwald fout.write('\n') 375d7ec1d24SMatthias Ringwald 376043f8832SMatthias Ringwalddef database_hash_append_uint8(value): 377043f8832SMatthias Ringwald global database_hash_message 378043f8832SMatthias Ringwald database_hash_message.append(value) 379043f8832SMatthias Ringwald 380043f8832SMatthias Ringwalddef database_hash_append_uint16(value): 381043f8832SMatthias Ringwald global database_hash_message 382043f8832SMatthias Ringwald database_hash_append_uint8(value & 0xff) 383043f8832SMatthias Ringwald database_hash_append_uint8((value >> 8) & 0xff) 384043f8832SMatthias Ringwald 385043f8832SMatthias Ringwalddef database_hash_append_value(value): 386043f8832SMatthias Ringwald global database_hash_message 387043f8832SMatthias Ringwald for byte in value: 388043f8832SMatthias Ringwald database_hash_append_uint8(byte) 389043f8832SMatthias Ringwald 390b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type): 391b3fcedb9SMatthias Ringwald global handle 392b3fcedb9SMatthias Ringwald global total_size 393b3fcedb9SMatthias Ringwald global current_service_uuid_string 394b3fcedb9SMatthias Ringwald global current_service_start_handle 395b3fcedb9SMatthias Ringwald 396729074c4SMatthias Ringwald serviceDefinitionComplete(fout) 397b3fcedb9SMatthias Ringwald 398d7ec1d24SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 399b3fcedb9SMatthias Ringwald 400b3fcedb9SMatthias Ringwald write_indent(fout) 401b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 402b3fcedb9SMatthias Ringwald 403b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 404b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 405b3fcedb9SMatthias Ringwald 406b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + uuid_size + 2 407b3fcedb9SMatthias Ringwald 408b3fcedb9SMatthias Ringwald if service_type == 0x2802: 409b3fcedb9SMatthias Ringwald size += 4 410b3fcedb9SMatthias Ringwald 411b3fcedb9SMatthias Ringwald write_indent(fout) 412b3fcedb9SMatthias Ringwald write_16(fout, size) 413d7ec1d24SMatthias Ringwald write_16(fout, read_only_anybody_flags) 414b3fcedb9SMatthias Ringwald write_16(fout, handle) 415b3fcedb9SMatthias Ringwald write_16(fout, service_type) 416285653b2SMatthias Ringwald write_uuid(fout, uuid) 417b3fcedb9SMatthias Ringwald fout.write("\n") 418b3fcedb9SMatthias Ringwald 419043f8832SMatthias Ringwald database_hash_append_uint16(handle) 420043f8832SMatthias Ringwald database_hash_append_uint16(service_type) 421043f8832SMatthias Ringwald database_hash_append_value(uuid) 422043f8832SMatthias Ringwald 423729074c4SMatthias Ringwald current_service_uuid_string = c_string_for_uuid(parts[1]) 424b3fcedb9SMatthias Ringwald current_service_start_handle = handle 425b3fcedb9SMatthias Ringwald handle = handle + 1 426b3fcedb9SMatthias Ringwald total_size = total_size + size 427b3fcedb9SMatthias Ringwald 428b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts): 429b3fcedb9SMatthias Ringwald parseService(fout, parts, 0x2800) 430b3fcedb9SMatthias Ringwald 431b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts): 432b3fcedb9SMatthias Ringwald parseService(fout, parts, 0x2801) 433b3fcedb9SMatthias Ringwald 434b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts): 435b3fcedb9SMatthias Ringwald global handle 436b3fcedb9SMatthias Ringwald global total_size 437b3fcedb9SMatthias Ringwald 438e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 439b3fcedb9SMatthias Ringwald 440b3fcedb9SMatthias Ringwald write_indent(fout) 441b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 442b3fcedb9SMatthias Ringwald 443b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 444b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 445b3fcedb9SMatthias Ringwald if uuid_size > 2: 446b3fcedb9SMatthias Ringwald uuid_size = 0 447729074c4SMatthias Ringwald # print("Include Service ", c_string_for_uuid(uuid)) 448b3fcedb9SMatthias Ringwald 449b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 4 + uuid_size 450b3fcedb9SMatthias Ringwald 451729074c4SMatthias Ringwald keyUUID = c_string_for_uuid(parts[1]) 452b3fcedb9SMatthias Ringwald 453b3fcedb9SMatthias Ringwald write_indent(fout) 454b3fcedb9SMatthias Ringwald write_16(fout, size) 455e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 456b3fcedb9SMatthias Ringwald write_16(fout, handle) 457b3fcedb9SMatthias Ringwald write_16(fout, 0x2802) 458b3fcedb9SMatthias Ringwald write_16(fout, services[keyUUID][0]) 459b3fcedb9SMatthias Ringwald write_16(fout, services[keyUUID][1]) 460b3fcedb9SMatthias Ringwald if uuid_size > 0: 461285653b2SMatthias Ringwald write_uuid(fout, uuid) 462b3fcedb9SMatthias Ringwald fout.write("\n") 463b3fcedb9SMatthias Ringwald 464043f8832SMatthias Ringwald database_hash_append_uint16(handle) 465043f8832SMatthias Ringwald database_hash_append_uint16(0x2802) 466043f8832SMatthias Ringwald database_hash_append_uint16(services[keyUUID][0]) 467043f8832SMatthias Ringwald database_hash_append_uint16(services[keyUUID][1]) 468043f8832SMatthias Ringwald if uuid_size > 0: 469043f8832SMatthias Ringwald database_hash_append_value(uuid) 470043f8832SMatthias Ringwald 471b3fcedb9SMatthias Ringwald handle = handle + 1 472b3fcedb9SMatthias Ringwald total_size = total_size + size 473b3fcedb9SMatthias Ringwald 474b3fcedb9SMatthias Ringwald 475b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts): 476b3fcedb9SMatthias Ringwald global handle 477b3fcedb9SMatthias Ringwald global total_size 478b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 479b3fcedb9SMatthias Ringwald global characteristic_indices 480b3fcedb9SMatthias Ringwald 481e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 482b3fcedb9SMatthias Ringwald 483b3fcedb9SMatthias Ringwald # enumerate characteristics with same UUID, using optional name tag if available 484b3fcedb9SMatthias Ringwald current_characteristic_uuid_string = c_string_for_uuid(parts[1]); 485b3fcedb9SMatthias Ringwald index = 1 486b3fcedb9SMatthias Ringwald if current_characteristic_uuid_string in characteristic_indices: 487b3fcedb9SMatthias Ringwald index = characteristic_indices[current_characteristic_uuid_string] + 1 488b3fcedb9SMatthias Ringwald characteristic_indices[current_characteristic_uuid_string] = index 489b3fcedb9SMatthias Ringwald if len(parts) > 4: 490b3fcedb9SMatthias Ringwald current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_') 491b3fcedb9SMatthias Ringwald else: 492b3fcedb9SMatthias Ringwald current_characteristic_uuid_string += ('_%02x' % index) 493b3fcedb9SMatthias Ringwald 494b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 495b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 496b3fcedb9SMatthias Ringwald properties = parseProperties(parts[2]) 497b3fcedb9SMatthias Ringwald value = ', '.join([str(x) for x in parts[3:]]) 498b3fcedb9SMatthias Ringwald 499b3fcedb9SMatthias Ringwald # reliable writes is defined in an extended properties 500b3fcedb9SMatthias Ringwald if (properties & property_flags['RELIABLE_WRITE']): 501b3fcedb9SMatthias Ringwald properties = properties | property_flags['EXTENDED_PROPERTIES'] 502b3fcedb9SMatthias Ringwald 503b3fcedb9SMatthias Ringwald write_indent(fout) 504b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3]))) 505b3fcedb9SMatthias Ringwald 506e22a2612SMatthias Ringwald 507e22a2612SMatthias Ringwald characteristic_properties = gatt_characteristic_properties(properties) 508b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + (1+2+uuid_size) 509b3fcedb9SMatthias Ringwald write_indent(fout) 510b3fcedb9SMatthias Ringwald write_16(fout, size) 511e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 512b3fcedb9SMatthias Ringwald write_16(fout, handle) 513b3fcedb9SMatthias Ringwald write_16(fout, 0x2803) 514e22a2612SMatthias Ringwald write_8(fout, characteristic_properties) 515b3fcedb9SMatthias Ringwald write_16(fout, handle+1) 516285653b2SMatthias Ringwald write_uuid(fout, uuid) 517b3fcedb9SMatthias Ringwald fout.write("\n") 518b3fcedb9SMatthias Ringwald handle = handle + 1 519b3fcedb9SMatthias Ringwald total_size = total_size + size 520b3fcedb9SMatthias Ringwald 521043f8832SMatthias Ringwald database_hash_append_uint16(handle) 522043f8832SMatthias Ringwald database_hash_append_uint16(0x2803) 523043f8832SMatthias Ringwald database_hash_append_uint8(characteristic_properties) 524043f8832SMatthias Ringwald database_hash_append_uint16(handle+1) 525043f8832SMatthias Ringwald database_hash_append_value(uuid) 526043f8832SMatthias Ringwald 527043f8832SMatthias Ringwald uuid_is_database_hash = len(uuid) == 2 and uuid[0] == 0x2a and uuid[1] == 0x2b 528043f8832SMatthias Ringwald 529b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + uuid_size 530043f8832SMatthias Ringwald if uuid_is_database_hash: 531043f8832SMatthias Ringwald size += 16 532043f8832SMatthias Ringwald else: 533b3fcedb9SMatthias Ringwald if is_string(value): 534b3fcedb9SMatthias Ringwald size = size + len(value) 535b3fcedb9SMatthias Ringwald else: 536b3fcedb9SMatthias Ringwald size = size + len(value.split()) 537b3fcedb9SMatthias Ringwald 538e22a2612SMatthias Ringwald value_flags = att_flags(properties) 5398ea3236cSMatthias Ringwald 5408ea3236cSMatthias Ringwald # add UUID128 flag for value handle 541b3fcedb9SMatthias Ringwald if uuid_size == 16: 542e22a2612SMatthias Ringwald value_flags = value_flags | property_flags['LONG_UUID']; 543b3fcedb9SMatthias Ringwald 544b3fcedb9SMatthias Ringwald write_indent(fout) 545b3fcedb9SMatthias Ringwald fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value)) 546d7ec1d24SMatthias Ringwald 547d7ec1d24SMatthias Ringwald dump_flags(fout, value_flags) 548d7ec1d24SMatthias Ringwald 549b3fcedb9SMatthias Ringwald write_indent(fout) 550b3fcedb9SMatthias Ringwald write_16(fout, size) 551e22a2612SMatthias Ringwald write_16(fout, value_flags) 552b3fcedb9SMatthias Ringwald write_16(fout, handle) 553285653b2SMatthias Ringwald write_uuid(fout, uuid) 554043f8832SMatthias Ringwald if uuid_is_database_hash: 555043f8832SMatthias Ringwald write_database_hash(fout) 556043f8832SMatthias Ringwald else: 557b3fcedb9SMatthias Ringwald if is_string(value): 558b3fcedb9SMatthias Ringwald write_string(fout, value) 559b3fcedb9SMatthias Ringwald else: 560b3fcedb9SMatthias Ringwald write_sequence(fout,value) 561b3fcedb9SMatthias Ringwald 562b3fcedb9SMatthias Ringwald fout.write("\n") 563729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 564b3fcedb9SMatthias Ringwald handle = handle + 1 565b3fcedb9SMatthias Ringwald 566b3fcedb9SMatthias Ringwald if add_client_characteristic_configuration(properties): 567e22a2612SMatthias Ringwald # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC 568d7ec1d24SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 569e22a2612SMatthias Ringwald flags |= property_flags['READ'] 570e22a2612SMatthias Ringwald flags |= property_flags['WRITE'] 5719be4aecfSMatthias Ringwald flags |= property_flags['WRITE_WITHOUT_RESPONSE'] 572e22a2612SMatthias Ringwald flags |= property_flags['DYNAMIC'] 573b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 2 574d7ec1d24SMatthias Ringwald 575b3fcedb9SMatthias Ringwald write_indent(fout) 576b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle)) 577d7ec1d24SMatthias Ringwald 578d7ec1d24SMatthias Ringwald dump_flags(fout, flags) 579d7ec1d24SMatthias Ringwald 580b3fcedb9SMatthias Ringwald write_indent(fout) 581b3fcedb9SMatthias Ringwald write_16(fout, size) 582e22a2612SMatthias Ringwald write_16(fout, flags) 583b3fcedb9SMatthias Ringwald write_16(fout, handle) 584b3fcedb9SMatthias Ringwald write_16(fout, 0x2902) 585b3fcedb9SMatthias Ringwald write_16(fout, 0) 586b3fcedb9SMatthias Ringwald fout.write("\n") 587043f8832SMatthias Ringwald 588043f8832SMatthias Ringwald database_hash_append_uint16(handle) 589043f8832SMatthias Ringwald database_hash_append_uint16(0x2902) 590043f8832SMatthias Ringwald 591729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 592b3fcedb9SMatthias Ringwald handle = handle + 1 593b3fcedb9SMatthias Ringwald 594043f8832SMatthias Ringwald 595b3fcedb9SMatthias Ringwald if properties & property_flags['RELIABLE_WRITE']: 596b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 2 597b3fcedb9SMatthias Ringwald write_indent(fout) 598b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle)) 599b3fcedb9SMatthias Ringwald write_indent(fout) 600b3fcedb9SMatthias Ringwald write_16(fout, size) 601e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 602b3fcedb9SMatthias Ringwald write_16(fout, handle) 603b3fcedb9SMatthias Ringwald write_16(fout, 0x2900) 604b3fcedb9SMatthias Ringwald write_16(fout, 1) # Reliable Write 605b3fcedb9SMatthias Ringwald fout.write("\n") 606043f8832SMatthias Ringwald 607043f8832SMatthias Ringwald database_hash_append_uint16(handle) 608043f8832SMatthias Ringwald database_hash_append_uint16(0x2900) 609043f8832SMatthias Ringwald database_hash_append_uint16(1) 610043f8832SMatthias Ringwald 611b3fcedb9SMatthias Ringwald handle = handle + 1 612b3fcedb9SMatthias Ringwald 613b3fcedb9SMatthias Ringwalddef parseCharacteristicUserDescription(fout, parts): 614b3fcedb9SMatthias Ringwald global handle 615b3fcedb9SMatthias Ringwald global total_size 616b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 617b3fcedb9SMatthias Ringwald 618b3fcedb9SMatthias Ringwald properties = parseProperties(parts[1]) 619b3fcedb9SMatthias Ringwald value = parts[2] 620b3fcedb9SMatthias Ringwald 621b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 622b3fcedb9SMatthias Ringwald if is_string(value): 623b7647eb6SMatthias Ringwald size = size + len(value) 624b3fcedb9SMatthias Ringwald else: 625b3fcedb9SMatthias Ringwald size = size + len(value.split()) 626b3fcedb9SMatthias Ringwald 627e22a2612SMatthias Ringwald # use write, write permissions and encryption key size from attribute value and set READ_ANYBODY 628d7ec1d24SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 629e22a2612SMatthias Ringwald flags |= properties & property_flags['WRITE'] 630e22a2612SMatthias Ringwald flags |= property_flags['READ'] 631e22a2612SMatthias Ringwald 632b3fcedb9SMatthias Ringwald write_indent(fout) 633b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:]))) 634d7ec1d24SMatthias Ringwald 635d7ec1d24SMatthias Ringwald dump_flags(fout, flags) 636d7ec1d24SMatthias Ringwald 637b3fcedb9SMatthias Ringwald write_indent(fout) 638b3fcedb9SMatthias Ringwald write_16(fout, size) 639e22a2612SMatthias Ringwald write_16(fout, flags) 640b3fcedb9SMatthias Ringwald write_16(fout, handle) 641b3fcedb9SMatthias Ringwald write_16(fout, 0x2901) 642b3fcedb9SMatthias Ringwald if is_string(value): 643b3fcedb9SMatthias Ringwald write_string(fout, value) 644b3fcedb9SMatthias Ringwald else: 645b3fcedb9SMatthias Ringwald write_sequence(fout,value) 646b3fcedb9SMatthias Ringwald fout.write("\n") 647043f8832SMatthias Ringwald 648043f8832SMatthias Ringwald database_hash_append_uint16(handle) 649043f8832SMatthias Ringwald database_hash_append_uint16(0x2901) 650043f8832SMatthias Ringwald 651729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 652b3fcedb9SMatthias Ringwald handle = handle + 1 653b3fcedb9SMatthias Ringwald 654b3fcedb9SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts): 655b3fcedb9SMatthias Ringwald global handle 656b3fcedb9SMatthias Ringwald global total_size 657b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 658b3fcedb9SMatthias Ringwald 659b3fcedb9SMatthias Ringwald properties = parseProperties(parts[1]) 660b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 661b3fcedb9SMatthias Ringwald 662e22a2612SMatthias Ringwald # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY 663d7ec1d24SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 664e22a2612SMatthias Ringwald flags |= property_flags['READ'] 665e22a2612SMatthias Ringwald flags |= property_flags['WRITE'] 666e22a2612SMatthias Ringwald flags |= property_flags['DYNAMIC'] 667e22a2612SMatthias Ringwald 668b3fcedb9SMatthias Ringwald write_indent(fout) 669b3fcedb9SMatthias Ringwald fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:]))) 670d7ec1d24SMatthias Ringwald 671d7ec1d24SMatthias Ringwald dump_flags(fout, flags) 672d7ec1d24SMatthias Ringwald 673b3fcedb9SMatthias Ringwald write_indent(fout) 674b3fcedb9SMatthias Ringwald write_16(fout, size) 675e22a2612SMatthias Ringwald write_16(fout, flags) 676b3fcedb9SMatthias Ringwald write_16(fout, handle) 677b3fcedb9SMatthias Ringwald write_16(fout, 0x2903) 678b3fcedb9SMatthias Ringwald fout.write("\n") 679043f8832SMatthias Ringwald 680043f8832SMatthias Ringwald database_hash_append_uint16(handle) 681043f8832SMatthias Ringwald database_hash_append_uint16(0x2903) 682043f8832SMatthias Ringwald 683729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 684b3fcedb9SMatthias Ringwald handle = handle + 1 685b3fcedb9SMatthias Ringwald 686b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts): 687b3fcedb9SMatthias Ringwald global handle 688b3fcedb9SMatthias Ringwald global total_size 689b3fcedb9SMatthias Ringwald 690e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 691b3fcedb9SMatthias Ringwald 692b3fcedb9SMatthias Ringwald identifier = parts[1] 693b3fcedb9SMatthias Ringwald presentation_formats[identifier] = handle 694b3fcedb9SMatthias Ringwald # print("format '%s' with handle %d\n" % (identifier, handle)) 695b3fcedb9SMatthias Ringwald 696b3fcedb9SMatthias Ringwald format = parts[2] 697b3fcedb9SMatthias Ringwald exponent = parts[3] 698b3fcedb9SMatthias Ringwald unit = parseUUID(parts[4]) 699b3fcedb9SMatthias Ringwald name_space = parts[5] 700b3fcedb9SMatthias Ringwald description = parseUUID(parts[6]) 701b3fcedb9SMatthias Ringwald 702b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 7 703b3fcedb9SMatthias Ringwald 704b3fcedb9SMatthias Ringwald write_indent(fout) 705b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 706b3fcedb9SMatthias Ringwald write_indent(fout) 707b3fcedb9SMatthias Ringwald write_16(fout, size) 708e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 709b3fcedb9SMatthias Ringwald write_16(fout, handle) 710b3fcedb9SMatthias Ringwald write_16(fout, 0x2904) 711b3fcedb9SMatthias Ringwald write_sequence(fout, format) 712b3fcedb9SMatthias Ringwald write_sequence(fout, exponent) 713285653b2SMatthias Ringwald write_uuid(fout, unit) 714b3fcedb9SMatthias Ringwald write_sequence(fout, name_space) 715285653b2SMatthias Ringwald write_uuid(fout, description) 716b3fcedb9SMatthias Ringwald fout.write("\n") 717043f8832SMatthias Ringwald 718043f8832SMatthias Ringwald database_hash_append_uint16(handle) 719043f8832SMatthias Ringwald database_hash_append_uint16(0x2904) 720043f8832SMatthias Ringwald 721b3fcedb9SMatthias Ringwald handle = handle + 1 722b3fcedb9SMatthias Ringwald 723b3fcedb9SMatthias Ringwald 724b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts): 725b3fcedb9SMatthias Ringwald global handle 726b3fcedb9SMatthias Ringwald global total_size 727b3fcedb9SMatthias Ringwald 728e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 729b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2 730b3fcedb9SMatthias Ringwald 731b3fcedb9SMatthias Ringwald write_indent(fout) 732b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 733b3fcedb9SMatthias Ringwald write_indent(fout) 734b3fcedb9SMatthias Ringwald write_16(fout, size) 735e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 736b3fcedb9SMatthias Ringwald write_16(fout, handle) 737b3fcedb9SMatthias Ringwald write_16(fout, 0x2905) 738b3fcedb9SMatthias Ringwald for identifier in parts[1:]: 739b3fcedb9SMatthias Ringwald format_handle = presentation_formats[identifier] 740b3fcedb9SMatthias Ringwald if format == 0: 741b3fcedb9SMatthias Ringwald print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier) 742b3fcedb9SMatthias Ringwald sys.exit(1) 743b3fcedb9SMatthias Ringwald write_16(fout, format_handle) 744b3fcedb9SMatthias Ringwald fout.write("\n") 745043f8832SMatthias Ringwald 746043f8832SMatthias Ringwald database_hash_append_uint16(handle) 747043f8832SMatthias Ringwald database_hash_append_uint16(0x2905) 748043f8832SMatthias Ringwald 749b3fcedb9SMatthias Ringwald handle = handle + 1 750b3fcedb9SMatthias Ringwald 751b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts): 752b3fcedb9SMatthias Ringwald global handle 753b3fcedb9SMatthias Ringwald global total_size 754b3fcedb9SMatthias Ringwald 755e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 756b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 1 + 1 757b3fcedb9SMatthias Ringwald 758231a3c5dSMatthias Ringwald report_id = parts[2] 759231a3c5dSMatthias Ringwald report_type = parts[3] 760b3fcedb9SMatthias Ringwald 761b3fcedb9SMatthias Ringwald write_indent(fout) 762b3fcedb9SMatthias Ringwald fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:]))) 763b3fcedb9SMatthias Ringwald write_indent(fout) 764b3fcedb9SMatthias Ringwald write_16(fout, size) 765e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 766b3fcedb9SMatthias Ringwald write_16(fout, handle) 767b3fcedb9SMatthias Ringwald write_16(fout, 0x2908) 768b3fcedb9SMatthias Ringwald write_sequence(fout, report_id) 769b3fcedb9SMatthias Ringwald write_sequence(fout, report_type) 770b3fcedb9SMatthias Ringwald fout.write("\n") 771b3fcedb9SMatthias Ringwald handle = handle + 1 772b3fcedb9SMatthias Ringwald 773b3fcedb9SMatthias Ringwald 774b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts): 775b3fcedb9SMatthias Ringwald global handle 776b3fcedb9SMatthias Ringwald global total_size 777b3fcedb9SMatthias Ringwald 778e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 779b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 1 780b3fcedb9SMatthias Ringwald 781b3fcedb9SMatthias Ringwald no_of_digitals = parts[1] 782b3fcedb9SMatthias Ringwald 783b3fcedb9SMatthias Ringwald write_indent(fout) 784b3fcedb9SMatthias Ringwald fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:]))) 785b3fcedb9SMatthias Ringwald write_indent(fout) 786b3fcedb9SMatthias Ringwald write_16(fout, size) 787e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 788b3fcedb9SMatthias Ringwald write_16(fout, handle) 789b3fcedb9SMatthias Ringwald write_16(fout, 0x2909) 790b3fcedb9SMatthias Ringwald write_sequence(fout, no_of_digitals) 791b3fcedb9SMatthias Ringwald fout.write("\n") 792b3fcedb9SMatthias Ringwald handle = handle + 1 793b3fcedb9SMatthias Ringwald 79460b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout): 795b3fcedb9SMatthias Ringwald global handle 796b3fcedb9SMatthias Ringwald global total_size 797b3fcedb9SMatthias Ringwald 798b165f97bSMatthias Ringwald line_count = 0; 799b3fcedb9SMatthias Ringwald for line in fin: 800b3fcedb9SMatthias Ringwald line = line.strip("\n\r ") 801b165f97bSMatthias Ringwald line_count += 1 802b3fcedb9SMatthias Ringwald 803b165f97bSMatthias Ringwald if line.startswith("//"): 804b165f97bSMatthias Ringwald fout.write(" //" + line.lstrip('/') + '\n') 805b165f97bSMatthias Ringwald continue 806b165f97bSMatthias Ringwald 80760b51a4cSMatthias Ringwald if line.startswith("#import"): 80860b51a4cSMatthias Ringwald imported_file = '' 80960b51a4cSMatthias Ringwald parts = re.match('#import\s+<(.*)>\w*',line) 81060b51a4cSMatthias Ringwald if parts and len(parts.groups()) == 1: 811dbb3997aSMilanka Ringwald imported_file = parts.groups()[0] 81260b51a4cSMatthias Ringwald parts = re.match('#import\s+"(.*)"\w*',line) 81360b51a4cSMatthias Ringwald if parts and len(parts.groups()) == 1: 814dbb3997aSMilanka Ringwald imported_file = parts.groups()[0] 81560b51a4cSMatthias Ringwald if len(imported_file) == 0: 81660b51a4cSMatthias Ringwald print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count)) 81760b51a4cSMatthias Ringwald continue 81860b51a4cSMatthias Ringwald 819dbb3997aSMilanka Ringwald imported_file = getFile( imported_file ) 82060b51a4cSMatthias Ringwald print("Importing %s" % imported_file) 82160b51a4cSMatthias Ringwald try: 82260b51a4cSMatthias Ringwald imported_fin = codecs.open (imported_file, encoding='utf-8') 82360b51a4cSMatthias Ringwald fout.write(' // ' + line + ' -- BEGIN\n') 82460b51a4cSMatthias Ringwald parseLines(imported_file, imported_fin, fout) 82560b51a4cSMatthias Ringwald fout.write(' // ' + line + ' -- END\n') 82660b51a4cSMatthias Ringwald except IOError as e: 82760b51a4cSMatthias Ringwald print('ERROR: Import failed. Please check path.') 82860b51a4cSMatthias Ringwald 82960b51a4cSMatthias Ringwald continue 83060b51a4cSMatthias Ringwald 83160b51a4cSMatthias Ringwald if line.startswith("#TODO"): 83260b51a4cSMatthias Ringwald print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count)) 833b165f97bSMatthias Ringwald print ("'%s'" % line) 834b165f97bSMatthias Ringwald fout.write("// " + line + '\n') 835b3fcedb9SMatthias Ringwald continue 836b3fcedb9SMatthias Ringwald 837b3fcedb9SMatthias Ringwald if len(line) == 0: 838b3fcedb9SMatthias Ringwald continue 839b3fcedb9SMatthias Ringwald 840b3fcedb9SMatthias Ringwald f = io.StringIO(line) 841b3fcedb9SMatthias Ringwald parts_list = csv.reader(f, delimiter=',', quotechar='"') 842b3fcedb9SMatthias Ringwald 843b3fcedb9SMatthias Ringwald for parts in parts_list: 844b3fcedb9SMatthias Ringwald for index, object in enumerate(parts): 845b3fcedb9SMatthias Ringwald parts[index] = object.strip().lstrip('"').rstrip('"') 846b3fcedb9SMatthias Ringwald 847b3fcedb9SMatthias Ringwald if parts[0] == 'PRIMARY_SERVICE': 848b3fcedb9SMatthias Ringwald parsePrimaryService(fout, parts) 849b3fcedb9SMatthias Ringwald continue 850b3fcedb9SMatthias Ringwald 851b3fcedb9SMatthias Ringwald if parts[0] == 'SECONDARY_SERVICE': 852b3fcedb9SMatthias Ringwald parseSecondaryService(fout, parts) 853b3fcedb9SMatthias Ringwald continue 854b3fcedb9SMatthias Ringwald 855b3fcedb9SMatthias Ringwald if parts[0] == 'INCLUDE_SERVICE': 856b3fcedb9SMatthias Ringwald parseIncludeService(fout, parts) 857b3fcedb9SMatthias Ringwald continue 858b3fcedb9SMatthias Ringwald 859b3fcedb9SMatthias Ringwald # 2803 860b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC': 861b3fcedb9SMatthias Ringwald parseCharacteristic(fout, parts) 862b3fcedb9SMatthias Ringwald continue 863b3fcedb9SMatthias Ringwald 864b3fcedb9SMatthias Ringwald # 2900 Characteristic Extended Properties 865b3fcedb9SMatthias Ringwald 866b3fcedb9SMatthias Ringwald # 2901 867b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION': 868b3fcedb9SMatthias Ringwald parseCharacteristicUserDescription(fout, parts) 869b3fcedb9SMatthias Ringwald continue 870b3fcedb9SMatthias Ringwald 871b165f97bSMatthias Ringwald 872b165f97bSMatthias Ringwald # 2902 Client Characteristic Configuration - automatically included in Characteristic if 873b3fcedb9SMatthias Ringwald # notification / indication is supported 874231a3c5dSMatthias Ringwald if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION': 875b165f97bSMatthias Ringwald continue 876b3fcedb9SMatthias Ringwald 877b3fcedb9SMatthias Ringwald # 2903 878b3fcedb9SMatthias Ringwald if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION': 879b3fcedb9SMatthias Ringwald parseServerCharacteristicConfiguration(fout, parts) 880b3fcedb9SMatthias Ringwald continue 881b3fcedb9SMatthias Ringwald 882b3fcedb9SMatthias Ringwald # 2904 883b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_FORMAT': 884b3fcedb9SMatthias Ringwald parseCharacteristicFormat(fout, parts) 885b3fcedb9SMatthias Ringwald continue 886b3fcedb9SMatthias Ringwald 887b3fcedb9SMatthias Ringwald # 2905 888b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT': 889b3fcedb9SMatthias Ringwald parseCharacteristicAggregateFormat(fout, parts) 890b3fcedb9SMatthias Ringwald continue 891b3fcedb9SMatthias Ringwald 892b3fcedb9SMatthias Ringwald # 2906 893b3fcedb9SMatthias Ringwald if parts[0] == 'VALID_RANGE': 894b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 895b3fcedb9SMatthias Ringwald continue 896b3fcedb9SMatthias Ringwald 897b3fcedb9SMatthias Ringwald # 2907 898b3fcedb9SMatthias Ringwald if parts[0] == 'EXTERNAL_REPORT_REFERENCE': 899b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 900b3fcedb9SMatthias Ringwald continue 901b3fcedb9SMatthias Ringwald 902b3fcedb9SMatthias Ringwald # 2908 903b3fcedb9SMatthias Ringwald if parts[0] == 'REPORT_REFERENCE': 904b3fcedb9SMatthias Ringwald parseReportReference(fout, parts) 905b3fcedb9SMatthias Ringwald continue 906b3fcedb9SMatthias Ringwald 907b3fcedb9SMatthias Ringwald # 2909 908b3fcedb9SMatthias Ringwald if parts[0] == 'NUMBER_OF_DIGITALS': 909b3fcedb9SMatthias Ringwald parseNumberOfDigitals(fout, parts) 910b3fcedb9SMatthias Ringwald continue 911b3fcedb9SMatthias Ringwald 912b3fcedb9SMatthias Ringwald # 290A 913b3fcedb9SMatthias Ringwald if parts[0] == 'VALUE_TRIGGER_SETTING': 914b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 915b3fcedb9SMatthias Ringwald continue 916b3fcedb9SMatthias Ringwald 917b3fcedb9SMatthias Ringwald # 290B 918b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION': 919b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 920b3fcedb9SMatthias Ringwald continue 921b3fcedb9SMatthias Ringwald 922b3fcedb9SMatthias Ringwald # 290C 923b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT': 924b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 925b3fcedb9SMatthias Ringwald continue 926b3fcedb9SMatthias Ringwald 927b3fcedb9SMatthias Ringwald # 290D 928b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING': 929b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 930b3fcedb9SMatthias Ringwald continue 931b3fcedb9SMatthias Ringwald 932b3fcedb9SMatthias Ringwald # 2906 933b3fcedb9SMatthias Ringwald if parts[0] == 'VALID_RANGE': 934b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 935b3fcedb9SMatthias Ringwald continue 936b3fcedb9SMatthias Ringwald 937b3fcedb9SMatthias Ringwald print("WARNING: unknown token: %s\n" % (parts[0])) 938b3fcedb9SMatthias Ringwald 9397050bf34SMatthias Ringwalddef parse(fname_in, fin, fname_out, tool_path, fout): 94060b51a4cSMatthias Ringwald global handle 94160b51a4cSMatthias Ringwald global total_size 94260b51a4cSMatthias Ringwald 9437050bf34SMatthias Ringwald fout.write(header.format(fname_out, fname_in, tool_path)) 94460b51a4cSMatthias Ringwald fout.write('{\n') 945fd1be25dSMatthias Ringwald write_indent(fout) 946fd1be25dSMatthias Ringwald fout.write('// ATT DB Version\n') 947fd1be25dSMatthias Ringwald write_indent(fout) 948fd1be25dSMatthias Ringwald fout.write('1,\n') 949fd1be25dSMatthias Ringwald fout.write("\n") 95060b51a4cSMatthias Ringwald 95160b51a4cSMatthias Ringwald parseLines(fname_in, fin, fout) 95260b51a4cSMatthias Ringwald 953729074c4SMatthias Ringwald serviceDefinitionComplete(fout) 954b3fcedb9SMatthias Ringwald write_indent(fout) 955b3fcedb9SMatthias Ringwald fout.write("// END\n"); 956b3fcedb9SMatthias Ringwald write_indent(fout) 957b3fcedb9SMatthias Ringwald write_16(fout,0) 958b3fcedb9SMatthias Ringwald fout.write("\n") 959b3fcedb9SMatthias Ringwald total_size = total_size + 2 960b3fcedb9SMatthias Ringwald 961b3fcedb9SMatthias Ringwald fout.write("}; // total size %u bytes \n" % total_size); 962b3fcedb9SMatthias Ringwald 963b3fcedb9SMatthias Ringwalddef listHandles(fout): 964b3fcedb9SMatthias Ringwald fout.write('\n\n') 965b3fcedb9SMatthias Ringwald fout.write('//\n') 966729074c4SMatthias Ringwald fout.write('// list service handle ranges\n') 967729074c4SMatthias Ringwald fout.write('//\n') 968729074c4SMatthias Ringwald for define in defines_for_services: 969729074c4SMatthias Ringwald fout.write(define) 970729074c4SMatthias Ringwald fout.write('\n') 971729074c4SMatthias Ringwald fout.write('\n') 972729074c4SMatthias Ringwald fout.write('//\n') 973b3fcedb9SMatthias Ringwald fout.write('// list mapping between characteristics and handles\n') 974b3fcedb9SMatthias Ringwald fout.write('//\n') 975729074c4SMatthias Ringwald for define in defines_for_characteristics: 976b3fcedb9SMatthias Ringwald fout.write(define) 977b3fcedb9SMatthias Ringwald fout.write('\n') 978b3fcedb9SMatthias Ringwald 979dbb3997aSMilanka Ringwalddef getFile( fileName ): 98078b65b0aSMatthias Ringwald for d in include_paths: 98178b65b0aSMatthias Ringwald fullFile = os.path.normpath(d + os.sep + fileName) # because Windows exists 98278b65b0aSMatthias Ringwald # print("test %s" % fullFile) 983dbb3997aSMilanka Ringwald if os.path.isfile( fullFile ) == True: 984dbb3997aSMilanka Ringwald return fullFile 985dbb3997aSMilanka Ringwald print ("'{0}' not found".format( fileName )) 98678b65b0aSMatthias Ringwald print ("Include paths: %s" % ", ".join(include_paths)) 987dbb3997aSMilanka Ringwald exit(-1) 988dbb3997aSMilanka Ringwald 989dbb3997aSMilanka Ringwald 990dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') 99178b65b0aSMatthias Ringwalddefault_includes = [os.path.normpath(path) for path in [ btstack_root + '/src/', btstack_root + '/src/ble/gatt-service/']] 992dbb3997aSMilanka Ringwald 993dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack') 994dbb3997aSMilanka Ringwald 995dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes', 996dbb3997aSMilanka Ringwald help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes)) 997dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str, 998dbb3997aSMilanka Ringwald help='gatt file to be compiled') 999dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str, 1000dbb3997aSMilanka Ringwald help='header file to be generated') 1001dbb3997aSMilanka Ringwald 1002dbb3997aSMilanka Ringwaldargs = parser.parse_args() 1003dbb3997aSMilanka Ringwald 100478b65b0aSMatthias Ringwald# add include path arguments 100578b65b0aSMatthias Ringwaldif args.I != None: 100678b65b0aSMatthias Ringwald for d in args.I: 100778b65b0aSMatthias Ringwald include_paths.append(os.path.normpath(d[0])) 100878b65b0aSMatthias Ringwald 1009dbb3997aSMilanka Ringwald# append default include paths 101078b65b0aSMatthias Ringwaldinclude_paths.extend(default_includes) 1011dbb3997aSMilanka Ringwald 1012b3fcedb9SMatthias Ringwaldtry: 1013b165f97bSMatthias Ringwald # read defines from bluetooth_gatt.h 1014dbb3997aSMilanka Ringwald gen_path = getFile( 'bluetooth_gatt.h' ) 1015b165f97bSMatthias Ringwald bluetooth_gatt = read_defines(gen_path) 1016b165f97bSMatthias Ringwald 1017dbb3997aSMilanka Ringwald filename = args.hfile 1018dbb3997aSMilanka Ringwald fin = codecs.open (args.gattfile, encoding='utf-8') 1019285653b2SMatthias Ringwald 1020285653b2SMatthias Ringwald # pass 1: create temp .h file 1021d78181d6SMatthias Ringwald ftemp = tempfile.TemporaryFile(mode='w+t') 1022285653b2SMatthias Ringwald parse(args.gattfile, fin, filename, sys.argv[0], ftemp) 1023285653b2SMatthias Ringwald listHandles(ftemp) 1024285653b2SMatthias Ringwald 1025043f8832SMatthias Ringwald # calc GATT Database Hash 1026043f8832SMatthias Ringwald db_hash = aes_cmac(bytearray(16), database_hash_message) 1027043f8832SMatthias Ringwald if isinstance(db_hash, str): 1028043f8832SMatthias Ringwald # python2 1029043f8832SMatthias Ringwald db_hash_sequence = [('0x%02x' % ord(i)) for i in db_hash] 1030043f8832SMatthias Ringwald elif isinstance(db_hash, bytes): 1031043f8832SMatthias Ringwald # python3 1032043f8832SMatthias Ringwald db_hash_sequence = [('0x%02x' % i) for i in db_hash] 1033043f8832SMatthias Ringwald else: 1034043f8832SMatthias Ringwald print("AES CMAC returns unexpected type %s, abort" % type(db_hash)) 1035043f8832SMatthias Ringwald sys.exit(1) 1036043f8832SMatthias Ringwald # reverse hash to get little endian 1037043f8832SMatthias Ringwald db_hash_sequence.reverse() 1038043f8832SMatthias Ringwald db_hash_string = ', '.join(db_hash_sequence) + ', ' 1039043f8832SMatthias Ringwald 1040285653b2SMatthias Ringwald # pass 2: insert GATT Database Hash 1041b3fcedb9SMatthias Ringwald fout = open (filename, 'w') 1042285653b2SMatthias Ringwald ftemp.seek(0) 1043285653b2SMatthias Ringwald for line in ftemp: 1044043f8832SMatthias Ringwald fout.write(line.replace('THE-DATABASE-HASH', db_hash_string)) 1045b3fcedb9SMatthias Ringwald fout.close() 1046285653b2SMatthias Ringwald ftemp.close() 1047285653b2SMatthias Ringwald 1048b165f97bSMatthias Ringwald print('Created %s' % filename) 1049b3fcedb9SMatthias Ringwald 1050b3fcedb9SMatthias Ringwaldexcept IOError as e: 1051e22a2612SMatthias Ringwald 1052b3fcedb9SMatthias Ringwald print(usage) 1053b3fcedb9SMatthias Ringwald sys.exit(1) 1054b3fcedb9SMatthias Ringwald 1055b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n') 1056