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