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