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 12ced5a857SMatthias Ringwald# alternatively, the pycryptodome package can be used instead 13ced5a857SMatthias 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 25ced5a857SMatthias Ringwaldhave_crypto = True 26ced5a857SMatthias Ringwald# try to import PyCryptodome independent from PyCrypto 27043f8832SMatthias Ringwaldtry: 28379d3aceSMatthias Ringwald from Cryptodome.Cipher import AES 29043f8832SMatthias Ringwald from Cryptodome.Hash import CMAC 30ced5a857SMatthias Ringwaldexcept ImportError: 31ced5a857SMatthias Ringwald # fallback: try to import PyCryptodome as (an almost drop-in) replacement for the PyCrypto library 32ced5a857SMatthias Ringwald try: 33ced5a857SMatthias Ringwald from Crypto.Cipher import AES 34ced5a857SMatthias 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)") 386ccd8248SMilanka Ringwald print("[!] Please install PyCryptodome, e.g. 'pip3 install pycryptodomex' or 'pip3 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 54fa529fa7SMatthias Ringwald// Reference: https://en.cppreference.com/w/cpp/feature_test 55fa529fa7SMatthias Ringwald#if __cplusplus >= 200704L 56fa529fa7SMatthias Ringwaldconstexpr 57fa529fa7SMatthias Ringwald#endif 58b3fcedb9SMatthias Ringwaldconst uint8_t profile_data[] = 59b3fcedb9SMatthias Ringwald''' 60b3fcedb9SMatthias Ringwald 61b3fcedb9SMatthias Ringwaldprint(''' 62dbb3997aSMilanka RingwaldBLE configuration generator for use with BTstack 63dbb3997aSMilanka RingwaldCopyright 2018 BlueKitchen GmbH 64b3fcedb9SMatthias Ringwald''') 65b3fcedb9SMatthias Ringwald 66b3fcedb9SMatthias Ringwaldassigned_uuids = { 67b3fcedb9SMatthias Ringwald 'GAP_SERVICE' : 0x1800, 68b3fcedb9SMatthias Ringwald 'GATT_SERVICE' : 0x1801, 69b3fcedb9SMatthias Ringwald 'GAP_DEVICE_NAME' : 0x2a00, 70b3fcedb9SMatthias Ringwald 'GAP_APPEARANCE' : 0x2a01, 71b3fcedb9SMatthias Ringwald 'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02, 72b3fcedb9SMatthias Ringwald 'GAP_RECONNECTION_ADDRESS' : 0x2A03, 73b3fcedb9SMatthias Ringwald 'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04, 74b3fcedb9SMatthias Ringwald 'GATT_SERVICE_CHANGED' : 0x2a05, 75043f8832SMatthias Ringwald 'GATT_DATABASE_HASH' : 0x2b2a 76b3fcedb9SMatthias Ringwald} 77b3fcedb9SMatthias Ringwald 78e72176f8SMatthias Ringwaldsecurity_permsission = ['ANYBODY','ENCRYPTED', 'AUTHENTICATED', 'AUTHORIZED', 'AUTHENTICATED_SC'] 79d7ec1d24SMatthias Ringwald 80b3fcedb9SMatthias Ringwaldproperty_flags = { 81eb6072adSMatthias Ringwald # GATT Characteristic Properties 82b3fcedb9SMatthias Ringwald 'BROADCAST' : 0x01, 83b3fcedb9SMatthias Ringwald 'READ' : 0x02, 84b3fcedb9SMatthias Ringwald 'WRITE_WITHOUT_RESPONSE' : 0x04, 85b3fcedb9SMatthias Ringwald 'WRITE' : 0x08, 86b3fcedb9SMatthias Ringwald 'NOTIFY': 0x10, 87b3fcedb9SMatthias Ringwald 'INDICATE' : 0x20, 88b3fcedb9SMatthias Ringwald 'AUTHENTICATED_SIGNED_WRITE' : 0x40, 89b3fcedb9SMatthias Ringwald 'EXTENDED_PROPERTIES' : 0x80, 90b3fcedb9SMatthias Ringwald # custom BTstack extension 91b3fcedb9SMatthias Ringwald 'DYNAMIC': 0x100, 92b3fcedb9SMatthias Ringwald 'LONG_UUID': 0x200, 93e22a2612SMatthias Ringwald 94e22a2612SMatthias Ringwald # read permissions 95e22a2612SMatthias Ringwald 'READ_PERMISSION_BIT_0': 0x400, 96e22a2612SMatthias Ringwald 'READ_PERMISSION_BIT_1': 0x800, 97e22a2612SMatthias Ringwald 98e22a2612SMatthias Ringwald # 99b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_7': 0x6000, 100b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_8': 0x7000, 101b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_9': 0x8000, 102b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_10': 0x9000, 103b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_11': 0xa000, 104b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_12': 0xb000, 105b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_13': 0xc000, 106b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_14': 0xd000, 107b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_15': 0xe000, 108b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_16': 0xf000, 109e22a2612SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_MASK': 0xf000, 110eb6072adSMatthias Ringwald 111b3fcedb9SMatthias Ringwald # only used by gatt compiler >= 0xffff 112b3fcedb9SMatthias Ringwald # Extended Properties 113e72176f8SMatthias Ringwald 'RELIABLE_WRITE': 0x00010000, 114e72176f8SMatthias Ringwald 'AUTHENTICATION_REQUIRED': 0x00020000, 115e72176f8SMatthias Ringwald 'AUTHORIZATION_REQUIRED': 0x00040000, 116e72176f8SMatthias Ringwald 'READ_ANYBODY': 0x00080000, 117e72176f8SMatthias Ringwald 'READ_ENCRYPTED': 0x00100000, 118e72176f8SMatthias Ringwald 'READ_AUTHENTICATED': 0x00200000, 119e72176f8SMatthias Ringwald 'READ_AUTHENTICATED_SC': 0x00400000, 120e72176f8SMatthias Ringwald 'READ_AUTHORIZED': 0x00800000, 121e72176f8SMatthias Ringwald 'WRITE_ANYBODY': 0x01000000, 122e72176f8SMatthias Ringwald 'WRITE_ENCRYPTED': 0x02000000, 123e72176f8SMatthias Ringwald 'WRITE_AUTHENTICATED': 0x04000000, 124e72176f8SMatthias Ringwald 'WRITE_AUTHENTICATED_SC': 0x08000000, 125e72176f8SMatthias Ringwald 'WRITE_AUTHORIZED': 0x10000000, 126eb6072adSMatthias Ringwald 127eb6072adSMatthias Ringwald # Broadcast, Notify, Indicate, Extended Properties are only used to describe a GATT Characteristic, but are free to use with att_db 128e72176f8SMatthias Ringwald # - write permissions 129e22a2612SMatthias Ringwald 'WRITE_PERMISSION_BIT_0': 0x01, 130e22a2612SMatthias Ringwald 'WRITE_PERMISSION_BIT_1': 0x10, 131e72176f8SMatthias Ringwald # - SC required 132e72176f8SMatthias Ringwald 'READ_PERMISSION_SC': 0x20, 133e72176f8SMatthias Ringwald 'WRITE_PERMISSION_SC': 0x80, 134b3fcedb9SMatthias Ringwald} 135b3fcedb9SMatthias Ringwald 136b3fcedb9SMatthias Ringwaldservices = dict() 137b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict() 138b3fcedb9SMatthias Ringwaldpresentation_formats = dict() 139b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = "" 140b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0 141b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = "" 142729074c4SMatthias Ringwalddefines_for_characteristics = [] 143729074c4SMatthias Ringwalddefines_for_services = [] 14478b65b0aSMatthias Ringwaldinclude_paths = [] 145043f8832SMatthias Ringwalddatabase_hash_message = bytearray() 14629ba805bSMatthias Ringwaldservice_counter = {} 147b3fcedb9SMatthias Ringwald 148b3fcedb9SMatthias Ringwaldhandle = 1 149b3fcedb9SMatthias Ringwaldtotal_size = 0 150b3fcedb9SMatthias Ringwald 151043f8832SMatthias Ringwalddef aes_cmac(key, n): 152043f8832SMatthias Ringwald if have_crypto: 153043f8832SMatthias Ringwald cobj = CMAC.new(key, ciphermod=AES) 154043f8832SMatthias Ringwald cobj.update(n) 155043f8832SMatthias Ringwald return cobj.digest() 156043f8832SMatthias Ringwald else: 1577490175eSMatthias Ringwald # return random value 1587490175eSMatthias Ringwald return os.urandom(16) 159043f8832SMatthias Ringwald 160b165f97bSMatthias Ringwalddef read_defines(infile): 161b165f97bSMatthias Ringwald defines = dict() 162b165f97bSMatthias Ringwald with open (infile, 'rt') as fin: 163b165f97bSMatthias Ringwald for line in fin: 164b165f97bSMatthias Ringwald parts = re.match('#define\s+(\w+)\s+(\w+)',line) 165b165f97bSMatthias Ringwald if parts and len(parts.groups()) == 2: 166b165f97bSMatthias Ringwald (key, value) = parts.groups() 167b165f97bSMatthias Ringwald defines[key] = int(value, 16) 168b165f97bSMatthias Ringwald return defines 169b165f97bSMatthias Ringwald 170b3fcedb9SMatthias Ringwalddef keyForUUID(uuid): 171b3fcedb9SMatthias Ringwald keyUUID = "" 172b3fcedb9SMatthias Ringwald for i in uuid: 173b3fcedb9SMatthias Ringwald keyUUID += "%02x" % i 174b3fcedb9SMatthias Ringwald return keyUUID 175b3fcedb9SMatthias Ringwald 176b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid): 177b3fcedb9SMatthias Ringwald return uuid.replace('-', '_') 178b3fcedb9SMatthias Ringwald 179b3fcedb9SMatthias Ringwalddef twoByteLEFor(value): 180b3fcedb9SMatthias Ringwald return [ (value & 0xff), (value >> 8)] 181b3fcedb9SMatthias Ringwald 182b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text): 183b3fcedb9SMatthias 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): 184b3fcedb9SMatthias Ringwald return True 185b3fcedb9SMatthias Ringwald return False 186b3fcedb9SMatthias Ringwald 187b3fcedb9SMatthias Ringwalddef parseUUID128(uuid): 188b3fcedb9SMatthias 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) 189b3fcedb9SMatthias Ringwald uuid_bytes = [] 190b3fcedb9SMatthias Ringwald for i in range(8, 0, -1): 191b3fcedb9SMatthias Ringwald uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16)) 192b3fcedb9SMatthias Ringwald return uuid_bytes 193b3fcedb9SMatthias Ringwald 194b3fcedb9SMatthias Ringwalddef parseUUID(uuid): 195b3fcedb9SMatthias Ringwald if uuid in assigned_uuids: 196b3fcedb9SMatthias Ringwald return twoByteLEFor(assigned_uuids[uuid]) 197b165f97bSMatthias Ringwald uuid_upper = uuid.upper().replace('.','_') 198b165f97bSMatthias Ringwald if uuid_upper in bluetooth_gatt: 199b165f97bSMatthias Ringwald return twoByteLEFor(bluetooth_gatt[uuid_upper]) 200b3fcedb9SMatthias Ringwald if is_128bit_uuid(uuid): 201b3fcedb9SMatthias Ringwald return parseUUID128(uuid) 202b3fcedb9SMatthias Ringwald uuidInt = int(uuid, 16) 203b3fcedb9SMatthias Ringwald return twoByteLEFor(uuidInt) 204b3fcedb9SMatthias Ringwald 205b3fcedb9SMatthias Ringwalddef parseProperties(properties): 206b3fcedb9SMatthias Ringwald value = 0 207b3fcedb9SMatthias Ringwald parts = properties.split("|") 208b3fcedb9SMatthias Ringwald for property in parts: 209b3fcedb9SMatthias Ringwald property = property.strip() 210b3fcedb9SMatthias Ringwald if property in property_flags: 211b3fcedb9SMatthias Ringwald value |= property_flags[property] 212b3fcedb9SMatthias Ringwald else: 213b3fcedb9SMatthias Ringwald print("WARNING: property %s undefined" % (property)) 214e22a2612SMatthias Ringwald 215e22a2612SMatthias Ringwald return value; 216e22a2612SMatthias Ringwald 217e22a2612SMatthias Ringwalddef gatt_characteristic_properties(properties): 218e22a2612SMatthias Ringwald return properties & 0xff 219e22a2612SMatthias Ringwald 220e22a2612SMatthias Ringwalddef att_flags(properties): 221e72176f8SMatthias Ringwald # drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Properties (0x80) - not used for flags 222e72176f8SMatthias Ringwald properties &= 0xffffff4e 223e22a2612SMatthias Ringwald 224e22a2612SMatthias Ringwald # rw permissions distinct 225e22a2612SMatthias Ringwald distinct_permissions_used = properties & ( 226e22a2612SMatthias Ringwald property_flags['READ_AUTHORIZED'] | 227e72176f8SMatthias Ringwald property_flags['READ_AUTHENTICATED_SC'] | 228e22a2612SMatthias Ringwald property_flags['READ_AUTHENTICATED'] | 229e22a2612SMatthias Ringwald property_flags['READ_ENCRYPTED'] | 230e22a2612SMatthias Ringwald property_flags['READ_ANYBODY'] | 231e22a2612SMatthias Ringwald property_flags['WRITE_AUTHORIZED'] | 232e22a2612SMatthias Ringwald property_flags['WRITE_AUTHENTICATED'] | 233e72176f8SMatthias Ringwald property_flags['WRITE_AUTHENTICATED_SC'] | 234e22a2612SMatthias Ringwald property_flags['WRITE_ENCRYPTED'] | 235e22a2612SMatthias Ringwald property_flags['WRITE_ANYBODY'] 236e22a2612SMatthias Ringwald ) != 0 237e22a2612SMatthias Ringwald 238e22a2612SMatthias Ringwald # post process properties 239e22a2612SMatthias Ringwald encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0 240e22a2612SMatthias Ringwald 241d7ec1d24SMatthias Ringwald # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted 242e22a2612SMatthias Ringwald if encryption_key_size_specified and not distinct_permissions_used: 243e22a2612SMatthias Ringwald properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED'] 244e22a2612SMatthias Ringwald 245d7ec1d24SMatthias Ringwald # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated 246d7ec1d24SMatthias Ringwald if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used: 247d7ec1d24SMatthias Ringwald properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED'] 248d7ec1d24SMatthias Ringwald 249d7ec1d24SMatthias Ringwald # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized 250d7ec1d24SMatthias Ringwald if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used: 251d7ec1d24SMatthias Ringwald properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED'] 252d7ec1d24SMatthias Ringwald 253d7ec1d24SMatthias Ringwald # determine read/write security requirements 254d7ec1d24SMatthias Ringwald read_security_level = 0 255d7ec1d24SMatthias Ringwald write_security_level = 0 256e72176f8SMatthias Ringwald read_requires_sc = False 257e72176f8SMatthias Ringwald write_requires_sc = False 258e22a2612SMatthias Ringwald if properties & property_flags['READ_AUTHORIZED']: 259d7ec1d24SMatthias Ringwald read_security_level = 3 260e22a2612SMatthias Ringwald elif properties & property_flags['READ_AUTHENTICATED']: 261d7ec1d24SMatthias Ringwald read_security_level = 2 262e72176f8SMatthias Ringwald elif properties & property_flags['READ_AUTHENTICATED_SC']: 263e72176f8SMatthias Ringwald read_security_level = 2 264e72176f8SMatthias Ringwald read_requires_sc = True 265e22a2612SMatthias Ringwald elif properties & property_flags['READ_ENCRYPTED']: 266d7ec1d24SMatthias Ringwald read_security_level = 1 267e22a2612SMatthias Ringwald if properties & property_flags['WRITE_AUTHORIZED']: 268d7ec1d24SMatthias Ringwald write_security_level = 3 269e22a2612SMatthias Ringwald elif properties & property_flags['WRITE_AUTHENTICATED']: 270d7ec1d24SMatthias Ringwald write_security_level = 2 271e72176f8SMatthias Ringwald elif properties & property_flags['WRITE_AUTHENTICATED_SC']: 272e72176f8SMatthias Ringwald write_security_level = 2 273e72176f8SMatthias Ringwald write_requires_sc = True 274e22a2612SMatthias Ringwald elif properties & property_flags['WRITE_ENCRYPTED']: 275d7ec1d24SMatthias Ringwald write_security_level = 1 276d7ec1d24SMatthias Ringwald 277d7ec1d24SMatthias Ringwald # map security requirements to flags 278d7ec1d24SMatthias Ringwald if read_security_level & 2: 279d7ec1d24SMatthias Ringwald properties |= property_flags['READ_PERMISSION_BIT_1'] 280d7ec1d24SMatthias Ringwald if read_security_level & 1: 281d7ec1d24SMatthias Ringwald properties |= property_flags['READ_PERMISSION_BIT_0'] 282e72176f8SMatthias Ringwald if read_requires_sc: 283e72176f8SMatthias Ringwald properties |= property_flags['READ_PERMISSION_SC'] 284d7ec1d24SMatthias Ringwald if write_security_level & 2: 285d7ec1d24SMatthias Ringwald properties |= property_flags['WRITE_PERMISSION_BIT_1'] 286d7ec1d24SMatthias Ringwald if write_security_level & 1: 287e22a2612SMatthias Ringwald properties |= property_flags['WRITE_PERMISSION_BIT_0'] 288e72176f8SMatthias Ringwald if write_requires_sc: 289e72176f8SMatthias Ringwald properties |= property_flags['WRITE_PERMISSION_SC'] 290e22a2612SMatthias Ringwald 291e22a2612SMatthias Ringwald return properties 292e22a2612SMatthias Ringwald 293d7ec1d24SMatthias Ringwalddef write_permissions_and_key_size_flags_from_properties(properties): 294e22a2612SMatthias Ringwald return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1']) 295b3fcedb9SMatthias Ringwald 296b3fcedb9SMatthias Ringwalddef write_8(fout, value): 297b3fcedb9SMatthias Ringwald fout.write( "0x%02x, " % (value & 0xff)) 298b3fcedb9SMatthias Ringwald 299b3fcedb9SMatthias Ringwalddef write_16(fout, value): 300b3fcedb9SMatthias Ringwald fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff)) 301b3fcedb9SMatthias Ringwald 302285653b2SMatthias Ringwalddef write_uuid(fout, uuid): 303b3fcedb9SMatthias Ringwald for byte in uuid: 304b3fcedb9SMatthias Ringwald fout.write( "0x%02x, " % byte) 305b3fcedb9SMatthias Ringwald 306b3fcedb9SMatthias Ringwalddef write_string(fout, text): 307b3fcedb9SMatthias Ringwald for l in text.lstrip('"').rstrip('"'): 308b3fcedb9SMatthias Ringwald write_8(fout, ord(l)) 309b3fcedb9SMatthias Ringwald 310b3fcedb9SMatthias Ringwalddef write_sequence(fout, text): 311b3fcedb9SMatthias Ringwald parts = text.split() 312b3fcedb9SMatthias Ringwald for part in parts: 313b3fcedb9SMatthias Ringwald fout.write("0x%s, " % (part.strip())) 314b3fcedb9SMatthias Ringwald 315043f8832SMatthias Ringwalddef write_database_hash(fout): 316043f8832SMatthias Ringwald fout.write("THE-DATABASE-HASH") 317043f8832SMatthias Ringwald 318b3fcedb9SMatthias Ringwalddef write_indent(fout): 319b3fcedb9SMatthias Ringwald fout.write(" ") 320b3fcedb9SMatthias Ringwald 321d7ec1d24SMatthias Ringwalddef read_permissions_from_flags(flags): 322d7ec1d24SMatthias Ringwald permissions = 0 323d7ec1d24SMatthias Ringwald if flags & property_flags['READ_PERMISSION_BIT_0']: 324d7ec1d24SMatthias Ringwald permissions |= 1 325d7ec1d24SMatthias Ringwald if flags & property_flags['READ_PERMISSION_BIT_1']: 326d7ec1d24SMatthias Ringwald permissions |= 2 327e72176f8SMatthias Ringwald if flags & property_flags['READ_PERMISSION_SC'] and permissions == 2: 328e72176f8SMatthias Ringwald permissions = 4 329d7ec1d24SMatthias Ringwald return permissions 330d7ec1d24SMatthias Ringwald 331d7ec1d24SMatthias Ringwalddef write_permissions_from_flags(flags): 332d7ec1d24SMatthias Ringwald permissions = 0 333d7ec1d24SMatthias Ringwald if flags & property_flags['WRITE_PERMISSION_BIT_0']: 334d7ec1d24SMatthias Ringwald permissions |= 1 335d7ec1d24SMatthias Ringwald if flags & property_flags['WRITE_PERMISSION_BIT_1']: 336d7ec1d24SMatthias Ringwald permissions |= 2 337e72176f8SMatthias Ringwald if flags & property_flags['WRITE_PERMISSION_SC'] and permissions == 2: 338e72176f8SMatthias Ringwald permissions = 4 339d7ec1d24SMatthias Ringwald return permissions 340d7ec1d24SMatthias Ringwald 341d7ec1d24SMatthias Ringwalddef encryption_key_size_from_flags(flags): 342d7ec1d24SMatthias Ringwald encryption_key_size = (flags & 0xf000) >> 12 343d7ec1d24SMatthias Ringwald if encryption_key_size > 0: 344d7ec1d24SMatthias Ringwald encryption_key_size += 1 345d7ec1d24SMatthias Ringwald return encryption_key_size 346d7ec1d24SMatthias Ringwald 347b3fcedb9SMatthias Ringwalddef is_string(text): 348b3fcedb9SMatthias Ringwald for item in text.split(" "): 349b3fcedb9SMatthias Ringwald if not all(c in string.hexdigits for c in item): 350b3fcedb9SMatthias Ringwald return True 351b3fcedb9SMatthias Ringwald return False 352b3fcedb9SMatthias Ringwald 353b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties): 354b3fcedb9SMatthias Ringwald return properties & (property_flags['NOTIFY'] | property_flags['INDICATE']) 355b3fcedb9SMatthias Ringwald 356729074c4SMatthias Ringwalddef serviceDefinitionComplete(fout): 357729074c4SMatthias Ringwald global services 358729074c4SMatthias Ringwald if current_service_uuid_string: 359729074c4SMatthias Ringwald fout.write("\n") 36029ba805bSMatthias Ringwald # update num instances for this service 36129ba805bSMatthias Ringwald count = 1 36229ba805bSMatthias Ringwald if current_service_uuid_string in service_counter: 36329ba805bSMatthias Ringwald count = service_counter[current_service_uuid_string] + 1 36429ba805bSMatthias Ringwald service_counter[current_service_uuid_string] = count 36529ba805bSMatthias Ringwald # add old defines without service counter for first instance 36629ba805bSMatthias Ringwald if count == 1: 367729074c4SMatthias Ringwald defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle)) 368729074c4SMatthias Ringwald defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1)) 369729074c4SMatthias Ringwald services[current_service_uuid_string] = [current_service_start_handle, handle-1] 37029ba805bSMatthias Ringwald # unified defines indicating instance 37129ba805bSMatthias Ringwald defines_for_services.append('#define ATT_SERVICE_%s_%02x_START_HANDLE 0x%04x' % (current_service_uuid_string, count, current_service_start_handle)) 37229ba805bSMatthias Ringwald defines_for_services.append('#define ATT_SERVICE_%s_%02x_END_HANDLE 0x%04x' % (current_service_uuid_string, count, handle-1)) 37329ba805bSMatthias Ringwald 374729074c4SMatthias Ringwald 375d7ec1d24SMatthias Ringwalddef dump_flags(fout, flags): 376d7ec1d24SMatthias Ringwald global security_permsission 377d7ec1d24SMatthias Ringwald encryption_key_size = encryption_key_size_from_flags(flags) 378d7ec1d24SMatthias Ringwald read_permissions = security_permsission[read_permissions_from_flags(flags)] 379d7ec1d24SMatthias Ringwald write_permissions = security_permsission[write_permissions_from_flags(flags)] 380d7ec1d24SMatthias Ringwald write_indent(fout) 381d7ec1d24SMatthias Ringwald fout.write('// ') 382d7ec1d24SMatthias Ringwald first = 1 383d7ec1d24SMatthias Ringwald if flags & property_flags['READ']: 384d7ec1d24SMatthias Ringwald fout.write('READ_%s' % read_permissions) 385d7ec1d24SMatthias Ringwald first = 0 386d7ec1d24SMatthias Ringwald if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']): 387d7ec1d24SMatthias Ringwald if not first: 388d7ec1d24SMatthias Ringwald fout.write(', ') 389d7ec1d24SMatthias Ringwald first = 0 390d7ec1d24SMatthias Ringwald fout.write('WRITE_%s' % write_permissions) 391d7ec1d24SMatthias Ringwald if encryption_key_size > 0: 392d7ec1d24SMatthias Ringwald if not first: 393d7ec1d24SMatthias Ringwald fout.write(', ') 394d7ec1d24SMatthias Ringwald first = 0 395d7ec1d24SMatthias Ringwald fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size) 396d7ec1d24SMatthias Ringwald fout.write('\n') 397d7ec1d24SMatthias Ringwald 398043f8832SMatthias Ringwalddef database_hash_append_uint8(value): 399043f8832SMatthias Ringwald global database_hash_message 400043f8832SMatthias Ringwald database_hash_message.append(value) 401043f8832SMatthias Ringwald 402043f8832SMatthias Ringwalddef database_hash_append_uint16(value): 403043f8832SMatthias Ringwald global database_hash_message 404043f8832SMatthias Ringwald database_hash_append_uint8(value & 0xff) 405043f8832SMatthias Ringwald database_hash_append_uint8((value >> 8) & 0xff) 406043f8832SMatthias Ringwald 407043f8832SMatthias Ringwalddef database_hash_append_value(value): 408043f8832SMatthias Ringwald global database_hash_message 409043f8832SMatthias Ringwald for byte in value: 410043f8832SMatthias Ringwald database_hash_append_uint8(byte) 411043f8832SMatthias Ringwald 412b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type): 413b3fcedb9SMatthias Ringwald global handle 414b3fcedb9SMatthias Ringwald global total_size 415b3fcedb9SMatthias Ringwald global current_service_uuid_string 416b3fcedb9SMatthias Ringwald global current_service_start_handle 417b3fcedb9SMatthias Ringwald 418729074c4SMatthias Ringwald serviceDefinitionComplete(fout) 419b3fcedb9SMatthias Ringwald 420d7ec1d24SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 421b3fcedb9SMatthias Ringwald 422b3fcedb9SMatthias Ringwald write_indent(fout) 423b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 424b3fcedb9SMatthias Ringwald 425b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 426b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 427b3fcedb9SMatthias Ringwald 428b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + uuid_size + 2 429b3fcedb9SMatthias Ringwald 430b3fcedb9SMatthias Ringwald if service_type == 0x2802: 431b3fcedb9SMatthias Ringwald size += 4 432b3fcedb9SMatthias Ringwald 433b3fcedb9SMatthias Ringwald write_indent(fout) 434b3fcedb9SMatthias Ringwald write_16(fout, size) 435d7ec1d24SMatthias Ringwald write_16(fout, read_only_anybody_flags) 436b3fcedb9SMatthias Ringwald write_16(fout, handle) 437b3fcedb9SMatthias Ringwald write_16(fout, service_type) 438285653b2SMatthias Ringwald write_uuid(fout, uuid) 439b3fcedb9SMatthias Ringwald fout.write("\n") 440b3fcedb9SMatthias Ringwald 441043f8832SMatthias Ringwald database_hash_append_uint16(handle) 442043f8832SMatthias Ringwald database_hash_append_uint16(service_type) 443043f8832SMatthias Ringwald database_hash_append_value(uuid) 444043f8832SMatthias Ringwald 445729074c4SMatthias Ringwald current_service_uuid_string = c_string_for_uuid(parts[1]) 446b3fcedb9SMatthias Ringwald current_service_start_handle = handle 447b3fcedb9SMatthias Ringwald handle = handle + 1 448b3fcedb9SMatthias Ringwald total_size = total_size + size 449b3fcedb9SMatthias Ringwald 450b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts): 451b3fcedb9SMatthias Ringwald parseService(fout, parts, 0x2800) 452b3fcedb9SMatthias Ringwald 453b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts): 454b3fcedb9SMatthias Ringwald parseService(fout, parts, 0x2801) 455b3fcedb9SMatthias Ringwald 456b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts): 457b3fcedb9SMatthias Ringwald global handle 458b3fcedb9SMatthias Ringwald global total_size 459b3fcedb9SMatthias Ringwald 460e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 461b3fcedb9SMatthias Ringwald 462b3fcedb9SMatthias Ringwald write_indent(fout) 463b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 464b3fcedb9SMatthias Ringwald 465b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 466b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 467b3fcedb9SMatthias Ringwald if uuid_size > 2: 468b3fcedb9SMatthias Ringwald uuid_size = 0 469729074c4SMatthias Ringwald # print("Include Service ", c_string_for_uuid(uuid)) 470b3fcedb9SMatthias Ringwald 471b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 4 + uuid_size 472b3fcedb9SMatthias Ringwald 473729074c4SMatthias Ringwald keyUUID = c_string_for_uuid(parts[1]) 474b3fcedb9SMatthias Ringwald 475b3fcedb9SMatthias Ringwald write_indent(fout) 476b3fcedb9SMatthias Ringwald write_16(fout, size) 477e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 478b3fcedb9SMatthias Ringwald write_16(fout, handle) 479b3fcedb9SMatthias Ringwald write_16(fout, 0x2802) 480b3fcedb9SMatthias Ringwald write_16(fout, services[keyUUID][0]) 481b3fcedb9SMatthias Ringwald write_16(fout, services[keyUUID][1]) 482b3fcedb9SMatthias Ringwald if uuid_size > 0: 483285653b2SMatthias Ringwald write_uuid(fout, uuid) 484b3fcedb9SMatthias Ringwald fout.write("\n") 485b3fcedb9SMatthias Ringwald 486043f8832SMatthias Ringwald database_hash_append_uint16(handle) 487043f8832SMatthias Ringwald database_hash_append_uint16(0x2802) 488043f8832SMatthias Ringwald database_hash_append_uint16(services[keyUUID][0]) 489043f8832SMatthias Ringwald database_hash_append_uint16(services[keyUUID][1]) 490043f8832SMatthias Ringwald if uuid_size > 0: 491043f8832SMatthias Ringwald database_hash_append_value(uuid) 492043f8832SMatthias Ringwald 493b3fcedb9SMatthias Ringwald handle = handle + 1 494b3fcedb9SMatthias Ringwald total_size = total_size + size 495b3fcedb9SMatthias Ringwald 496b3fcedb9SMatthias Ringwald 497b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts): 498b3fcedb9SMatthias Ringwald global handle 499b3fcedb9SMatthias Ringwald global total_size 500b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 501b3fcedb9SMatthias Ringwald global characteristic_indices 502b3fcedb9SMatthias Ringwald 503e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 504b3fcedb9SMatthias Ringwald 505b3fcedb9SMatthias Ringwald # enumerate characteristics with same UUID, using optional name tag if available 506b3fcedb9SMatthias Ringwald current_characteristic_uuid_string = c_string_for_uuid(parts[1]); 507b3fcedb9SMatthias Ringwald index = 1 508b3fcedb9SMatthias Ringwald if current_characteristic_uuid_string in characteristic_indices: 509b3fcedb9SMatthias Ringwald index = characteristic_indices[current_characteristic_uuid_string] + 1 510b3fcedb9SMatthias Ringwald characteristic_indices[current_characteristic_uuid_string] = index 511b3fcedb9SMatthias Ringwald if len(parts) > 4: 512b3fcedb9SMatthias Ringwald current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_') 513b3fcedb9SMatthias Ringwald else: 514b3fcedb9SMatthias Ringwald current_characteristic_uuid_string += ('_%02x' % index) 515b3fcedb9SMatthias Ringwald 516b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 517b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 518b3fcedb9SMatthias Ringwald properties = parseProperties(parts[2]) 519b3fcedb9SMatthias Ringwald value = ', '.join([str(x) for x in parts[3:]]) 520b3fcedb9SMatthias Ringwald 521b3fcedb9SMatthias Ringwald # reliable writes is defined in an extended properties 522b3fcedb9SMatthias Ringwald if (properties & property_flags['RELIABLE_WRITE']): 523b3fcedb9SMatthias Ringwald properties = properties | property_flags['EXTENDED_PROPERTIES'] 524b3fcedb9SMatthias Ringwald 525b3fcedb9SMatthias Ringwald write_indent(fout) 526b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3]))) 527b3fcedb9SMatthias Ringwald 528e22a2612SMatthias Ringwald 529e22a2612SMatthias Ringwald characteristic_properties = gatt_characteristic_properties(properties) 530b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + (1+2+uuid_size) 531b3fcedb9SMatthias Ringwald write_indent(fout) 532b3fcedb9SMatthias Ringwald write_16(fout, size) 533e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 534b3fcedb9SMatthias Ringwald write_16(fout, handle) 535b3fcedb9SMatthias Ringwald write_16(fout, 0x2803) 536e22a2612SMatthias Ringwald write_8(fout, characteristic_properties) 537b3fcedb9SMatthias Ringwald write_16(fout, handle+1) 538285653b2SMatthias Ringwald write_uuid(fout, uuid) 539b3fcedb9SMatthias Ringwald fout.write("\n") 540b3fcedb9SMatthias Ringwald handle = handle + 1 541b3fcedb9SMatthias Ringwald total_size = total_size + size 542b3fcedb9SMatthias Ringwald 543043f8832SMatthias Ringwald database_hash_append_uint16(handle) 544043f8832SMatthias Ringwald database_hash_append_uint16(0x2803) 545043f8832SMatthias Ringwald database_hash_append_uint8(characteristic_properties) 546043f8832SMatthias Ringwald database_hash_append_uint16(handle+1) 547043f8832SMatthias Ringwald database_hash_append_value(uuid) 548043f8832SMatthias Ringwald 549043f8832SMatthias Ringwald uuid_is_database_hash = len(uuid) == 2 and uuid[0] == 0x2a and uuid[1] == 0x2b 550043f8832SMatthias Ringwald 551b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + uuid_size 552043f8832SMatthias Ringwald if uuid_is_database_hash: 553043f8832SMatthias Ringwald size += 16 554043f8832SMatthias Ringwald else: 555b3fcedb9SMatthias Ringwald if is_string(value): 556b3fcedb9SMatthias Ringwald size = size + len(value) 557b3fcedb9SMatthias Ringwald else: 558b3fcedb9SMatthias Ringwald size = size + len(value.split()) 559b3fcedb9SMatthias Ringwald 560e22a2612SMatthias Ringwald value_flags = att_flags(properties) 5618ea3236cSMatthias Ringwald 5628ea3236cSMatthias Ringwald # add UUID128 flag for value handle 563b3fcedb9SMatthias Ringwald if uuid_size == 16: 564e22a2612SMatthias Ringwald value_flags = value_flags | property_flags['LONG_UUID']; 565b3fcedb9SMatthias Ringwald 566b3fcedb9SMatthias Ringwald write_indent(fout) 567b3fcedb9SMatthias Ringwald fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value)) 568d7ec1d24SMatthias Ringwald 569d7ec1d24SMatthias Ringwald dump_flags(fout, value_flags) 570d7ec1d24SMatthias Ringwald 571b3fcedb9SMatthias Ringwald write_indent(fout) 572b3fcedb9SMatthias Ringwald write_16(fout, size) 573e22a2612SMatthias Ringwald write_16(fout, value_flags) 574b3fcedb9SMatthias Ringwald write_16(fout, handle) 575285653b2SMatthias Ringwald write_uuid(fout, uuid) 576043f8832SMatthias Ringwald if uuid_is_database_hash: 577043f8832SMatthias Ringwald write_database_hash(fout) 578043f8832SMatthias Ringwald else: 579b3fcedb9SMatthias Ringwald if is_string(value): 580b3fcedb9SMatthias Ringwald write_string(fout, value) 581b3fcedb9SMatthias Ringwald else: 582b3fcedb9SMatthias Ringwald write_sequence(fout,value) 583b3fcedb9SMatthias Ringwald 584b3fcedb9SMatthias Ringwald fout.write("\n") 585729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 586b3fcedb9SMatthias Ringwald handle = handle + 1 587b3fcedb9SMatthias Ringwald 588b3fcedb9SMatthias Ringwald if add_client_characteristic_configuration(properties): 589e22a2612SMatthias Ringwald # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC 590d7ec1d24SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 591e22a2612SMatthias Ringwald flags |= property_flags['READ'] 592e22a2612SMatthias Ringwald flags |= property_flags['WRITE'] 5939be4aecfSMatthias Ringwald flags |= property_flags['WRITE_WITHOUT_RESPONSE'] 594e22a2612SMatthias Ringwald flags |= property_flags['DYNAMIC'] 595b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 2 596d7ec1d24SMatthias Ringwald 597b3fcedb9SMatthias Ringwald write_indent(fout) 598b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle)) 599d7ec1d24SMatthias Ringwald 600d7ec1d24SMatthias Ringwald dump_flags(fout, flags) 601d7ec1d24SMatthias Ringwald 602b3fcedb9SMatthias Ringwald write_indent(fout) 603b3fcedb9SMatthias Ringwald write_16(fout, size) 604e22a2612SMatthias Ringwald write_16(fout, flags) 605b3fcedb9SMatthias Ringwald write_16(fout, handle) 606b3fcedb9SMatthias Ringwald write_16(fout, 0x2902) 607b3fcedb9SMatthias Ringwald write_16(fout, 0) 608b3fcedb9SMatthias Ringwald fout.write("\n") 609043f8832SMatthias Ringwald 610043f8832SMatthias Ringwald database_hash_append_uint16(handle) 611043f8832SMatthias Ringwald database_hash_append_uint16(0x2902) 612043f8832SMatthias Ringwald 613729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 614b3fcedb9SMatthias Ringwald handle = handle + 1 615b3fcedb9SMatthias Ringwald 616043f8832SMatthias Ringwald 617b3fcedb9SMatthias Ringwald if properties & property_flags['RELIABLE_WRITE']: 618b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 2 619b3fcedb9SMatthias Ringwald write_indent(fout) 620b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle)) 621b3fcedb9SMatthias Ringwald write_indent(fout) 622b3fcedb9SMatthias Ringwald write_16(fout, size) 623e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 624b3fcedb9SMatthias Ringwald write_16(fout, handle) 625b3fcedb9SMatthias Ringwald write_16(fout, 0x2900) 626b3fcedb9SMatthias Ringwald write_16(fout, 1) # Reliable Write 627b3fcedb9SMatthias Ringwald fout.write("\n") 628043f8832SMatthias Ringwald 629043f8832SMatthias Ringwald database_hash_append_uint16(handle) 630043f8832SMatthias Ringwald database_hash_append_uint16(0x2900) 631043f8832SMatthias Ringwald database_hash_append_uint16(1) 632043f8832SMatthias Ringwald 633b3fcedb9SMatthias Ringwald handle = handle + 1 634b3fcedb9SMatthias Ringwald 635b3fcedb9SMatthias Ringwalddef parseCharacteristicUserDescription(fout, parts): 636b3fcedb9SMatthias Ringwald global handle 637b3fcedb9SMatthias Ringwald global total_size 638b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 639b3fcedb9SMatthias Ringwald 640b3fcedb9SMatthias Ringwald properties = parseProperties(parts[1]) 641b3fcedb9SMatthias Ringwald value = parts[2] 642b3fcedb9SMatthias Ringwald 643b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 644b3fcedb9SMatthias Ringwald if is_string(value): 645b7647eb6SMatthias Ringwald size = size + len(value) 646b3fcedb9SMatthias Ringwald else: 647b3fcedb9SMatthias Ringwald size = size + len(value.split()) 648b3fcedb9SMatthias Ringwald 649e22a2612SMatthias Ringwald # use write, write permissions and encryption key size from attribute value and set READ_ANYBODY 650d7ec1d24SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 651e22a2612SMatthias Ringwald flags |= properties & property_flags['WRITE'] 652e22a2612SMatthias Ringwald flags |= property_flags['READ'] 653e22a2612SMatthias Ringwald 654b3fcedb9SMatthias Ringwald write_indent(fout) 655b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:]))) 656d7ec1d24SMatthias Ringwald 657d7ec1d24SMatthias Ringwald dump_flags(fout, flags) 658d7ec1d24SMatthias Ringwald 659b3fcedb9SMatthias Ringwald write_indent(fout) 660b3fcedb9SMatthias Ringwald write_16(fout, size) 661e22a2612SMatthias Ringwald write_16(fout, flags) 662b3fcedb9SMatthias Ringwald write_16(fout, handle) 663b3fcedb9SMatthias Ringwald write_16(fout, 0x2901) 664b3fcedb9SMatthias Ringwald if is_string(value): 665b3fcedb9SMatthias Ringwald write_string(fout, value) 666b3fcedb9SMatthias Ringwald else: 667b3fcedb9SMatthias Ringwald write_sequence(fout,value) 668b3fcedb9SMatthias Ringwald fout.write("\n") 669043f8832SMatthias Ringwald 670043f8832SMatthias Ringwald database_hash_append_uint16(handle) 671043f8832SMatthias Ringwald database_hash_append_uint16(0x2901) 672043f8832SMatthias Ringwald 673729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 674b3fcedb9SMatthias Ringwald handle = handle + 1 675b3fcedb9SMatthias Ringwald 67617215335SMatthias Ringwalddef parseGenericDynamicDescriptor(fout, parts, uuid, name): 677b3fcedb9SMatthias Ringwald global handle 678b3fcedb9SMatthias Ringwald global total_size 679b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 680b3fcedb9SMatthias Ringwald 681b3fcedb9SMatthias Ringwald properties = parseProperties(parts[1]) 682b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 683b3fcedb9SMatthias Ringwald 684e22a2612SMatthias Ringwald # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY 685d7ec1d24SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 686e22a2612SMatthias Ringwald flags |= property_flags['READ'] 687e22a2612SMatthias Ringwald flags |= property_flags['WRITE'] 688e22a2612SMatthias Ringwald flags |= property_flags['DYNAMIC'] 689e22a2612SMatthias Ringwald 690b3fcedb9SMatthias Ringwald write_indent(fout) 69117215335SMatthias Ringwald fout.write('// 0x%04x %s-%s\n' % (handle, name, '-'.join(parts[1:]))) 692d7ec1d24SMatthias Ringwald 693d7ec1d24SMatthias Ringwald dump_flags(fout, flags) 694d7ec1d24SMatthias Ringwald 695b3fcedb9SMatthias Ringwald write_indent(fout) 696b3fcedb9SMatthias Ringwald write_16(fout, size) 697e22a2612SMatthias Ringwald write_16(fout, flags) 698b3fcedb9SMatthias Ringwald write_16(fout, handle) 699b3fcedb9SMatthias Ringwald write_16(fout, 0x2903) 700b3fcedb9SMatthias Ringwald fout.write("\n") 701043f8832SMatthias Ringwald 702043f8832SMatthias Ringwald database_hash_append_uint16(handle) 70317215335SMatthias Ringwald database_hash_append_uint16(uuid) 704043f8832SMatthias Ringwald 70517215335SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_%s_HANDLE 0x%04x' % (current_characteristic_uuid_string, name, handle)) 706b3fcedb9SMatthias Ringwald handle = handle + 1 707b3fcedb9SMatthias Ringwald 70817215335SMatthias Ringwalddef parseGenericDynamicReadOnlyDescriptor(fout, parts, uuid, name): 70917215335SMatthias Ringwald global handle 71017215335SMatthias Ringwald global total_size 71117215335SMatthias Ringwald global current_characteristic_uuid_string 71217215335SMatthias Ringwald 71317215335SMatthias Ringwald properties = parseProperties(parts[1]) 71417215335SMatthias Ringwald size = 2 + 2 + 2 + 2 71517215335SMatthias Ringwald 71617215335SMatthias Ringwald # use write permissions and encryption key size from attribute value and set READ, DYNAMIC, READ_ANYBODY 71717215335SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 71817215335SMatthias Ringwald flags |= property_flags['READ'] 71917215335SMatthias Ringwald flags |= property_flags['DYNAMIC'] 72017215335SMatthias Ringwald 72117215335SMatthias Ringwald write_indent(fout) 72217215335SMatthias Ringwald fout.write('// 0x%04x %s-%s\n' % (handle, name, '-'.join(parts[1:]))) 72317215335SMatthias Ringwald 72417215335SMatthias Ringwald dump_flags(fout, flags) 72517215335SMatthias Ringwald 72617215335SMatthias Ringwald write_indent(fout) 72717215335SMatthias Ringwald write_16(fout, size) 72817215335SMatthias Ringwald write_16(fout, flags) 72917215335SMatthias Ringwald write_16(fout, handle) 73017215335SMatthias Ringwald write_16(fout, 0x2903) 73117215335SMatthias Ringwald fout.write("\n") 73217215335SMatthias Ringwald 73317215335SMatthias Ringwald database_hash_append_uint16(handle) 73417215335SMatthias Ringwald database_hash_append_uint16(uuid) 73517215335SMatthias Ringwald 73617215335SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_%s_HANDLE 0x%04x' % (current_characteristic_uuid_string, name, handle)) 73717215335SMatthias Ringwald handle = handle + 1 73817215335SMatthias Ringwald 73917215335SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts): 74017215335SMatthias Ringwald parseGenericDynamicDescriptor(fout, parts, 0x2903, 'SERVER_CONFIGURATION') 74117215335SMatthias Ringwald 742b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts): 743b3fcedb9SMatthias Ringwald global handle 744b3fcedb9SMatthias Ringwald global total_size 745b3fcedb9SMatthias Ringwald 746e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 747b3fcedb9SMatthias Ringwald 748b3fcedb9SMatthias Ringwald identifier = parts[1] 749b3fcedb9SMatthias Ringwald presentation_formats[identifier] = handle 750b3fcedb9SMatthias Ringwald # print("format '%s' with handle %d\n" % (identifier, handle)) 751b3fcedb9SMatthias Ringwald 752b3fcedb9SMatthias Ringwald format = parts[2] 753b3fcedb9SMatthias Ringwald exponent = parts[3] 754b3fcedb9SMatthias Ringwald unit = parseUUID(parts[4]) 755b3fcedb9SMatthias Ringwald name_space = parts[5] 756b3fcedb9SMatthias Ringwald description = parseUUID(parts[6]) 757b3fcedb9SMatthias Ringwald 758b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 7 759b3fcedb9SMatthias Ringwald 760b3fcedb9SMatthias Ringwald write_indent(fout) 761b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 762b3fcedb9SMatthias Ringwald write_indent(fout) 763b3fcedb9SMatthias Ringwald write_16(fout, size) 764e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 765b3fcedb9SMatthias Ringwald write_16(fout, handle) 766b3fcedb9SMatthias Ringwald write_16(fout, 0x2904) 767b3fcedb9SMatthias Ringwald write_sequence(fout, format) 768b3fcedb9SMatthias Ringwald write_sequence(fout, exponent) 769285653b2SMatthias Ringwald write_uuid(fout, unit) 770b3fcedb9SMatthias Ringwald write_sequence(fout, name_space) 771285653b2SMatthias Ringwald write_uuid(fout, description) 772b3fcedb9SMatthias Ringwald fout.write("\n") 773043f8832SMatthias Ringwald 774043f8832SMatthias Ringwald database_hash_append_uint16(handle) 775043f8832SMatthias Ringwald database_hash_append_uint16(0x2904) 776043f8832SMatthias Ringwald 777b3fcedb9SMatthias Ringwald handle = handle + 1 778b3fcedb9SMatthias Ringwald 779b3fcedb9SMatthias Ringwald 780b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts): 781b3fcedb9SMatthias Ringwald global handle 782b3fcedb9SMatthias Ringwald global total_size 783b3fcedb9SMatthias Ringwald 784e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 785b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2 786b3fcedb9SMatthias Ringwald 787b3fcedb9SMatthias Ringwald write_indent(fout) 788b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 789b3fcedb9SMatthias Ringwald write_indent(fout) 790b3fcedb9SMatthias Ringwald write_16(fout, size) 791e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 792b3fcedb9SMatthias Ringwald write_16(fout, handle) 793b3fcedb9SMatthias Ringwald write_16(fout, 0x2905) 794b3fcedb9SMatthias Ringwald for identifier in parts[1:]: 795b3fcedb9SMatthias Ringwald format_handle = presentation_formats[identifier] 796b3fcedb9SMatthias Ringwald if format == 0: 797b3fcedb9SMatthias Ringwald print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier) 798b3fcedb9SMatthias Ringwald sys.exit(1) 799b3fcedb9SMatthias Ringwald write_16(fout, format_handle) 800b3fcedb9SMatthias Ringwald fout.write("\n") 801043f8832SMatthias Ringwald 802043f8832SMatthias Ringwald database_hash_append_uint16(handle) 803043f8832SMatthias Ringwald database_hash_append_uint16(0x2905) 804043f8832SMatthias Ringwald 805b3fcedb9SMatthias Ringwald handle = handle + 1 806b3fcedb9SMatthias Ringwald 807*05146de0SMilanka Ringwalddef parseExternalReportReference(fout, parts): 808*05146de0SMilanka Ringwald global handle 809*05146de0SMilanka Ringwald global total_size 810*05146de0SMilanka Ringwald 811*05146de0SMilanka Ringwald read_only_anybody_flags = property_flags['READ']; 812*05146de0SMilanka Ringwald size = 2 + 2 + 2 + 2 + 2 813*05146de0SMilanka Ringwald 814*05146de0SMilanka Ringwald report_uuid = int(parts[2], 16) 815*05146de0SMilanka Ringwald 816*05146de0SMilanka Ringwald write_indent(fout) 817*05146de0SMilanka Ringwald fout.write('// 0x%04x EXTERNAL_REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:]))) 818*05146de0SMilanka Ringwald write_indent(fout) 819*05146de0SMilanka Ringwald write_16(fout, size) 820*05146de0SMilanka Ringwald write_16(fout, read_only_anybody_flags) 821*05146de0SMilanka Ringwald write_16(fout, handle) 822*05146de0SMilanka Ringwald write_16(fout, 0x2907) 823*05146de0SMilanka Ringwald write_16(fout, report_uuid) 824*05146de0SMilanka Ringwald fout.write("\n") 825*05146de0SMilanka Ringwald handle = handle + 1 826*05146de0SMilanka Ringwald 827b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts): 828b3fcedb9SMatthias Ringwald global handle 829b3fcedb9SMatthias Ringwald global total_size 830b3fcedb9SMatthias Ringwald 831e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 832b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 1 + 1 833b3fcedb9SMatthias Ringwald 834231a3c5dSMatthias Ringwald report_id = parts[2] 835231a3c5dSMatthias Ringwald report_type = parts[3] 836b3fcedb9SMatthias Ringwald 837b3fcedb9SMatthias Ringwald write_indent(fout) 838b3fcedb9SMatthias Ringwald fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:]))) 839b3fcedb9SMatthias Ringwald write_indent(fout) 840b3fcedb9SMatthias Ringwald write_16(fout, size) 841e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 842b3fcedb9SMatthias Ringwald write_16(fout, handle) 843b3fcedb9SMatthias Ringwald write_16(fout, 0x2908) 844b3fcedb9SMatthias Ringwald write_sequence(fout, report_id) 845b3fcedb9SMatthias Ringwald write_sequence(fout, report_type) 846b3fcedb9SMatthias Ringwald fout.write("\n") 847b3fcedb9SMatthias Ringwald handle = handle + 1 848b3fcedb9SMatthias Ringwald 849b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts): 850b3fcedb9SMatthias Ringwald global handle 851b3fcedb9SMatthias Ringwald global total_size 852b3fcedb9SMatthias Ringwald 853e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 854b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 1 855b3fcedb9SMatthias Ringwald 856b3fcedb9SMatthias Ringwald no_of_digitals = parts[1] 857b3fcedb9SMatthias Ringwald 858b3fcedb9SMatthias Ringwald write_indent(fout) 859b3fcedb9SMatthias Ringwald fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:]))) 860b3fcedb9SMatthias Ringwald write_indent(fout) 861b3fcedb9SMatthias Ringwald write_16(fout, size) 862e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 863b3fcedb9SMatthias Ringwald write_16(fout, handle) 864b3fcedb9SMatthias Ringwald write_16(fout, 0x2909) 865b3fcedb9SMatthias Ringwald write_sequence(fout, no_of_digitals) 866b3fcedb9SMatthias Ringwald fout.write("\n") 867b3fcedb9SMatthias Ringwald handle = handle + 1 868b3fcedb9SMatthias Ringwald 86960b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout): 870b3fcedb9SMatthias Ringwald global handle 871b3fcedb9SMatthias Ringwald global total_size 872b3fcedb9SMatthias Ringwald 873b165f97bSMatthias Ringwald line_count = 0; 874b3fcedb9SMatthias Ringwald for line in fin: 875b3fcedb9SMatthias Ringwald line = line.strip("\n\r ") 876b165f97bSMatthias Ringwald line_count += 1 877b3fcedb9SMatthias Ringwald 878b165f97bSMatthias Ringwald if line.startswith("//"): 879b165f97bSMatthias Ringwald fout.write(" //" + line.lstrip('/') + '\n') 880b165f97bSMatthias Ringwald continue 881b165f97bSMatthias Ringwald 88260b51a4cSMatthias Ringwald if line.startswith("#import"): 88360b51a4cSMatthias Ringwald imported_file = '' 88460b51a4cSMatthias Ringwald parts = re.match('#import\s+<(.*)>\w*',line) 88560b51a4cSMatthias Ringwald if parts and len(parts.groups()) == 1: 886dbb3997aSMilanka Ringwald imported_file = parts.groups()[0] 88760b51a4cSMatthias Ringwald parts = re.match('#import\s+"(.*)"\w*',line) 88860b51a4cSMatthias Ringwald if parts and len(parts.groups()) == 1: 889dbb3997aSMilanka Ringwald imported_file = parts.groups()[0] 89060b51a4cSMatthias Ringwald if len(imported_file) == 0: 89160b51a4cSMatthias Ringwald print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count)) 89260b51a4cSMatthias Ringwald continue 89360b51a4cSMatthias Ringwald 894dbb3997aSMilanka Ringwald imported_file = getFile( imported_file ) 89560b51a4cSMatthias Ringwald print("Importing %s" % imported_file) 89660b51a4cSMatthias Ringwald try: 89760b51a4cSMatthias Ringwald imported_fin = codecs.open (imported_file, encoding='utf-8') 89860b51a4cSMatthias Ringwald fout.write(' // ' + line + ' -- BEGIN\n') 89960b51a4cSMatthias Ringwald parseLines(imported_file, imported_fin, fout) 90060b51a4cSMatthias Ringwald fout.write(' // ' + line + ' -- END\n') 90160b51a4cSMatthias Ringwald except IOError as e: 90260b51a4cSMatthias Ringwald print('ERROR: Import failed. Please check path.') 90360b51a4cSMatthias Ringwald 90460b51a4cSMatthias Ringwald continue 90560b51a4cSMatthias Ringwald 90660b51a4cSMatthias Ringwald if line.startswith("#TODO"): 90760b51a4cSMatthias Ringwald print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count)) 908b165f97bSMatthias Ringwald print ("'%s'" % line) 909b165f97bSMatthias Ringwald fout.write("// " + line + '\n') 910b3fcedb9SMatthias Ringwald continue 911b3fcedb9SMatthias Ringwald 912b3fcedb9SMatthias Ringwald if len(line) == 0: 913b3fcedb9SMatthias Ringwald continue 914b3fcedb9SMatthias Ringwald 915b3fcedb9SMatthias Ringwald f = io.StringIO(line) 916b3fcedb9SMatthias Ringwald parts_list = csv.reader(f, delimiter=',', quotechar='"') 917b3fcedb9SMatthias Ringwald 918b3fcedb9SMatthias Ringwald for parts in parts_list: 919b3fcedb9SMatthias Ringwald for index, object in enumerate(parts): 920b3fcedb9SMatthias Ringwald parts[index] = object.strip().lstrip('"').rstrip('"') 921b3fcedb9SMatthias Ringwald 922b3fcedb9SMatthias Ringwald if parts[0] == 'PRIMARY_SERVICE': 923b3fcedb9SMatthias Ringwald parsePrimaryService(fout, parts) 924b3fcedb9SMatthias Ringwald continue 925b3fcedb9SMatthias Ringwald 926b3fcedb9SMatthias Ringwald if parts[0] == 'SECONDARY_SERVICE': 927b3fcedb9SMatthias Ringwald parseSecondaryService(fout, parts) 928b3fcedb9SMatthias Ringwald continue 929b3fcedb9SMatthias Ringwald 930b3fcedb9SMatthias Ringwald if parts[0] == 'INCLUDE_SERVICE': 931b3fcedb9SMatthias Ringwald parseIncludeService(fout, parts) 932b3fcedb9SMatthias Ringwald continue 933b3fcedb9SMatthias Ringwald 934b3fcedb9SMatthias Ringwald # 2803 935b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC': 936b3fcedb9SMatthias Ringwald parseCharacteristic(fout, parts) 937b3fcedb9SMatthias Ringwald continue 938b3fcedb9SMatthias Ringwald 939b3fcedb9SMatthias Ringwald # 2900 Characteristic Extended Properties 940b3fcedb9SMatthias Ringwald 941b3fcedb9SMatthias Ringwald # 2901 942b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION': 943b3fcedb9SMatthias Ringwald parseCharacteristicUserDescription(fout, parts) 944b3fcedb9SMatthias Ringwald continue 945b3fcedb9SMatthias Ringwald 946b165f97bSMatthias Ringwald 947b165f97bSMatthias Ringwald # 2902 Client Characteristic Configuration - automatically included in Characteristic if 948b3fcedb9SMatthias Ringwald # notification / indication is supported 949231a3c5dSMatthias Ringwald if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION': 950b165f97bSMatthias Ringwald continue 951b3fcedb9SMatthias Ringwald 952b3fcedb9SMatthias Ringwald # 2903 953b3fcedb9SMatthias Ringwald if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION': 95417215335SMatthias Ringwald parseGenericDynamicDescriptor(fout, parts, 0x2903, 'SERVER_CONFIGURATION') 955b3fcedb9SMatthias Ringwald continue 956b3fcedb9SMatthias Ringwald 957b3fcedb9SMatthias Ringwald # 2904 958b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_FORMAT': 959b3fcedb9SMatthias Ringwald parseCharacteristicFormat(fout, parts) 960b3fcedb9SMatthias Ringwald continue 961b3fcedb9SMatthias Ringwald 962b3fcedb9SMatthias Ringwald # 2905 963b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT': 964b3fcedb9SMatthias Ringwald parseCharacteristicAggregateFormat(fout, parts) 965b3fcedb9SMatthias Ringwald continue 966b3fcedb9SMatthias Ringwald 967b3fcedb9SMatthias Ringwald # 2906 968b3fcedb9SMatthias Ringwald if parts[0] == 'VALID_RANGE': 9696f08f159SMatthias Ringwald parseGenericDynamicReadOnlyDescriptor(fout, parts, 0x2906, 'VALID_RANGE') 970b3fcedb9SMatthias Ringwald continue 971b3fcedb9SMatthias Ringwald 972b3fcedb9SMatthias Ringwald # 2907 973b3fcedb9SMatthias Ringwald if parts[0] == 'EXTERNAL_REPORT_REFERENCE': 974*05146de0SMilanka Ringwald parseExternalReportReference(fout, parts) 975b3fcedb9SMatthias Ringwald continue 976b3fcedb9SMatthias Ringwald 977b3fcedb9SMatthias Ringwald # 2908 978b3fcedb9SMatthias Ringwald if parts[0] == 'REPORT_REFERENCE': 979b3fcedb9SMatthias Ringwald parseReportReference(fout, parts) 980b3fcedb9SMatthias Ringwald continue 981b3fcedb9SMatthias Ringwald 982b3fcedb9SMatthias Ringwald # 2909 983b3fcedb9SMatthias Ringwald if parts[0] == 'NUMBER_OF_DIGITALS': 984b3fcedb9SMatthias Ringwald parseNumberOfDigitals(fout, parts) 985b3fcedb9SMatthias Ringwald continue 986b3fcedb9SMatthias Ringwald 987b3fcedb9SMatthias Ringwald # 290A 988b3fcedb9SMatthias Ringwald if parts[0] == 'VALUE_TRIGGER_SETTING': 9896f08f159SMatthias Ringwald parseGenericDynamicDescriptor(fout, parts, 0x290A, 'VALUE_TRIGGER_SETTING') 990b3fcedb9SMatthias Ringwald continue 991b3fcedb9SMatthias Ringwald 992b3fcedb9SMatthias Ringwald # 290B 993b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION': 9946f08f159SMatthias Ringwald parseGenericDynamicDescriptor(fout, parts, 0x290B, 'ENVIRONMENTAL_SENSING_CONFIGURATION') 995b3fcedb9SMatthias Ringwald continue 996b3fcedb9SMatthias Ringwald 997b3fcedb9SMatthias Ringwald # 290C 998b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT': 9996f08f159SMatthias Ringwald parseGenericDynamicReadOnlyDescriptor(fout, parts, 0x290C, 'ENVIRONMENTAL_SENSING_MEASUREMENT') 1000b3fcedb9SMatthias Ringwald continue 1001b3fcedb9SMatthias Ringwald 1002b3fcedb9SMatthias Ringwald # 290D 1003b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING': 10046f08f159SMatthias Ringwald parseGenericDynamicDescriptor(fout, parts, 0x290D, 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING') 1005b3fcedb9SMatthias Ringwald continue 1006b3fcedb9SMatthias Ringwald 1007b3fcedb9SMatthias Ringwald print("WARNING: unknown token: %s\n" % (parts[0])) 1008b3fcedb9SMatthias Ringwald 10097050bf34SMatthias Ringwalddef parse(fname_in, fin, fname_out, tool_path, fout): 101060b51a4cSMatthias Ringwald global handle 101160b51a4cSMatthias Ringwald global total_size 101260b51a4cSMatthias Ringwald 10137050bf34SMatthias Ringwald fout.write(header.format(fname_out, fname_in, tool_path)) 101460b51a4cSMatthias Ringwald fout.write('{\n') 1015fd1be25dSMatthias Ringwald write_indent(fout) 1016fd1be25dSMatthias Ringwald fout.write('// ATT DB Version\n') 1017fd1be25dSMatthias Ringwald write_indent(fout) 1018fd1be25dSMatthias Ringwald fout.write('1,\n') 1019fd1be25dSMatthias Ringwald fout.write("\n") 102060b51a4cSMatthias Ringwald 102160b51a4cSMatthias Ringwald parseLines(fname_in, fin, fout) 102260b51a4cSMatthias Ringwald 1023729074c4SMatthias Ringwald serviceDefinitionComplete(fout) 1024b3fcedb9SMatthias Ringwald write_indent(fout) 1025b3fcedb9SMatthias Ringwald fout.write("// END\n"); 1026b3fcedb9SMatthias Ringwald write_indent(fout) 1027b3fcedb9SMatthias Ringwald write_16(fout,0) 1028b3fcedb9SMatthias Ringwald fout.write("\n") 1029b3fcedb9SMatthias Ringwald total_size = total_size + 2 1030b3fcedb9SMatthias Ringwald 1031b3fcedb9SMatthias Ringwald fout.write("}; // total size %u bytes \n" % total_size); 1032b3fcedb9SMatthias Ringwald 1033b3fcedb9SMatthias Ringwalddef listHandles(fout): 1034b3fcedb9SMatthias Ringwald fout.write('\n\n') 1035b3fcedb9SMatthias Ringwald fout.write('//\n') 1036729074c4SMatthias Ringwald fout.write('// list service handle ranges\n') 1037729074c4SMatthias Ringwald fout.write('//\n') 1038729074c4SMatthias Ringwald for define in defines_for_services: 1039729074c4SMatthias Ringwald fout.write(define) 1040729074c4SMatthias Ringwald fout.write('\n') 1041729074c4SMatthias Ringwald fout.write('\n') 1042729074c4SMatthias Ringwald fout.write('//\n') 1043b3fcedb9SMatthias Ringwald fout.write('// list mapping between characteristics and handles\n') 1044b3fcedb9SMatthias Ringwald fout.write('//\n') 1045729074c4SMatthias Ringwald for define in defines_for_characteristics: 1046b3fcedb9SMatthias Ringwald fout.write(define) 1047b3fcedb9SMatthias Ringwald fout.write('\n') 1048b3fcedb9SMatthias Ringwald 1049dbb3997aSMilanka Ringwalddef getFile( fileName ): 105078b65b0aSMatthias Ringwald for d in include_paths: 105178b65b0aSMatthias Ringwald fullFile = os.path.normpath(d + os.sep + fileName) # because Windows exists 105278b65b0aSMatthias Ringwald # print("test %s" % fullFile) 1053dbb3997aSMilanka Ringwald if os.path.isfile( fullFile ) == True: 1054dbb3997aSMilanka Ringwald return fullFile 1055dbb3997aSMilanka Ringwald print ("'{0}' not found".format( fileName )) 105678b65b0aSMatthias Ringwald print ("Include paths: %s" % ", ".join(include_paths)) 1057dbb3997aSMilanka Ringwald exit(-1) 1058dbb3997aSMilanka Ringwald 1059dbb3997aSMilanka Ringwald 1060dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') 106178b65b0aSMatthias Ringwalddefault_includes = [os.path.normpath(path) for path in [ btstack_root + '/src/', btstack_root + '/src/ble/gatt-service/']] 1062dbb3997aSMilanka Ringwald 1063dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack') 1064dbb3997aSMilanka Ringwald 1065dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes', 1066dbb3997aSMilanka Ringwald help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes)) 1067dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str, 1068dbb3997aSMilanka Ringwald help='gatt file to be compiled') 1069dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str, 1070dbb3997aSMilanka Ringwald help='header file to be generated') 1071dbb3997aSMilanka Ringwald 1072dbb3997aSMilanka Ringwaldargs = parser.parse_args() 1073dbb3997aSMilanka Ringwald 107478b65b0aSMatthias Ringwald# add include path arguments 107578b65b0aSMatthias Ringwaldif args.I != None: 107678b65b0aSMatthias Ringwald for d in args.I: 107778b65b0aSMatthias Ringwald include_paths.append(os.path.normpath(d[0])) 107878b65b0aSMatthias Ringwald 1079dbb3997aSMilanka Ringwald# append default include paths 108078b65b0aSMatthias Ringwaldinclude_paths.extend(default_includes) 1081dbb3997aSMilanka Ringwald 1082b3fcedb9SMatthias Ringwaldtry: 1083b165f97bSMatthias Ringwald # read defines from bluetooth_gatt.h 1084dbb3997aSMilanka Ringwald gen_path = getFile( 'bluetooth_gatt.h' ) 1085b165f97bSMatthias Ringwald bluetooth_gatt = read_defines(gen_path) 1086b165f97bSMatthias Ringwald 1087dbb3997aSMilanka Ringwald filename = args.hfile 1088dbb3997aSMilanka Ringwald fin = codecs.open (args.gattfile, encoding='utf-8') 1089285653b2SMatthias Ringwald 1090285653b2SMatthias Ringwald # pass 1: create temp .h file 1091d78181d6SMatthias Ringwald ftemp = tempfile.TemporaryFile(mode='w+t') 1092285653b2SMatthias Ringwald parse(args.gattfile, fin, filename, sys.argv[0], ftemp) 1093285653b2SMatthias Ringwald listHandles(ftemp) 1094285653b2SMatthias Ringwald 1095043f8832SMatthias Ringwald # calc GATT Database Hash 1096043f8832SMatthias Ringwald db_hash = aes_cmac(bytearray(16), database_hash_message) 1097043f8832SMatthias Ringwald if isinstance(db_hash, str): 1098043f8832SMatthias Ringwald # python2 1099043f8832SMatthias Ringwald db_hash_sequence = [('0x%02x' % ord(i)) for i in db_hash] 1100043f8832SMatthias Ringwald elif isinstance(db_hash, bytes): 1101043f8832SMatthias Ringwald # python3 1102043f8832SMatthias Ringwald db_hash_sequence = [('0x%02x' % i) for i in db_hash] 1103043f8832SMatthias Ringwald else: 1104043f8832SMatthias Ringwald print("AES CMAC returns unexpected type %s, abort" % type(db_hash)) 1105043f8832SMatthias Ringwald sys.exit(1) 1106043f8832SMatthias Ringwald # reverse hash to get little endian 1107043f8832SMatthias Ringwald db_hash_sequence.reverse() 1108043f8832SMatthias Ringwald db_hash_string = ', '.join(db_hash_sequence) + ', ' 1109043f8832SMatthias Ringwald 1110285653b2SMatthias Ringwald # pass 2: insert GATT Database Hash 1111b3fcedb9SMatthias Ringwald fout = open (filename, 'w') 1112285653b2SMatthias Ringwald ftemp.seek(0) 1113285653b2SMatthias Ringwald for line in ftemp: 1114043f8832SMatthias Ringwald fout.write(line.replace('THE-DATABASE-HASH', db_hash_string)) 1115b3fcedb9SMatthias Ringwald fout.close() 1116285653b2SMatthias Ringwald ftemp.close() 1117285653b2SMatthias Ringwald 1118b165f97bSMatthias Ringwald print('Created %s' % filename) 1119b3fcedb9SMatthias Ringwald 1120b3fcedb9SMatthias Ringwaldexcept IOError as e: 1121e22a2612SMatthias Ringwald 1122b3fcedb9SMatthias Ringwald print(usage) 1123b3fcedb9SMatthias Ringwald sys.exit(1) 1124b3fcedb9SMatthias Ringwald 1125b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n') 1126