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