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