14783d256SMatheus 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 12*ced5a857SMatthias Ringwald# alternatively, the pycryptodome package can be used instead 13*ced5a857SMatthias Ringwald# - pip3 install pycryptodome 14043f8832SMatthias Ringwald 15b3fcedb9SMatthias Ringwaldimport codecs 16b165f97bSMatthias Ringwaldimport csv 17b165f97bSMatthias Ringwaldimport io 18b165f97bSMatthias Ringwaldimport os 19b165f97bSMatthias Ringwaldimport re 20b165f97bSMatthias Ringwaldimport string 2160b51a4cSMatthias Ringwaldimport sys 22dbb3997aSMilanka Ringwaldimport argparse 23285653b2SMatthias Ringwaldimport tempfile 24b3fcedb9SMatthias Ringwald 25*ced5a857SMatthias Ringwaldhave_crypto = True 26*ced5a857SMatthias Ringwald# try to import PyCryptodome independent from PyCrypto 27043f8832SMatthias Ringwaldtry: 28379d3aceSMatthias Ringwald from Cryptodome.Cipher import AES 29043f8832SMatthias Ringwald from Cryptodome.Hash import CMAC 30*ced5a857SMatthias Ringwaldexcept ImportError: 31*ced5a857SMatthias Ringwald # fallback: try to import PyCryptodome as (an almost drop-in) replacement for the PyCrypto library 32*ced5a857SMatthias Ringwald try: 33*ced5a857SMatthias Ringwald from Crypto.Cipher import AES 34*ced5a857SMatthias Ringwald from Crypto.Hash import CMAC 35043f8832SMatthias Ringwald except ImportError: 36043f8832SMatthias Ringwald have_crypto = False 377490175eSMatthias Ringwald print("\n[!] PyCryptodome required to calculate GATT Database Hash but not installed (using random value instead)") 38*ced5a857SMatthias Ringwald print("[!] Please install PyCryptodome, e.g. 'pip install pycryptodomex' or 'pip install pycryptodome'\n") 39043f8832SMatthias Ringwald 40b3fcedb9SMatthias Ringwaldheader = ''' 41b3fcedb9SMatthias Ringwald// {0} generated from {1} for BTstack 427050bf34SMatthias Ringwald// it needs to be regenerated when the .gatt file is updated. 437050bf34SMatthias Ringwald 447050bf34SMatthias Ringwald// To generate {0}: 457050bf34SMatthias Ringwald// {2} {1} {0} 467050bf34SMatthias Ringwald 47fd1be25dSMatthias Ringwald// att db format version 1 48b3fcedb9SMatthias Ringwald 49fd1be25dSMatthias Ringwald// binary attribute representation: 50fd1be25dSMatthias Ringwald// - size in bytes (16), flags(16), handle (16), uuid (16/128), value(...) 51b3fcedb9SMatthias Ringwald 52b3fcedb9SMatthias Ringwald#include <stdint.h> 53b3fcedb9SMatthias Ringwald 54b3fcedb9SMatthias Ringwaldconst uint8_t profile_data[] = 55b3fcedb9SMatthias Ringwald''' 56b3fcedb9SMatthias Ringwald 57b3fcedb9SMatthias Ringwaldprint(''' 58dbb3997aSMilanka RingwaldBLE configuration generator for use with BTstack 59dbb3997aSMilanka RingwaldCopyright 2018 BlueKitchen GmbH 60b3fcedb9SMatthias Ringwald''') 61b3fcedb9SMatthias Ringwald 62b3fcedb9SMatthias Ringwaldassigned_uuids = { 63b3fcedb9SMatthias Ringwald 'GAP_SERVICE' : 0x1800, 64b3fcedb9SMatthias Ringwald 'GATT_SERVICE' : 0x1801, 65b3fcedb9SMatthias Ringwald 'GAP_DEVICE_NAME' : 0x2a00, 66b3fcedb9SMatthias Ringwald 'GAP_APPEARANCE' : 0x2a01, 67b3fcedb9SMatthias Ringwald 'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02, 68b3fcedb9SMatthias Ringwald 'GAP_RECONNECTION_ADDRESS' : 0x2A03, 69b3fcedb9SMatthias Ringwald 'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04, 70b3fcedb9SMatthias Ringwald 'GATT_SERVICE_CHANGED' : 0x2a05, 71043f8832SMatthias Ringwald 'GATT_DATABASE_HASH' : 0x2b2a 72b3fcedb9SMatthias Ringwald} 73b3fcedb9SMatthias Ringwald 74e72176f8SMatthias Ringwaldsecurity_permsission = ['ANYBODY','ENCRYPTED', 'AUTHENTICATED', 'AUTHORIZED', 'AUTHENTICATED_SC'] 75d7ec1d24SMatthias Ringwald 76b3fcedb9SMatthias Ringwaldproperty_flags = { 77eb6072adSMatthias Ringwald # GATT Characteristic Properties 78b3fcedb9SMatthias Ringwald 'BROADCAST' : 0x01, 79b3fcedb9SMatthias Ringwald 'READ' : 0x02, 80b3fcedb9SMatthias Ringwald 'WRITE_WITHOUT_RESPONSE' : 0x04, 81b3fcedb9SMatthias Ringwald 'WRITE' : 0x08, 82b3fcedb9SMatthias Ringwald 'NOTIFY': 0x10, 83b3fcedb9SMatthias Ringwald 'INDICATE' : 0x20, 84b3fcedb9SMatthias Ringwald 'AUTHENTICATED_SIGNED_WRITE' : 0x40, 85b3fcedb9SMatthias Ringwald 'EXTENDED_PROPERTIES' : 0x80, 86b3fcedb9SMatthias Ringwald # custom BTstack extension 87b3fcedb9SMatthias Ringwald 'DYNAMIC': 0x100, 88b3fcedb9SMatthias Ringwald 'LONG_UUID': 0x200, 89e22a2612SMatthias Ringwald 90e22a2612SMatthias Ringwald # read permissions 91e22a2612SMatthias Ringwald 'READ_PERMISSION_BIT_0': 0x400, 92e22a2612SMatthias Ringwald 'READ_PERMISSION_BIT_1': 0x800, 93e22a2612SMatthias Ringwald 94e22a2612SMatthias Ringwald # 95b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_7': 0x6000, 96b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_8': 0x7000, 97b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_9': 0x8000, 98b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_10': 0x9000, 99b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_11': 0xa000, 100b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_12': 0xb000, 101b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_13': 0xc000, 102b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_14': 0xd000, 103b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_15': 0xe000, 104b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_16': 0xf000, 105e22a2612SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_MASK': 0xf000, 106eb6072adSMatthias Ringwald 107b3fcedb9SMatthias Ringwald # only used by gatt compiler >= 0xffff 108b3fcedb9SMatthias Ringwald # Extended Properties 109e72176f8SMatthias Ringwald 'RELIABLE_WRITE': 0x00010000, 110e72176f8SMatthias Ringwald 'AUTHENTICATION_REQUIRED': 0x00020000, 111e72176f8SMatthias Ringwald 'AUTHORIZATION_REQUIRED': 0x00040000, 112e72176f8SMatthias Ringwald 'READ_ANYBODY': 0x00080000, 113e72176f8SMatthias Ringwald 'READ_ENCRYPTED': 0x00100000, 114e72176f8SMatthias Ringwald 'READ_AUTHENTICATED': 0x00200000, 115e72176f8SMatthias Ringwald 'READ_AUTHENTICATED_SC': 0x00400000, 116e72176f8SMatthias Ringwald 'READ_AUTHORIZED': 0x00800000, 117e72176f8SMatthias Ringwald 'WRITE_ANYBODY': 0x01000000, 118e72176f8SMatthias Ringwald 'WRITE_ENCRYPTED': 0x02000000, 119e72176f8SMatthias Ringwald 'WRITE_AUTHENTICATED': 0x04000000, 120e72176f8SMatthias Ringwald 'WRITE_AUTHENTICATED_SC': 0x08000000, 121e72176f8SMatthias Ringwald 'WRITE_AUTHORIZED': 0x10000000, 122eb6072adSMatthias Ringwald 123eb6072adSMatthias Ringwald # Broadcast, Notify, Indicate, Extended Properties are only used to describe a GATT Characteristic, but are free to use with att_db 124e72176f8SMatthias Ringwald # - write permissions 125e22a2612SMatthias Ringwald 'WRITE_PERMISSION_BIT_0': 0x01, 126e22a2612SMatthias Ringwald 'WRITE_PERMISSION_BIT_1': 0x10, 127e72176f8SMatthias Ringwald # - SC required 128e72176f8SMatthias Ringwald 'READ_PERMISSION_SC': 0x20, 129e72176f8SMatthias Ringwald 'WRITE_PERMISSION_SC': 0x80, 130b3fcedb9SMatthias Ringwald} 131b3fcedb9SMatthias Ringwald 132b3fcedb9SMatthias Ringwaldservices = dict() 133b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict() 134b3fcedb9SMatthias Ringwaldpresentation_formats = dict() 135b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = "" 136b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0 137b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = "" 138729074c4SMatthias Ringwalddefines_for_characteristics = [] 139729074c4SMatthias Ringwalddefines_for_services = [] 14078b65b0aSMatthias Ringwaldinclude_paths = [] 141043f8832SMatthias Ringwalddatabase_hash_message = bytearray() 142b3fcedb9SMatthias Ringwald 143b3fcedb9SMatthias Ringwaldhandle = 1 144b3fcedb9SMatthias Ringwaldtotal_size = 0 145b3fcedb9SMatthias Ringwald 146043f8832SMatthias Ringwalddef aes_cmac(key, n): 147043f8832SMatthias Ringwald if have_crypto: 148043f8832SMatthias Ringwald cobj = CMAC.new(key, ciphermod=AES) 149043f8832SMatthias Ringwald cobj.update(n) 150043f8832SMatthias Ringwald return cobj.digest() 151043f8832SMatthias Ringwald else: 1527490175eSMatthias Ringwald # return random value 1537490175eSMatthias Ringwald return os.urandom(16) 154043f8832SMatthias Ringwald 155b165f97bSMatthias Ringwalddef read_defines(infile): 156b165f97bSMatthias Ringwald defines = dict() 157b165f97bSMatthias Ringwald with open (infile, 'rt') as fin: 158b165f97bSMatthias Ringwald for line in fin: 159b165f97bSMatthias Ringwald parts = re.match('#define\s+(\w+)\s+(\w+)',line) 160b165f97bSMatthias Ringwald if parts and len(parts.groups()) == 2: 161b165f97bSMatthias Ringwald (key, value) = parts.groups() 162b165f97bSMatthias Ringwald defines[key] = int(value, 16) 163b165f97bSMatthias Ringwald return defines 164b165f97bSMatthias Ringwald 165b3fcedb9SMatthias Ringwalddef keyForUUID(uuid): 166b3fcedb9SMatthias Ringwald keyUUID = "" 167b3fcedb9SMatthias Ringwald for i in uuid: 168b3fcedb9SMatthias Ringwald keyUUID += "%02x" % i 169b3fcedb9SMatthias Ringwald return keyUUID 170b3fcedb9SMatthias Ringwald 171b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid): 172b3fcedb9SMatthias Ringwald return uuid.replace('-', '_') 173b3fcedb9SMatthias Ringwald 174b3fcedb9SMatthias Ringwalddef twoByteLEFor(value): 175b3fcedb9SMatthias Ringwald return [ (value & 0xff), (value >> 8)] 176b3fcedb9SMatthias Ringwald 177b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text): 178b3fcedb9SMatthias 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): 179b3fcedb9SMatthias Ringwald return True 180b3fcedb9SMatthias Ringwald return False 181b3fcedb9SMatthias Ringwald 182b3fcedb9SMatthias Ringwalddef parseUUID128(uuid): 183b3fcedb9SMatthias 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) 184b3fcedb9SMatthias Ringwald uuid_bytes = [] 185b3fcedb9SMatthias Ringwald for i in range(8, 0, -1): 186b3fcedb9SMatthias Ringwald uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16)) 187b3fcedb9SMatthias Ringwald return uuid_bytes 188b3fcedb9SMatthias Ringwald 189b3fcedb9SMatthias Ringwalddef parseUUID(uuid): 190b3fcedb9SMatthias Ringwald if uuid in assigned_uuids: 191b3fcedb9SMatthias Ringwald return twoByteLEFor(assigned_uuids[uuid]) 192b165f97bSMatthias Ringwald uuid_upper = uuid.upper().replace('.','_') 193b165f97bSMatthias Ringwald if uuid_upper in bluetooth_gatt: 194b165f97bSMatthias Ringwald return twoByteLEFor(bluetooth_gatt[uuid_upper]) 195b3fcedb9SMatthias Ringwald if is_128bit_uuid(uuid): 196b3fcedb9SMatthias Ringwald return parseUUID128(uuid) 197b3fcedb9SMatthias Ringwald uuidInt = int(uuid, 16) 198b3fcedb9SMatthias Ringwald return twoByteLEFor(uuidInt) 199b3fcedb9SMatthias Ringwald 200b3fcedb9SMatthias Ringwalddef parseProperties(properties): 201b3fcedb9SMatthias Ringwald value = 0 202b3fcedb9SMatthias Ringwald parts = properties.split("|") 203b3fcedb9SMatthias Ringwald for property in parts: 204b3fcedb9SMatthias Ringwald property = property.strip() 205b3fcedb9SMatthias Ringwald if property in property_flags: 206b3fcedb9SMatthias Ringwald value |= property_flags[property] 207b3fcedb9SMatthias Ringwald else: 208b3fcedb9SMatthias Ringwald print("WARNING: property %s undefined" % (property)) 209e22a2612SMatthias Ringwald 210e22a2612SMatthias Ringwald return value; 211e22a2612SMatthias Ringwald 212e22a2612SMatthias Ringwalddef gatt_characteristic_properties(properties): 213e22a2612SMatthias Ringwald return properties & 0xff 214e22a2612SMatthias Ringwald 215e22a2612SMatthias Ringwalddef att_flags(properties): 216e72176f8SMatthias Ringwald # drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Properties (0x80) - not used for flags 217e72176f8SMatthias Ringwald properties &= 0xffffff4e 218e22a2612SMatthias Ringwald 219e22a2612SMatthias Ringwald # rw permissions distinct 220e22a2612SMatthias Ringwald distinct_permissions_used = properties & ( 221e22a2612SMatthias Ringwald property_flags['READ_AUTHORIZED'] | 222e72176f8SMatthias Ringwald property_flags['READ_AUTHENTICATED_SC'] | 223e22a2612SMatthias Ringwald property_flags['READ_AUTHENTICATED'] | 224e22a2612SMatthias Ringwald property_flags['READ_ENCRYPTED'] | 225e22a2612SMatthias Ringwald property_flags['READ_ANYBODY'] | 226e22a2612SMatthias Ringwald property_flags['WRITE_AUTHORIZED'] | 227e22a2612SMatthias Ringwald property_flags['WRITE_AUTHENTICATED'] | 228e72176f8SMatthias Ringwald property_flags['WRITE_AUTHENTICATED_SC'] | 229e22a2612SMatthias Ringwald property_flags['WRITE_ENCRYPTED'] | 230e22a2612SMatthias Ringwald property_flags['WRITE_ANYBODY'] 231e22a2612SMatthias Ringwald ) != 0 232e22a2612SMatthias Ringwald 233e22a2612SMatthias Ringwald # post process properties 234e22a2612SMatthias Ringwald encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0 235e22a2612SMatthias Ringwald 236d7ec1d24SMatthias Ringwald # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted 237e22a2612SMatthias Ringwald if encryption_key_size_specified and not distinct_permissions_used: 238e22a2612SMatthias Ringwald properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED'] 239e22a2612SMatthias Ringwald 240d7ec1d24SMatthias Ringwald # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated 241d7ec1d24SMatthias Ringwald if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used: 242d7ec1d24SMatthias Ringwald properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED'] 243d7ec1d24SMatthias Ringwald 244d7ec1d24SMatthias Ringwald # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized 245d7ec1d24SMatthias Ringwald if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used: 246d7ec1d24SMatthias Ringwald properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED'] 247d7ec1d24SMatthias Ringwald 248d7ec1d24SMatthias Ringwald # determine read/write security requirements 249d7ec1d24SMatthias Ringwald read_security_level = 0 250d7ec1d24SMatthias Ringwald write_security_level = 0 251e72176f8SMatthias Ringwald read_requires_sc = False 252e72176f8SMatthias Ringwald write_requires_sc = False 253e22a2612SMatthias Ringwald if properties & property_flags['READ_AUTHORIZED']: 254d7ec1d24SMatthias Ringwald read_security_level = 3 255e22a2612SMatthias Ringwald elif properties & property_flags['READ_AUTHENTICATED']: 256d7ec1d24SMatthias Ringwald read_security_level = 2 257e72176f8SMatthias Ringwald elif properties & property_flags['READ_AUTHENTICATED_SC']: 258e72176f8SMatthias Ringwald read_security_level = 2 259e72176f8SMatthias Ringwald read_requires_sc = True 260e22a2612SMatthias Ringwald elif properties & property_flags['READ_ENCRYPTED']: 261d7ec1d24SMatthias Ringwald read_security_level = 1 262e22a2612SMatthias Ringwald if properties & property_flags['WRITE_AUTHORIZED']: 263d7ec1d24SMatthias Ringwald write_security_level = 3 264e22a2612SMatthias Ringwald elif properties & property_flags['WRITE_AUTHENTICATED']: 265d7ec1d24SMatthias Ringwald write_security_level = 2 266e72176f8SMatthias Ringwald elif properties & property_flags['WRITE_AUTHENTICATED_SC']: 267e72176f8SMatthias Ringwald write_security_level = 2 268e72176f8SMatthias Ringwald write_requires_sc = True 269e22a2612SMatthias Ringwald elif properties & property_flags['WRITE_ENCRYPTED']: 270d7ec1d24SMatthias Ringwald write_security_level = 1 271d7ec1d24SMatthias Ringwald 272d7ec1d24SMatthias Ringwald # map security requirements to flags 273d7ec1d24SMatthias Ringwald if read_security_level & 2: 274d7ec1d24SMatthias Ringwald properties |= property_flags['READ_PERMISSION_BIT_1'] 275d7ec1d24SMatthias Ringwald if read_security_level & 1: 276d7ec1d24SMatthias Ringwald properties |= property_flags['READ_PERMISSION_BIT_0'] 277e72176f8SMatthias Ringwald if read_requires_sc: 278e72176f8SMatthias Ringwald properties |= property_flags['READ_PERMISSION_SC'] 279d7ec1d24SMatthias Ringwald if write_security_level & 2: 280d7ec1d24SMatthias Ringwald properties |= property_flags['WRITE_PERMISSION_BIT_1'] 281d7ec1d24SMatthias Ringwald if write_security_level & 1: 282e22a2612SMatthias Ringwald properties |= property_flags['WRITE_PERMISSION_BIT_0'] 283e72176f8SMatthias Ringwald if write_requires_sc: 284e72176f8SMatthias Ringwald properties |= property_flags['WRITE_PERMISSION_SC'] 285e22a2612SMatthias Ringwald 286e22a2612SMatthias Ringwald return properties 287e22a2612SMatthias Ringwald 288d7ec1d24SMatthias Ringwalddef write_permissions_and_key_size_flags_from_properties(properties): 289e22a2612SMatthias Ringwald return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1']) 290b3fcedb9SMatthias Ringwald 291b3fcedb9SMatthias Ringwalddef write_8(fout, value): 292b3fcedb9SMatthias Ringwald fout.write( "0x%02x, " % (value & 0xff)) 293b3fcedb9SMatthias Ringwald 294b3fcedb9SMatthias Ringwalddef write_16(fout, value): 295b3fcedb9SMatthias Ringwald fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff)) 296b3fcedb9SMatthias Ringwald 297285653b2SMatthias Ringwalddef write_uuid(fout, uuid): 298b3fcedb9SMatthias Ringwald for byte in uuid: 299b3fcedb9SMatthias Ringwald fout.write( "0x%02x, " % byte) 300b3fcedb9SMatthias Ringwald 301b3fcedb9SMatthias Ringwalddef write_string(fout, text): 302b3fcedb9SMatthias Ringwald for l in text.lstrip('"').rstrip('"'): 303b3fcedb9SMatthias Ringwald write_8(fout, ord(l)) 304b3fcedb9SMatthias Ringwald 305b3fcedb9SMatthias Ringwalddef write_sequence(fout, text): 306b3fcedb9SMatthias Ringwald parts = text.split() 307b3fcedb9SMatthias Ringwald for part in parts: 308b3fcedb9SMatthias Ringwald fout.write("0x%s, " % (part.strip())) 309b3fcedb9SMatthias Ringwald 310043f8832SMatthias Ringwalddef write_database_hash(fout): 311043f8832SMatthias Ringwald fout.write("THE-DATABASE-HASH") 312043f8832SMatthias Ringwald 313b3fcedb9SMatthias Ringwalddef write_indent(fout): 314b3fcedb9SMatthias Ringwald fout.write(" ") 315b3fcedb9SMatthias Ringwald 316d7ec1d24SMatthias Ringwalddef read_permissions_from_flags(flags): 317d7ec1d24SMatthias Ringwald permissions = 0 318d7ec1d24SMatthias Ringwald if flags & property_flags['READ_PERMISSION_BIT_0']: 319d7ec1d24SMatthias Ringwald permissions |= 1 320d7ec1d24SMatthias Ringwald if flags & property_flags['READ_PERMISSION_BIT_1']: 321d7ec1d24SMatthias Ringwald permissions |= 2 322e72176f8SMatthias Ringwald if flags & property_flags['READ_PERMISSION_SC'] and permissions == 2: 323e72176f8SMatthias Ringwald permissions = 4 324d7ec1d24SMatthias Ringwald return permissions 325d7ec1d24SMatthias Ringwald 326d7ec1d24SMatthias Ringwalddef write_permissions_from_flags(flags): 327d7ec1d24SMatthias Ringwald permissions = 0 328d7ec1d24SMatthias Ringwald if flags & property_flags['WRITE_PERMISSION_BIT_0']: 329d7ec1d24SMatthias Ringwald permissions |= 1 330d7ec1d24SMatthias Ringwald if flags & property_flags['WRITE_PERMISSION_BIT_1']: 331d7ec1d24SMatthias Ringwald permissions |= 2 332e72176f8SMatthias Ringwald if flags & property_flags['WRITE_PERMISSION_SC'] and permissions == 2: 333e72176f8SMatthias Ringwald permissions = 4 334d7ec1d24SMatthias Ringwald return permissions 335d7ec1d24SMatthias Ringwald 336d7ec1d24SMatthias Ringwalddef encryption_key_size_from_flags(flags): 337d7ec1d24SMatthias Ringwald encryption_key_size = (flags & 0xf000) >> 12 338d7ec1d24SMatthias Ringwald if encryption_key_size > 0: 339d7ec1d24SMatthias Ringwald encryption_key_size += 1 340d7ec1d24SMatthias Ringwald return encryption_key_size 341d7ec1d24SMatthias Ringwald 342b3fcedb9SMatthias Ringwalddef is_string(text): 343b3fcedb9SMatthias Ringwald for item in text.split(" "): 344b3fcedb9SMatthias Ringwald if not all(c in string.hexdigits for c in item): 345b3fcedb9SMatthias Ringwald return True 346b3fcedb9SMatthias Ringwald return False 347b3fcedb9SMatthias Ringwald 348b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties): 349b3fcedb9SMatthias Ringwald return properties & (property_flags['NOTIFY'] | property_flags['INDICATE']) 350b3fcedb9SMatthias Ringwald 351729074c4SMatthias Ringwalddef serviceDefinitionComplete(fout): 352729074c4SMatthias Ringwald global services 353729074c4SMatthias Ringwald if current_service_uuid_string: 354729074c4SMatthias Ringwald fout.write("\n") 355729074c4SMatthias Ringwald # print("append service %s = [%d, %d]" % (current_characteristic_uuid_string, current_service_start_handle, handle-1)) 356729074c4SMatthias Ringwald defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle)) 357729074c4SMatthias Ringwald defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1)) 358729074c4SMatthias Ringwald services[current_service_uuid_string] = [current_service_start_handle, handle-1] 359729074c4SMatthias Ringwald 360d7ec1d24SMatthias Ringwalddef dump_flags(fout, flags): 361d7ec1d24SMatthias Ringwald global security_permsission 362d7ec1d24SMatthias Ringwald encryption_key_size = encryption_key_size_from_flags(flags) 363d7ec1d24SMatthias Ringwald read_permissions = security_permsission[read_permissions_from_flags(flags)] 364d7ec1d24SMatthias Ringwald write_permissions = security_permsission[write_permissions_from_flags(flags)] 365d7ec1d24SMatthias Ringwald write_indent(fout) 366d7ec1d24SMatthias Ringwald fout.write('// ') 367d7ec1d24SMatthias Ringwald first = 1 368d7ec1d24SMatthias Ringwald if flags & property_flags['READ']: 369d7ec1d24SMatthias Ringwald fout.write('READ_%s' % read_permissions) 370d7ec1d24SMatthias Ringwald first = 0 371d7ec1d24SMatthias Ringwald if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']): 372d7ec1d24SMatthias Ringwald if not first: 373d7ec1d24SMatthias Ringwald fout.write(', ') 374d7ec1d24SMatthias Ringwald first = 0 375d7ec1d24SMatthias Ringwald fout.write('WRITE_%s' % write_permissions) 376d7ec1d24SMatthias Ringwald if encryption_key_size > 0: 377d7ec1d24SMatthias Ringwald if not first: 378d7ec1d24SMatthias Ringwald fout.write(', ') 379d7ec1d24SMatthias Ringwald first = 0 380d7ec1d24SMatthias Ringwald fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size) 381d7ec1d24SMatthias Ringwald fout.write('\n') 382d7ec1d24SMatthias Ringwald 383043f8832SMatthias Ringwalddef database_hash_append_uint8(value): 384043f8832SMatthias Ringwald global database_hash_message 385043f8832SMatthias Ringwald database_hash_message.append(value) 386043f8832SMatthias Ringwald 387043f8832SMatthias Ringwalddef database_hash_append_uint16(value): 388043f8832SMatthias Ringwald global database_hash_message 389043f8832SMatthias Ringwald database_hash_append_uint8(value & 0xff) 390043f8832SMatthias Ringwald database_hash_append_uint8((value >> 8) & 0xff) 391043f8832SMatthias Ringwald 392043f8832SMatthias Ringwalddef database_hash_append_value(value): 393043f8832SMatthias Ringwald global database_hash_message 394043f8832SMatthias Ringwald for byte in value: 395043f8832SMatthias Ringwald database_hash_append_uint8(byte) 396043f8832SMatthias Ringwald 397b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type): 398b3fcedb9SMatthias Ringwald global handle 399b3fcedb9SMatthias Ringwald global total_size 400b3fcedb9SMatthias Ringwald global current_service_uuid_string 401b3fcedb9SMatthias Ringwald global current_service_start_handle 402b3fcedb9SMatthias Ringwald 403729074c4SMatthias Ringwald serviceDefinitionComplete(fout) 404b3fcedb9SMatthias Ringwald 405d7ec1d24SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 406b3fcedb9SMatthias Ringwald 407b3fcedb9SMatthias Ringwald write_indent(fout) 408b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 409b3fcedb9SMatthias Ringwald 410b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 411b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 412b3fcedb9SMatthias Ringwald 413b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + uuid_size + 2 414b3fcedb9SMatthias Ringwald 415b3fcedb9SMatthias Ringwald if service_type == 0x2802: 416b3fcedb9SMatthias Ringwald size += 4 417b3fcedb9SMatthias Ringwald 418b3fcedb9SMatthias Ringwald write_indent(fout) 419b3fcedb9SMatthias Ringwald write_16(fout, size) 420d7ec1d24SMatthias Ringwald write_16(fout, read_only_anybody_flags) 421b3fcedb9SMatthias Ringwald write_16(fout, handle) 422b3fcedb9SMatthias Ringwald write_16(fout, service_type) 423285653b2SMatthias Ringwald write_uuid(fout, uuid) 424b3fcedb9SMatthias Ringwald fout.write("\n") 425b3fcedb9SMatthias Ringwald 426043f8832SMatthias Ringwald database_hash_append_uint16(handle) 427043f8832SMatthias Ringwald database_hash_append_uint16(service_type) 428043f8832SMatthias Ringwald database_hash_append_value(uuid) 429043f8832SMatthias Ringwald 430729074c4SMatthias Ringwald current_service_uuid_string = c_string_for_uuid(parts[1]) 431b3fcedb9SMatthias Ringwald current_service_start_handle = handle 432b3fcedb9SMatthias Ringwald handle = handle + 1 433b3fcedb9SMatthias Ringwald total_size = total_size + size 434b3fcedb9SMatthias Ringwald 435b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts): 436b3fcedb9SMatthias Ringwald parseService(fout, parts, 0x2800) 437b3fcedb9SMatthias Ringwald 438b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts): 439b3fcedb9SMatthias Ringwald parseService(fout, parts, 0x2801) 440b3fcedb9SMatthias Ringwald 441b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts): 442b3fcedb9SMatthias Ringwald global handle 443b3fcedb9SMatthias Ringwald global total_size 444b3fcedb9SMatthias Ringwald 445e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 446b3fcedb9SMatthias Ringwald 447b3fcedb9SMatthias Ringwald write_indent(fout) 448b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 449b3fcedb9SMatthias Ringwald 450b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 451b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 452b3fcedb9SMatthias Ringwald if uuid_size > 2: 453b3fcedb9SMatthias Ringwald uuid_size = 0 454729074c4SMatthias Ringwald # print("Include Service ", c_string_for_uuid(uuid)) 455b3fcedb9SMatthias Ringwald 456b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 4 + uuid_size 457b3fcedb9SMatthias Ringwald 458729074c4SMatthias Ringwald keyUUID = c_string_for_uuid(parts[1]) 459b3fcedb9SMatthias Ringwald 460b3fcedb9SMatthias Ringwald write_indent(fout) 461b3fcedb9SMatthias Ringwald write_16(fout, size) 462e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 463b3fcedb9SMatthias Ringwald write_16(fout, handle) 464b3fcedb9SMatthias Ringwald write_16(fout, 0x2802) 465b3fcedb9SMatthias Ringwald write_16(fout, services[keyUUID][0]) 466b3fcedb9SMatthias Ringwald write_16(fout, services[keyUUID][1]) 467b3fcedb9SMatthias Ringwald if uuid_size > 0: 468285653b2SMatthias Ringwald write_uuid(fout, uuid) 469b3fcedb9SMatthias Ringwald fout.write("\n") 470b3fcedb9SMatthias Ringwald 471043f8832SMatthias Ringwald database_hash_append_uint16(handle) 472043f8832SMatthias Ringwald database_hash_append_uint16(0x2802) 473043f8832SMatthias Ringwald database_hash_append_uint16(services[keyUUID][0]) 474043f8832SMatthias Ringwald database_hash_append_uint16(services[keyUUID][1]) 475043f8832SMatthias Ringwald if uuid_size > 0: 476043f8832SMatthias Ringwald database_hash_append_value(uuid) 477043f8832SMatthias Ringwald 478b3fcedb9SMatthias Ringwald handle = handle + 1 479b3fcedb9SMatthias Ringwald total_size = total_size + size 480b3fcedb9SMatthias Ringwald 481b3fcedb9SMatthias Ringwald 482b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts): 483b3fcedb9SMatthias Ringwald global handle 484b3fcedb9SMatthias Ringwald global total_size 485b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 486b3fcedb9SMatthias Ringwald global characteristic_indices 487b3fcedb9SMatthias Ringwald 488e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 489b3fcedb9SMatthias Ringwald 490b3fcedb9SMatthias Ringwald # enumerate characteristics with same UUID, using optional name tag if available 491b3fcedb9SMatthias Ringwald current_characteristic_uuid_string = c_string_for_uuid(parts[1]); 492b3fcedb9SMatthias Ringwald index = 1 493b3fcedb9SMatthias Ringwald if current_characteristic_uuid_string in characteristic_indices: 494b3fcedb9SMatthias Ringwald index = characteristic_indices[current_characteristic_uuid_string] + 1 495b3fcedb9SMatthias Ringwald characteristic_indices[current_characteristic_uuid_string] = index 496b3fcedb9SMatthias Ringwald if len(parts) > 4: 497b3fcedb9SMatthias Ringwald current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_') 498b3fcedb9SMatthias Ringwald else: 499b3fcedb9SMatthias Ringwald current_characteristic_uuid_string += ('_%02x' % index) 500b3fcedb9SMatthias Ringwald 501b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 502b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 503b3fcedb9SMatthias Ringwald properties = parseProperties(parts[2]) 504b3fcedb9SMatthias Ringwald value = ', '.join([str(x) for x in parts[3:]]) 505b3fcedb9SMatthias Ringwald 506b3fcedb9SMatthias Ringwald # reliable writes is defined in an extended properties 507b3fcedb9SMatthias Ringwald if (properties & property_flags['RELIABLE_WRITE']): 508b3fcedb9SMatthias Ringwald properties = properties | property_flags['EXTENDED_PROPERTIES'] 509b3fcedb9SMatthias Ringwald 510b3fcedb9SMatthias Ringwald write_indent(fout) 511b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3]))) 512b3fcedb9SMatthias Ringwald 513e22a2612SMatthias Ringwald 514e22a2612SMatthias Ringwald characteristic_properties = gatt_characteristic_properties(properties) 515b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + (1+2+uuid_size) 516b3fcedb9SMatthias Ringwald write_indent(fout) 517b3fcedb9SMatthias Ringwald write_16(fout, size) 518e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 519b3fcedb9SMatthias Ringwald write_16(fout, handle) 520b3fcedb9SMatthias Ringwald write_16(fout, 0x2803) 521e22a2612SMatthias Ringwald write_8(fout, characteristic_properties) 522b3fcedb9SMatthias Ringwald write_16(fout, handle+1) 523285653b2SMatthias Ringwald write_uuid(fout, uuid) 524b3fcedb9SMatthias Ringwald fout.write("\n") 525b3fcedb9SMatthias Ringwald handle = handle + 1 526b3fcedb9SMatthias Ringwald total_size = total_size + size 527b3fcedb9SMatthias Ringwald 528043f8832SMatthias Ringwald database_hash_append_uint16(handle) 529043f8832SMatthias Ringwald database_hash_append_uint16(0x2803) 530043f8832SMatthias Ringwald database_hash_append_uint8(characteristic_properties) 531043f8832SMatthias Ringwald database_hash_append_uint16(handle+1) 532043f8832SMatthias Ringwald database_hash_append_value(uuid) 533043f8832SMatthias Ringwald 534043f8832SMatthias Ringwald uuid_is_database_hash = len(uuid) == 2 and uuid[0] == 0x2a and uuid[1] == 0x2b 535043f8832SMatthias Ringwald 536b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + uuid_size 537043f8832SMatthias Ringwald if uuid_is_database_hash: 538043f8832SMatthias Ringwald size += 16 539043f8832SMatthias Ringwald else: 540b3fcedb9SMatthias Ringwald if is_string(value): 541b3fcedb9SMatthias Ringwald size = size + len(value) 542b3fcedb9SMatthias Ringwald else: 543b3fcedb9SMatthias Ringwald size = size + len(value.split()) 544b3fcedb9SMatthias Ringwald 545e22a2612SMatthias Ringwald value_flags = att_flags(properties) 5468ea3236cSMatthias Ringwald 5478ea3236cSMatthias Ringwald # add UUID128 flag for value handle 548b3fcedb9SMatthias Ringwald if uuid_size == 16: 549e22a2612SMatthias Ringwald value_flags = value_flags | property_flags['LONG_UUID']; 550b3fcedb9SMatthias Ringwald 551b3fcedb9SMatthias Ringwald write_indent(fout) 552b3fcedb9SMatthias Ringwald fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value)) 553d7ec1d24SMatthias Ringwald 554d7ec1d24SMatthias Ringwald dump_flags(fout, value_flags) 555d7ec1d24SMatthias Ringwald 556b3fcedb9SMatthias Ringwald write_indent(fout) 557b3fcedb9SMatthias Ringwald write_16(fout, size) 558e22a2612SMatthias Ringwald write_16(fout, value_flags) 559b3fcedb9SMatthias Ringwald write_16(fout, handle) 560285653b2SMatthias Ringwald write_uuid(fout, uuid) 561043f8832SMatthias Ringwald if uuid_is_database_hash: 562043f8832SMatthias Ringwald write_database_hash(fout) 563043f8832SMatthias Ringwald else: 564b3fcedb9SMatthias Ringwald if is_string(value): 565b3fcedb9SMatthias Ringwald write_string(fout, value) 566b3fcedb9SMatthias Ringwald else: 567b3fcedb9SMatthias Ringwald write_sequence(fout,value) 568b3fcedb9SMatthias Ringwald 569b3fcedb9SMatthias Ringwald fout.write("\n") 570729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 571b3fcedb9SMatthias Ringwald handle = handle + 1 572b3fcedb9SMatthias Ringwald 573b3fcedb9SMatthias Ringwald if add_client_characteristic_configuration(properties): 574e22a2612SMatthias Ringwald # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC 575d7ec1d24SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 576e22a2612SMatthias Ringwald flags |= property_flags['READ'] 577e22a2612SMatthias Ringwald flags |= property_flags['WRITE'] 5789be4aecfSMatthias Ringwald flags |= property_flags['WRITE_WITHOUT_RESPONSE'] 579e22a2612SMatthias Ringwald flags |= property_flags['DYNAMIC'] 580b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 2 581d7ec1d24SMatthias Ringwald 582b3fcedb9SMatthias Ringwald write_indent(fout) 583b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle)) 584d7ec1d24SMatthias Ringwald 585d7ec1d24SMatthias Ringwald dump_flags(fout, flags) 586d7ec1d24SMatthias Ringwald 587b3fcedb9SMatthias Ringwald write_indent(fout) 588b3fcedb9SMatthias Ringwald write_16(fout, size) 589e22a2612SMatthias Ringwald write_16(fout, flags) 590b3fcedb9SMatthias Ringwald write_16(fout, handle) 591b3fcedb9SMatthias Ringwald write_16(fout, 0x2902) 592b3fcedb9SMatthias Ringwald write_16(fout, 0) 593b3fcedb9SMatthias Ringwald fout.write("\n") 594043f8832SMatthias Ringwald 595043f8832SMatthias Ringwald database_hash_append_uint16(handle) 596043f8832SMatthias Ringwald database_hash_append_uint16(0x2902) 597043f8832SMatthias Ringwald 598729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 599b3fcedb9SMatthias Ringwald handle = handle + 1 600b3fcedb9SMatthias Ringwald 601043f8832SMatthias Ringwald 602b3fcedb9SMatthias Ringwald if properties & property_flags['RELIABLE_WRITE']: 603b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 2 604b3fcedb9SMatthias Ringwald write_indent(fout) 605b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle)) 606b3fcedb9SMatthias Ringwald write_indent(fout) 607b3fcedb9SMatthias Ringwald write_16(fout, size) 608e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 609b3fcedb9SMatthias Ringwald write_16(fout, handle) 610b3fcedb9SMatthias Ringwald write_16(fout, 0x2900) 611b3fcedb9SMatthias Ringwald write_16(fout, 1) # Reliable Write 612b3fcedb9SMatthias Ringwald fout.write("\n") 613043f8832SMatthias Ringwald 614043f8832SMatthias Ringwald database_hash_append_uint16(handle) 615043f8832SMatthias Ringwald database_hash_append_uint16(0x2900) 616043f8832SMatthias Ringwald database_hash_append_uint16(1) 617043f8832SMatthias Ringwald 618b3fcedb9SMatthias Ringwald handle = handle + 1 619b3fcedb9SMatthias Ringwald 620b3fcedb9SMatthias Ringwalddef parseCharacteristicUserDescription(fout, parts): 621b3fcedb9SMatthias Ringwald global handle 622b3fcedb9SMatthias Ringwald global total_size 623b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 624b3fcedb9SMatthias Ringwald 625b3fcedb9SMatthias Ringwald properties = parseProperties(parts[1]) 626b3fcedb9SMatthias Ringwald value = parts[2] 627b3fcedb9SMatthias Ringwald 628b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 629b3fcedb9SMatthias Ringwald if is_string(value): 630b7647eb6SMatthias Ringwald size = size + len(value) 631b3fcedb9SMatthias Ringwald else: 632b3fcedb9SMatthias Ringwald size = size + len(value.split()) 633b3fcedb9SMatthias Ringwald 634e22a2612SMatthias Ringwald # use write, write permissions and encryption key size from attribute value and set READ_ANYBODY 635d7ec1d24SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 636e22a2612SMatthias Ringwald flags |= properties & property_flags['WRITE'] 637e22a2612SMatthias Ringwald flags |= property_flags['READ'] 638e22a2612SMatthias Ringwald 639b3fcedb9SMatthias Ringwald write_indent(fout) 640b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:]))) 641d7ec1d24SMatthias Ringwald 642d7ec1d24SMatthias Ringwald dump_flags(fout, flags) 643d7ec1d24SMatthias Ringwald 644b3fcedb9SMatthias Ringwald write_indent(fout) 645b3fcedb9SMatthias Ringwald write_16(fout, size) 646e22a2612SMatthias Ringwald write_16(fout, flags) 647b3fcedb9SMatthias Ringwald write_16(fout, handle) 648b3fcedb9SMatthias Ringwald write_16(fout, 0x2901) 649b3fcedb9SMatthias Ringwald if is_string(value): 650b3fcedb9SMatthias Ringwald write_string(fout, value) 651b3fcedb9SMatthias Ringwald else: 652b3fcedb9SMatthias Ringwald write_sequence(fout,value) 653b3fcedb9SMatthias Ringwald fout.write("\n") 654043f8832SMatthias Ringwald 655043f8832SMatthias Ringwald database_hash_append_uint16(handle) 656043f8832SMatthias Ringwald database_hash_append_uint16(0x2901) 657043f8832SMatthias Ringwald 658729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 659b3fcedb9SMatthias Ringwald handle = handle + 1 660b3fcedb9SMatthias Ringwald 661b3fcedb9SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts): 662b3fcedb9SMatthias Ringwald global handle 663b3fcedb9SMatthias Ringwald global total_size 664b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 665b3fcedb9SMatthias Ringwald 666b3fcedb9SMatthias Ringwald properties = parseProperties(parts[1]) 667b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 668b3fcedb9SMatthias Ringwald 669e22a2612SMatthias Ringwald # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY 670d7ec1d24SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 671e22a2612SMatthias Ringwald flags |= property_flags['READ'] 672e22a2612SMatthias Ringwald flags |= property_flags['WRITE'] 673e22a2612SMatthias Ringwald flags |= property_flags['DYNAMIC'] 674e22a2612SMatthias Ringwald 675b3fcedb9SMatthias Ringwald write_indent(fout) 676b3fcedb9SMatthias Ringwald fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:]))) 677d7ec1d24SMatthias Ringwald 678d7ec1d24SMatthias Ringwald dump_flags(fout, flags) 679d7ec1d24SMatthias Ringwald 680b3fcedb9SMatthias Ringwald write_indent(fout) 681b3fcedb9SMatthias Ringwald write_16(fout, size) 682e22a2612SMatthias Ringwald write_16(fout, flags) 683b3fcedb9SMatthias Ringwald write_16(fout, handle) 684b3fcedb9SMatthias Ringwald write_16(fout, 0x2903) 685b3fcedb9SMatthias Ringwald fout.write("\n") 686043f8832SMatthias Ringwald 687043f8832SMatthias Ringwald database_hash_append_uint16(handle) 688043f8832SMatthias Ringwald database_hash_append_uint16(0x2903) 689043f8832SMatthias Ringwald 690729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 691b3fcedb9SMatthias Ringwald handle = handle + 1 692b3fcedb9SMatthias Ringwald 693b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts): 694b3fcedb9SMatthias Ringwald global handle 695b3fcedb9SMatthias Ringwald global total_size 696b3fcedb9SMatthias Ringwald 697e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 698b3fcedb9SMatthias Ringwald 699b3fcedb9SMatthias Ringwald identifier = parts[1] 700b3fcedb9SMatthias Ringwald presentation_formats[identifier] = handle 701b3fcedb9SMatthias Ringwald # print("format '%s' with handle %d\n" % (identifier, handle)) 702b3fcedb9SMatthias Ringwald 703b3fcedb9SMatthias Ringwald format = parts[2] 704b3fcedb9SMatthias Ringwald exponent = parts[3] 705b3fcedb9SMatthias Ringwald unit = parseUUID(parts[4]) 706b3fcedb9SMatthias Ringwald name_space = parts[5] 707b3fcedb9SMatthias Ringwald description = parseUUID(parts[6]) 708b3fcedb9SMatthias Ringwald 709b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 7 710b3fcedb9SMatthias Ringwald 711b3fcedb9SMatthias Ringwald write_indent(fout) 712b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 713b3fcedb9SMatthias Ringwald write_indent(fout) 714b3fcedb9SMatthias Ringwald write_16(fout, size) 715e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 716b3fcedb9SMatthias Ringwald write_16(fout, handle) 717b3fcedb9SMatthias Ringwald write_16(fout, 0x2904) 718b3fcedb9SMatthias Ringwald write_sequence(fout, format) 719b3fcedb9SMatthias Ringwald write_sequence(fout, exponent) 720285653b2SMatthias Ringwald write_uuid(fout, unit) 721b3fcedb9SMatthias Ringwald write_sequence(fout, name_space) 722285653b2SMatthias Ringwald write_uuid(fout, description) 723b3fcedb9SMatthias Ringwald fout.write("\n") 724043f8832SMatthias Ringwald 725043f8832SMatthias Ringwald database_hash_append_uint16(handle) 726043f8832SMatthias Ringwald database_hash_append_uint16(0x2904) 727043f8832SMatthias Ringwald 728b3fcedb9SMatthias Ringwald handle = handle + 1 729b3fcedb9SMatthias Ringwald 730b3fcedb9SMatthias Ringwald 731b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts): 732b3fcedb9SMatthias Ringwald global handle 733b3fcedb9SMatthias Ringwald global total_size 734b3fcedb9SMatthias Ringwald 735e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 736b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2 737b3fcedb9SMatthias Ringwald 738b3fcedb9SMatthias Ringwald write_indent(fout) 739b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 740b3fcedb9SMatthias Ringwald write_indent(fout) 741b3fcedb9SMatthias Ringwald write_16(fout, size) 742e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 743b3fcedb9SMatthias Ringwald write_16(fout, handle) 744b3fcedb9SMatthias Ringwald write_16(fout, 0x2905) 745b3fcedb9SMatthias Ringwald for identifier in parts[1:]: 746b3fcedb9SMatthias Ringwald format_handle = presentation_formats[identifier] 747b3fcedb9SMatthias Ringwald if format == 0: 748b3fcedb9SMatthias Ringwald print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier) 749b3fcedb9SMatthias Ringwald sys.exit(1) 750b3fcedb9SMatthias Ringwald write_16(fout, format_handle) 751b3fcedb9SMatthias Ringwald fout.write("\n") 752043f8832SMatthias Ringwald 753043f8832SMatthias Ringwald database_hash_append_uint16(handle) 754043f8832SMatthias Ringwald database_hash_append_uint16(0x2905) 755043f8832SMatthias Ringwald 756b3fcedb9SMatthias Ringwald handle = handle + 1 757b3fcedb9SMatthias Ringwald 758b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts): 759b3fcedb9SMatthias Ringwald global handle 760b3fcedb9SMatthias Ringwald global total_size 761b3fcedb9SMatthias Ringwald 762e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 763b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 1 + 1 764b3fcedb9SMatthias Ringwald 765231a3c5dSMatthias Ringwald report_id = parts[2] 766231a3c5dSMatthias Ringwald report_type = parts[3] 767b3fcedb9SMatthias Ringwald 768b3fcedb9SMatthias Ringwald write_indent(fout) 769b3fcedb9SMatthias Ringwald fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:]))) 770b3fcedb9SMatthias Ringwald write_indent(fout) 771b3fcedb9SMatthias Ringwald write_16(fout, size) 772e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 773b3fcedb9SMatthias Ringwald write_16(fout, handle) 774b3fcedb9SMatthias Ringwald write_16(fout, 0x2908) 775b3fcedb9SMatthias Ringwald write_sequence(fout, report_id) 776b3fcedb9SMatthias Ringwald write_sequence(fout, report_type) 777b3fcedb9SMatthias Ringwald fout.write("\n") 778b3fcedb9SMatthias Ringwald handle = handle + 1 779b3fcedb9SMatthias Ringwald 780b3fcedb9SMatthias Ringwald 781b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts): 782b3fcedb9SMatthias Ringwald global handle 783b3fcedb9SMatthias Ringwald global total_size 784b3fcedb9SMatthias Ringwald 785e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 786b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 1 787b3fcedb9SMatthias Ringwald 788b3fcedb9SMatthias Ringwald no_of_digitals = parts[1] 789b3fcedb9SMatthias Ringwald 790b3fcedb9SMatthias Ringwald write_indent(fout) 791b3fcedb9SMatthias Ringwald fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:]))) 792b3fcedb9SMatthias Ringwald write_indent(fout) 793b3fcedb9SMatthias Ringwald write_16(fout, size) 794e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 795b3fcedb9SMatthias Ringwald write_16(fout, handle) 796b3fcedb9SMatthias Ringwald write_16(fout, 0x2909) 797b3fcedb9SMatthias Ringwald write_sequence(fout, no_of_digitals) 798b3fcedb9SMatthias Ringwald fout.write("\n") 799b3fcedb9SMatthias Ringwald handle = handle + 1 800b3fcedb9SMatthias Ringwald 80160b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout): 802b3fcedb9SMatthias Ringwald global handle 803b3fcedb9SMatthias Ringwald global total_size 804b3fcedb9SMatthias Ringwald 805b165f97bSMatthias Ringwald line_count = 0; 806b3fcedb9SMatthias Ringwald for line in fin: 807b3fcedb9SMatthias Ringwald line = line.strip("\n\r ") 808b165f97bSMatthias Ringwald line_count += 1 809b3fcedb9SMatthias Ringwald 810b165f97bSMatthias Ringwald if line.startswith("//"): 811b165f97bSMatthias Ringwald fout.write(" //" + line.lstrip('/') + '\n') 812b165f97bSMatthias Ringwald continue 813b165f97bSMatthias Ringwald 81460b51a4cSMatthias Ringwald if line.startswith("#import"): 81560b51a4cSMatthias Ringwald imported_file = '' 81660b51a4cSMatthias Ringwald parts = re.match('#import\s+<(.*)>\w*',line) 81760b51a4cSMatthias Ringwald if parts and len(parts.groups()) == 1: 818dbb3997aSMilanka Ringwald imported_file = parts.groups()[0] 81960b51a4cSMatthias Ringwald parts = re.match('#import\s+"(.*)"\w*',line) 82060b51a4cSMatthias Ringwald if parts and len(parts.groups()) == 1: 821dbb3997aSMilanka Ringwald imported_file = parts.groups()[0] 82260b51a4cSMatthias Ringwald if len(imported_file) == 0: 82360b51a4cSMatthias Ringwald print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count)) 82460b51a4cSMatthias Ringwald continue 82560b51a4cSMatthias Ringwald 826dbb3997aSMilanka Ringwald imported_file = getFile( imported_file ) 82760b51a4cSMatthias Ringwald print("Importing %s" % imported_file) 82860b51a4cSMatthias Ringwald try: 82960b51a4cSMatthias Ringwald imported_fin = codecs.open (imported_file, encoding='utf-8') 83060b51a4cSMatthias Ringwald fout.write(' // ' + line + ' -- BEGIN\n') 83160b51a4cSMatthias Ringwald parseLines(imported_file, imported_fin, fout) 83260b51a4cSMatthias Ringwald fout.write(' // ' + line + ' -- END\n') 83360b51a4cSMatthias Ringwald except IOError as e: 83460b51a4cSMatthias Ringwald print('ERROR: Import failed. Please check path.') 83560b51a4cSMatthias Ringwald 83660b51a4cSMatthias Ringwald continue 83760b51a4cSMatthias Ringwald 83860b51a4cSMatthias Ringwald if line.startswith("#TODO"): 83960b51a4cSMatthias Ringwald print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count)) 840b165f97bSMatthias Ringwald print ("'%s'" % line) 841b165f97bSMatthias Ringwald fout.write("// " + line + '\n') 842b3fcedb9SMatthias Ringwald continue 843b3fcedb9SMatthias Ringwald 844b3fcedb9SMatthias Ringwald if len(line) == 0: 845b3fcedb9SMatthias Ringwald continue 846b3fcedb9SMatthias Ringwald 847b3fcedb9SMatthias Ringwald f = io.StringIO(line) 848b3fcedb9SMatthias Ringwald parts_list = csv.reader(f, delimiter=',', quotechar='"') 849b3fcedb9SMatthias Ringwald 850b3fcedb9SMatthias Ringwald for parts in parts_list: 851b3fcedb9SMatthias Ringwald for index, object in enumerate(parts): 852b3fcedb9SMatthias Ringwald parts[index] = object.strip().lstrip('"').rstrip('"') 853b3fcedb9SMatthias Ringwald 854b3fcedb9SMatthias Ringwald if parts[0] == 'PRIMARY_SERVICE': 855b3fcedb9SMatthias Ringwald parsePrimaryService(fout, parts) 856b3fcedb9SMatthias Ringwald continue 857b3fcedb9SMatthias Ringwald 858b3fcedb9SMatthias Ringwald if parts[0] == 'SECONDARY_SERVICE': 859b3fcedb9SMatthias Ringwald parseSecondaryService(fout, parts) 860b3fcedb9SMatthias Ringwald continue 861b3fcedb9SMatthias Ringwald 862b3fcedb9SMatthias Ringwald if parts[0] == 'INCLUDE_SERVICE': 863b3fcedb9SMatthias Ringwald parseIncludeService(fout, parts) 864b3fcedb9SMatthias Ringwald continue 865b3fcedb9SMatthias Ringwald 866b3fcedb9SMatthias Ringwald # 2803 867b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC': 868b3fcedb9SMatthias Ringwald parseCharacteristic(fout, parts) 869b3fcedb9SMatthias Ringwald continue 870b3fcedb9SMatthias Ringwald 871b3fcedb9SMatthias Ringwald # 2900 Characteristic Extended Properties 872b3fcedb9SMatthias Ringwald 873b3fcedb9SMatthias Ringwald # 2901 874b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION': 875b3fcedb9SMatthias Ringwald parseCharacteristicUserDescription(fout, parts) 876b3fcedb9SMatthias Ringwald continue 877b3fcedb9SMatthias Ringwald 878b165f97bSMatthias Ringwald 879b165f97bSMatthias Ringwald # 2902 Client Characteristic Configuration - automatically included in Characteristic if 880b3fcedb9SMatthias Ringwald # notification / indication is supported 881231a3c5dSMatthias Ringwald if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION': 882b165f97bSMatthias Ringwald continue 883b3fcedb9SMatthias Ringwald 884b3fcedb9SMatthias Ringwald # 2903 885b3fcedb9SMatthias Ringwald if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION': 886b3fcedb9SMatthias Ringwald parseServerCharacteristicConfiguration(fout, parts) 887b3fcedb9SMatthias Ringwald continue 888b3fcedb9SMatthias Ringwald 889b3fcedb9SMatthias Ringwald # 2904 890b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_FORMAT': 891b3fcedb9SMatthias Ringwald parseCharacteristicFormat(fout, parts) 892b3fcedb9SMatthias Ringwald continue 893b3fcedb9SMatthias Ringwald 894b3fcedb9SMatthias Ringwald # 2905 895b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT': 896b3fcedb9SMatthias Ringwald parseCharacteristicAggregateFormat(fout, parts) 897b3fcedb9SMatthias Ringwald continue 898b3fcedb9SMatthias Ringwald 899b3fcedb9SMatthias Ringwald # 2906 900b3fcedb9SMatthias Ringwald if parts[0] == 'VALID_RANGE': 901b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 902b3fcedb9SMatthias Ringwald continue 903b3fcedb9SMatthias Ringwald 904b3fcedb9SMatthias Ringwald # 2907 905b3fcedb9SMatthias Ringwald if parts[0] == 'EXTERNAL_REPORT_REFERENCE': 906b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 907b3fcedb9SMatthias Ringwald continue 908b3fcedb9SMatthias Ringwald 909b3fcedb9SMatthias Ringwald # 2908 910b3fcedb9SMatthias Ringwald if parts[0] == 'REPORT_REFERENCE': 911b3fcedb9SMatthias Ringwald parseReportReference(fout, parts) 912b3fcedb9SMatthias Ringwald continue 913b3fcedb9SMatthias Ringwald 914b3fcedb9SMatthias Ringwald # 2909 915b3fcedb9SMatthias Ringwald if parts[0] == 'NUMBER_OF_DIGITALS': 916b3fcedb9SMatthias Ringwald parseNumberOfDigitals(fout, parts) 917b3fcedb9SMatthias Ringwald continue 918b3fcedb9SMatthias Ringwald 919b3fcedb9SMatthias Ringwald # 290A 920b3fcedb9SMatthias Ringwald if parts[0] == 'VALUE_TRIGGER_SETTING': 921b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 922b3fcedb9SMatthias Ringwald continue 923b3fcedb9SMatthias Ringwald 924b3fcedb9SMatthias Ringwald # 290B 925b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION': 926b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 927b3fcedb9SMatthias Ringwald continue 928b3fcedb9SMatthias Ringwald 929b3fcedb9SMatthias Ringwald # 290C 930b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT': 931b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 932b3fcedb9SMatthias Ringwald continue 933b3fcedb9SMatthias Ringwald 934b3fcedb9SMatthias Ringwald # 290D 935b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING': 936b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 937b3fcedb9SMatthias Ringwald continue 938b3fcedb9SMatthias Ringwald 939b3fcedb9SMatthias Ringwald # 2906 940b3fcedb9SMatthias Ringwald if parts[0] == 'VALID_RANGE': 941b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 942b3fcedb9SMatthias Ringwald continue 943b3fcedb9SMatthias Ringwald 944b3fcedb9SMatthias Ringwald print("WARNING: unknown token: %s\n" % (parts[0])) 945b3fcedb9SMatthias Ringwald 9467050bf34SMatthias Ringwalddef parse(fname_in, fin, fname_out, tool_path, fout): 94760b51a4cSMatthias Ringwald global handle 94860b51a4cSMatthias Ringwald global total_size 94960b51a4cSMatthias Ringwald 9507050bf34SMatthias Ringwald fout.write(header.format(fname_out, fname_in, tool_path)) 95160b51a4cSMatthias Ringwald fout.write('{\n') 952fd1be25dSMatthias Ringwald write_indent(fout) 953fd1be25dSMatthias Ringwald fout.write('// ATT DB Version\n') 954fd1be25dSMatthias Ringwald write_indent(fout) 955fd1be25dSMatthias Ringwald fout.write('1,\n') 956fd1be25dSMatthias Ringwald fout.write("\n") 95760b51a4cSMatthias Ringwald 95860b51a4cSMatthias Ringwald parseLines(fname_in, fin, fout) 95960b51a4cSMatthias Ringwald 960729074c4SMatthias Ringwald serviceDefinitionComplete(fout) 961b3fcedb9SMatthias Ringwald write_indent(fout) 962b3fcedb9SMatthias Ringwald fout.write("// END\n"); 963b3fcedb9SMatthias Ringwald write_indent(fout) 964b3fcedb9SMatthias Ringwald write_16(fout,0) 965b3fcedb9SMatthias Ringwald fout.write("\n") 966b3fcedb9SMatthias Ringwald total_size = total_size + 2 967b3fcedb9SMatthias Ringwald 968b3fcedb9SMatthias Ringwald fout.write("}; // total size %u bytes \n" % total_size); 969b3fcedb9SMatthias Ringwald 970b3fcedb9SMatthias Ringwalddef listHandles(fout): 971b3fcedb9SMatthias Ringwald fout.write('\n\n') 972b3fcedb9SMatthias Ringwald fout.write('//\n') 973729074c4SMatthias Ringwald fout.write('// list service handle ranges\n') 974729074c4SMatthias Ringwald fout.write('//\n') 975729074c4SMatthias Ringwald for define in defines_for_services: 976729074c4SMatthias Ringwald fout.write(define) 977729074c4SMatthias Ringwald fout.write('\n') 978729074c4SMatthias Ringwald fout.write('\n') 979729074c4SMatthias Ringwald fout.write('//\n') 980b3fcedb9SMatthias Ringwald fout.write('// list mapping between characteristics and handles\n') 981b3fcedb9SMatthias Ringwald fout.write('//\n') 982729074c4SMatthias Ringwald for define in defines_for_characteristics: 983b3fcedb9SMatthias Ringwald fout.write(define) 984b3fcedb9SMatthias Ringwald fout.write('\n') 985b3fcedb9SMatthias Ringwald 986dbb3997aSMilanka Ringwalddef getFile( fileName ): 98778b65b0aSMatthias Ringwald for d in include_paths: 98878b65b0aSMatthias Ringwald fullFile = os.path.normpath(d + os.sep + fileName) # because Windows exists 98978b65b0aSMatthias Ringwald # print("test %s" % fullFile) 990dbb3997aSMilanka Ringwald if os.path.isfile( fullFile ) == True: 991dbb3997aSMilanka Ringwald return fullFile 992dbb3997aSMilanka Ringwald print ("'{0}' not found".format( fileName )) 99378b65b0aSMatthias Ringwald print ("Include paths: %s" % ", ".join(include_paths)) 994dbb3997aSMilanka Ringwald exit(-1) 995dbb3997aSMilanka Ringwald 996dbb3997aSMilanka Ringwald 997dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') 99878b65b0aSMatthias Ringwalddefault_includes = [os.path.normpath(path) for path in [ btstack_root + '/src/', btstack_root + '/src/ble/gatt-service/']] 999dbb3997aSMilanka Ringwald 1000dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack') 1001dbb3997aSMilanka Ringwald 1002dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes', 1003dbb3997aSMilanka Ringwald help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes)) 1004dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str, 1005dbb3997aSMilanka Ringwald help='gatt file to be compiled') 1006dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str, 1007dbb3997aSMilanka Ringwald help='header file to be generated') 1008dbb3997aSMilanka Ringwald 1009dbb3997aSMilanka Ringwaldargs = parser.parse_args() 1010dbb3997aSMilanka Ringwald 101178b65b0aSMatthias Ringwald# add include path arguments 101278b65b0aSMatthias Ringwaldif args.I != None: 101378b65b0aSMatthias Ringwald for d in args.I: 101478b65b0aSMatthias Ringwald include_paths.append(os.path.normpath(d[0])) 101578b65b0aSMatthias Ringwald 1016dbb3997aSMilanka Ringwald# append default include paths 101778b65b0aSMatthias Ringwaldinclude_paths.extend(default_includes) 1018dbb3997aSMilanka Ringwald 1019b3fcedb9SMatthias Ringwaldtry: 1020b165f97bSMatthias Ringwald # read defines from bluetooth_gatt.h 1021dbb3997aSMilanka Ringwald gen_path = getFile( 'bluetooth_gatt.h' ) 1022b165f97bSMatthias Ringwald bluetooth_gatt = read_defines(gen_path) 1023b165f97bSMatthias Ringwald 1024dbb3997aSMilanka Ringwald filename = args.hfile 1025dbb3997aSMilanka Ringwald fin = codecs.open (args.gattfile, encoding='utf-8') 1026285653b2SMatthias Ringwald 1027285653b2SMatthias Ringwald # pass 1: create temp .h file 1028d78181d6SMatthias Ringwald ftemp = tempfile.TemporaryFile(mode='w+t') 1029285653b2SMatthias Ringwald parse(args.gattfile, fin, filename, sys.argv[0], ftemp) 1030285653b2SMatthias Ringwald listHandles(ftemp) 1031285653b2SMatthias Ringwald 1032043f8832SMatthias Ringwald # calc GATT Database Hash 1033043f8832SMatthias Ringwald db_hash = aes_cmac(bytearray(16), database_hash_message) 1034043f8832SMatthias Ringwald if isinstance(db_hash, str): 1035043f8832SMatthias Ringwald # python2 1036043f8832SMatthias Ringwald db_hash_sequence = [('0x%02x' % ord(i)) for i in db_hash] 1037043f8832SMatthias Ringwald elif isinstance(db_hash, bytes): 1038043f8832SMatthias Ringwald # python3 1039043f8832SMatthias Ringwald db_hash_sequence = [('0x%02x' % i) for i in db_hash] 1040043f8832SMatthias Ringwald else: 1041043f8832SMatthias Ringwald print("AES CMAC returns unexpected type %s, abort" % type(db_hash)) 1042043f8832SMatthias Ringwald sys.exit(1) 1043043f8832SMatthias Ringwald # reverse hash to get little endian 1044043f8832SMatthias Ringwald db_hash_sequence.reverse() 1045043f8832SMatthias Ringwald db_hash_string = ', '.join(db_hash_sequence) + ', ' 1046043f8832SMatthias Ringwald 1047285653b2SMatthias Ringwald # pass 2: insert GATT Database Hash 1048b3fcedb9SMatthias Ringwald fout = open (filename, 'w') 1049285653b2SMatthias Ringwald ftemp.seek(0) 1050285653b2SMatthias Ringwald for line in ftemp: 1051043f8832SMatthias Ringwald fout.write(line.replace('THE-DATABASE-HASH', db_hash_string)) 1052b3fcedb9SMatthias Ringwald fout.close() 1053285653b2SMatthias Ringwald ftemp.close() 1054285653b2SMatthias Ringwald 1055b165f97bSMatthias Ringwald print('Created %s' % filename) 1056b3fcedb9SMatthias Ringwald 1057b3fcedb9SMatthias Ringwaldexcept IOError as e: 1058e22a2612SMatthias Ringwald 1059b3fcedb9SMatthias Ringwald print(usage) 1060b3fcedb9SMatthias Ringwald sys.exit(1) 1061b3fcedb9SMatthias Ringwald 1062b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n') 1063