1b3fcedb9SMatthias Ringwald#!/usr/bin/env python 2b3fcedb9SMatthias Ringwald# 3dbb3997aSMilanka Ringwald# BLE GATT configuration generator for use with BTstack 4dbb3997aSMilanka Ringwald# Copyright 2018 BlueKitchen GmbH 5b3fcedb9SMatthias Ringwald# 6b3fcedb9SMatthias Ringwald# Format of input file: 7b3fcedb9SMatthias Ringwald# PRIMARY_SERVICE, SERVICE_UUID 8b3fcedb9SMatthias Ringwald# CHARACTERISTIC, ATTRIBUTE_TYPE_UUID, [READ | WRITE | DYNAMIC], VALUE 9b3fcedb9SMatthias Ringwald 10b3fcedb9SMatthias Ringwaldimport codecs 11b165f97bSMatthias Ringwaldimport csv 12b165f97bSMatthias Ringwaldimport io 13b165f97bSMatthias Ringwaldimport os 14b165f97bSMatthias Ringwaldimport re 15b165f97bSMatthias Ringwaldimport string 1660b51a4cSMatthias Ringwaldimport sys 17dbb3997aSMilanka Ringwaldimport argparse 18*285653b2SMatthias Ringwaldimport tempfile 19b3fcedb9SMatthias Ringwald 20b3fcedb9SMatthias Ringwaldheader = ''' 21b3fcedb9SMatthias Ringwald// {0} generated from {1} for BTstack 227050bf34SMatthias Ringwald// it needs to be regenerated when the .gatt file is updated. 237050bf34SMatthias Ringwald 247050bf34SMatthias Ringwald// To generate {0}: 257050bf34SMatthias Ringwald// {2} {1} {0} 267050bf34SMatthias Ringwald 27fd1be25dSMatthias Ringwald// att db format version 1 28b3fcedb9SMatthias Ringwald 29fd1be25dSMatthias Ringwald// binary attribute representation: 30fd1be25dSMatthias Ringwald// - size in bytes (16), flags(16), handle (16), uuid (16/128), value(...) 31b3fcedb9SMatthias Ringwald 32b3fcedb9SMatthias Ringwald#include <stdint.h> 33b3fcedb9SMatthias Ringwald 34b3fcedb9SMatthias Ringwaldconst uint8_t profile_data[] = 35b3fcedb9SMatthias Ringwald''' 36b3fcedb9SMatthias Ringwald 37b3fcedb9SMatthias Ringwaldprint(''' 38dbb3997aSMilanka RingwaldBLE configuration generator for use with BTstack 39dbb3997aSMilanka RingwaldCopyright 2018 BlueKitchen GmbH 40b3fcedb9SMatthias Ringwald''') 41b3fcedb9SMatthias Ringwald 42b3fcedb9SMatthias Ringwaldassigned_uuids = { 43b3fcedb9SMatthias Ringwald 'GAP_SERVICE' : 0x1800, 44b3fcedb9SMatthias Ringwald 'GATT_SERVICE' : 0x1801, 45b3fcedb9SMatthias Ringwald 'GAP_DEVICE_NAME' : 0x2a00, 46b3fcedb9SMatthias Ringwald 'GAP_APPEARANCE' : 0x2a01, 47b3fcedb9SMatthias Ringwald 'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02, 48b3fcedb9SMatthias Ringwald 'GAP_RECONNECTION_ADDRESS' : 0x2A03, 49b3fcedb9SMatthias Ringwald 'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04, 50b3fcedb9SMatthias Ringwald 'GATT_SERVICE_CHANGED' : 0x2a05, 51b3fcedb9SMatthias Ringwald} 52b3fcedb9SMatthias Ringwald 53e72176f8SMatthias Ringwaldsecurity_permsission = ['ANYBODY','ENCRYPTED', 'AUTHENTICATED', 'AUTHORIZED', 'AUTHENTICATED_SC'] 54d7ec1d24SMatthias Ringwald 55b3fcedb9SMatthias Ringwaldproperty_flags = { 56eb6072adSMatthias Ringwald # GATT Characteristic Properties 57b3fcedb9SMatthias Ringwald 'BROADCAST' : 0x01, 58b3fcedb9SMatthias Ringwald 'READ' : 0x02, 59b3fcedb9SMatthias Ringwald 'WRITE_WITHOUT_RESPONSE' : 0x04, 60b3fcedb9SMatthias Ringwald 'WRITE' : 0x08, 61b3fcedb9SMatthias Ringwald 'NOTIFY': 0x10, 62b3fcedb9SMatthias Ringwald 'INDICATE' : 0x20, 63b3fcedb9SMatthias Ringwald 'AUTHENTICATED_SIGNED_WRITE' : 0x40, 64b3fcedb9SMatthias Ringwald 'EXTENDED_PROPERTIES' : 0x80, 65b3fcedb9SMatthias Ringwald # custom BTstack extension 66b3fcedb9SMatthias Ringwald 'DYNAMIC': 0x100, 67b3fcedb9SMatthias Ringwald 'LONG_UUID': 0x200, 68e22a2612SMatthias Ringwald 69e22a2612SMatthias Ringwald # read permissions 70e22a2612SMatthias Ringwald 'READ_PERMISSION_BIT_0': 0x400, 71e22a2612SMatthias Ringwald 'READ_PERMISSION_BIT_1': 0x800, 72e22a2612SMatthias Ringwald 73e22a2612SMatthias Ringwald # 74b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_7': 0x6000, 75b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_8': 0x7000, 76b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_9': 0x8000, 77b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_10': 0x9000, 78b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_11': 0xa000, 79b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_12': 0xb000, 80b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_13': 0xc000, 81b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_14': 0xd000, 82b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_15': 0xe000, 83b3fcedb9SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_16': 0xf000, 84e22a2612SMatthias Ringwald 'ENCRYPTION_KEY_SIZE_MASK': 0xf000, 85eb6072adSMatthias Ringwald 86b3fcedb9SMatthias Ringwald # only used by gatt compiler >= 0xffff 87b3fcedb9SMatthias Ringwald # Extended Properties 88e72176f8SMatthias Ringwald 'RELIABLE_WRITE': 0x00010000, 89e72176f8SMatthias Ringwald 'AUTHENTICATION_REQUIRED': 0x00020000, 90e72176f8SMatthias Ringwald 'AUTHORIZATION_REQUIRED': 0x00040000, 91e72176f8SMatthias Ringwald 'READ_ANYBODY': 0x00080000, 92e72176f8SMatthias Ringwald 'READ_ENCRYPTED': 0x00100000, 93e72176f8SMatthias Ringwald 'READ_AUTHENTICATED': 0x00200000, 94e72176f8SMatthias Ringwald 'READ_AUTHENTICATED_SC': 0x00400000, 95e72176f8SMatthias Ringwald 'READ_AUTHORIZED': 0x00800000, 96e72176f8SMatthias Ringwald 'WRITE_ANYBODY': 0x01000000, 97e72176f8SMatthias Ringwald 'WRITE_ENCRYPTED': 0x02000000, 98e72176f8SMatthias Ringwald 'WRITE_AUTHENTICATED': 0x04000000, 99e72176f8SMatthias Ringwald 'WRITE_AUTHENTICATED_SC': 0x08000000, 100e72176f8SMatthias Ringwald 'WRITE_AUTHORIZED': 0x10000000, 101eb6072adSMatthias Ringwald 102eb6072adSMatthias Ringwald # Broadcast, Notify, Indicate, Extended Properties are only used to describe a GATT Characteristic, but are free to use with att_db 103e72176f8SMatthias Ringwald # - write permissions 104e22a2612SMatthias Ringwald 'WRITE_PERMISSION_BIT_0': 0x01, 105e22a2612SMatthias Ringwald 'WRITE_PERMISSION_BIT_1': 0x10, 106e72176f8SMatthias Ringwald # - SC required 107e72176f8SMatthias Ringwald 'READ_PERMISSION_SC': 0x20, 108e72176f8SMatthias Ringwald 'WRITE_PERMISSION_SC': 0x80, 109b3fcedb9SMatthias Ringwald} 110b3fcedb9SMatthias Ringwald 111b3fcedb9SMatthias Ringwaldservices = dict() 112b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict() 113b3fcedb9SMatthias Ringwaldpresentation_formats = dict() 114b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = "" 115b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0 116b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = "" 117729074c4SMatthias Ringwalddefines_for_characteristics = [] 118729074c4SMatthias Ringwalddefines_for_services = [] 11978b65b0aSMatthias Ringwaldinclude_paths = [] 120b3fcedb9SMatthias Ringwald 121b3fcedb9SMatthias Ringwaldhandle = 1 122b3fcedb9SMatthias Ringwaldtotal_size = 0 123b3fcedb9SMatthias Ringwald 124b165f97bSMatthias Ringwalddef read_defines(infile): 125b165f97bSMatthias Ringwald defines = dict() 126b165f97bSMatthias Ringwald with open (infile, 'rt') as fin: 127b165f97bSMatthias Ringwald for line in fin: 128b165f97bSMatthias Ringwald parts = re.match('#define\s+(\w+)\s+(\w+)',line) 129b165f97bSMatthias Ringwald if parts and len(parts.groups()) == 2: 130b165f97bSMatthias Ringwald (key, value) = parts.groups() 131b165f97bSMatthias Ringwald defines[key] = int(value, 16) 132b165f97bSMatthias Ringwald return defines 133b165f97bSMatthias Ringwald 134b3fcedb9SMatthias Ringwalddef keyForUUID(uuid): 135b3fcedb9SMatthias Ringwald keyUUID = "" 136b3fcedb9SMatthias Ringwald for i in uuid: 137b3fcedb9SMatthias Ringwald keyUUID += "%02x" % i 138b3fcedb9SMatthias Ringwald return keyUUID 139b3fcedb9SMatthias Ringwald 140b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid): 141b3fcedb9SMatthias Ringwald return uuid.replace('-', '_') 142b3fcedb9SMatthias Ringwald 143b3fcedb9SMatthias Ringwalddef twoByteLEFor(value): 144b3fcedb9SMatthias Ringwald return [ (value & 0xff), (value >> 8)] 145b3fcedb9SMatthias Ringwald 146b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text): 147b3fcedb9SMatthias 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): 148b3fcedb9SMatthias Ringwald return True 149b3fcedb9SMatthias Ringwald return False 150b3fcedb9SMatthias Ringwald 151b3fcedb9SMatthias Ringwalddef parseUUID128(uuid): 152b3fcedb9SMatthias 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) 153b3fcedb9SMatthias Ringwald uuid_bytes = [] 154b3fcedb9SMatthias Ringwald for i in range(8, 0, -1): 155b3fcedb9SMatthias Ringwald uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16)) 156b3fcedb9SMatthias Ringwald return uuid_bytes 157b3fcedb9SMatthias Ringwald 158b3fcedb9SMatthias Ringwalddef parseUUID(uuid): 159b3fcedb9SMatthias Ringwald if uuid in assigned_uuids: 160b3fcedb9SMatthias Ringwald return twoByteLEFor(assigned_uuids[uuid]) 161b165f97bSMatthias Ringwald uuid_upper = uuid.upper().replace('.','_') 162b165f97bSMatthias Ringwald if uuid_upper in bluetooth_gatt: 163b165f97bSMatthias Ringwald return twoByteLEFor(bluetooth_gatt[uuid_upper]) 164b3fcedb9SMatthias Ringwald if is_128bit_uuid(uuid): 165b3fcedb9SMatthias Ringwald return parseUUID128(uuid) 166b3fcedb9SMatthias Ringwald uuidInt = int(uuid, 16) 167b3fcedb9SMatthias Ringwald return twoByteLEFor(uuidInt) 168b3fcedb9SMatthias Ringwald 169b3fcedb9SMatthias Ringwalddef parseProperties(properties): 170b3fcedb9SMatthias Ringwald value = 0 171b3fcedb9SMatthias Ringwald parts = properties.split("|") 172b3fcedb9SMatthias Ringwald for property in parts: 173b3fcedb9SMatthias Ringwald property = property.strip() 174b3fcedb9SMatthias Ringwald if property in property_flags: 175b3fcedb9SMatthias Ringwald value |= property_flags[property] 176b3fcedb9SMatthias Ringwald else: 177b3fcedb9SMatthias Ringwald print("WARNING: property %s undefined" % (property)) 178e22a2612SMatthias Ringwald 179e22a2612SMatthias Ringwald return value; 180e22a2612SMatthias Ringwald 181e22a2612SMatthias Ringwalddef gatt_characteristic_properties(properties): 182e22a2612SMatthias Ringwald return properties & 0xff 183e22a2612SMatthias Ringwald 184e22a2612SMatthias Ringwalddef att_flags(properties): 185e72176f8SMatthias Ringwald # drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Properties (0x80) - not used for flags 186e72176f8SMatthias Ringwald properties &= 0xffffff4e 187e22a2612SMatthias Ringwald 188e22a2612SMatthias Ringwald # rw permissions distinct 189e22a2612SMatthias Ringwald distinct_permissions_used = properties & ( 190e22a2612SMatthias Ringwald property_flags['READ_AUTHORIZED'] | 191e72176f8SMatthias Ringwald property_flags['READ_AUTHENTICATED_SC'] | 192e22a2612SMatthias Ringwald property_flags['READ_AUTHENTICATED'] | 193e22a2612SMatthias Ringwald property_flags['READ_ENCRYPTED'] | 194e22a2612SMatthias Ringwald property_flags['READ_ANYBODY'] | 195e22a2612SMatthias Ringwald property_flags['WRITE_AUTHORIZED'] | 196e22a2612SMatthias Ringwald property_flags['WRITE_AUTHENTICATED'] | 197e72176f8SMatthias Ringwald property_flags['WRITE_AUTHENTICATED_SC'] | 198e22a2612SMatthias Ringwald property_flags['WRITE_ENCRYPTED'] | 199e22a2612SMatthias Ringwald property_flags['WRITE_ANYBODY'] 200e22a2612SMatthias Ringwald ) != 0 201e22a2612SMatthias Ringwald 202e22a2612SMatthias Ringwald # post process properties 203e22a2612SMatthias Ringwald encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0 204e22a2612SMatthias Ringwald 205d7ec1d24SMatthias Ringwald # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted 206e22a2612SMatthias Ringwald if encryption_key_size_specified and not distinct_permissions_used: 207e22a2612SMatthias Ringwald properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED'] 208e22a2612SMatthias Ringwald 209d7ec1d24SMatthias Ringwald # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated 210d7ec1d24SMatthias Ringwald if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used: 211d7ec1d24SMatthias Ringwald properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED'] 212d7ec1d24SMatthias Ringwald 213d7ec1d24SMatthias Ringwald # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized 214d7ec1d24SMatthias Ringwald if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used: 215d7ec1d24SMatthias Ringwald properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED'] 216d7ec1d24SMatthias Ringwald 217d7ec1d24SMatthias Ringwald # determine read/write security requirements 218d7ec1d24SMatthias Ringwald read_security_level = 0 219d7ec1d24SMatthias Ringwald write_security_level = 0 220e72176f8SMatthias Ringwald read_requires_sc = False 221e72176f8SMatthias Ringwald write_requires_sc = False 222e22a2612SMatthias Ringwald if properties & property_flags['READ_AUTHORIZED']: 223d7ec1d24SMatthias Ringwald read_security_level = 3 224e22a2612SMatthias Ringwald elif properties & property_flags['READ_AUTHENTICATED']: 225d7ec1d24SMatthias Ringwald read_security_level = 2 226e72176f8SMatthias Ringwald elif properties & property_flags['READ_AUTHENTICATED_SC']: 227e72176f8SMatthias Ringwald read_security_level = 2 228e72176f8SMatthias Ringwald read_requires_sc = True 229e22a2612SMatthias Ringwald elif properties & property_flags['READ_ENCRYPTED']: 230d7ec1d24SMatthias Ringwald read_security_level = 1 231e22a2612SMatthias Ringwald if properties & property_flags['WRITE_AUTHORIZED']: 232d7ec1d24SMatthias Ringwald write_security_level = 3 233e22a2612SMatthias Ringwald elif properties & property_flags['WRITE_AUTHENTICATED']: 234d7ec1d24SMatthias Ringwald write_security_level = 2 235e72176f8SMatthias Ringwald elif properties & property_flags['WRITE_AUTHENTICATED_SC']: 236e72176f8SMatthias Ringwald write_security_level = 2 237e72176f8SMatthias Ringwald write_requires_sc = True 238e22a2612SMatthias Ringwald elif properties & property_flags['WRITE_ENCRYPTED']: 239d7ec1d24SMatthias Ringwald write_security_level = 1 240d7ec1d24SMatthias Ringwald 241d7ec1d24SMatthias Ringwald # map security requirements to flags 242d7ec1d24SMatthias Ringwald if read_security_level & 2: 243d7ec1d24SMatthias Ringwald properties |= property_flags['READ_PERMISSION_BIT_1'] 244d7ec1d24SMatthias Ringwald if read_security_level & 1: 245d7ec1d24SMatthias Ringwald properties |= property_flags['READ_PERMISSION_BIT_0'] 246e72176f8SMatthias Ringwald if read_requires_sc: 247e72176f8SMatthias Ringwald properties |= property_flags['READ_PERMISSION_SC'] 248d7ec1d24SMatthias Ringwald if write_security_level & 2: 249d7ec1d24SMatthias Ringwald properties |= property_flags['WRITE_PERMISSION_BIT_1'] 250d7ec1d24SMatthias Ringwald if write_security_level & 1: 251e22a2612SMatthias Ringwald properties |= property_flags['WRITE_PERMISSION_BIT_0'] 252e72176f8SMatthias Ringwald if write_requires_sc: 253e72176f8SMatthias Ringwald properties |= property_flags['WRITE_PERMISSION_SC'] 254e22a2612SMatthias Ringwald 255e22a2612SMatthias Ringwald return properties 256e22a2612SMatthias Ringwald 257d7ec1d24SMatthias Ringwalddef write_permissions_and_key_size_flags_from_properties(properties): 258e22a2612SMatthias Ringwald return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1']) 259b3fcedb9SMatthias Ringwald 260b3fcedb9SMatthias Ringwalddef write_8(fout, value): 261b3fcedb9SMatthias Ringwald fout.write( "0x%02x, " % (value & 0xff)) 262b3fcedb9SMatthias Ringwald 263b3fcedb9SMatthias Ringwalddef write_16(fout, value): 264b3fcedb9SMatthias Ringwald fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff)) 265b3fcedb9SMatthias Ringwald 266*285653b2SMatthias Ringwalddef write_uuid(fout, uuid): 267b3fcedb9SMatthias Ringwald for byte in uuid: 268b3fcedb9SMatthias Ringwald fout.write( "0x%02x, " % byte) 269b3fcedb9SMatthias Ringwald 270b3fcedb9SMatthias Ringwalddef write_string(fout, text): 271b3fcedb9SMatthias Ringwald for l in text.lstrip('"').rstrip('"'): 272b3fcedb9SMatthias Ringwald write_8(fout, ord(l)) 273b3fcedb9SMatthias Ringwald 274b3fcedb9SMatthias Ringwalddef write_sequence(fout, text): 275b3fcedb9SMatthias Ringwald parts = text.split() 276b3fcedb9SMatthias Ringwald for part in parts: 277b3fcedb9SMatthias Ringwald fout.write("0x%s, " % (part.strip())) 278b3fcedb9SMatthias Ringwald 279b3fcedb9SMatthias Ringwalddef write_indent(fout): 280b3fcedb9SMatthias Ringwald fout.write(" ") 281b3fcedb9SMatthias Ringwald 282d7ec1d24SMatthias Ringwalddef read_permissions_from_flags(flags): 283d7ec1d24SMatthias Ringwald permissions = 0 284d7ec1d24SMatthias Ringwald if flags & property_flags['READ_PERMISSION_BIT_0']: 285d7ec1d24SMatthias Ringwald permissions |= 1 286d7ec1d24SMatthias Ringwald if flags & property_flags['READ_PERMISSION_BIT_1']: 287d7ec1d24SMatthias Ringwald permissions |= 2 288e72176f8SMatthias Ringwald if flags & property_flags['READ_PERMISSION_SC'] and permissions == 2: 289e72176f8SMatthias Ringwald permissions = 4 290d7ec1d24SMatthias Ringwald return permissions 291d7ec1d24SMatthias Ringwald 292d7ec1d24SMatthias Ringwalddef write_permissions_from_flags(flags): 293d7ec1d24SMatthias Ringwald permissions = 0 294d7ec1d24SMatthias Ringwald if flags & property_flags['WRITE_PERMISSION_BIT_0']: 295d7ec1d24SMatthias Ringwald permissions |= 1 296d7ec1d24SMatthias Ringwald if flags & property_flags['WRITE_PERMISSION_BIT_1']: 297d7ec1d24SMatthias Ringwald permissions |= 2 298e72176f8SMatthias Ringwald if flags & property_flags['WRITE_PERMISSION_SC'] and permissions == 2: 299e72176f8SMatthias Ringwald permissions = 4 300d7ec1d24SMatthias Ringwald return permissions 301d7ec1d24SMatthias Ringwald 302d7ec1d24SMatthias Ringwalddef encryption_key_size_from_flags(flags): 303d7ec1d24SMatthias Ringwald encryption_key_size = (flags & 0xf000) >> 12 304d7ec1d24SMatthias Ringwald if encryption_key_size > 0: 305d7ec1d24SMatthias Ringwald encryption_key_size += 1 306d7ec1d24SMatthias Ringwald return encryption_key_size 307d7ec1d24SMatthias Ringwald 308b3fcedb9SMatthias Ringwalddef is_string(text): 309b3fcedb9SMatthias Ringwald for item in text.split(" "): 310b3fcedb9SMatthias Ringwald if not all(c in string.hexdigits for c in item): 311b3fcedb9SMatthias Ringwald return True 312b3fcedb9SMatthias Ringwald return False 313b3fcedb9SMatthias Ringwald 314b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties): 315b3fcedb9SMatthias Ringwald return properties & (property_flags['NOTIFY'] | property_flags['INDICATE']) 316b3fcedb9SMatthias Ringwald 317729074c4SMatthias Ringwalddef serviceDefinitionComplete(fout): 318729074c4SMatthias Ringwald global services 319729074c4SMatthias Ringwald if current_service_uuid_string: 320729074c4SMatthias Ringwald fout.write("\n") 321729074c4SMatthias Ringwald # print("append service %s = [%d, %d]" % (current_characteristic_uuid_string, current_service_start_handle, handle-1)) 322729074c4SMatthias Ringwald defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle)) 323729074c4SMatthias Ringwald defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1)) 324729074c4SMatthias Ringwald services[current_service_uuid_string] = [current_service_start_handle, handle-1] 325729074c4SMatthias Ringwald 326d7ec1d24SMatthias Ringwalddef dump_flags(fout, flags): 327d7ec1d24SMatthias Ringwald global security_permsission 328d7ec1d24SMatthias Ringwald encryption_key_size = encryption_key_size_from_flags(flags) 329d7ec1d24SMatthias Ringwald read_permissions = security_permsission[read_permissions_from_flags(flags)] 330d7ec1d24SMatthias Ringwald write_permissions = security_permsission[write_permissions_from_flags(flags)] 331d7ec1d24SMatthias Ringwald write_indent(fout) 332d7ec1d24SMatthias Ringwald fout.write('// ') 333d7ec1d24SMatthias Ringwald first = 1 334d7ec1d24SMatthias Ringwald if flags & property_flags['READ']: 335d7ec1d24SMatthias Ringwald fout.write('READ_%s' % read_permissions) 336d7ec1d24SMatthias Ringwald first = 0 337d7ec1d24SMatthias Ringwald if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']): 338d7ec1d24SMatthias Ringwald if not first: 339d7ec1d24SMatthias Ringwald fout.write(', ') 340d7ec1d24SMatthias Ringwald first = 0 341d7ec1d24SMatthias Ringwald fout.write('WRITE_%s' % write_permissions) 342d7ec1d24SMatthias Ringwald if encryption_key_size > 0: 343d7ec1d24SMatthias Ringwald if not first: 344d7ec1d24SMatthias Ringwald fout.write(', ') 345d7ec1d24SMatthias Ringwald first = 0 346d7ec1d24SMatthias Ringwald fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size) 347d7ec1d24SMatthias Ringwald fout.write('\n') 348d7ec1d24SMatthias Ringwald 349b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type): 350b3fcedb9SMatthias Ringwald global handle 351b3fcedb9SMatthias Ringwald global total_size 352b3fcedb9SMatthias Ringwald global current_service_uuid_string 353b3fcedb9SMatthias Ringwald global current_service_start_handle 354b3fcedb9SMatthias Ringwald 355729074c4SMatthias Ringwald serviceDefinitionComplete(fout) 356b3fcedb9SMatthias Ringwald 357d7ec1d24SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 358b3fcedb9SMatthias Ringwald 359b3fcedb9SMatthias Ringwald write_indent(fout) 360b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 361b3fcedb9SMatthias Ringwald 362b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 363b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 364b3fcedb9SMatthias Ringwald 365b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + uuid_size + 2 366b3fcedb9SMatthias Ringwald 367b3fcedb9SMatthias Ringwald if service_type == 0x2802: 368b3fcedb9SMatthias Ringwald size += 4 369b3fcedb9SMatthias Ringwald 370b3fcedb9SMatthias Ringwald write_indent(fout) 371b3fcedb9SMatthias Ringwald write_16(fout, size) 372d7ec1d24SMatthias Ringwald write_16(fout, read_only_anybody_flags) 373b3fcedb9SMatthias Ringwald write_16(fout, handle) 374b3fcedb9SMatthias Ringwald write_16(fout, service_type) 375*285653b2SMatthias Ringwald write_uuid(fout, uuid) 376b3fcedb9SMatthias Ringwald fout.write("\n") 377b3fcedb9SMatthias Ringwald 378729074c4SMatthias Ringwald current_service_uuid_string = c_string_for_uuid(parts[1]) 379b3fcedb9SMatthias Ringwald current_service_start_handle = handle 380b3fcedb9SMatthias Ringwald handle = handle + 1 381b3fcedb9SMatthias Ringwald total_size = total_size + size 382b3fcedb9SMatthias Ringwald 383b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts): 384b3fcedb9SMatthias Ringwald parseService(fout, parts, 0x2800) 385b3fcedb9SMatthias Ringwald 386b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts): 387b3fcedb9SMatthias Ringwald parseService(fout, parts, 0x2801) 388b3fcedb9SMatthias Ringwald 389b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts): 390b3fcedb9SMatthias Ringwald global handle 391b3fcedb9SMatthias Ringwald global total_size 392b3fcedb9SMatthias Ringwald 393e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 394b3fcedb9SMatthias Ringwald 395b3fcedb9SMatthias Ringwald write_indent(fout) 396b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts))) 397b3fcedb9SMatthias Ringwald 398b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 399b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 400b3fcedb9SMatthias Ringwald if uuid_size > 2: 401b3fcedb9SMatthias Ringwald uuid_size = 0 402729074c4SMatthias Ringwald # print("Include Service ", c_string_for_uuid(uuid)) 403b3fcedb9SMatthias Ringwald 404b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 4 + uuid_size 405b3fcedb9SMatthias Ringwald 406729074c4SMatthias Ringwald keyUUID = c_string_for_uuid(parts[1]) 407b3fcedb9SMatthias Ringwald 408b3fcedb9SMatthias Ringwald write_indent(fout) 409b3fcedb9SMatthias Ringwald write_16(fout, size) 410e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 411b3fcedb9SMatthias Ringwald write_16(fout, handle) 412b3fcedb9SMatthias Ringwald write_16(fout, 0x2802) 413b3fcedb9SMatthias Ringwald write_16(fout, services[keyUUID][0]) 414b3fcedb9SMatthias Ringwald write_16(fout, services[keyUUID][1]) 415b3fcedb9SMatthias Ringwald if uuid_size > 0: 416*285653b2SMatthias Ringwald write_uuid(fout, uuid) 417b3fcedb9SMatthias Ringwald fout.write("\n") 418b3fcedb9SMatthias Ringwald 419b3fcedb9SMatthias Ringwald handle = handle + 1 420b3fcedb9SMatthias Ringwald total_size = total_size + size 421b3fcedb9SMatthias Ringwald 422b3fcedb9SMatthias Ringwald 423b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts): 424b3fcedb9SMatthias Ringwald global handle 425b3fcedb9SMatthias Ringwald global total_size 426b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 427b3fcedb9SMatthias Ringwald global characteristic_indices 428b3fcedb9SMatthias Ringwald 429e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 430b3fcedb9SMatthias Ringwald 431b3fcedb9SMatthias Ringwald # enumerate characteristics with same UUID, using optional name tag if available 432b3fcedb9SMatthias Ringwald current_characteristic_uuid_string = c_string_for_uuid(parts[1]); 433b3fcedb9SMatthias Ringwald index = 1 434b3fcedb9SMatthias Ringwald if current_characteristic_uuid_string in characteristic_indices: 435b3fcedb9SMatthias Ringwald index = characteristic_indices[current_characteristic_uuid_string] + 1 436b3fcedb9SMatthias Ringwald characteristic_indices[current_characteristic_uuid_string] = index 437b3fcedb9SMatthias Ringwald if len(parts) > 4: 438b3fcedb9SMatthias Ringwald current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_') 439b3fcedb9SMatthias Ringwald else: 440b3fcedb9SMatthias Ringwald current_characteristic_uuid_string += ('_%02x' % index) 441b3fcedb9SMatthias Ringwald 442b3fcedb9SMatthias Ringwald uuid = parseUUID(parts[1]) 443b3fcedb9SMatthias Ringwald uuid_size = len(uuid) 444b3fcedb9SMatthias Ringwald properties = parseProperties(parts[2]) 445b3fcedb9SMatthias Ringwald value = ', '.join([str(x) for x in parts[3:]]) 446b3fcedb9SMatthias Ringwald 447b3fcedb9SMatthias Ringwald # reliable writes is defined in an extended properties 448b3fcedb9SMatthias Ringwald if (properties & property_flags['RELIABLE_WRITE']): 449b3fcedb9SMatthias Ringwald properties = properties | property_flags['EXTENDED_PROPERTIES'] 450b3fcedb9SMatthias Ringwald 451b3fcedb9SMatthias Ringwald write_indent(fout) 452b3fcedb9SMatthias Ringwald fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3]))) 453b3fcedb9SMatthias Ringwald 454e22a2612SMatthias Ringwald 455e22a2612SMatthias Ringwald characteristic_properties = gatt_characteristic_properties(properties) 456b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + (1+2+uuid_size) 457b3fcedb9SMatthias Ringwald write_indent(fout) 458b3fcedb9SMatthias Ringwald write_16(fout, size) 459e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 460b3fcedb9SMatthias Ringwald write_16(fout, handle) 461b3fcedb9SMatthias Ringwald write_16(fout, 0x2803) 462e22a2612SMatthias Ringwald write_8(fout, characteristic_properties) 463b3fcedb9SMatthias Ringwald write_16(fout, handle+1) 464*285653b2SMatthias Ringwald write_uuid(fout, uuid) 465b3fcedb9SMatthias Ringwald fout.write("\n") 466b3fcedb9SMatthias Ringwald handle = handle + 1 467b3fcedb9SMatthias Ringwald total_size = total_size + size 468b3fcedb9SMatthias Ringwald 469b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + uuid_size 470b3fcedb9SMatthias Ringwald if is_string(value): 471b3fcedb9SMatthias Ringwald size = size + len(value) 472b3fcedb9SMatthias Ringwald else: 473b3fcedb9SMatthias Ringwald size = size + len(value.split()) 474b3fcedb9SMatthias Ringwald 475e22a2612SMatthias Ringwald value_flags = att_flags(properties) 4768ea3236cSMatthias Ringwald 4778ea3236cSMatthias Ringwald # add UUID128 flag for value handle 478b3fcedb9SMatthias Ringwald if uuid_size == 16: 479e22a2612SMatthias Ringwald value_flags = value_flags | property_flags['LONG_UUID']; 480b3fcedb9SMatthias Ringwald 481b3fcedb9SMatthias Ringwald write_indent(fout) 482b3fcedb9SMatthias Ringwald fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value)) 483d7ec1d24SMatthias Ringwald 484d7ec1d24SMatthias Ringwald dump_flags(fout, value_flags) 485d7ec1d24SMatthias Ringwald 486b3fcedb9SMatthias Ringwald write_indent(fout) 487b3fcedb9SMatthias Ringwald write_16(fout, size) 488e22a2612SMatthias Ringwald write_16(fout, value_flags) 489b3fcedb9SMatthias Ringwald write_16(fout, handle) 490*285653b2SMatthias Ringwald write_uuid(fout, uuid) 491b3fcedb9SMatthias Ringwald if is_string(value): 492b3fcedb9SMatthias Ringwald write_string(fout, value) 493b3fcedb9SMatthias Ringwald else: 494b3fcedb9SMatthias Ringwald write_sequence(fout,value) 495b3fcedb9SMatthias Ringwald 496b3fcedb9SMatthias Ringwald fout.write("\n") 497729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 498b3fcedb9SMatthias Ringwald handle = handle + 1 499b3fcedb9SMatthias Ringwald 500b3fcedb9SMatthias Ringwald if add_client_characteristic_configuration(properties): 501e22a2612SMatthias Ringwald # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC 502d7ec1d24SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 503e22a2612SMatthias Ringwald flags |= property_flags['READ'] 504e22a2612SMatthias Ringwald flags |= property_flags['WRITE'] 5059be4aecfSMatthias Ringwald flags |= property_flags['WRITE_WITHOUT_RESPONSE'] 506e22a2612SMatthias Ringwald flags |= property_flags['DYNAMIC'] 507b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 2 508d7ec1d24SMatthias Ringwald 509b3fcedb9SMatthias Ringwald write_indent(fout) 510b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle)) 511d7ec1d24SMatthias Ringwald 512d7ec1d24SMatthias Ringwald dump_flags(fout, flags) 513d7ec1d24SMatthias Ringwald 514b3fcedb9SMatthias Ringwald write_indent(fout) 515b3fcedb9SMatthias Ringwald write_16(fout, size) 516e22a2612SMatthias Ringwald write_16(fout, flags) 517b3fcedb9SMatthias Ringwald write_16(fout, handle) 518b3fcedb9SMatthias Ringwald write_16(fout, 0x2902) 519b3fcedb9SMatthias Ringwald write_16(fout, 0) 520b3fcedb9SMatthias Ringwald fout.write("\n") 521729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 522b3fcedb9SMatthias Ringwald handle = handle + 1 523b3fcedb9SMatthias Ringwald 524b3fcedb9SMatthias Ringwald if properties & property_flags['RELIABLE_WRITE']: 525b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 2 526b3fcedb9SMatthias Ringwald write_indent(fout) 527b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle)) 528b3fcedb9SMatthias Ringwald write_indent(fout) 529b3fcedb9SMatthias Ringwald write_16(fout, size) 530e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 531b3fcedb9SMatthias Ringwald write_16(fout, handle) 532b3fcedb9SMatthias Ringwald write_16(fout, 0x2900) 533b3fcedb9SMatthias Ringwald write_16(fout, 1) # Reliable Write 534b3fcedb9SMatthias Ringwald fout.write("\n") 535b3fcedb9SMatthias Ringwald handle = handle + 1 536b3fcedb9SMatthias Ringwald 537b3fcedb9SMatthias Ringwalddef parseCharacteristicUserDescription(fout, parts): 538b3fcedb9SMatthias Ringwald global handle 539b3fcedb9SMatthias Ringwald global total_size 540b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 541b3fcedb9SMatthias Ringwald 542b3fcedb9SMatthias Ringwald properties = parseProperties(parts[1]) 543b3fcedb9SMatthias Ringwald value = parts[2] 544b3fcedb9SMatthias Ringwald 545b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 546b3fcedb9SMatthias Ringwald if is_string(value): 547b7647eb6SMatthias Ringwald size = size + len(value) 548b3fcedb9SMatthias Ringwald else: 549b3fcedb9SMatthias Ringwald size = size + len(value.split()) 550b3fcedb9SMatthias Ringwald 551e22a2612SMatthias Ringwald # use write, write permissions and encryption key size from attribute value and set READ_ANYBODY 552d7ec1d24SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 553e22a2612SMatthias Ringwald flags |= properties & property_flags['WRITE'] 554e22a2612SMatthias Ringwald flags |= property_flags['READ'] 555e22a2612SMatthias Ringwald 556b3fcedb9SMatthias Ringwald write_indent(fout) 557b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:]))) 558d7ec1d24SMatthias Ringwald 559d7ec1d24SMatthias Ringwald dump_flags(fout, flags) 560d7ec1d24SMatthias Ringwald 561b3fcedb9SMatthias Ringwald write_indent(fout) 562b3fcedb9SMatthias Ringwald write_16(fout, size) 563e22a2612SMatthias Ringwald write_16(fout, flags) 564b3fcedb9SMatthias Ringwald write_16(fout, handle) 565b3fcedb9SMatthias Ringwald write_16(fout, 0x2901) 566b3fcedb9SMatthias Ringwald if is_string(value): 567b3fcedb9SMatthias Ringwald write_string(fout, value) 568b3fcedb9SMatthias Ringwald else: 569b3fcedb9SMatthias Ringwald write_sequence(fout,value) 570b3fcedb9SMatthias Ringwald fout.write("\n") 571729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 572b3fcedb9SMatthias Ringwald handle = handle + 1 573b3fcedb9SMatthias Ringwald 574b3fcedb9SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts): 575b3fcedb9SMatthias Ringwald global handle 576b3fcedb9SMatthias Ringwald global total_size 577b3fcedb9SMatthias Ringwald global current_characteristic_uuid_string 578b3fcedb9SMatthias Ringwald 579b3fcedb9SMatthias Ringwald properties = parseProperties(parts[1]) 580b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 581b3fcedb9SMatthias Ringwald 582e22a2612SMatthias Ringwald # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY 583d7ec1d24SMatthias Ringwald flags = write_permissions_and_key_size_flags_from_properties(properties) 584e22a2612SMatthias Ringwald flags |= property_flags['READ'] 585e22a2612SMatthias Ringwald flags |= property_flags['WRITE'] 586e22a2612SMatthias Ringwald flags |= property_flags['DYNAMIC'] 587e22a2612SMatthias Ringwald 588b3fcedb9SMatthias Ringwald write_indent(fout) 589b3fcedb9SMatthias Ringwald fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:]))) 590d7ec1d24SMatthias Ringwald 591d7ec1d24SMatthias Ringwald dump_flags(fout, flags) 592d7ec1d24SMatthias Ringwald 593b3fcedb9SMatthias Ringwald write_indent(fout) 594b3fcedb9SMatthias Ringwald write_16(fout, size) 595e22a2612SMatthias Ringwald write_16(fout, flags) 596b3fcedb9SMatthias Ringwald write_16(fout, handle) 597b3fcedb9SMatthias Ringwald write_16(fout, 0x2903) 598b3fcedb9SMatthias Ringwald fout.write("\n") 599729074c4SMatthias Ringwald defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle)) 600b3fcedb9SMatthias Ringwald handle = handle + 1 601b3fcedb9SMatthias Ringwald 602b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts): 603b3fcedb9SMatthias Ringwald global handle 604b3fcedb9SMatthias Ringwald global total_size 605b3fcedb9SMatthias Ringwald 606e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 607b3fcedb9SMatthias Ringwald 608b3fcedb9SMatthias Ringwald identifier = parts[1] 609b3fcedb9SMatthias Ringwald presentation_formats[identifier] = handle 610b3fcedb9SMatthias Ringwald # print("format '%s' with handle %d\n" % (identifier, handle)) 611b3fcedb9SMatthias Ringwald 612b3fcedb9SMatthias Ringwald format = parts[2] 613b3fcedb9SMatthias Ringwald exponent = parts[3] 614b3fcedb9SMatthias Ringwald unit = parseUUID(parts[4]) 615b3fcedb9SMatthias Ringwald name_space = parts[5] 616b3fcedb9SMatthias Ringwald description = parseUUID(parts[6]) 617b3fcedb9SMatthias Ringwald 618b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 7 619b3fcedb9SMatthias Ringwald 620b3fcedb9SMatthias Ringwald write_indent(fout) 621b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 622b3fcedb9SMatthias Ringwald write_indent(fout) 623b3fcedb9SMatthias Ringwald write_16(fout, size) 624e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 625b3fcedb9SMatthias Ringwald write_16(fout, handle) 626b3fcedb9SMatthias Ringwald write_16(fout, 0x2904) 627b3fcedb9SMatthias Ringwald write_sequence(fout, format) 628b3fcedb9SMatthias Ringwald write_sequence(fout, exponent) 629*285653b2SMatthias Ringwald write_uuid(fout, unit) 630b3fcedb9SMatthias Ringwald write_sequence(fout, name_space) 631*285653b2SMatthias Ringwald write_uuid(fout, description) 632b3fcedb9SMatthias Ringwald fout.write("\n") 633b3fcedb9SMatthias Ringwald handle = handle + 1 634b3fcedb9SMatthias Ringwald 635b3fcedb9SMatthias Ringwald 636b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts): 637b3fcedb9SMatthias Ringwald global handle 638b3fcedb9SMatthias Ringwald global total_size 639b3fcedb9SMatthias Ringwald 640e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 641b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2 642b3fcedb9SMatthias Ringwald 643b3fcedb9SMatthias Ringwald write_indent(fout) 644b3fcedb9SMatthias Ringwald fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:]))) 645b3fcedb9SMatthias Ringwald write_indent(fout) 646b3fcedb9SMatthias Ringwald write_16(fout, size) 647e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 648b3fcedb9SMatthias Ringwald write_16(fout, handle) 649b3fcedb9SMatthias Ringwald write_16(fout, 0x2905) 650b3fcedb9SMatthias Ringwald for identifier in parts[1:]: 651b3fcedb9SMatthias Ringwald format_handle = presentation_formats[identifier] 652b3fcedb9SMatthias Ringwald if format == 0: 653b3fcedb9SMatthias Ringwald print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier) 654b3fcedb9SMatthias Ringwald sys.exit(1) 655b3fcedb9SMatthias Ringwald write_16(fout, format_handle) 656b3fcedb9SMatthias Ringwald fout.write("\n") 657b3fcedb9SMatthias Ringwald handle = handle + 1 658b3fcedb9SMatthias Ringwald 659b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts): 660b3fcedb9SMatthias Ringwald global handle 661b3fcedb9SMatthias Ringwald global total_size 662b3fcedb9SMatthias Ringwald 663e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 664b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 1 + 1 665b3fcedb9SMatthias Ringwald 666231a3c5dSMatthias Ringwald report_id = parts[2] 667231a3c5dSMatthias Ringwald report_type = parts[3] 668b3fcedb9SMatthias Ringwald 669b3fcedb9SMatthias Ringwald write_indent(fout) 670b3fcedb9SMatthias Ringwald fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:]))) 671b3fcedb9SMatthias Ringwald write_indent(fout) 672b3fcedb9SMatthias Ringwald write_16(fout, size) 673e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 674b3fcedb9SMatthias Ringwald write_16(fout, handle) 675b3fcedb9SMatthias Ringwald write_16(fout, 0x2908) 676b3fcedb9SMatthias Ringwald write_sequence(fout, report_id) 677b3fcedb9SMatthias Ringwald write_sequence(fout, report_type) 678b3fcedb9SMatthias Ringwald fout.write("\n") 679b3fcedb9SMatthias Ringwald handle = handle + 1 680b3fcedb9SMatthias Ringwald 681b3fcedb9SMatthias Ringwald 682b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts): 683b3fcedb9SMatthias Ringwald global handle 684b3fcedb9SMatthias Ringwald global total_size 685b3fcedb9SMatthias Ringwald 686e22a2612SMatthias Ringwald read_only_anybody_flags = property_flags['READ']; 687b3fcedb9SMatthias Ringwald size = 2 + 2 + 2 + 2 + 1 688b3fcedb9SMatthias Ringwald 689b3fcedb9SMatthias Ringwald no_of_digitals = parts[1] 690b3fcedb9SMatthias Ringwald 691b3fcedb9SMatthias Ringwald write_indent(fout) 692b3fcedb9SMatthias Ringwald fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:]))) 693b3fcedb9SMatthias Ringwald write_indent(fout) 694b3fcedb9SMatthias Ringwald write_16(fout, size) 695e22a2612SMatthias Ringwald write_16(fout, read_only_anybody_flags) 696b3fcedb9SMatthias Ringwald write_16(fout, handle) 697b3fcedb9SMatthias Ringwald write_16(fout, 0x2909) 698b3fcedb9SMatthias Ringwald write_sequence(fout, no_of_digitals) 699b3fcedb9SMatthias Ringwald fout.write("\n") 700b3fcedb9SMatthias Ringwald handle = handle + 1 701b3fcedb9SMatthias Ringwald 70260b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout): 703b3fcedb9SMatthias Ringwald global handle 704b3fcedb9SMatthias Ringwald global total_size 705b3fcedb9SMatthias Ringwald 706b165f97bSMatthias Ringwald line_count = 0; 707b3fcedb9SMatthias Ringwald for line in fin: 708b3fcedb9SMatthias Ringwald line = line.strip("\n\r ") 709b165f97bSMatthias Ringwald line_count += 1 710b3fcedb9SMatthias Ringwald 711b165f97bSMatthias Ringwald if line.startswith("//"): 712b165f97bSMatthias Ringwald fout.write(" //" + line.lstrip('/') + '\n') 713b165f97bSMatthias Ringwald continue 714b165f97bSMatthias Ringwald 71560b51a4cSMatthias Ringwald if line.startswith("#import"): 71660b51a4cSMatthias Ringwald imported_file = '' 71760b51a4cSMatthias Ringwald parts = re.match('#import\s+<(.*)>\w*',line) 71860b51a4cSMatthias Ringwald if parts and len(parts.groups()) == 1: 719dbb3997aSMilanka Ringwald imported_file = parts.groups()[0] 72060b51a4cSMatthias Ringwald parts = re.match('#import\s+"(.*)"\w*',line) 72160b51a4cSMatthias Ringwald if parts and len(parts.groups()) == 1: 722dbb3997aSMilanka Ringwald imported_file = parts.groups()[0] 72360b51a4cSMatthias Ringwald if len(imported_file) == 0: 72460b51a4cSMatthias Ringwald print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count)) 72560b51a4cSMatthias Ringwald continue 72660b51a4cSMatthias Ringwald 727dbb3997aSMilanka Ringwald imported_file = getFile( imported_file ) 72860b51a4cSMatthias Ringwald print("Importing %s" % imported_file) 72960b51a4cSMatthias Ringwald try: 73060b51a4cSMatthias Ringwald imported_fin = codecs.open (imported_file, encoding='utf-8') 73160b51a4cSMatthias Ringwald fout.write(' // ' + line + ' -- BEGIN\n') 73260b51a4cSMatthias Ringwald parseLines(imported_file, imported_fin, fout) 73360b51a4cSMatthias Ringwald fout.write(' // ' + line + ' -- END\n') 73460b51a4cSMatthias Ringwald except IOError as e: 73560b51a4cSMatthias Ringwald print('ERROR: Import failed. Please check path.') 73660b51a4cSMatthias Ringwald 73760b51a4cSMatthias Ringwald continue 73860b51a4cSMatthias Ringwald 73960b51a4cSMatthias Ringwald if line.startswith("#TODO"): 74060b51a4cSMatthias Ringwald print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count)) 741b165f97bSMatthias Ringwald print ("'%s'" % line) 742b165f97bSMatthias Ringwald fout.write("// " + line + '\n') 743b3fcedb9SMatthias Ringwald continue 744b3fcedb9SMatthias Ringwald 745b3fcedb9SMatthias Ringwald if len(line) == 0: 746b3fcedb9SMatthias Ringwald continue 747b3fcedb9SMatthias Ringwald 748b3fcedb9SMatthias Ringwald f = io.StringIO(line) 749b3fcedb9SMatthias Ringwald parts_list = csv.reader(f, delimiter=',', quotechar='"') 750b3fcedb9SMatthias Ringwald 751b3fcedb9SMatthias Ringwald for parts in parts_list: 752b3fcedb9SMatthias Ringwald for index, object in enumerate(parts): 753b3fcedb9SMatthias Ringwald parts[index] = object.strip().lstrip('"').rstrip('"') 754b3fcedb9SMatthias Ringwald 755b3fcedb9SMatthias Ringwald if parts[0] == 'PRIMARY_SERVICE': 756b3fcedb9SMatthias Ringwald parsePrimaryService(fout, parts) 757b3fcedb9SMatthias Ringwald continue 758b3fcedb9SMatthias Ringwald 759b3fcedb9SMatthias Ringwald if parts[0] == 'SECONDARY_SERVICE': 760b3fcedb9SMatthias Ringwald parseSecondaryService(fout, parts) 761b3fcedb9SMatthias Ringwald continue 762b3fcedb9SMatthias Ringwald 763b3fcedb9SMatthias Ringwald if parts[0] == 'INCLUDE_SERVICE': 764b3fcedb9SMatthias Ringwald parseIncludeService(fout, parts) 765b3fcedb9SMatthias Ringwald continue 766b3fcedb9SMatthias Ringwald 767b3fcedb9SMatthias Ringwald # 2803 768b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC': 769b3fcedb9SMatthias Ringwald parseCharacteristic(fout, parts) 770b3fcedb9SMatthias Ringwald continue 771b3fcedb9SMatthias Ringwald 772b3fcedb9SMatthias Ringwald # 2900 Characteristic Extended Properties 773b3fcedb9SMatthias Ringwald 774b3fcedb9SMatthias Ringwald # 2901 775b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION': 776b3fcedb9SMatthias Ringwald parseCharacteristicUserDescription(fout, parts) 777b3fcedb9SMatthias Ringwald continue 778b3fcedb9SMatthias Ringwald 779b165f97bSMatthias Ringwald 780b165f97bSMatthias Ringwald # 2902 Client Characteristic Configuration - automatically included in Characteristic if 781b3fcedb9SMatthias Ringwald # notification / indication is supported 782231a3c5dSMatthias Ringwald if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION': 783b165f97bSMatthias Ringwald continue 784b3fcedb9SMatthias Ringwald 785b3fcedb9SMatthias Ringwald # 2903 786b3fcedb9SMatthias Ringwald if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION': 787b3fcedb9SMatthias Ringwald parseServerCharacteristicConfiguration(fout, parts) 788b3fcedb9SMatthias Ringwald continue 789b3fcedb9SMatthias Ringwald 790b3fcedb9SMatthias Ringwald # 2904 791b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_FORMAT': 792b3fcedb9SMatthias Ringwald parseCharacteristicFormat(fout, parts) 793b3fcedb9SMatthias Ringwald continue 794b3fcedb9SMatthias Ringwald 795b3fcedb9SMatthias Ringwald # 2905 796b3fcedb9SMatthias Ringwald if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT': 797b3fcedb9SMatthias Ringwald parseCharacteristicAggregateFormat(fout, parts) 798b3fcedb9SMatthias Ringwald continue 799b3fcedb9SMatthias Ringwald 800b3fcedb9SMatthias Ringwald # 2906 801b3fcedb9SMatthias Ringwald if parts[0] == 'VALID_RANGE': 802b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 803b3fcedb9SMatthias Ringwald continue 804b3fcedb9SMatthias Ringwald 805b3fcedb9SMatthias Ringwald # 2907 806b3fcedb9SMatthias Ringwald if parts[0] == 'EXTERNAL_REPORT_REFERENCE': 807b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 808b3fcedb9SMatthias Ringwald continue 809b3fcedb9SMatthias Ringwald 810b3fcedb9SMatthias Ringwald # 2908 811b3fcedb9SMatthias Ringwald if parts[0] == 'REPORT_REFERENCE': 812b3fcedb9SMatthias Ringwald parseReportReference(fout, parts) 813b3fcedb9SMatthias Ringwald continue 814b3fcedb9SMatthias Ringwald 815b3fcedb9SMatthias Ringwald # 2909 816b3fcedb9SMatthias Ringwald if parts[0] == 'NUMBER_OF_DIGITALS': 817b3fcedb9SMatthias Ringwald parseNumberOfDigitals(fout, parts) 818b3fcedb9SMatthias Ringwald continue 819b3fcedb9SMatthias Ringwald 820b3fcedb9SMatthias Ringwald # 290A 821b3fcedb9SMatthias Ringwald if parts[0] == 'VALUE_TRIGGER_SETTING': 822b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 823b3fcedb9SMatthias Ringwald continue 824b3fcedb9SMatthias Ringwald 825b3fcedb9SMatthias Ringwald # 290B 826b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION': 827b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 828b3fcedb9SMatthias Ringwald continue 829b3fcedb9SMatthias Ringwald 830b3fcedb9SMatthias Ringwald # 290C 831b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT': 832b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 833b3fcedb9SMatthias Ringwald continue 834b3fcedb9SMatthias Ringwald 835b3fcedb9SMatthias Ringwald # 290D 836b3fcedb9SMatthias Ringwald if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING': 837b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 838b3fcedb9SMatthias Ringwald continue 839b3fcedb9SMatthias Ringwald 840b3fcedb9SMatthias Ringwald # 2906 841b3fcedb9SMatthias Ringwald if parts[0] == 'VALID_RANGE': 842b3fcedb9SMatthias Ringwald print("WARNING: %s not implemented yet\n" % (parts[0])) 843b3fcedb9SMatthias Ringwald continue 844b3fcedb9SMatthias Ringwald 845b3fcedb9SMatthias Ringwald print("WARNING: unknown token: %s\n" % (parts[0])) 846b3fcedb9SMatthias Ringwald 8477050bf34SMatthias Ringwalddef parse(fname_in, fin, fname_out, tool_path, fout): 84860b51a4cSMatthias Ringwald global handle 84960b51a4cSMatthias Ringwald global total_size 85060b51a4cSMatthias Ringwald 8517050bf34SMatthias Ringwald fout.write(header.format(fname_out, fname_in, tool_path)) 85260b51a4cSMatthias Ringwald fout.write('{\n') 853fd1be25dSMatthias Ringwald write_indent(fout) 854fd1be25dSMatthias Ringwald fout.write('// ATT DB Version\n') 855fd1be25dSMatthias Ringwald write_indent(fout) 856fd1be25dSMatthias Ringwald fout.write('1,\n') 857fd1be25dSMatthias Ringwald fout.write("\n") 85860b51a4cSMatthias Ringwald 85960b51a4cSMatthias Ringwald parseLines(fname_in, fin, fout) 86060b51a4cSMatthias Ringwald 861729074c4SMatthias Ringwald serviceDefinitionComplete(fout) 862b3fcedb9SMatthias Ringwald write_indent(fout) 863b3fcedb9SMatthias Ringwald fout.write("// END\n"); 864b3fcedb9SMatthias Ringwald write_indent(fout) 865b3fcedb9SMatthias Ringwald write_16(fout,0) 866b3fcedb9SMatthias Ringwald fout.write("\n") 867b3fcedb9SMatthias Ringwald total_size = total_size + 2 868b3fcedb9SMatthias Ringwald 869b3fcedb9SMatthias Ringwald fout.write("}; // total size %u bytes \n" % total_size); 870b3fcedb9SMatthias Ringwald 871b3fcedb9SMatthias Ringwalddef listHandles(fout): 872b3fcedb9SMatthias Ringwald fout.write('\n\n') 873b3fcedb9SMatthias Ringwald fout.write('//\n') 874729074c4SMatthias Ringwald fout.write('// list service handle ranges\n') 875729074c4SMatthias Ringwald fout.write('//\n') 876729074c4SMatthias Ringwald for define in defines_for_services: 877729074c4SMatthias Ringwald fout.write(define) 878729074c4SMatthias Ringwald fout.write('\n') 879729074c4SMatthias Ringwald fout.write('\n') 880729074c4SMatthias Ringwald fout.write('//\n') 881b3fcedb9SMatthias Ringwald fout.write('// list mapping between characteristics and handles\n') 882b3fcedb9SMatthias Ringwald fout.write('//\n') 883729074c4SMatthias Ringwald for define in defines_for_characteristics: 884b3fcedb9SMatthias Ringwald fout.write(define) 885b3fcedb9SMatthias Ringwald fout.write('\n') 886b3fcedb9SMatthias Ringwald 887dbb3997aSMilanka Ringwalddef getFile( fileName ): 88878b65b0aSMatthias Ringwald for d in include_paths: 88978b65b0aSMatthias Ringwald fullFile = os.path.normpath(d + os.sep + fileName) # because Windows exists 89078b65b0aSMatthias Ringwald # print("test %s" % fullFile) 891dbb3997aSMilanka Ringwald if os.path.isfile( fullFile ) == True: 892dbb3997aSMilanka Ringwald return fullFile 893dbb3997aSMilanka Ringwald print ("'{0}' not found".format( fileName )) 89478b65b0aSMatthias Ringwald print ("Include paths: %s" % ", ".join(include_paths)) 895dbb3997aSMilanka Ringwald exit(-1) 896dbb3997aSMilanka Ringwald 897dbb3997aSMilanka Ringwald 898dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') 89978b65b0aSMatthias Ringwalddefault_includes = [os.path.normpath(path) for path in [ btstack_root + '/src/', btstack_root + '/src/ble/gatt-service/']] 900dbb3997aSMilanka Ringwald 901dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack') 902dbb3997aSMilanka Ringwald 903dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes', 904dbb3997aSMilanka Ringwald help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes)) 905dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str, 906dbb3997aSMilanka Ringwald help='gatt file to be compiled') 907dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str, 908dbb3997aSMilanka Ringwald help='header file to be generated') 909dbb3997aSMilanka Ringwald 910dbb3997aSMilanka Ringwaldargs = parser.parse_args() 911dbb3997aSMilanka Ringwald 91278b65b0aSMatthias Ringwald# add include path arguments 91378b65b0aSMatthias Ringwaldif args.I != None: 91478b65b0aSMatthias Ringwald for d in args.I: 91578b65b0aSMatthias Ringwald include_paths.append(os.path.normpath(d[0])) 91678b65b0aSMatthias Ringwald 917dbb3997aSMilanka Ringwald# append default include paths 91878b65b0aSMatthias Ringwaldinclude_paths.extend(default_includes) 919dbb3997aSMilanka Ringwald 920b3fcedb9SMatthias Ringwaldtry: 921b165f97bSMatthias Ringwald # read defines from bluetooth_gatt.h 922dbb3997aSMilanka Ringwald gen_path = getFile( 'bluetooth_gatt.h' ) 923b165f97bSMatthias Ringwald bluetooth_gatt = read_defines(gen_path) 924b165f97bSMatthias Ringwald 925dbb3997aSMilanka Ringwald filename = args.hfile 926dbb3997aSMilanka Ringwald fin = codecs.open (args.gattfile, encoding='utf-8') 927*285653b2SMatthias Ringwald 928*285653b2SMatthias Ringwald # pass 1: create temp .h file 929*285653b2SMatthias Ringwald ftemp = tempfile.TemporaryFile() 930*285653b2SMatthias Ringwald parse(args.gattfile, fin, filename, sys.argv[0], ftemp) 931*285653b2SMatthias Ringwald listHandles(ftemp) 932*285653b2SMatthias Ringwald 933*285653b2SMatthias Ringwald # pass 2: insert GATT Database Hash 934b3fcedb9SMatthias Ringwald fout = open (filename, 'w') 935*285653b2SMatthias Ringwald ftemp.seek(0) 936*285653b2SMatthias Ringwald for line in ftemp: 937*285653b2SMatthias Ringwald fout.write(line) 938b3fcedb9SMatthias Ringwald fout.close() 939*285653b2SMatthias Ringwald ftemp.close() 940*285653b2SMatthias Ringwald 941b165f97bSMatthias Ringwald print('Created %s' % filename) 942b3fcedb9SMatthias Ringwald 943b3fcedb9SMatthias Ringwaldexcept IOError as e: 944e22a2612SMatthias Ringwald 945b3fcedb9SMatthias Ringwald print(usage) 946b3fcedb9SMatthias Ringwald sys.exit(1) 947b3fcedb9SMatthias Ringwald 948b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n') 949