xref: /btstack/tool/compile_gatt.py (revision 285653b26b50c8effcfc5ebe44d84015baed9bdd)
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
18*285653b2SMatthias Ringwaldimport tempfile
19b3fcedb9SMatthias Ringwald
20b3fcedb9SMatthias Ringwaldheader = '''
21b3fcedb9SMatthias Ringwald// {0} generated from {1} for BTstack
227050bf34SMatthias Ringwald// it needs to be regenerated when the .gatt file is updated.
237050bf34SMatthias Ringwald
247050bf34SMatthias Ringwald// To generate {0}:
257050bf34SMatthias Ringwald// {2} {1} {0}
267050bf34SMatthias Ringwald
27fd1be25dSMatthias Ringwald// att db format version 1
28b3fcedb9SMatthias Ringwald
29fd1be25dSMatthias Ringwald// binary attribute representation:
30fd1be25dSMatthias Ringwald// - size in bytes (16), flags(16), handle (16), uuid (16/128), value(...)
31b3fcedb9SMatthias Ringwald
32b3fcedb9SMatthias Ringwald#include <stdint.h>
33b3fcedb9SMatthias Ringwald
34b3fcedb9SMatthias Ringwaldconst uint8_t profile_data[] =
35b3fcedb9SMatthias Ringwald'''
36b3fcedb9SMatthias Ringwald
37b3fcedb9SMatthias Ringwaldprint('''
38dbb3997aSMilanka RingwaldBLE configuration generator for use with BTstack
39dbb3997aSMilanka RingwaldCopyright 2018 BlueKitchen GmbH
40b3fcedb9SMatthias Ringwald''')
41b3fcedb9SMatthias Ringwald
42b3fcedb9SMatthias Ringwaldassigned_uuids = {
43b3fcedb9SMatthias Ringwald    'GAP_SERVICE'          : 0x1800,
44b3fcedb9SMatthias Ringwald    'GATT_SERVICE'         : 0x1801,
45b3fcedb9SMatthias Ringwald    'GAP_DEVICE_NAME'      : 0x2a00,
46b3fcedb9SMatthias Ringwald    'GAP_APPEARANCE'       : 0x2a01,
47b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02,
48b3fcedb9SMatthias Ringwald    'GAP_RECONNECTION_ADDRESS'    : 0x2A03,
49b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04,
50b3fcedb9SMatthias Ringwald    'GATT_SERVICE_CHANGED' : 0x2a05,
51b3fcedb9SMatthias Ringwald}
52b3fcedb9SMatthias Ringwald
53e72176f8SMatthias Ringwaldsecurity_permsission = ['ANYBODY','ENCRYPTED', 'AUTHENTICATED', 'AUTHORIZED', 'AUTHENTICATED_SC']
54d7ec1d24SMatthias Ringwald
55b3fcedb9SMatthias Ringwaldproperty_flags = {
56eb6072adSMatthias Ringwald    # GATT Characteristic Properties
57b3fcedb9SMatthias Ringwald    'BROADCAST' :                   0x01,
58b3fcedb9SMatthias Ringwald    'READ' :                        0x02,
59b3fcedb9SMatthias Ringwald    'WRITE_WITHOUT_RESPONSE' :      0x04,
60b3fcedb9SMatthias Ringwald    'WRITE' :                       0x08,
61b3fcedb9SMatthias Ringwald    'NOTIFY':                       0x10,
62b3fcedb9SMatthias Ringwald    'INDICATE' :                    0x20,
63b3fcedb9SMatthias Ringwald    'AUTHENTICATED_SIGNED_WRITE' :  0x40,
64b3fcedb9SMatthias Ringwald    'EXTENDED_PROPERTIES' :         0x80,
65b3fcedb9SMatthias Ringwald    # custom BTstack extension
66b3fcedb9SMatthias Ringwald    'DYNAMIC':                      0x100,
67b3fcedb9SMatthias Ringwald    'LONG_UUID':                    0x200,
68e22a2612SMatthias Ringwald
69e22a2612SMatthias Ringwald    # read permissions
70e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_0':        0x400,
71e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_1':        0x800,
72e22a2612SMatthias Ringwald
73e22a2612SMatthias Ringwald    #
74b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_7':       0x6000,
75b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_8':       0x7000,
76b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_9':       0x8000,
77b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_10':      0x9000,
78b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_11':      0xa000,
79b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_12':      0xb000,
80b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_13':      0xc000,
81b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_14':      0xd000,
82b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_15':      0xe000,
83b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_16':      0xf000,
84e22a2612SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_MASK':    0xf000,
85eb6072adSMatthias Ringwald
86b3fcedb9SMatthias Ringwald    # only used by gatt compiler >= 0xffff
87b3fcedb9SMatthias Ringwald    # Extended Properties
88e72176f8SMatthias Ringwald    'RELIABLE_WRITE':              0x00010000,
89e72176f8SMatthias Ringwald    'AUTHENTICATION_REQUIRED':     0x00020000,
90e72176f8SMatthias Ringwald    'AUTHORIZATION_REQUIRED':      0x00040000,
91e72176f8SMatthias Ringwald    'READ_ANYBODY':                0x00080000,
92e72176f8SMatthias Ringwald    'READ_ENCRYPTED':              0x00100000,
93e72176f8SMatthias Ringwald    'READ_AUTHENTICATED':          0x00200000,
94e72176f8SMatthias Ringwald    'READ_AUTHENTICATED_SC':       0x00400000,
95e72176f8SMatthias Ringwald    'READ_AUTHORIZED':             0x00800000,
96e72176f8SMatthias Ringwald    'WRITE_ANYBODY':               0x01000000,
97e72176f8SMatthias Ringwald    'WRITE_ENCRYPTED':             0x02000000,
98e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED':         0x04000000,
99e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED_SC':      0x08000000,
100e72176f8SMatthias Ringwald    'WRITE_AUTHORIZED':            0x10000000,
101eb6072adSMatthias Ringwald
102eb6072adSMatthias Ringwald    # Broadcast, Notify, Indicate, Extended Properties are only used to describe a GATT Characteristic, but are free to use with att_db
103e72176f8SMatthias Ringwald    # - write permissions
104e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_0':      0x01,
105e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_1':      0x10,
106e72176f8SMatthias Ringwald    # - SC required
107e72176f8SMatthias Ringwald    'READ_PERMISSION_SC':          0x20,
108e72176f8SMatthias Ringwald    'WRITE_PERMISSION_SC':         0x80,
109b3fcedb9SMatthias Ringwald}
110b3fcedb9SMatthias Ringwald
111b3fcedb9SMatthias Ringwaldservices = dict()
112b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict()
113b3fcedb9SMatthias Ringwaldpresentation_formats = dict()
114b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = ""
115b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0
116b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = ""
117729074c4SMatthias Ringwalddefines_for_characteristics = []
118729074c4SMatthias Ringwalddefines_for_services = []
11978b65b0aSMatthias Ringwaldinclude_paths = []
120b3fcedb9SMatthias Ringwald
121b3fcedb9SMatthias Ringwaldhandle = 1
122b3fcedb9SMatthias Ringwaldtotal_size = 0
123b3fcedb9SMatthias Ringwald
124b165f97bSMatthias Ringwalddef read_defines(infile):
125b165f97bSMatthias Ringwald    defines = dict()
126b165f97bSMatthias Ringwald    with open (infile, 'rt') as fin:
127b165f97bSMatthias Ringwald        for line in fin:
128b165f97bSMatthias Ringwald            parts = re.match('#define\s+(\w+)\s+(\w+)',line)
129b165f97bSMatthias Ringwald            if parts and len(parts.groups()) == 2:
130b165f97bSMatthias Ringwald                (key, value) = parts.groups()
131b165f97bSMatthias Ringwald                defines[key] = int(value, 16)
132b165f97bSMatthias Ringwald    return defines
133b165f97bSMatthias Ringwald
134b3fcedb9SMatthias Ringwalddef keyForUUID(uuid):
135b3fcedb9SMatthias Ringwald    keyUUID = ""
136b3fcedb9SMatthias Ringwald    for i in uuid:
137b3fcedb9SMatthias Ringwald        keyUUID += "%02x" % i
138b3fcedb9SMatthias Ringwald    return keyUUID
139b3fcedb9SMatthias Ringwald
140b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid):
141b3fcedb9SMatthias Ringwald    return uuid.replace('-', '_')
142b3fcedb9SMatthias Ringwald
143b3fcedb9SMatthias Ringwalddef twoByteLEFor(value):
144b3fcedb9SMatthias Ringwald    return [ (value & 0xff), (value >> 8)]
145b3fcedb9SMatthias Ringwald
146b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text):
147b3fcedb9SMatthias 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):
148b3fcedb9SMatthias Ringwald        return True
149b3fcedb9SMatthias Ringwald    return False
150b3fcedb9SMatthias Ringwald
151b3fcedb9SMatthias Ringwalddef parseUUID128(uuid):
152b3fcedb9SMatthias 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)
153b3fcedb9SMatthias Ringwald    uuid_bytes = []
154b3fcedb9SMatthias Ringwald    for i in range(8, 0, -1):
155b3fcedb9SMatthias Ringwald        uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16))
156b3fcedb9SMatthias Ringwald    return uuid_bytes
157b3fcedb9SMatthias Ringwald
158b3fcedb9SMatthias Ringwalddef parseUUID(uuid):
159b3fcedb9SMatthias Ringwald    if uuid in assigned_uuids:
160b3fcedb9SMatthias Ringwald        return twoByteLEFor(assigned_uuids[uuid])
161b165f97bSMatthias Ringwald    uuid_upper = uuid.upper().replace('.','_')
162b165f97bSMatthias Ringwald    if uuid_upper in bluetooth_gatt:
163b165f97bSMatthias Ringwald        return twoByteLEFor(bluetooth_gatt[uuid_upper])
164b3fcedb9SMatthias Ringwald    if is_128bit_uuid(uuid):
165b3fcedb9SMatthias Ringwald        return parseUUID128(uuid)
166b3fcedb9SMatthias Ringwald    uuidInt = int(uuid, 16)
167b3fcedb9SMatthias Ringwald    return twoByteLEFor(uuidInt)
168b3fcedb9SMatthias Ringwald
169b3fcedb9SMatthias Ringwalddef parseProperties(properties):
170b3fcedb9SMatthias Ringwald    value = 0
171b3fcedb9SMatthias Ringwald    parts = properties.split("|")
172b3fcedb9SMatthias Ringwald    for property in parts:
173b3fcedb9SMatthias Ringwald        property = property.strip()
174b3fcedb9SMatthias Ringwald        if property in property_flags:
175b3fcedb9SMatthias Ringwald            value |= property_flags[property]
176b3fcedb9SMatthias Ringwald        else:
177b3fcedb9SMatthias Ringwald            print("WARNING: property %s undefined" % (property))
178e22a2612SMatthias Ringwald
179e22a2612SMatthias Ringwald    return value;
180e22a2612SMatthias Ringwald
181e22a2612SMatthias Ringwalddef gatt_characteristic_properties(properties):
182e22a2612SMatthias Ringwald    return properties & 0xff
183e22a2612SMatthias Ringwald
184e22a2612SMatthias Ringwalddef att_flags(properties):
185e72176f8SMatthias Ringwald    # drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Properties (0x80) - not used for flags
186e72176f8SMatthias Ringwald    properties &= 0xffffff4e
187e22a2612SMatthias Ringwald
188e22a2612SMatthias Ringwald    # rw permissions distinct
189e22a2612SMatthias Ringwald    distinct_permissions_used = properties & (
190e22a2612SMatthias Ringwald        property_flags['READ_AUTHORIZED'] |
191e72176f8SMatthias Ringwald        property_flags['READ_AUTHENTICATED_SC'] |
192e22a2612SMatthias Ringwald        property_flags['READ_AUTHENTICATED'] |
193e22a2612SMatthias Ringwald        property_flags['READ_ENCRYPTED'] |
194e22a2612SMatthias Ringwald        property_flags['READ_ANYBODY'] |
195e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHORIZED'] |
196e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED'] |
197e72176f8SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED_SC'] |
198e22a2612SMatthias Ringwald        property_flags['WRITE_ENCRYPTED'] |
199e22a2612SMatthias Ringwald        property_flags['WRITE_ANYBODY']
200e22a2612SMatthias Ringwald    ) != 0
201e22a2612SMatthias Ringwald
202e22a2612SMatthias Ringwald    # post process properties
203e22a2612SMatthias Ringwald    encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0
204e22a2612SMatthias Ringwald
205d7ec1d24SMatthias Ringwald    # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted
206e22a2612SMatthias Ringwald    if encryption_key_size_specified and not distinct_permissions_used:
207e22a2612SMatthias Ringwald        properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED']
208e22a2612SMatthias Ringwald
209d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated
210d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used:
211d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED']
212d7ec1d24SMatthias Ringwald
213d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized
214d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used:
215d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED']
216d7ec1d24SMatthias Ringwald
217d7ec1d24SMatthias Ringwald    # determine read/write security requirements
218d7ec1d24SMatthias Ringwald    read_security_level  = 0
219d7ec1d24SMatthias Ringwald    write_security_level = 0
220e72176f8SMatthias Ringwald    read_requires_sc     = False
221e72176f8SMatthias Ringwald    write_requires_sc    = False
222e22a2612SMatthias Ringwald    if properties & property_flags['READ_AUTHORIZED']:
223d7ec1d24SMatthias Ringwald        read_security_level = 3
224e22a2612SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED']:
225d7ec1d24SMatthias Ringwald        read_security_level = 2
226e72176f8SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED_SC']:
227e72176f8SMatthias Ringwald        read_security_level = 2
228e72176f8SMatthias Ringwald        read_requires_sc = True
229e22a2612SMatthias Ringwald    elif properties & property_flags['READ_ENCRYPTED']:
230d7ec1d24SMatthias Ringwald        read_security_level = 1
231e22a2612SMatthias Ringwald    if properties & property_flags['WRITE_AUTHORIZED']:
232d7ec1d24SMatthias Ringwald        write_security_level = 3
233e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED']:
234d7ec1d24SMatthias Ringwald        write_security_level = 2
235e72176f8SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED_SC']:
236e72176f8SMatthias Ringwald        write_security_level = 2
237e72176f8SMatthias Ringwald        write_requires_sc = True
238e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_ENCRYPTED']:
239d7ec1d24SMatthias Ringwald        write_security_level = 1
240d7ec1d24SMatthias Ringwald
241d7ec1d24SMatthias Ringwald    # map security requirements to flags
242d7ec1d24SMatthias Ringwald    if read_security_level & 2:
243d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_1']
244d7ec1d24SMatthias Ringwald    if read_security_level & 1:
245d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_0']
246e72176f8SMatthias Ringwald    if read_requires_sc:
247e72176f8SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_SC']
248d7ec1d24SMatthias Ringwald    if write_security_level & 2:
249d7ec1d24SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_1']
250d7ec1d24SMatthias Ringwald    if write_security_level & 1:
251e22a2612SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_0']
252e72176f8SMatthias Ringwald    if write_requires_sc:
253e72176f8SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_SC']
254e22a2612SMatthias Ringwald
255e22a2612SMatthias Ringwald    return properties
256e22a2612SMatthias Ringwald
257d7ec1d24SMatthias Ringwalddef write_permissions_and_key_size_flags_from_properties(properties):
258e22a2612SMatthias Ringwald    return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1'])
259b3fcedb9SMatthias Ringwald
260b3fcedb9SMatthias Ringwalddef write_8(fout, value):
261b3fcedb9SMatthias Ringwald    fout.write( "0x%02x, " % (value & 0xff))
262b3fcedb9SMatthias Ringwald
263b3fcedb9SMatthias Ringwalddef write_16(fout, value):
264b3fcedb9SMatthias Ringwald    fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff))
265b3fcedb9SMatthias Ringwald
266*285653b2SMatthias Ringwalddef write_uuid(fout, uuid):
267b3fcedb9SMatthias Ringwald    for byte in uuid:
268b3fcedb9SMatthias Ringwald        fout.write( "0x%02x, " % byte)
269b3fcedb9SMatthias Ringwald
270b3fcedb9SMatthias Ringwalddef write_string(fout, text):
271b3fcedb9SMatthias Ringwald    for l in text.lstrip('"').rstrip('"'):
272b3fcedb9SMatthias Ringwald        write_8(fout, ord(l))
273b3fcedb9SMatthias Ringwald
274b3fcedb9SMatthias Ringwalddef write_sequence(fout, text):
275b3fcedb9SMatthias Ringwald    parts = text.split()
276b3fcedb9SMatthias Ringwald    for part in parts:
277b3fcedb9SMatthias Ringwald        fout.write("0x%s, " % (part.strip()))
278b3fcedb9SMatthias Ringwald
279b3fcedb9SMatthias Ringwalddef write_indent(fout):
280b3fcedb9SMatthias Ringwald    fout.write("    ")
281b3fcedb9SMatthias Ringwald
282d7ec1d24SMatthias Ringwalddef read_permissions_from_flags(flags):
283d7ec1d24SMatthias Ringwald    permissions = 0
284d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_0']:
285d7ec1d24SMatthias Ringwald        permissions |= 1
286d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_1']:
287d7ec1d24SMatthias Ringwald        permissions |= 2
288e72176f8SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_SC'] and permissions == 2:
289e72176f8SMatthias Ringwald        permissions = 4
290d7ec1d24SMatthias Ringwald    return permissions
291d7ec1d24SMatthias Ringwald
292d7ec1d24SMatthias Ringwalddef write_permissions_from_flags(flags):
293d7ec1d24SMatthias Ringwald    permissions = 0
294d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_0']:
295d7ec1d24SMatthias Ringwald        permissions |= 1
296d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_1']:
297d7ec1d24SMatthias Ringwald        permissions |= 2
298e72176f8SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_SC'] and permissions == 2:
299e72176f8SMatthias Ringwald        permissions = 4
300d7ec1d24SMatthias Ringwald    return permissions
301d7ec1d24SMatthias Ringwald
302d7ec1d24SMatthias Ringwalddef encryption_key_size_from_flags(flags):
303d7ec1d24SMatthias Ringwald    encryption_key_size = (flags & 0xf000) >> 12
304d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
305d7ec1d24SMatthias Ringwald        encryption_key_size += 1
306d7ec1d24SMatthias Ringwald    return encryption_key_size
307d7ec1d24SMatthias Ringwald
308b3fcedb9SMatthias Ringwalddef is_string(text):
309b3fcedb9SMatthias Ringwald    for item in text.split(" "):
310b3fcedb9SMatthias Ringwald        if not all(c in string.hexdigits for c in item):
311b3fcedb9SMatthias Ringwald            return True
312b3fcedb9SMatthias Ringwald    return False
313b3fcedb9SMatthias Ringwald
314b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties):
315b3fcedb9SMatthias Ringwald    return properties & (property_flags['NOTIFY'] | property_flags['INDICATE'])
316b3fcedb9SMatthias Ringwald
317729074c4SMatthias Ringwalddef serviceDefinitionComplete(fout):
318729074c4SMatthias Ringwald    global services
319729074c4SMatthias Ringwald    if current_service_uuid_string:
320729074c4SMatthias Ringwald        fout.write("\n")
321729074c4SMatthias Ringwald        # print("append service %s = [%d, %d]" % (current_characteristic_uuid_string, current_service_start_handle, handle-1))
322729074c4SMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle))
323729074c4SMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1))
324729074c4SMatthias Ringwald        services[current_service_uuid_string] = [current_service_start_handle, handle-1]
325729074c4SMatthias Ringwald
326d7ec1d24SMatthias Ringwalddef dump_flags(fout, flags):
327d7ec1d24SMatthias Ringwald    global security_permsission
328d7ec1d24SMatthias Ringwald    encryption_key_size = encryption_key_size_from_flags(flags)
329d7ec1d24SMatthias Ringwald    read_permissions    = security_permsission[read_permissions_from_flags(flags)]
330d7ec1d24SMatthias Ringwald    write_permissions   = security_permsission[write_permissions_from_flags(flags)]
331d7ec1d24SMatthias Ringwald    write_indent(fout)
332d7ec1d24SMatthias Ringwald    fout.write('// ')
333d7ec1d24SMatthias Ringwald    first = 1
334d7ec1d24SMatthias Ringwald    if flags & property_flags['READ']:
335d7ec1d24SMatthias Ringwald        fout.write('READ_%s' % read_permissions)
336d7ec1d24SMatthias Ringwald        first = 0
337d7ec1d24SMatthias Ringwald    if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']):
338d7ec1d24SMatthias Ringwald        if not first:
339d7ec1d24SMatthias Ringwald            fout.write(', ')
340d7ec1d24SMatthias Ringwald        first = 0
341d7ec1d24SMatthias Ringwald        fout.write('WRITE_%s' % write_permissions)
342d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
343d7ec1d24SMatthias Ringwald        if not first:
344d7ec1d24SMatthias Ringwald            fout.write(', ')
345d7ec1d24SMatthias Ringwald        first = 0
346d7ec1d24SMatthias Ringwald        fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size)
347d7ec1d24SMatthias Ringwald    fout.write('\n')
348d7ec1d24SMatthias Ringwald
349b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type):
350b3fcedb9SMatthias Ringwald    global handle
351b3fcedb9SMatthias Ringwald    global total_size
352b3fcedb9SMatthias Ringwald    global current_service_uuid_string
353b3fcedb9SMatthias Ringwald    global current_service_start_handle
354b3fcedb9SMatthias Ringwald
355729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
356b3fcedb9SMatthias Ringwald
357d7ec1d24SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
358b3fcedb9SMatthias Ringwald
359b3fcedb9SMatthias Ringwald    write_indent(fout)
360b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
361b3fcedb9SMatthias Ringwald
362b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
363b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
364b3fcedb9SMatthias Ringwald
365b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size + 2
366b3fcedb9SMatthias Ringwald
367b3fcedb9SMatthias Ringwald    if service_type == 0x2802:
368b3fcedb9SMatthias Ringwald        size += 4
369b3fcedb9SMatthias Ringwald
370b3fcedb9SMatthias Ringwald    write_indent(fout)
371b3fcedb9SMatthias Ringwald    write_16(fout, size)
372d7ec1d24SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
373b3fcedb9SMatthias Ringwald    write_16(fout, handle)
374b3fcedb9SMatthias Ringwald    write_16(fout, service_type)
375*285653b2SMatthias Ringwald    write_uuid(fout, uuid)
376b3fcedb9SMatthias Ringwald    fout.write("\n")
377b3fcedb9SMatthias Ringwald
378729074c4SMatthias Ringwald    current_service_uuid_string = c_string_for_uuid(parts[1])
379b3fcedb9SMatthias Ringwald    current_service_start_handle = handle
380b3fcedb9SMatthias Ringwald    handle = handle + 1
381b3fcedb9SMatthias Ringwald    total_size = total_size + size
382b3fcedb9SMatthias Ringwald
383b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts):
384b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2800)
385b3fcedb9SMatthias Ringwald
386b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts):
387b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2801)
388b3fcedb9SMatthias Ringwald
389b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts):
390b3fcedb9SMatthias Ringwald    global handle
391b3fcedb9SMatthias Ringwald    global total_size
392b3fcedb9SMatthias Ringwald
393e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
394b3fcedb9SMatthias Ringwald
395b3fcedb9SMatthias Ringwald    write_indent(fout)
396b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
397b3fcedb9SMatthias Ringwald
398b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
399b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
400b3fcedb9SMatthias Ringwald    if uuid_size > 2:
401b3fcedb9SMatthias Ringwald        uuid_size = 0
402729074c4SMatthias Ringwald    # print("Include Service ", c_string_for_uuid(uuid))
403b3fcedb9SMatthias Ringwald
404b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 4 + uuid_size
405b3fcedb9SMatthias Ringwald
406729074c4SMatthias Ringwald    keyUUID = c_string_for_uuid(parts[1])
407b3fcedb9SMatthias Ringwald
408b3fcedb9SMatthias Ringwald    write_indent(fout)
409b3fcedb9SMatthias Ringwald    write_16(fout, size)
410e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
411b3fcedb9SMatthias Ringwald    write_16(fout, handle)
412b3fcedb9SMatthias Ringwald    write_16(fout, 0x2802)
413b3fcedb9SMatthias Ringwald    write_16(fout, services[keyUUID][0])
414b3fcedb9SMatthias Ringwald    write_16(fout, services[keyUUID][1])
415b3fcedb9SMatthias Ringwald    if uuid_size > 0:
416*285653b2SMatthias Ringwald        write_uuid(fout, uuid)
417b3fcedb9SMatthias Ringwald    fout.write("\n")
418b3fcedb9SMatthias Ringwald
419b3fcedb9SMatthias Ringwald    handle = handle + 1
420b3fcedb9SMatthias Ringwald    total_size = total_size + size
421b3fcedb9SMatthias Ringwald
422b3fcedb9SMatthias Ringwald
423b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts):
424b3fcedb9SMatthias Ringwald    global handle
425b3fcedb9SMatthias Ringwald    global total_size
426b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
427b3fcedb9SMatthias Ringwald    global characteristic_indices
428b3fcedb9SMatthias Ringwald
429e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
430b3fcedb9SMatthias Ringwald
431b3fcedb9SMatthias Ringwald    # enumerate characteristics with same UUID, using optional name tag if available
432b3fcedb9SMatthias Ringwald    current_characteristic_uuid_string = c_string_for_uuid(parts[1]);
433b3fcedb9SMatthias Ringwald    index = 1
434b3fcedb9SMatthias Ringwald    if current_characteristic_uuid_string in characteristic_indices:
435b3fcedb9SMatthias Ringwald        index = characteristic_indices[current_characteristic_uuid_string] + 1
436b3fcedb9SMatthias Ringwald    characteristic_indices[current_characteristic_uuid_string] = index
437b3fcedb9SMatthias Ringwald    if len(parts) > 4:
438b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_')
439b3fcedb9SMatthias Ringwald    else:
440b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += ('_%02x' % index)
441b3fcedb9SMatthias Ringwald
442b3fcedb9SMatthias Ringwald    uuid       = parseUUID(parts[1])
443b3fcedb9SMatthias Ringwald    uuid_size  = len(uuid)
444b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[2])
445b3fcedb9SMatthias Ringwald    value = ', '.join([str(x) for x in parts[3:]])
446b3fcedb9SMatthias Ringwald
447b3fcedb9SMatthias Ringwald    # reliable writes is defined in an extended properties
448b3fcedb9SMatthias Ringwald    if (properties & property_flags['RELIABLE_WRITE']):
449b3fcedb9SMatthias Ringwald        properties = properties | property_flags['EXTENDED_PROPERTIES']
450b3fcedb9SMatthias Ringwald
451b3fcedb9SMatthias Ringwald    write_indent(fout)
452b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3])))
453b3fcedb9SMatthias Ringwald
454e22a2612SMatthias Ringwald
455e22a2612SMatthias Ringwald    characteristic_properties = gatt_characteristic_properties(properties)
456b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (1+2+uuid_size)
457b3fcedb9SMatthias Ringwald    write_indent(fout)
458b3fcedb9SMatthias Ringwald    write_16(fout, size)
459e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
460b3fcedb9SMatthias Ringwald    write_16(fout, handle)
461b3fcedb9SMatthias Ringwald    write_16(fout, 0x2803)
462e22a2612SMatthias Ringwald    write_8(fout, characteristic_properties)
463b3fcedb9SMatthias Ringwald    write_16(fout, handle+1)
464*285653b2SMatthias Ringwald    write_uuid(fout, uuid)
465b3fcedb9SMatthias Ringwald    fout.write("\n")
466b3fcedb9SMatthias Ringwald    handle = handle + 1
467b3fcedb9SMatthias Ringwald    total_size = total_size + size
468b3fcedb9SMatthias Ringwald
469b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size
470b3fcedb9SMatthias Ringwald    if is_string(value):
471b3fcedb9SMatthias Ringwald        size = size + len(value)
472b3fcedb9SMatthias Ringwald    else:
473b3fcedb9SMatthias Ringwald        size = size + len(value.split())
474b3fcedb9SMatthias Ringwald
475e22a2612SMatthias Ringwald    value_flags = att_flags(properties)
4768ea3236cSMatthias Ringwald
4778ea3236cSMatthias Ringwald    # add UUID128 flag for value handle
478b3fcedb9SMatthias Ringwald    if uuid_size == 16:
479e22a2612SMatthias Ringwald        value_flags = value_flags | property_flags['LONG_UUID'];
480b3fcedb9SMatthias Ringwald
481b3fcedb9SMatthias Ringwald    write_indent(fout)
482b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value))
483d7ec1d24SMatthias Ringwald
484d7ec1d24SMatthias Ringwald    dump_flags(fout, value_flags)
485d7ec1d24SMatthias Ringwald
486b3fcedb9SMatthias Ringwald    write_indent(fout)
487b3fcedb9SMatthias Ringwald    write_16(fout, size)
488e22a2612SMatthias Ringwald    write_16(fout, value_flags)
489b3fcedb9SMatthias Ringwald    write_16(fout, handle)
490*285653b2SMatthias Ringwald    write_uuid(fout, uuid)
491b3fcedb9SMatthias Ringwald    if is_string(value):
492b3fcedb9SMatthias Ringwald        write_string(fout, value)
493b3fcedb9SMatthias Ringwald    else:
494b3fcedb9SMatthias Ringwald        write_sequence(fout,value)
495b3fcedb9SMatthias Ringwald
496b3fcedb9SMatthias Ringwald    fout.write("\n")
497729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
498b3fcedb9SMatthias Ringwald    handle = handle + 1
499b3fcedb9SMatthias Ringwald
500b3fcedb9SMatthias Ringwald    if add_client_characteristic_configuration(properties):
501e22a2612SMatthias Ringwald        # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC
502d7ec1d24SMatthias Ringwald        flags  = write_permissions_and_key_size_flags_from_properties(properties)
503e22a2612SMatthias Ringwald        flags |= property_flags['READ']
504e22a2612SMatthias Ringwald        flags |= property_flags['WRITE']
5059be4aecfSMatthias Ringwald        flags |= property_flags['WRITE_WITHOUT_RESPONSE']
506e22a2612SMatthias Ringwald        flags |= property_flags['DYNAMIC']
507b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
508d7ec1d24SMatthias Ringwald
509b3fcedb9SMatthias Ringwald        write_indent(fout)
510b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle))
511d7ec1d24SMatthias Ringwald
512d7ec1d24SMatthias Ringwald        dump_flags(fout, flags)
513d7ec1d24SMatthias Ringwald
514b3fcedb9SMatthias Ringwald        write_indent(fout)
515b3fcedb9SMatthias Ringwald        write_16(fout, size)
516e22a2612SMatthias Ringwald        write_16(fout, flags)
517b3fcedb9SMatthias Ringwald        write_16(fout, handle)
518b3fcedb9SMatthias Ringwald        write_16(fout, 0x2902)
519b3fcedb9SMatthias Ringwald        write_16(fout, 0)
520b3fcedb9SMatthias Ringwald        fout.write("\n")
521729074c4SMatthias Ringwald        defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
522b3fcedb9SMatthias Ringwald        handle = handle + 1
523b3fcedb9SMatthias Ringwald
524b3fcedb9SMatthias Ringwald    if properties & property_flags['RELIABLE_WRITE']:
525b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
526b3fcedb9SMatthias Ringwald        write_indent(fout)
527b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle))
528b3fcedb9SMatthias Ringwald        write_indent(fout)
529b3fcedb9SMatthias Ringwald        write_16(fout, size)
530e22a2612SMatthias Ringwald        write_16(fout, read_only_anybody_flags)
531b3fcedb9SMatthias Ringwald        write_16(fout, handle)
532b3fcedb9SMatthias Ringwald        write_16(fout, 0x2900)
533b3fcedb9SMatthias Ringwald        write_16(fout, 1)   # Reliable Write
534b3fcedb9SMatthias Ringwald        fout.write("\n")
535b3fcedb9SMatthias Ringwald        handle = handle + 1
536b3fcedb9SMatthias Ringwald
537b3fcedb9SMatthias Ringwalddef parseCharacteristicUserDescription(fout, parts):
538b3fcedb9SMatthias Ringwald    global handle
539b3fcedb9SMatthias Ringwald    global total_size
540b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
541b3fcedb9SMatthias Ringwald
542b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
543b3fcedb9SMatthias Ringwald    value      = parts[2]
544b3fcedb9SMatthias Ringwald
545b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
546b3fcedb9SMatthias Ringwald    if is_string(value):
547b7647eb6SMatthias Ringwald        size = size + len(value)
548b3fcedb9SMatthias Ringwald    else:
549b3fcedb9SMatthias Ringwald        size = size + len(value.split())
550b3fcedb9SMatthias Ringwald
551e22a2612SMatthias Ringwald    # use write, write permissions and encryption key size from attribute value and set READ_ANYBODY
552d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
553e22a2612SMatthias Ringwald    flags |= properties & property_flags['WRITE']
554e22a2612SMatthias Ringwald    flags |= property_flags['READ']
555e22a2612SMatthias Ringwald
556b3fcedb9SMatthias Ringwald    write_indent(fout)
557b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:])))
558d7ec1d24SMatthias Ringwald
559d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
560d7ec1d24SMatthias Ringwald
561b3fcedb9SMatthias Ringwald    write_indent(fout)
562b3fcedb9SMatthias Ringwald    write_16(fout, size)
563e22a2612SMatthias Ringwald    write_16(fout, flags)
564b3fcedb9SMatthias Ringwald    write_16(fout, handle)
565b3fcedb9SMatthias Ringwald    write_16(fout, 0x2901)
566b3fcedb9SMatthias Ringwald    if is_string(value):
567b3fcedb9SMatthias Ringwald        write_string(fout, value)
568b3fcedb9SMatthias Ringwald    else:
569b3fcedb9SMatthias Ringwald        write_sequence(fout,value)
570b3fcedb9SMatthias Ringwald    fout.write("\n")
571729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
572b3fcedb9SMatthias Ringwald    handle = handle + 1
573b3fcedb9SMatthias Ringwald
574b3fcedb9SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts):
575b3fcedb9SMatthias Ringwald    global handle
576b3fcedb9SMatthias Ringwald    global total_size
577b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
578b3fcedb9SMatthias Ringwald
579b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
580b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
581b3fcedb9SMatthias Ringwald
582e22a2612SMatthias Ringwald    # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY
583d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
584e22a2612SMatthias Ringwald    flags |= property_flags['READ']
585e22a2612SMatthias Ringwald    flags |= property_flags['WRITE']
586e22a2612SMatthias Ringwald    flags |= property_flags['DYNAMIC']
587e22a2612SMatthias Ringwald
588b3fcedb9SMatthias Ringwald    write_indent(fout)
589b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:])))
590d7ec1d24SMatthias Ringwald
591d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
592d7ec1d24SMatthias Ringwald
593b3fcedb9SMatthias Ringwald    write_indent(fout)
594b3fcedb9SMatthias Ringwald    write_16(fout, size)
595e22a2612SMatthias Ringwald    write_16(fout, flags)
596b3fcedb9SMatthias Ringwald    write_16(fout, handle)
597b3fcedb9SMatthias Ringwald    write_16(fout, 0x2903)
598b3fcedb9SMatthias Ringwald    fout.write("\n")
599729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
600b3fcedb9SMatthias Ringwald    handle = handle + 1
601b3fcedb9SMatthias Ringwald
602b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts):
603b3fcedb9SMatthias Ringwald    global handle
604b3fcedb9SMatthias Ringwald    global total_size
605b3fcedb9SMatthias Ringwald
606e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
607b3fcedb9SMatthias Ringwald
608b3fcedb9SMatthias Ringwald    identifier = parts[1]
609b3fcedb9SMatthias Ringwald    presentation_formats[identifier] = handle
610b3fcedb9SMatthias Ringwald    # print("format '%s' with handle %d\n" % (identifier, handle))
611b3fcedb9SMatthias Ringwald
612b3fcedb9SMatthias Ringwald    format     = parts[2]
613b3fcedb9SMatthias Ringwald    exponent   = parts[3]
614b3fcedb9SMatthias Ringwald    unit       = parseUUID(parts[4])
615b3fcedb9SMatthias Ringwald    name_space = parts[5]
616b3fcedb9SMatthias Ringwald    description = parseUUID(parts[6])
617b3fcedb9SMatthias Ringwald
618b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 7
619b3fcedb9SMatthias Ringwald
620b3fcedb9SMatthias Ringwald    write_indent(fout)
621b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
622b3fcedb9SMatthias Ringwald    write_indent(fout)
623b3fcedb9SMatthias Ringwald    write_16(fout, size)
624e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
625b3fcedb9SMatthias Ringwald    write_16(fout, handle)
626b3fcedb9SMatthias Ringwald    write_16(fout, 0x2904)
627b3fcedb9SMatthias Ringwald    write_sequence(fout, format)
628b3fcedb9SMatthias Ringwald    write_sequence(fout, exponent)
629*285653b2SMatthias Ringwald    write_uuid(fout, unit)
630b3fcedb9SMatthias Ringwald    write_sequence(fout, name_space)
631*285653b2SMatthias Ringwald    write_uuid(fout, description)
632b3fcedb9SMatthias Ringwald    fout.write("\n")
633b3fcedb9SMatthias Ringwald    handle = handle + 1
634b3fcedb9SMatthias Ringwald
635b3fcedb9SMatthias Ringwald
636b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts):
637b3fcedb9SMatthias Ringwald    global handle
638b3fcedb9SMatthias Ringwald    global total_size
639b3fcedb9SMatthias Ringwald
640e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
641b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2
642b3fcedb9SMatthias Ringwald
643b3fcedb9SMatthias Ringwald    write_indent(fout)
644b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
645b3fcedb9SMatthias Ringwald    write_indent(fout)
646b3fcedb9SMatthias Ringwald    write_16(fout, size)
647e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
648b3fcedb9SMatthias Ringwald    write_16(fout, handle)
649b3fcedb9SMatthias Ringwald    write_16(fout, 0x2905)
650b3fcedb9SMatthias Ringwald    for identifier in parts[1:]:
651b3fcedb9SMatthias Ringwald        format_handle = presentation_formats[identifier]
652b3fcedb9SMatthias Ringwald        if format == 0:
653b3fcedb9SMatthias Ringwald            print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier)
654b3fcedb9SMatthias Ringwald            sys.exit(1)
655b3fcedb9SMatthias Ringwald        write_16(fout, format_handle)
656b3fcedb9SMatthias Ringwald    fout.write("\n")
657b3fcedb9SMatthias Ringwald    handle = handle + 1
658b3fcedb9SMatthias Ringwald
659b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts):
660b3fcedb9SMatthias Ringwald    global handle
661b3fcedb9SMatthias Ringwald    global total_size
662b3fcedb9SMatthias Ringwald
663e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
664b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1 + 1
665b3fcedb9SMatthias Ringwald
666231a3c5dSMatthias Ringwald    report_id = parts[2]
667231a3c5dSMatthias Ringwald    report_type = parts[3]
668b3fcedb9SMatthias Ringwald
669b3fcedb9SMatthias Ringwald    write_indent(fout)
670b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
671b3fcedb9SMatthias Ringwald    write_indent(fout)
672b3fcedb9SMatthias Ringwald    write_16(fout, size)
673e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
674b3fcedb9SMatthias Ringwald    write_16(fout, handle)
675b3fcedb9SMatthias Ringwald    write_16(fout, 0x2908)
676b3fcedb9SMatthias Ringwald    write_sequence(fout, report_id)
677b3fcedb9SMatthias Ringwald    write_sequence(fout, report_type)
678b3fcedb9SMatthias Ringwald    fout.write("\n")
679b3fcedb9SMatthias Ringwald    handle = handle + 1
680b3fcedb9SMatthias Ringwald
681b3fcedb9SMatthias Ringwald
682b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts):
683b3fcedb9SMatthias Ringwald    global handle
684b3fcedb9SMatthias Ringwald    global total_size
685b3fcedb9SMatthias Ringwald
686e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
687b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1
688b3fcedb9SMatthias Ringwald
689b3fcedb9SMatthias Ringwald    no_of_digitals = parts[1]
690b3fcedb9SMatthias Ringwald
691b3fcedb9SMatthias Ringwald    write_indent(fout)
692b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:])))
693b3fcedb9SMatthias Ringwald    write_indent(fout)
694b3fcedb9SMatthias Ringwald    write_16(fout, size)
695e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
696b3fcedb9SMatthias Ringwald    write_16(fout, handle)
697b3fcedb9SMatthias Ringwald    write_16(fout, 0x2909)
698b3fcedb9SMatthias Ringwald    write_sequence(fout, no_of_digitals)
699b3fcedb9SMatthias Ringwald    fout.write("\n")
700b3fcedb9SMatthias Ringwald    handle = handle + 1
701b3fcedb9SMatthias Ringwald
70260b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout):
703b3fcedb9SMatthias Ringwald    global handle
704b3fcedb9SMatthias Ringwald    global total_size
705b3fcedb9SMatthias Ringwald
706b165f97bSMatthias Ringwald    line_count = 0;
707b3fcedb9SMatthias Ringwald    for line in fin:
708b3fcedb9SMatthias Ringwald        line = line.strip("\n\r ")
709b165f97bSMatthias Ringwald        line_count += 1
710b3fcedb9SMatthias Ringwald
711b165f97bSMatthias Ringwald        if line.startswith("//"):
712b165f97bSMatthias Ringwald            fout.write("    //" + line.lstrip('/') + '\n')
713b165f97bSMatthias Ringwald            continue
714b165f97bSMatthias Ringwald
71560b51a4cSMatthias Ringwald        if line.startswith("#import"):
71660b51a4cSMatthias Ringwald            imported_file = ''
71760b51a4cSMatthias Ringwald            parts = re.match('#import\s+<(.*)>\w*',line)
71860b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
719dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
72060b51a4cSMatthias Ringwald            parts = re.match('#import\s+"(.*)"\w*',line)
72160b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
722dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
72360b51a4cSMatthias Ringwald            if len(imported_file) == 0:
72460b51a4cSMatthias Ringwald                print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count))
72560b51a4cSMatthias Ringwald                continue
72660b51a4cSMatthias Ringwald
727dbb3997aSMilanka Ringwald            imported_file = getFile( imported_file )
72860b51a4cSMatthias Ringwald            print("Importing %s" % imported_file)
72960b51a4cSMatthias Ringwald            try:
73060b51a4cSMatthias Ringwald                imported_fin = codecs.open (imported_file, encoding='utf-8')
73160b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- BEGIN\n')
73260b51a4cSMatthias Ringwald                parseLines(imported_file, imported_fin, fout)
73360b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- END\n')
73460b51a4cSMatthias Ringwald            except IOError as e:
73560b51a4cSMatthias Ringwald                print('ERROR: Import failed. Please check path.')
73660b51a4cSMatthias Ringwald
73760b51a4cSMatthias Ringwald            continue
73860b51a4cSMatthias Ringwald
73960b51a4cSMatthias Ringwald        if line.startswith("#TODO"):
74060b51a4cSMatthias Ringwald            print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count))
741b165f97bSMatthias Ringwald            print ("'%s'" % line)
742b165f97bSMatthias Ringwald            fout.write("// " + line + '\n')
743b3fcedb9SMatthias Ringwald            continue
744b3fcedb9SMatthias Ringwald
745b3fcedb9SMatthias Ringwald        if len(line) == 0:
746b3fcedb9SMatthias Ringwald            continue
747b3fcedb9SMatthias Ringwald
748b3fcedb9SMatthias Ringwald        f = io.StringIO(line)
749b3fcedb9SMatthias Ringwald        parts_list = csv.reader(f, delimiter=',', quotechar='"')
750b3fcedb9SMatthias Ringwald
751b3fcedb9SMatthias Ringwald        for parts in parts_list:
752b3fcedb9SMatthias Ringwald            for index, object in enumerate(parts):
753b3fcedb9SMatthias Ringwald                parts[index] = object.strip().lstrip('"').rstrip('"')
754b3fcedb9SMatthias Ringwald
755b3fcedb9SMatthias Ringwald            if parts[0] == 'PRIMARY_SERVICE':
756b3fcedb9SMatthias Ringwald                parsePrimaryService(fout, parts)
757b3fcedb9SMatthias Ringwald                continue
758b3fcedb9SMatthias Ringwald
759b3fcedb9SMatthias Ringwald            if parts[0] == 'SECONDARY_SERVICE':
760b3fcedb9SMatthias Ringwald                parseSecondaryService(fout, parts)
761b3fcedb9SMatthias Ringwald                continue
762b3fcedb9SMatthias Ringwald
763b3fcedb9SMatthias Ringwald            if parts[0] == 'INCLUDE_SERVICE':
764b3fcedb9SMatthias Ringwald                parseIncludeService(fout, parts)
765b3fcedb9SMatthias Ringwald                continue
766b3fcedb9SMatthias Ringwald
767b3fcedb9SMatthias Ringwald            # 2803
768b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC':
769b3fcedb9SMatthias Ringwald                parseCharacteristic(fout, parts)
770b3fcedb9SMatthias Ringwald                continue
771b3fcedb9SMatthias Ringwald
772b3fcedb9SMatthias Ringwald            # 2900 Characteristic Extended Properties
773b3fcedb9SMatthias Ringwald
774b3fcedb9SMatthias Ringwald            # 2901
775b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
776b3fcedb9SMatthias Ringwald                parseCharacteristicUserDescription(fout, parts)
777b3fcedb9SMatthias Ringwald                continue
778b3fcedb9SMatthias Ringwald
779b165f97bSMatthias Ringwald
780b165f97bSMatthias Ringwald            # 2902 Client Characteristic Configuration - automatically included in Characteristic if
781b3fcedb9SMatthias Ringwald            # notification / indication is supported
782231a3c5dSMatthias Ringwald            if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION':
783b165f97bSMatthias Ringwald                continue
784b3fcedb9SMatthias Ringwald
785b3fcedb9SMatthias Ringwald            # 2903
786b3fcedb9SMatthias Ringwald            if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
787b3fcedb9SMatthias Ringwald                parseServerCharacteristicConfiguration(fout, parts)
788b3fcedb9SMatthias Ringwald                continue
789b3fcedb9SMatthias Ringwald
790b3fcedb9SMatthias Ringwald            # 2904
791b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_FORMAT':
792b3fcedb9SMatthias Ringwald                parseCharacteristicFormat(fout, parts)
793b3fcedb9SMatthias Ringwald                continue
794b3fcedb9SMatthias Ringwald
795b3fcedb9SMatthias Ringwald            # 2905
796b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
797b3fcedb9SMatthias Ringwald                parseCharacteristicAggregateFormat(fout, parts)
798b3fcedb9SMatthias Ringwald                continue
799b3fcedb9SMatthias Ringwald
800b3fcedb9SMatthias Ringwald            # 2906
801b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
802b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
803b3fcedb9SMatthias Ringwald                continue
804b3fcedb9SMatthias Ringwald
805b3fcedb9SMatthias Ringwald            # 2907
806b3fcedb9SMatthias Ringwald            if parts[0] == 'EXTERNAL_REPORT_REFERENCE':
807b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
808b3fcedb9SMatthias Ringwald                continue
809b3fcedb9SMatthias Ringwald
810b3fcedb9SMatthias Ringwald            # 2908
811b3fcedb9SMatthias Ringwald            if parts[0] == 'REPORT_REFERENCE':
812b3fcedb9SMatthias Ringwald                parseReportReference(fout, parts)
813b3fcedb9SMatthias Ringwald                continue
814b3fcedb9SMatthias Ringwald
815b3fcedb9SMatthias Ringwald            # 2909
816b3fcedb9SMatthias Ringwald            if parts[0] == 'NUMBER_OF_DIGITALS':
817b3fcedb9SMatthias Ringwald                parseNumberOfDigitals(fout, parts)
818b3fcedb9SMatthias Ringwald                continue
819b3fcedb9SMatthias Ringwald
820b3fcedb9SMatthias Ringwald            # 290A
821b3fcedb9SMatthias Ringwald            if parts[0] == 'VALUE_TRIGGER_SETTING':
822b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
823b3fcedb9SMatthias Ringwald                continue
824b3fcedb9SMatthias Ringwald
825b3fcedb9SMatthias Ringwald            # 290B
826b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION':
827b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
828b3fcedb9SMatthias Ringwald                continue
829b3fcedb9SMatthias Ringwald
830b3fcedb9SMatthias Ringwald            # 290C
831b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT':
832b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
833b3fcedb9SMatthias Ringwald                continue
834b3fcedb9SMatthias Ringwald
835b3fcedb9SMatthias Ringwald            # 290D
836b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING':
837b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
838b3fcedb9SMatthias Ringwald                continue
839b3fcedb9SMatthias Ringwald
840b3fcedb9SMatthias Ringwald            # 2906
841b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
842b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
843b3fcedb9SMatthias Ringwald                continue
844b3fcedb9SMatthias Ringwald
845b3fcedb9SMatthias Ringwald            print("WARNING: unknown token: %s\n" % (parts[0]))
846b3fcedb9SMatthias Ringwald
8477050bf34SMatthias Ringwalddef parse(fname_in, fin, fname_out, tool_path, fout):
84860b51a4cSMatthias Ringwald    global handle
84960b51a4cSMatthias Ringwald    global total_size
85060b51a4cSMatthias Ringwald
8517050bf34SMatthias Ringwald    fout.write(header.format(fname_out, fname_in, tool_path))
85260b51a4cSMatthias Ringwald    fout.write('{\n')
853fd1be25dSMatthias Ringwald    write_indent(fout)
854fd1be25dSMatthias Ringwald    fout.write('// ATT DB Version\n')
855fd1be25dSMatthias Ringwald    write_indent(fout)
856fd1be25dSMatthias Ringwald    fout.write('1,\n')
857fd1be25dSMatthias Ringwald    fout.write("\n")
85860b51a4cSMatthias Ringwald
85960b51a4cSMatthias Ringwald    parseLines(fname_in, fin, fout)
86060b51a4cSMatthias Ringwald
861729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
862b3fcedb9SMatthias Ringwald    write_indent(fout)
863b3fcedb9SMatthias Ringwald    fout.write("// END\n");
864b3fcedb9SMatthias Ringwald    write_indent(fout)
865b3fcedb9SMatthias Ringwald    write_16(fout,0)
866b3fcedb9SMatthias Ringwald    fout.write("\n")
867b3fcedb9SMatthias Ringwald    total_size = total_size + 2
868b3fcedb9SMatthias Ringwald
869b3fcedb9SMatthias Ringwald    fout.write("}; // total size %u bytes \n" % total_size);
870b3fcedb9SMatthias Ringwald
871b3fcedb9SMatthias Ringwalddef listHandles(fout):
872b3fcedb9SMatthias Ringwald    fout.write('\n\n')
873b3fcedb9SMatthias Ringwald    fout.write('//\n')
874729074c4SMatthias Ringwald    fout.write('// list service handle ranges\n')
875729074c4SMatthias Ringwald    fout.write('//\n')
876729074c4SMatthias Ringwald    for define in defines_for_services:
877729074c4SMatthias Ringwald        fout.write(define)
878729074c4SMatthias Ringwald        fout.write('\n')
879729074c4SMatthias Ringwald    fout.write('\n')
880729074c4SMatthias Ringwald    fout.write('//\n')
881b3fcedb9SMatthias Ringwald    fout.write('// list mapping between characteristics and handles\n')
882b3fcedb9SMatthias Ringwald    fout.write('//\n')
883729074c4SMatthias Ringwald    for define in defines_for_characteristics:
884b3fcedb9SMatthias Ringwald        fout.write(define)
885b3fcedb9SMatthias Ringwald        fout.write('\n')
886b3fcedb9SMatthias Ringwald
887dbb3997aSMilanka Ringwalddef getFile( fileName ):
88878b65b0aSMatthias Ringwald    for d in include_paths:
88978b65b0aSMatthias Ringwald        fullFile = os.path.normpath(d + os.sep + fileName) # because Windows exists
89078b65b0aSMatthias Ringwald        # print("test %s" % fullFile)
891dbb3997aSMilanka Ringwald        if os.path.isfile( fullFile ) == True:
892dbb3997aSMilanka Ringwald            return fullFile
893dbb3997aSMilanka Ringwald    print ("'{0}' not found".format( fileName ))
89478b65b0aSMatthias Ringwald    print ("Include paths: %s" % ", ".join(include_paths))
895dbb3997aSMilanka Ringwald    exit(-1)
896dbb3997aSMilanka Ringwald
897dbb3997aSMilanka Ringwald
898dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
89978b65b0aSMatthias Ringwalddefault_includes = [os.path.normpath(path) for path in [ btstack_root + '/src/', btstack_root + '/src/ble/gatt-service/']]
900dbb3997aSMilanka Ringwald
901dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack')
902dbb3997aSMilanka Ringwald
903dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes',
904dbb3997aSMilanka Ringwald        help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes))
905dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str,
906dbb3997aSMilanka Ringwald        help='gatt file to be compiled')
907dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str,
908dbb3997aSMilanka Ringwald        help='header file to be generated')
909dbb3997aSMilanka Ringwald
910dbb3997aSMilanka Ringwaldargs = parser.parse_args()
911dbb3997aSMilanka Ringwald
91278b65b0aSMatthias Ringwald# add include path arguments
91378b65b0aSMatthias Ringwaldif args.I != None:
91478b65b0aSMatthias Ringwald    for d in args.I:
91578b65b0aSMatthias Ringwald        include_paths.append(os.path.normpath(d[0]))
91678b65b0aSMatthias Ringwald
917dbb3997aSMilanka Ringwald# append default include paths
91878b65b0aSMatthias Ringwaldinclude_paths.extend(default_includes)
919dbb3997aSMilanka Ringwald
920b3fcedb9SMatthias Ringwaldtry:
921b165f97bSMatthias Ringwald    # read defines from bluetooth_gatt.h
922dbb3997aSMilanka Ringwald    gen_path = getFile( 'bluetooth_gatt.h' )
923b165f97bSMatthias Ringwald    bluetooth_gatt = read_defines(gen_path)
924b165f97bSMatthias Ringwald
925dbb3997aSMilanka Ringwald    filename = args.hfile
926dbb3997aSMilanka Ringwald    fin  = codecs.open (args.gattfile, encoding='utf-8')
927*285653b2SMatthias Ringwald
928*285653b2SMatthias Ringwald    # pass 1: create temp .h file
929*285653b2SMatthias Ringwald    ftemp = tempfile.TemporaryFile()
930*285653b2SMatthias Ringwald    parse(args.gattfile, fin, filename, sys.argv[0], ftemp)
931*285653b2SMatthias Ringwald    listHandles(ftemp)
932*285653b2SMatthias Ringwald
933*285653b2SMatthias Ringwald    # pass 2: insert GATT Database Hash
934b3fcedb9SMatthias Ringwald    fout = open (filename, 'w')
935*285653b2SMatthias Ringwald    ftemp.seek(0)
936*285653b2SMatthias Ringwald    for line in ftemp:
937*285653b2SMatthias Ringwald        fout.write(line)
938b3fcedb9SMatthias Ringwald    fout.close()
939*285653b2SMatthias Ringwald    ftemp.close()
940*285653b2SMatthias Ringwald
941b165f97bSMatthias Ringwald    print('Created %s' % filename)
942b3fcedb9SMatthias Ringwald
943b3fcedb9SMatthias Ringwaldexcept IOError as e:
944e22a2612SMatthias Ringwald
945b3fcedb9SMatthias Ringwald    print(usage)
946b3fcedb9SMatthias Ringwald    sys.exit(1)
947b3fcedb9SMatthias Ringwald
948b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n')
949