xref: /btstack/tool/compile_gatt.py (revision dbb3997aeefadd2a87b3f2d45a5835e455e3b2ab)
1b3fcedb9SMatthias Ringwald#!/usr/bin/env python
2b3fcedb9SMatthias Ringwald#
3*dbb3997aSMilanka Ringwald# BLE GATT configuration generator for use with BTstack
4*dbb3997aSMilanka 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
17*dbb3997aSMilanka 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('''
32*dbb3997aSMilanka RingwaldBLE configuration generator for use with BTstack
33*dbb3997aSMilanka 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']
478e22a2612SMatthias Ringwald        flags |= property_flags['DYNAMIC']
479b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
480d7ec1d24SMatthias Ringwald
481b3fcedb9SMatthias Ringwald        write_indent(fout)
482b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle))
483d7ec1d24SMatthias Ringwald
484d7ec1d24SMatthias Ringwald        dump_flags(fout, flags)
485d7ec1d24SMatthias Ringwald
486b3fcedb9SMatthias Ringwald        write_indent(fout)
487b3fcedb9SMatthias Ringwald        write_16(fout, size)
488e22a2612SMatthias Ringwald        write_16(fout, flags)
489b3fcedb9SMatthias Ringwald        write_16(fout, handle)
490b3fcedb9SMatthias Ringwald        write_16(fout, 0x2902)
491b3fcedb9SMatthias Ringwald        write_16(fout, 0)
492b3fcedb9SMatthias Ringwald        fout.write("\n")
493729074c4SMatthias Ringwald        defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
494b3fcedb9SMatthias Ringwald        handle = handle + 1
495b3fcedb9SMatthias Ringwald
496b3fcedb9SMatthias Ringwald    if properties & property_flags['RELIABLE_WRITE']:
497b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
498b3fcedb9SMatthias Ringwald        write_indent(fout)
499b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle))
500b3fcedb9SMatthias Ringwald        write_indent(fout)
501b3fcedb9SMatthias Ringwald        write_16(fout, size)
502e22a2612SMatthias Ringwald        write_16(fout, read_only_anybody_flags)
503b3fcedb9SMatthias Ringwald        write_16(fout, handle)
504b3fcedb9SMatthias Ringwald        write_16(fout, 0x2900)
505b3fcedb9SMatthias Ringwald        write_16(fout, 1)   # Reliable Write
506b3fcedb9SMatthias Ringwald        fout.write("\n")
507b3fcedb9SMatthias Ringwald        handle = handle + 1
508b3fcedb9SMatthias Ringwald
509b3fcedb9SMatthias Ringwalddef parseCharacteristicUserDescription(fout, parts):
510b3fcedb9SMatthias Ringwald    global handle
511b3fcedb9SMatthias Ringwald    global total_size
512b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
513b3fcedb9SMatthias Ringwald
514b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
515b3fcedb9SMatthias Ringwald    value      = parts[2]
516b3fcedb9SMatthias Ringwald
517b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
518b3fcedb9SMatthias Ringwald    if is_string(value):
519b7647eb6SMatthias Ringwald        size = size + len(value)
520b3fcedb9SMatthias Ringwald    else:
521b3fcedb9SMatthias Ringwald        size = size + len(value.split())
522b3fcedb9SMatthias Ringwald
523e22a2612SMatthias Ringwald    # use write, write permissions and encryption key size from attribute value and set READ_ANYBODY
524d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
525e22a2612SMatthias Ringwald    flags |= properties & property_flags['WRITE']
526e22a2612SMatthias Ringwald    flags |= property_flags['READ']
527e22a2612SMatthias Ringwald
528b3fcedb9SMatthias Ringwald    write_indent(fout)
529b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:])))
530d7ec1d24SMatthias Ringwald
531d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
532d7ec1d24SMatthias Ringwald
533b3fcedb9SMatthias Ringwald    write_indent(fout)
534b3fcedb9SMatthias Ringwald    write_16(fout, size)
535e22a2612SMatthias Ringwald    write_16(fout, flags)
536b3fcedb9SMatthias Ringwald    write_16(fout, handle)
537b3fcedb9SMatthias Ringwald    write_16(fout, 0x2901)
538b3fcedb9SMatthias Ringwald    if is_string(value):
539b3fcedb9SMatthias Ringwald        write_string(fout, value)
540b3fcedb9SMatthias Ringwald    else:
541b3fcedb9SMatthias Ringwald        write_sequence(fout,value)
542b3fcedb9SMatthias Ringwald    fout.write("\n")
543729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
544b3fcedb9SMatthias Ringwald    handle = handle + 1
545b3fcedb9SMatthias Ringwald
546b3fcedb9SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts):
547b3fcedb9SMatthias Ringwald    global handle
548b3fcedb9SMatthias Ringwald    global total_size
549b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
550b3fcedb9SMatthias Ringwald
551b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
552b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
553b3fcedb9SMatthias Ringwald
554e22a2612SMatthias Ringwald    # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY
555d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
556e22a2612SMatthias Ringwald    flags |= property_flags['READ']
557e22a2612SMatthias Ringwald    flags |= property_flags['WRITE']
558e22a2612SMatthias Ringwald    flags |= property_flags['DYNAMIC']
559e22a2612SMatthias Ringwald
560b3fcedb9SMatthias Ringwald    write_indent(fout)
561b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:])))
562d7ec1d24SMatthias Ringwald
563d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
564d7ec1d24SMatthias Ringwald
565b3fcedb9SMatthias Ringwald    write_indent(fout)
566b3fcedb9SMatthias Ringwald    write_16(fout, size)
567e22a2612SMatthias Ringwald    write_16(fout, flags)
568b3fcedb9SMatthias Ringwald    write_16(fout, handle)
569b3fcedb9SMatthias Ringwald    write_16(fout, 0x2903)
570b3fcedb9SMatthias Ringwald    fout.write("\n")
571729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
572b3fcedb9SMatthias Ringwald    handle = handle + 1
573b3fcedb9SMatthias Ringwald
574b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts):
575b3fcedb9SMatthias Ringwald    global handle
576b3fcedb9SMatthias Ringwald    global total_size
577b3fcedb9SMatthias Ringwald
578e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
579b3fcedb9SMatthias Ringwald
580b3fcedb9SMatthias Ringwald    identifier = parts[1]
581b3fcedb9SMatthias Ringwald    presentation_formats[identifier] = handle
582b3fcedb9SMatthias Ringwald    # print("format '%s' with handle %d\n" % (identifier, handle))
583b3fcedb9SMatthias Ringwald
584b3fcedb9SMatthias Ringwald    format     = parts[2]
585b3fcedb9SMatthias Ringwald    exponent   = parts[3]
586b3fcedb9SMatthias Ringwald    unit       = parseUUID(parts[4])
587b3fcedb9SMatthias Ringwald    name_space = parts[5]
588b3fcedb9SMatthias Ringwald    description = parseUUID(parts[6])
589b3fcedb9SMatthias Ringwald
590b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 7
591b3fcedb9SMatthias Ringwald
592b3fcedb9SMatthias Ringwald    write_indent(fout)
593b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
594b3fcedb9SMatthias Ringwald    write_indent(fout)
595b3fcedb9SMatthias Ringwald    write_16(fout, size)
596e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
597b3fcedb9SMatthias Ringwald    write_16(fout, handle)
598b3fcedb9SMatthias Ringwald    write_16(fout, 0x2904)
599b3fcedb9SMatthias Ringwald    write_sequence(fout, format)
600b3fcedb9SMatthias Ringwald    write_sequence(fout, exponent)
601b3fcedb9SMatthias Ringwald    write_uuid(unit)
602b3fcedb9SMatthias Ringwald    write_sequence(fout, name_space)
603b3fcedb9SMatthias Ringwald    write_uuid(description)
604b3fcedb9SMatthias Ringwald    fout.write("\n")
605b3fcedb9SMatthias Ringwald    handle = handle + 1
606b3fcedb9SMatthias Ringwald
607b3fcedb9SMatthias Ringwald
608b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts):
609b3fcedb9SMatthias Ringwald    global handle
610b3fcedb9SMatthias Ringwald    global total_size
611b3fcedb9SMatthias Ringwald
612e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
613b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2
614b3fcedb9SMatthias Ringwald
615b3fcedb9SMatthias Ringwald    write_indent(fout)
616b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
617b3fcedb9SMatthias Ringwald    write_indent(fout)
618b3fcedb9SMatthias Ringwald    write_16(fout, size)
619e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
620b3fcedb9SMatthias Ringwald    write_16(fout, handle)
621b3fcedb9SMatthias Ringwald    write_16(fout, 0x2905)
622b3fcedb9SMatthias Ringwald    for identifier in parts[1:]:
623b3fcedb9SMatthias Ringwald        format_handle = presentation_formats[identifier]
624b3fcedb9SMatthias Ringwald        if format == 0:
625b3fcedb9SMatthias Ringwald            print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier)
626b3fcedb9SMatthias Ringwald            sys.exit(1)
627b3fcedb9SMatthias Ringwald        write_16(fout, format_handle)
628b3fcedb9SMatthias Ringwald    fout.write("\n")
629b3fcedb9SMatthias Ringwald    handle = handle + 1
630b3fcedb9SMatthias Ringwald
631b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts):
632b3fcedb9SMatthias Ringwald    global handle
633b3fcedb9SMatthias Ringwald    global total_size
634b3fcedb9SMatthias Ringwald
635e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
636b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1 + 1
637b3fcedb9SMatthias Ringwald
638231a3c5dSMatthias Ringwald    report_id = parts[2]
639231a3c5dSMatthias Ringwald    report_type = parts[3]
640b3fcedb9SMatthias Ringwald
641b3fcedb9SMatthias Ringwald    write_indent(fout)
642b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
643b3fcedb9SMatthias Ringwald    write_indent(fout)
644b3fcedb9SMatthias Ringwald    write_16(fout, size)
645e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
646b3fcedb9SMatthias Ringwald    write_16(fout, handle)
647b3fcedb9SMatthias Ringwald    write_16(fout, 0x2908)
648b3fcedb9SMatthias Ringwald    write_sequence(fout, report_id)
649b3fcedb9SMatthias Ringwald    write_sequence(fout, report_type)
650b3fcedb9SMatthias Ringwald    fout.write("\n")
651b3fcedb9SMatthias Ringwald    handle = handle + 1
652b3fcedb9SMatthias Ringwald
653b3fcedb9SMatthias Ringwald
654b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts):
655b3fcedb9SMatthias Ringwald    global handle
656b3fcedb9SMatthias Ringwald    global total_size
657b3fcedb9SMatthias Ringwald
658e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
659b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1
660b3fcedb9SMatthias Ringwald
661b3fcedb9SMatthias Ringwald    no_of_digitals = parts[1]
662b3fcedb9SMatthias Ringwald
663b3fcedb9SMatthias Ringwald    write_indent(fout)
664b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:])))
665b3fcedb9SMatthias Ringwald    write_indent(fout)
666b3fcedb9SMatthias Ringwald    write_16(fout, size)
667e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
668b3fcedb9SMatthias Ringwald    write_16(fout, handle)
669b3fcedb9SMatthias Ringwald    write_16(fout, 0x2909)
670b3fcedb9SMatthias Ringwald    write_sequence(fout, no_of_digitals)
671b3fcedb9SMatthias Ringwald    fout.write("\n")
672b3fcedb9SMatthias Ringwald    handle = handle + 1
673b3fcedb9SMatthias Ringwald
67460b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout):
675b3fcedb9SMatthias Ringwald    global handle
676b3fcedb9SMatthias Ringwald    global total_size
677b3fcedb9SMatthias Ringwald
678b165f97bSMatthias Ringwald    line_count = 0;
679b3fcedb9SMatthias Ringwald    for line in fin:
680b3fcedb9SMatthias Ringwald        line = line.strip("\n\r ")
681b165f97bSMatthias Ringwald        line_count += 1
682b3fcedb9SMatthias Ringwald
683b165f97bSMatthias Ringwald        if line.startswith("//"):
684b165f97bSMatthias Ringwald            fout.write("    //" + line.lstrip('/') + '\n')
685b165f97bSMatthias Ringwald            continue
686b165f97bSMatthias Ringwald
68760b51a4cSMatthias Ringwald        if line.startswith("#import"):
68860b51a4cSMatthias Ringwald            imported_file = ''
68960b51a4cSMatthias Ringwald            parts = re.match('#import\s+<(.*)>\w*',line)
69060b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
691*dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
69260b51a4cSMatthias Ringwald            parts = re.match('#import\s+"(.*)"\w*',line)
69360b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
694*dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
69560b51a4cSMatthias Ringwald            if len(imported_file) == 0:
69660b51a4cSMatthias Ringwald                print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count))
69760b51a4cSMatthias Ringwald                continue
69860b51a4cSMatthias Ringwald
699*dbb3997aSMilanka Ringwald            imported_file = getFile( imported_file )
70060b51a4cSMatthias Ringwald            print("Importing %s" % imported_file)
70160b51a4cSMatthias Ringwald            try:
70260b51a4cSMatthias Ringwald                imported_fin = codecs.open (imported_file, encoding='utf-8')
70360b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- BEGIN\n')
70460b51a4cSMatthias Ringwald                parseLines(imported_file, imported_fin, fout)
70560b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- END\n')
70660b51a4cSMatthias Ringwald            except IOError as e:
70760b51a4cSMatthias Ringwald                print('ERROR: Import failed. Please check path.')
70860b51a4cSMatthias Ringwald
70960b51a4cSMatthias Ringwald            continue
71060b51a4cSMatthias Ringwald
71160b51a4cSMatthias Ringwald        if line.startswith("#TODO"):
71260b51a4cSMatthias Ringwald            print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count))
713b165f97bSMatthias Ringwald            print ("'%s'" % line)
714b165f97bSMatthias Ringwald            fout.write("// " + line + '\n')
715b3fcedb9SMatthias Ringwald            continue
716b3fcedb9SMatthias Ringwald
717b3fcedb9SMatthias Ringwald        if len(line) == 0:
718b3fcedb9SMatthias Ringwald            continue
719b3fcedb9SMatthias Ringwald
720b3fcedb9SMatthias Ringwald        f = io.StringIO(line)
721b3fcedb9SMatthias Ringwald        parts_list = csv.reader(f, delimiter=',', quotechar='"')
722b3fcedb9SMatthias Ringwald
723b3fcedb9SMatthias Ringwald        for parts in parts_list:
724b3fcedb9SMatthias Ringwald            for index, object in enumerate(parts):
725b3fcedb9SMatthias Ringwald                parts[index] = object.strip().lstrip('"').rstrip('"')
726b3fcedb9SMatthias Ringwald
727b3fcedb9SMatthias Ringwald            if parts[0] == 'PRIMARY_SERVICE':
728b3fcedb9SMatthias Ringwald                parsePrimaryService(fout, parts)
729b3fcedb9SMatthias Ringwald                continue
730b3fcedb9SMatthias Ringwald
731b3fcedb9SMatthias Ringwald            if parts[0] == 'SECONDARY_SERVICE':
732b3fcedb9SMatthias Ringwald                parseSecondaryService(fout, parts)
733b3fcedb9SMatthias Ringwald                continue
734b3fcedb9SMatthias Ringwald
735b3fcedb9SMatthias Ringwald            if parts[0] == 'INCLUDE_SERVICE':
736b3fcedb9SMatthias Ringwald                parseIncludeService(fout, parts)
737b3fcedb9SMatthias Ringwald                continue
738b3fcedb9SMatthias Ringwald
739b3fcedb9SMatthias Ringwald            # 2803
740b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC':
741b3fcedb9SMatthias Ringwald                parseCharacteristic(fout, parts)
742b3fcedb9SMatthias Ringwald                continue
743b3fcedb9SMatthias Ringwald
744b3fcedb9SMatthias Ringwald            # 2900 Characteristic Extended Properties
745b3fcedb9SMatthias Ringwald
746b3fcedb9SMatthias Ringwald            # 2901
747b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
748b3fcedb9SMatthias Ringwald                parseCharacteristicUserDescription(fout, parts)
749b3fcedb9SMatthias Ringwald                continue
750b3fcedb9SMatthias Ringwald
751b165f97bSMatthias Ringwald
752b165f97bSMatthias Ringwald            # 2902 Client Characteristic Configuration - automatically included in Characteristic if
753b3fcedb9SMatthias Ringwald            # notification / indication is supported
754231a3c5dSMatthias Ringwald            if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION':
755b165f97bSMatthias Ringwald                continue
756b3fcedb9SMatthias Ringwald
757b3fcedb9SMatthias Ringwald            # 2903
758b3fcedb9SMatthias Ringwald            if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
759b3fcedb9SMatthias Ringwald                parseServerCharacteristicConfiguration(fout, parts)
760b3fcedb9SMatthias Ringwald                continue
761b3fcedb9SMatthias Ringwald
762b3fcedb9SMatthias Ringwald            # 2904
763b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_FORMAT':
764b3fcedb9SMatthias Ringwald                parseCharacteristicFormat(fout, parts)
765b3fcedb9SMatthias Ringwald                continue
766b3fcedb9SMatthias Ringwald
767b3fcedb9SMatthias Ringwald            # 2905
768b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
769b3fcedb9SMatthias Ringwald                parseCharacteristicAggregateFormat(fout, parts)
770b3fcedb9SMatthias Ringwald                continue
771b3fcedb9SMatthias Ringwald
772b3fcedb9SMatthias Ringwald            # 2906
773b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
774b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
775b3fcedb9SMatthias Ringwald                continue
776b3fcedb9SMatthias Ringwald
777b3fcedb9SMatthias Ringwald            # 2907
778b3fcedb9SMatthias Ringwald            if parts[0] == 'EXTERNAL_REPORT_REFERENCE':
779b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
780b3fcedb9SMatthias Ringwald                continue
781b3fcedb9SMatthias Ringwald
782b3fcedb9SMatthias Ringwald            # 2908
783b3fcedb9SMatthias Ringwald            if parts[0] == 'REPORT_REFERENCE':
784b3fcedb9SMatthias Ringwald                parseReportReference(fout, parts)
785b3fcedb9SMatthias Ringwald                continue
786b3fcedb9SMatthias Ringwald
787b3fcedb9SMatthias Ringwald            # 2909
788b3fcedb9SMatthias Ringwald            if parts[0] == 'NUMBER_OF_DIGITALS':
789b3fcedb9SMatthias Ringwald                parseNumberOfDigitals(fout, parts)
790b3fcedb9SMatthias Ringwald                continue
791b3fcedb9SMatthias Ringwald
792b3fcedb9SMatthias Ringwald            # 290A
793b3fcedb9SMatthias Ringwald            if parts[0] == 'VALUE_TRIGGER_SETTING':
794b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
795b3fcedb9SMatthias Ringwald                continue
796b3fcedb9SMatthias Ringwald
797b3fcedb9SMatthias Ringwald            # 290B
798b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION':
799b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
800b3fcedb9SMatthias Ringwald                continue
801b3fcedb9SMatthias Ringwald
802b3fcedb9SMatthias Ringwald            # 290C
803b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT':
804b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
805b3fcedb9SMatthias Ringwald                continue
806b3fcedb9SMatthias Ringwald
807b3fcedb9SMatthias Ringwald            # 290D
808b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING':
809b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
810b3fcedb9SMatthias Ringwald                continue
811b3fcedb9SMatthias Ringwald
812b3fcedb9SMatthias Ringwald            # 2906
813b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
814b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
815b3fcedb9SMatthias Ringwald                continue
816b3fcedb9SMatthias Ringwald
817b3fcedb9SMatthias Ringwald            print("WARNING: unknown token: %s\n" % (parts[0]))
818b3fcedb9SMatthias Ringwald
81960b51a4cSMatthias Ringwalddef parse(fname_in, fin, fname_out, fout):
82060b51a4cSMatthias Ringwald    global handle
82160b51a4cSMatthias Ringwald    global total_size
82260b51a4cSMatthias Ringwald
82360b51a4cSMatthias Ringwald    fout.write(header.format(fname_out, fname_in))
82460b51a4cSMatthias Ringwald    fout.write('{\n')
825fd1be25dSMatthias Ringwald    write_indent(fout)
826fd1be25dSMatthias Ringwald    fout.write('// ATT DB Version\n')
827fd1be25dSMatthias Ringwald    write_indent(fout)
828fd1be25dSMatthias Ringwald    fout.write('1,\n')
829fd1be25dSMatthias Ringwald    fout.write("\n")
83060b51a4cSMatthias Ringwald
83160b51a4cSMatthias Ringwald    parseLines(fname_in, fin, fout)
83260b51a4cSMatthias Ringwald
833729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
834b3fcedb9SMatthias Ringwald    write_indent(fout)
835b3fcedb9SMatthias Ringwald    fout.write("// END\n");
836b3fcedb9SMatthias Ringwald    write_indent(fout)
837b3fcedb9SMatthias Ringwald    write_16(fout,0)
838b3fcedb9SMatthias Ringwald    fout.write("\n")
839b3fcedb9SMatthias Ringwald    total_size = total_size + 2
840b3fcedb9SMatthias Ringwald
841b3fcedb9SMatthias Ringwald    fout.write("}; // total size %u bytes \n" % total_size);
842b3fcedb9SMatthias Ringwald
843b3fcedb9SMatthias Ringwalddef listHandles(fout):
844b3fcedb9SMatthias Ringwald    fout.write('\n\n')
845b3fcedb9SMatthias Ringwald    fout.write('//\n')
846729074c4SMatthias Ringwald    fout.write('// list service handle ranges\n')
847729074c4SMatthias Ringwald    fout.write('//\n')
848729074c4SMatthias Ringwald    for define in defines_for_services:
849729074c4SMatthias Ringwald        fout.write(define)
850729074c4SMatthias Ringwald        fout.write('\n')
851729074c4SMatthias Ringwald    fout.write('\n')
852729074c4SMatthias Ringwald    fout.write('//\n')
853b3fcedb9SMatthias Ringwald    fout.write('// list mapping between characteristics and handles\n')
854b3fcedb9SMatthias Ringwald    fout.write('//\n')
855729074c4SMatthias Ringwald    for define in defines_for_characteristics:
856b3fcedb9SMatthias Ringwald        fout.write(define)
857b3fcedb9SMatthias Ringwald        fout.write('\n')
858b3fcedb9SMatthias Ringwald
859*dbb3997aSMilanka Ringwalddef getFile( fileName ):
860*dbb3997aSMilanka Ringwald    inc = args.I
861*dbb3997aSMilanka Ringwald    for d in inc:
862*dbb3997aSMilanka Ringwald        fullFile = d[0] + fileName
863*dbb3997aSMilanka Ringwald        print("test %s" % fullFile)
864*dbb3997aSMilanka Ringwald        if os.path.isfile( fullFile ) == True:
865*dbb3997aSMilanka Ringwald            return fullFile
866*dbb3997aSMilanka Ringwald    print ("'{0}' not found".format( fileName ))
867*dbb3997aSMilanka Ringwald    print ("Include paths: %s" % ", ".join(inc))
868*dbb3997aSMilanka Ringwald    exit(-1)
869*dbb3997aSMilanka Ringwald
870*dbb3997aSMilanka Ringwald
871*dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
872*dbb3997aSMilanka Ringwalddefault_includes = [ btstack_root + '/src/', btstack_root + '/src/ble/gatt-service/']
873*dbb3997aSMilanka Ringwald
874*dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack')
875*dbb3997aSMilanka Ringwald
876*dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes',
877*dbb3997aSMilanka Ringwald        help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes))
878*dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str,
879*dbb3997aSMilanka Ringwald        help='gatt file to be compiled')
880*dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str,
881*dbb3997aSMilanka Ringwald        help='header file to be generated')
882*dbb3997aSMilanka Ringwald
883*dbb3997aSMilanka Ringwaldargs = parser.parse_args()
884*dbb3997aSMilanka Ringwald
885*dbb3997aSMilanka Ringwald# append default include paths
886*dbb3997aSMilanka Ringwaldif args.I == None:
887*dbb3997aSMilanka Ringwald    args.I = []
888*dbb3997aSMilanka Ringwaldfor d in default_includes:
889*dbb3997aSMilanka Ringwald    args.I.append([d])
890*dbb3997aSMilanka Ringwald
891b3fcedb9SMatthias Ringwaldtry:
892b165f97bSMatthias Ringwald    # read defines from bluetooth_gatt.h
893*dbb3997aSMilanka Ringwald    gen_path = getFile( 'bluetooth_gatt.h' )
894b165f97bSMatthias Ringwald    bluetooth_gatt = read_defines(gen_path)
895b165f97bSMatthias Ringwald
896*dbb3997aSMilanka Ringwald    filename = args.hfile
897*dbb3997aSMilanka Ringwald    fin  = codecs.open (args.gattfile, encoding='utf-8')
898b3fcedb9SMatthias Ringwald    fout = open (filename, 'w')
899*dbb3997aSMilanka Ringwald    parse(args.gattfile, fin, filename, fout)
900b3fcedb9SMatthias Ringwald    listHandles(fout)
901b3fcedb9SMatthias Ringwald    fout.close()
902b165f97bSMatthias Ringwald    print('Created %s' % filename)
903b3fcedb9SMatthias Ringwald
904b3fcedb9SMatthias Ringwaldexcept IOError as e:
905e22a2612SMatthias Ringwald
906b3fcedb9SMatthias Ringwald    print(usage)
907b3fcedb9SMatthias Ringwald    sys.exit(1)
908b3fcedb9SMatthias Ringwald
909b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n')
910