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