xref: /btstack/tool/compile_gatt.py (revision bae152386e44036644d06c0cdc0e57b23d13458f)
14783d256SMatheus Garbelini#!/usr/bin/env python3
2b3fcedb9SMatthias Ringwald#
3dbb3997aSMilanka Ringwald# BLE GATT configuration generator for use with BTstack
4043f8832SMatthias Ringwald# Copyright 2019 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
10043f8832SMatthias Ringwald# dependencies:
11043f8832SMatthias Ringwald# - pip3 install pycryptodomex
12ced5a857SMatthias Ringwald# alternatively, the pycryptodome package can be used instead
13ced5a857SMatthias Ringwald# - pip3 install pycryptodome
14043f8832SMatthias Ringwald
15b3fcedb9SMatthias Ringwaldimport codecs
16b165f97bSMatthias Ringwaldimport csv
17b165f97bSMatthias Ringwaldimport io
18b165f97bSMatthias Ringwaldimport os
19b165f97bSMatthias Ringwaldimport re
20b165f97bSMatthias Ringwaldimport string
2160b51a4cSMatthias Ringwaldimport sys
22dbb3997aSMilanka Ringwaldimport argparse
23285653b2SMatthias Ringwaldimport tempfile
24b3fcedb9SMatthias Ringwald
25ced5a857SMatthias Ringwaldhave_crypto = True
26ced5a857SMatthias Ringwald# try to import PyCryptodome independent from PyCrypto
27043f8832SMatthias Ringwaldtry:
28379d3aceSMatthias Ringwald    from Cryptodome.Cipher import AES
29043f8832SMatthias Ringwald    from Cryptodome.Hash import CMAC
30ced5a857SMatthias Ringwaldexcept ImportError:
31ced5a857SMatthias Ringwald    # fallback: try to import PyCryptodome as (an almost drop-in) replacement for the PyCrypto library
32ced5a857SMatthias Ringwald    try:
33ced5a857SMatthias Ringwald        from Crypto.Cipher import AES
34ced5a857SMatthias Ringwald        from Crypto.Hash import CMAC
35043f8832SMatthias Ringwald    except ImportError:
36043f8832SMatthias Ringwald        have_crypto = False
377490175eSMatthias Ringwald        print("\n[!] PyCryptodome required to calculate GATT Database Hash but not installed (using random value instead)")
386ccd8248SMilanka Ringwald        print("[!] Please install PyCryptodome, e.g. 'pip3 install pycryptodomex' or 'pip3 install pycryptodome'\n")
39043f8832SMatthias Ringwald
40b3fcedb9SMatthias Ringwaldheader = '''
41b3fcedb9SMatthias Ringwald// {0} generated from {1} for BTstack
427050bf34SMatthias Ringwald// it needs to be regenerated when the .gatt file is updated.
437050bf34SMatthias Ringwald
447050bf34SMatthias Ringwald// To generate {0}:
457050bf34SMatthias Ringwald// {2} {1} {0}
467050bf34SMatthias Ringwald
47fd1be25dSMatthias Ringwald// att db format version 1
48b3fcedb9SMatthias Ringwald
49fd1be25dSMatthias Ringwald// binary attribute representation:
50fd1be25dSMatthias Ringwald// - size in bytes (16), flags(16), handle (16), uuid (16/128), value(...)
51b3fcedb9SMatthias Ringwald
52b3fcedb9SMatthias Ringwald#include <stdint.h>
53b3fcedb9SMatthias Ringwald
54fa529fa7SMatthias Ringwald// Reference: https://en.cppreference.com/w/cpp/feature_test
55fa529fa7SMatthias Ringwald#if __cplusplus >= 200704L
56fa529fa7SMatthias Ringwaldconstexpr
57fa529fa7SMatthias Ringwald#endif
58b3fcedb9SMatthias Ringwaldconst uint8_t profile_data[] =
59b3fcedb9SMatthias Ringwald'''
60b3fcedb9SMatthias Ringwald
61b3fcedb9SMatthias Ringwaldprint('''
62dbb3997aSMilanka RingwaldBLE configuration generator for use with BTstack
63dbb3997aSMilanka RingwaldCopyright 2018 BlueKitchen GmbH
64b3fcedb9SMatthias Ringwald''')
65b3fcedb9SMatthias Ringwald
66b3fcedb9SMatthias Ringwaldassigned_uuids = {
67b3fcedb9SMatthias Ringwald    'GAP_SERVICE'          : 0x1800,
68b3fcedb9SMatthias Ringwald    'GATT_SERVICE'         : 0x1801,
69b3fcedb9SMatthias Ringwald    'GAP_DEVICE_NAME'      : 0x2a00,
70b3fcedb9SMatthias Ringwald    'GAP_APPEARANCE'       : 0x2a01,
71b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02,
72b3fcedb9SMatthias Ringwald    'GAP_RECONNECTION_ADDRESS'    : 0x2A03,
73b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04,
74bd8291e9SMatthias Ringwald    'GAP_CENTRAL_ADDRESS_RESOLUTION' : 0x2aa6,
75bd8291e9SMatthias Ringwald    'GAP_RESOLVABLE_PRIVATE_ADDRESS_ONLY' : 0x2AC9,
76bd8291e9SMatthias Ringwald    'GAP_ENCRYPTED_DATA_KEY_MATERIAL' : 0x2B88,
77bd8291e9SMatthias Ringwald    'GAP_LE_GATT_SECURITY_LEVELS' : 0x2BF5,
78b3fcedb9SMatthias Ringwald    'GATT_SERVICE_CHANGED' : 0x2a05,
79c7a36ba4SMatthias Ringwald    'GATT_CLIENT_SUPPORTED_FEATURES' : 0x2b29,
80c7a36ba4SMatthias Ringwald    'GATT_SERVER_SUPPORTED_FEATURES' : 0x2b3a,
81043f8832SMatthias Ringwald    'GATT_DATABASE_HASH' : 0x2b2a
82b3fcedb9SMatthias Ringwald}
83b3fcedb9SMatthias Ringwald
84e72176f8SMatthias Ringwaldsecurity_permsission = ['ANYBODY','ENCRYPTED', 'AUTHENTICATED', 'AUTHORIZED', 'AUTHENTICATED_SC']
85d7ec1d24SMatthias Ringwald
86b3fcedb9SMatthias Ringwaldproperty_flags = {
87eb6072adSMatthias Ringwald    # GATT Characteristic Properties
88b3fcedb9SMatthias Ringwald    'BROADCAST' :                   0x01,
89b3fcedb9SMatthias Ringwald    'READ' :                        0x02,
90b3fcedb9SMatthias Ringwald    'WRITE_WITHOUT_RESPONSE' :      0x04,
91b3fcedb9SMatthias Ringwald    'WRITE' :                       0x08,
92b3fcedb9SMatthias Ringwald    'NOTIFY':                       0x10,
93b3fcedb9SMatthias Ringwald    'INDICATE' :                    0x20,
94b3fcedb9SMatthias Ringwald    'AUTHENTICATED_SIGNED_WRITE' :  0x40,
95b3fcedb9SMatthias Ringwald    'EXTENDED_PROPERTIES' :         0x80,
96b3fcedb9SMatthias Ringwald    # custom BTstack extension
97b3fcedb9SMatthias Ringwald    'DYNAMIC':                      0x100,
98b3fcedb9SMatthias Ringwald    'LONG_UUID':                    0x200,
99e22a2612SMatthias Ringwald
100e22a2612SMatthias Ringwald    # read permissions
101e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_0':        0x400,
102e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_1':        0x800,
103e22a2612SMatthias Ringwald
104e22a2612SMatthias Ringwald    #
105b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_7':       0x6000,
106b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_8':       0x7000,
107b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_9':       0x8000,
108b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_10':      0x9000,
109b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_11':      0xa000,
110b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_12':      0xb000,
111b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_13':      0xc000,
112b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_14':      0xd000,
113b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_15':      0xe000,
114b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_16':      0xf000,
115e22a2612SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_MASK':    0xf000,
116eb6072adSMatthias Ringwald
117b3fcedb9SMatthias Ringwald    # only used by gatt compiler >= 0xffff
118b3fcedb9SMatthias Ringwald    # Extended Properties
119e72176f8SMatthias Ringwald    'RELIABLE_WRITE':              0x00010000,
120e72176f8SMatthias Ringwald    'AUTHENTICATION_REQUIRED':     0x00020000,
121e72176f8SMatthias Ringwald    'AUTHORIZATION_REQUIRED':      0x00040000,
122e72176f8SMatthias Ringwald    'READ_ANYBODY':                0x00080000,
123e72176f8SMatthias Ringwald    'READ_ENCRYPTED':              0x00100000,
124e72176f8SMatthias Ringwald    'READ_AUTHENTICATED':          0x00200000,
125e72176f8SMatthias Ringwald    'READ_AUTHENTICATED_SC':       0x00400000,
126e72176f8SMatthias Ringwald    'READ_AUTHORIZED':             0x00800000,
127e72176f8SMatthias Ringwald    'WRITE_ANYBODY':               0x01000000,
128e72176f8SMatthias Ringwald    'WRITE_ENCRYPTED':             0x02000000,
129e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED':         0x04000000,
130e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED_SC':      0x08000000,
131e72176f8SMatthias Ringwald    'WRITE_AUTHORIZED':            0x10000000,
132eb6072adSMatthias Ringwald
133eb6072adSMatthias Ringwald    # Broadcast, Notify, Indicate, Extended Properties are only used to describe a GATT Characteristic, but are free to use with att_db
134e72176f8SMatthias Ringwald    # - write permissions
135e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_0':      0x01,
136e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_1':      0x10,
137e72176f8SMatthias Ringwald    # - SC required
138e72176f8SMatthias Ringwald    'READ_PERMISSION_SC':          0x20,
139e72176f8SMatthias Ringwald    'WRITE_PERMISSION_SC':         0x80,
140b3fcedb9SMatthias Ringwald}
141b3fcedb9SMatthias Ringwald
142b3fcedb9SMatthias Ringwaldservices = dict()
143b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict()
144b3fcedb9SMatthias Ringwaldpresentation_formats = dict()
145b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = ""
146b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0
147b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = ""
148729074c4SMatthias Ringwalddefines_for_characteristics = []
149729074c4SMatthias Ringwalddefines_for_services = []
15078b65b0aSMatthias Ringwaldinclude_paths = []
151043f8832SMatthias Ringwalddatabase_hash_message = bytearray()
15229ba805bSMatthias Ringwaldservice_counter = {}
153b3fcedb9SMatthias Ringwald
154b3fcedb9SMatthias Ringwaldhandle = 1
155b3fcedb9SMatthias Ringwaldtotal_size = 0
156b3fcedb9SMatthias Ringwald
157043f8832SMatthias Ringwalddef aes_cmac(key, n):
158043f8832SMatthias Ringwald    if have_crypto:
159043f8832SMatthias Ringwald        cobj = CMAC.new(key, ciphermod=AES)
160043f8832SMatthias Ringwald        cobj.update(n)
161043f8832SMatthias Ringwald        return cobj.digest()
162043f8832SMatthias Ringwald    else:
1637490175eSMatthias Ringwald        # return random value
1647490175eSMatthias Ringwald        return os.urandom(16)
165043f8832SMatthias Ringwald
166b165f97bSMatthias Ringwalddef read_defines(infile):
167b165f97bSMatthias Ringwald    defines = dict()
168b165f97bSMatthias Ringwald    with open (infile, 'rt') as fin:
169b165f97bSMatthias Ringwald        for line in fin:
170*bae15238SMatthias Ringwald            parts = re.match('#define\\s+(\\w+)\\s+(\\w+)',line)
171b165f97bSMatthias Ringwald            if parts and len(parts.groups()) == 2:
172b165f97bSMatthias Ringwald                (key, value) = parts.groups()
173b165f97bSMatthias Ringwald                defines[key] = int(value, 16)
174b165f97bSMatthias Ringwald    return defines
175b165f97bSMatthias Ringwald
176b3fcedb9SMatthias Ringwalddef keyForUUID(uuid):
177b3fcedb9SMatthias Ringwald    keyUUID = ""
178b3fcedb9SMatthias Ringwald    for i in uuid:
179b3fcedb9SMatthias Ringwald        keyUUID += "%02x" % i
180b3fcedb9SMatthias Ringwald    return keyUUID
181b3fcedb9SMatthias Ringwald
182b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid):
183b3fcedb9SMatthias Ringwald    return uuid.replace('-', '_')
184b3fcedb9SMatthias Ringwald
185b3fcedb9SMatthias Ringwalddef twoByteLEFor(value):
186b3fcedb9SMatthias Ringwald    return [ (value & 0xff), (value >> 8)]
187b3fcedb9SMatthias Ringwald
188b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text):
189b3fcedb9SMatthias 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):
190b3fcedb9SMatthias Ringwald        return True
191b3fcedb9SMatthias Ringwald    return False
192b3fcedb9SMatthias Ringwald
193b3fcedb9SMatthias Ringwalddef parseUUID128(uuid):
194b3fcedb9SMatthias 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)
195b3fcedb9SMatthias Ringwald    uuid_bytes = []
196b3fcedb9SMatthias Ringwald    for i in range(8, 0, -1):
197b3fcedb9SMatthias Ringwald        uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16))
198b3fcedb9SMatthias Ringwald    return uuid_bytes
199b3fcedb9SMatthias Ringwald
200b3fcedb9SMatthias Ringwalddef parseUUID(uuid):
201b3fcedb9SMatthias Ringwald    if uuid in assigned_uuids:
202b3fcedb9SMatthias Ringwald        return twoByteLEFor(assigned_uuids[uuid])
203b165f97bSMatthias Ringwald    uuid_upper = uuid.upper().replace('.','_')
204b165f97bSMatthias Ringwald    if uuid_upper in bluetooth_gatt:
205b165f97bSMatthias Ringwald        return twoByteLEFor(bluetooth_gatt[uuid_upper])
206b3fcedb9SMatthias Ringwald    if is_128bit_uuid(uuid):
207b3fcedb9SMatthias Ringwald        return parseUUID128(uuid)
208b3fcedb9SMatthias Ringwald    uuidInt = int(uuid, 16)
209b3fcedb9SMatthias Ringwald    return twoByteLEFor(uuidInt)
210b3fcedb9SMatthias Ringwald
211b3fcedb9SMatthias Ringwalddef parseProperties(properties):
212b3fcedb9SMatthias Ringwald    value = 0
213b3fcedb9SMatthias Ringwald    parts = properties.split("|")
214b3fcedb9SMatthias Ringwald    for property in parts:
215b3fcedb9SMatthias Ringwald        property = property.strip()
216b3fcedb9SMatthias Ringwald        if property in property_flags:
217b3fcedb9SMatthias Ringwald            value |= property_flags[property]
218b3fcedb9SMatthias Ringwald        else:
219b3fcedb9SMatthias Ringwald            print("WARNING: property %s undefined" % (property))
220e22a2612SMatthias Ringwald
221e5ce8e0eSMilanka Ringwald    return value
222e5ce8e0eSMilanka Ringwald
223e5ce8e0eSMilanka Ringwalddef prettyPrintProperties(properties):
224e5ce8e0eSMilanka Ringwald    value = ""
225e5ce8e0eSMilanka Ringwald    parts = properties.split("|")
226e5ce8e0eSMilanka Ringwald    for property in parts:
227e5ce8e0eSMilanka Ringwald        property = property.strip()
228e5ce8e0eSMilanka Ringwald        if property in property_flags:
229e5ce8e0eSMilanka Ringwald            if value != "":
230e5ce8e0eSMilanka Ringwald                value += " | "
231e5ce8e0eSMilanka Ringwald            value += property
232e5ce8e0eSMilanka Ringwald        else:
233e5ce8e0eSMilanka Ringwald            print("WARNING: property %s undefined" % (property))
234e5ce8e0eSMilanka Ringwald
235e5ce8e0eSMilanka Ringwald    return value
236e5ce8e0eSMilanka Ringwald
237e22a2612SMatthias Ringwald
238e22a2612SMatthias Ringwalddef gatt_characteristic_properties(properties):
239e22a2612SMatthias Ringwald    return properties & 0xff
240e22a2612SMatthias Ringwald
241e22a2612SMatthias Ringwalddef att_flags(properties):
242e72176f8SMatthias Ringwald    # drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Properties (0x80) - not used for flags
243e72176f8SMatthias Ringwald    properties &= 0xffffff4e
244e22a2612SMatthias Ringwald
245e22a2612SMatthias Ringwald    # rw permissions distinct
246e22a2612SMatthias Ringwald    distinct_permissions_used = properties & (
247e22a2612SMatthias Ringwald        property_flags['READ_AUTHORIZED'] |
248e72176f8SMatthias Ringwald        property_flags['READ_AUTHENTICATED_SC'] |
249e22a2612SMatthias Ringwald        property_flags['READ_AUTHENTICATED'] |
250e22a2612SMatthias Ringwald        property_flags['READ_ENCRYPTED'] |
251e22a2612SMatthias Ringwald        property_flags['READ_ANYBODY'] |
252e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHORIZED'] |
253e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED'] |
254e72176f8SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED_SC'] |
255e22a2612SMatthias Ringwald        property_flags['WRITE_ENCRYPTED'] |
256e22a2612SMatthias Ringwald        property_flags['WRITE_ANYBODY']
257e22a2612SMatthias Ringwald    ) != 0
258e22a2612SMatthias Ringwald
259e22a2612SMatthias Ringwald    # post process properties
260e22a2612SMatthias Ringwald    encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0
261e22a2612SMatthias Ringwald
262d7ec1d24SMatthias Ringwald    # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted
263e22a2612SMatthias Ringwald    if encryption_key_size_specified and not distinct_permissions_used:
264e22a2612SMatthias Ringwald        properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED']
265e22a2612SMatthias Ringwald
266d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated
267d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used:
268d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED']
269d7ec1d24SMatthias Ringwald
270d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized
271d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used:
272d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED']
273d7ec1d24SMatthias Ringwald
274d7ec1d24SMatthias Ringwald    # determine read/write security requirements
275d7ec1d24SMatthias Ringwald    read_security_level  = 0
276d7ec1d24SMatthias Ringwald    write_security_level = 0
277e72176f8SMatthias Ringwald    read_requires_sc     = False
278e72176f8SMatthias Ringwald    write_requires_sc    = False
279e22a2612SMatthias Ringwald    if properties & property_flags['READ_AUTHORIZED']:
280d7ec1d24SMatthias Ringwald        read_security_level = 3
281e22a2612SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED']:
282d7ec1d24SMatthias Ringwald        read_security_level = 2
283e72176f8SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED_SC']:
284e72176f8SMatthias Ringwald        read_security_level = 2
285e72176f8SMatthias Ringwald        read_requires_sc = True
286e22a2612SMatthias Ringwald    elif properties & property_flags['READ_ENCRYPTED']:
287d7ec1d24SMatthias Ringwald        read_security_level = 1
288e22a2612SMatthias Ringwald    if properties & property_flags['WRITE_AUTHORIZED']:
289d7ec1d24SMatthias Ringwald        write_security_level = 3
290e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED']:
291d7ec1d24SMatthias Ringwald        write_security_level = 2
292e72176f8SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED_SC']:
293e72176f8SMatthias Ringwald        write_security_level = 2
294e72176f8SMatthias Ringwald        write_requires_sc = True
295e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_ENCRYPTED']:
296d7ec1d24SMatthias Ringwald        write_security_level = 1
297d7ec1d24SMatthias Ringwald
298d7ec1d24SMatthias Ringwald    # map security requirements to flags
299d7ec1d24SMatthias Ringwald    if read_security_level & 2:
300d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_1']
301d7ec1d24SMatthias Ringwald    if read_security_level & 1:
302d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_0']
303e72176f8SMatthias Ringwald    if read_requires_sc:
304e72176f8SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_SC']
305d7ec1d24SMatthias Ringwald    if write_security_level & 2:
306d7ec1d24SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_1']
307d7ec1d24SMatthias Ringwald    if write_security_level & 1:
308e22a2612SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_0']
309e72176f8SMatthias Ringwald    if write_requires_sc:
310e72176f8SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_SC']
311e22a2612SMatthias Ringwald
312e22a2612SMatthias Ringwald    return properties
313e22a2612SMatthias Ringwald
314d7ec1d24SMatthias Ringwalddef write_permissions_and_key_size_flags_from_properties(properties):
315e22a2612SMatthias Ringwald    return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1'])
316b3fcedb9SMatthias Ringwald
317b3fcedb9SMatthias Ringwalddef write_8(fout, value):
318b3fcedb9SMatthias Ringwald    fout.write( "0x%02x, " % (value & 0xff))
319b3fcedb9SMatthias Ringwald
320b3fcedb9SMatthias Ringwalddef write_16(fout, value):
321b3fcedb9SMatthias Ringwald    fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff))
322b3fcedb9SMatthias Ringwald
323285653b2SMatthias Ringwalddef write_uuid(fout, uuid):
324b3fcedb9SMatthias Ringwald    for byte in uuid:
325b3fcedb9SMatthias Ringwald        fout.write( "0x%02x, " % byte)
326b3fcedb9SMatthias Ringwald
327b3fcedb9SMatthias Ringwalddef write_string(fout, text):
328b3fcedb9SMatthias Ringwald    for l in text.lstrip('"').rstrip('"'):
329b3fcedb9SMatthias Ringwald        write_8(fout, ord(l))
330b3fcedb9SMatthias Ringwald
331b3fcedb9SMatthias Ringwalddef write_sequence(fout, text):
332b3fcedb9SMatthias Ringwald    parts = text.split()
333b3fcedb9SMatthias Ringwald    for part in parts:
334b3fcedb9SMatthias Ringwald        fout.write("0x%s, " % (part.strip()))
335b3fcedb9SMatthias Ringwald
336043f8832SMatthias Ringwalddef write_database_hash(fout):
337043f8832SMatthias Ringwald    fout.write("THE-DATABASE-HASH")
338043f8832SMatthias Ringwald
339b3fcedb9SMatthias Ringwalddef write_indent(fout):
340b3fcedb9SMatthias Ringwald    fout.write("    ")
341b3fcedb9SMatthias Ringwald
342d7ec1d24SMatthias Ringwalddef read_permissions_from_flags(flags):
343d7ec1d24SMatthias Ringwald    permissions = 0
344d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_0']:
345d7ec1d24SMatthias Ringwald        permissions |= 1
346d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_1']:
347d7ec1d24SMatthias Ringwald        permissions |= 2
348e72176f8SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_SC'] and permissions == 2:
349e72176f8SMatthias Ringwald        permissions = 4
350d7ec1d24SMatthias Ringwald    return permissions
351d7ec1d24SMatthias Ringwald
352d7ec1d24SMatthias Ringwalddef write_permissions_from_flags(flags):
353d7ec1d24SMatthias Ringwald    permissions = 0
354d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_0']:
355d7ec1d24SMatthias Ringwald        permissions |= 1
356d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_1']:
357d7ec1d24SMatthias Ringwald        permissions |= 2
358e72176f8SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_SC'] and permissions == 2:
359e72176f8SMatthias Ringwald        permissions = 4
360d7ec1d24SMatthias Ringwald    return permissions
361d7ec1d24SMatthias Ringwald
362d7ec1d24SMatthias Ringwalddef encryption_key_size_from_flags(flags):
363d7ec1d24SMatthias Ringwald    encryption_key_size = (flags & 0xf000) >> 12
364d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
365d7ec1d24SMatthias Ringwald        encryption_key_size += 1
366d7ec1d24SMatthias Ringwald    return encryption_key_size
367d7ec1d24SMatthias Ringwald
368b3fcedb9SMatthias Ringwalddef is_string(text):
369b3fcedb9SMatthias Ringwald    for item in text.split(" "):
370b3fcedb9SMatthias Ringwald        if not all(c in string.hexdigits for c in item):
371b3fcedb9SMatthias Ringwald            return True
372b3fcedb9SMatthias Ringwald    return False
373b3fcedb9SMatthias Ringwald
374b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties):
375b3fcedb9SMatthias Ringwald    return properties & (property_flags['NOTIFY'] | property_flags['INDICATE'])
376b3fcedb9SMatthias Ringwald
377729074c4SMatthias Ringwalddef serviceDefinitionComplete(fout):
378729074c4SMatthias Ringwald    global services
379729074c4SMatthias Ringwald    if current_service_uuid_string:
380e5ce8e0eSMilanka Ringwald        # fout.write("\n")
38129ba805bSMatthias Ringwald        # update num instances for this service
38229ba805bSMatthias Ringwald        count = 1
38329ba805bSMatthias Ringwald        if current_service_uuid_string in service_counter:
38429ba805bSMatthias Ringwald            count = service_counter[current_service_uuid_string] + 1
38529ba805bSMatthias Ringwald        service_counter[current_service_uuid_string] = count
38609e90ba6SMilanka Ringwald        # add old defines without service counter for first instance for backward compatibility
38729ba805bSMatthias Ringwald        if count == 1:
388729074c4SMatthias Ringwald            defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle))
389729074c4SMatthias Ringwald            defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1))
39009e90ba6SMilanka Ringwald
39129ba805bSMatthias Ringwald        # unified defines indicating instance
39229ba805bSMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_%02x_START_HANDLE 0x%04x' % (current_service_uuid_string, count, current_service_start_handle))
39329ba805bSMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_%02x_END_HANDLE 0x%04x' % (current_service_uuid_string, count, handle-1))
39409e90ba6SMilanka Ringwald        services[current_service_uuid_string+"_" + str(count)] = [current_service_start_handle, handle - 1, count]
395729074c4SMatthias Ringwald
396d7ec1d24SMatthias Ringwalddef dump_flags(fout, flags):
397d7ec1d24SMatthias Ringwald    global security_permsission
398d7ec1d24SMatthias Ringwald    encryption_key_size = encryption_key_size_from_flags(flags)
399d7ec1d24SMatthias Ringwald    read_permissions    = security_permsission[read_permissions_from_flags(flags)]
400d7ec1d24SMatthias Ringwald    write_permissions   = security_permsission[write_permissions_from_flags(flags)]
401d7ec1d24SMatthias Ringwald    write_indent(fout)
402d7ec1d24SMatthias Ringwald    fout.write('// ')
403d7ec1d24SMatthias Ringwald    first = 1
404d7ec1d24SMatthias Ringwald    if flags & property_flags['READ']:
405d7ec1d24SMatthias Ringwald        fout.write('READ_%s' % read_permissions)
406d7ec1d24SMatthias Ringwald        first = 0
407d7ec1d24SMatthias Ringwald    if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']):
408d7ec1d24SMatthias Ringwald        if not first:
409d7ec1d24SMatthias Ringwald            fout.write(', ')
410d7ec1d24SMatthias Ringwald        first = 0
411d7ec1d24SMatthias Ringwald        fout.write('WRITE_%s' % write_permissions)
412d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
413d7ec1d24SMatthias Ringwald        if not first:
414d7ec1d24SMatthias Ringwald            fout.write(', ')
415d7ec1d24SMatthias Ringwald        first = 0
416d7ec1d24SMatthias Ringwald        fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size)
417d7ec1d24SMatthias Ringwald    fout.write('\n')
418d7ec1d24SMatthias Ringwald
419043f8832SMatthias Ringwalddef database_hash_append_uint8(value):
420043f8832SMatthias Ringwald    global database_hash_message
421043f8832SMatthias Ringwald    database_hash_message.append(value)
422043f8832SMatthias Ringwald
423043f8832SMatthias Ringwalddef database_hash_append_uint16(value):
424043f8832SMatthias Ringwald    global database_hash_message
425043f8832SMatthias Ringwald    database_hash_append_uint8(value & 0xff)
426043f8832SMatthias Ringwald    database_hash_append_uint8((value >> 8) & 0xff)
427043f8832SMatthias Ringwald
428043f8832SMatthias Ringwalddef database_hash_append_value(value):
429043f8832SMatthias Ringwald    global database_hash_message
430043f8832SMatthias Ringwald    for byte in value:
431043f8832SMatthias Ringwald        database_hash_append_uint8(byte)
432043f8832SMatthias Ringwald
433b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type):
434b3fcedb9SMatthias Ringwald    global handle
435b3fcedb9SMatthias Ringwald    global total_size
436b3fcedb9SMatthias Ringwald    global current_service_uuid_string
437b3fcedb9SMatthias Ringwald    global current_service_start_handle
438b3fcedb9SMatthias Ringwald
439729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
440b3fcedb9SMatthias Ringwald
441d7ec1d24SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
442b3fcedb9SMatthias Ringwald
443b3fcedb9SMatthias Ringwald    write_indent(fout)
444b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
445b3fcedb9SMatthias Ringwald
446b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
447b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
448b3fcedb9SMatthias Ringwald
449b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size + 2
450b3fcedb9SMatthias Ringwald
451b3fcedb9SMatthias Ringwald    if service_type == 0x2802:
452b3fcedb9SMatthias Ringwald        size += 4
453b3fcedb9SMatthias Ringwald
454b3fcedb9SMatthias Ringwald    write_indent(fout)
455b3fcedb9SMatthias Ringwald    write_16(fout, size)
456d7ec1d24SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
457b3fcedb9SMatthias Ringwald    write_16(fout, handle)
458b3fcedb9SMatthias Ringwald    write_16(fout, service_type)
459285653b2SMatthias Ringwald    write_uuid(fout, uuid)
460b3fcedb9SMatthias Ringwald    fout.write("\n")
461b3fcedb9SMatthias Ringwald
462043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
463043f8832SMatthias Ringwald    database_hash_append_uint16(service_type)
464043f8832SMatthias Ringwald    database_hash_append_value(uuid)
465043f8832SMatthias Ringwald
466729074c4SMatthias Ringwald    current_service_uuid_string = c_string_for_uuid(parts[1])
467b3fcedb9SMatthias Ringwald    current_service_start_handle = handle
468b3fcedb9SMatthias Ringwald    handle = handle + 1
469b3fcedb9SMatthias Ringwald    total_size = total_size + size
470b3fcedb9SMatthias Ringwald
471b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts):
472b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2800)
473b3fcedb9SMatthias Ringwald
474b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts):
475b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2801)
476b3fcedb9SMatthias Ringwald
477b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts):
478b3fcedb9SMatthias Ringwald    global handle
479b3fcedb9SMatthias Ringwald    global total_size
480b3fcedb9SMatthias Ringwald
481e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
482b3fcedb9SMatthias Ringwald
483b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
484b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
485b3fcedb9SMatthias Ringwald    if uuid_size > 2:
486b3fcedb9SMatthias Ringwald        uuid_size = 0
487b3fcedb9SMatthias Ringwald
488b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 4 + uuid_size
489b3fcedb9SMatthias Ringwald
490729074c4SMatthias Ringwald    keyUUID = c_string_for_uuid(parts[1])
4916a72ce79SMilanka Ringwald    keys_to_delete = []
4926a72ce79SMilanka Ringwald
49309e90ba6SMilanka Ringwald    for (serviceUUID, service) in services.items():
49409e90ba6SMilanka Ringwald        if serviceUUID.startswith(keyUUID):
49509e90ba6SMilanka Ringwald            write_indent(fout)
49609e90ba6SMilanka Ringwald            fout.write('// 0x%04x %s - range [0x%04x, 0x%04x]\n' % (handle, '-'.join(parts), services[serviceUUID][0], services[serviceUUID][1]))
497b3fcedb9SMatthias Ringwald
498b3fcedb9SMatthias Ringwald            write_indent(fout)
499b3fcedb9SMatthias Ringwald            write_16(fout, size)
500e22a2612SMatthias Ringwald            write_16(fout, read_only_anybody_flags)
501b3fcedb9SMatthias Ringwald            write_16(fout, handle)
502b3fcedb9SMatthias Ringwald            write_16(fout, 0x2802)
50309e90ba6SMilanka Ringwald            write_16(fout, services[serviceUUID][0])
50409e90ba6SMilanka Ringwald            write_16(fout, services[serviceUUID][1])
505b3fcedb9SMatthias Ringwald            if uuid_size > 0:
506285653b2SMatthias Ringwald                write_uuid(fout, uuid)
507b3fcedb9SMatthias Ringwald            fout.write("\n")
508b3fcedb9SMatthias Ringwald
509043f8832SMatthias Ringwald            database_hash_append_uint16(handle)
510043f8832SMatthias Ringwald            database_hash_append_uint16(0x2802)
51109e90ba6SMilanka Ringwald            database_hash_append_uint16(services[serviceUUID][0])
51209e90ba6SMilanka Ringwald            database_hash_append_uint16(services[serviceUUID][1])
513043f8832SMatthias Ringwald            if uuid_size > 0:
514043f8832SMatthias Ringwald                database_hash_append_value(uuid)
515043f8832SMatthias Ringwald
5166a72ce79SMilanka Ringwald            keys_to_delete.append(serviceUUID)
5176a72ce79SMilanka Ringwald
518b3fcedb9SMatthias Ringwald            handle = handle + 1
519b3fcedb9SMatthias Ringwald            total_size = total_size + size
520b3fcedb9SMatthias Ringwald
5216a72ce79SMilanka Ringwald    for key in keys_to_delete:
5226a72ce79SMilanka Ringwald        services.pop(key)
5236a72ce79SMilanka Ringwald
5246a72ce79SMilanka Ringwald
525b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts):
526b3fcedb9SMatthias Ringwald    global handle
527b3fcedb9SMatthias Ringwald    global total_size
528b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
529b3fcedb9SMatthias Ringwald    global characteristic_indices
530b3fcedb9SMatthias Ringwald
531e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
532b3fcedb9SMatthias Ringwald
533b3fcedb9SMatthias Ringwald    # enumerate characteristics with same UUID, using optional name tag if available
534b3fcedb9SMatthias Ringwald    current_characteristic_uuid_string = c_string_for_uuid(parts[1]);
535b3fcedb9SMatthias Ringwald    index = 1
536b3fcedb9SMatthias Ringwald    if current_characteristic_uuid_string in characteristic_indices:
537b3fcedb9SMatthias Ringwald        index = characteristic_indices[current_characteristic_uuid_string] + 1
538b3fcedb9SMatthias Ringwald    characteristic_indices[current_characteristic_uuid_string] = index
539b3fcedb9SMatthias Ringwald    if len(parts) > 4:
540b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_')
541b3fcedb9SMatthias Ringwald    else:
542b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += ('_%02x' % index)
543b3fcedb9SMatthias Ringwald
544b3fcedb9SMatthias Ringwald    uuid       = parseUUID(parts[1])
545b3fcedb9SMatthias Ringwald    uuid_size  = len(uuid)
546b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[2])
547b3fcedb9SMatthias Ringwald    value = ', '.join([str(x) for x in parts[3:]])
548b3fcedb9SMatthias Ringwald
549b3fcedb9SMatthias Ringwald    # reliable writes is defined in an extended properties
550b3fcedb9SMatthias Ringwald    if (properties & property_flags['RELIABLE_WRITE']):
551b3fcedb9SMatthias Ringwald        properties = properties | property_flags['EXTENDED_PROPERTIES']
552b3fcedb9SMatthias Ringwald
553b3fcedb9SMatthias Ringwald    write_indent(fout)
554e5ce8e0eSMilanka Ringwald    fout.write('// 0x%04x %s - %s\n' % (handle, '-'.join(parts[0:2]), prettyPrintProperties(parts[2])))
555b3fcedb9SMatthias Ringwald
556e22a2612SMatthias Ringwald
557e22a2612SMatthias Ringwald    characteristic_properties = gatt_characteristic_properties(properties)
558b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (1+2+uuid_size)
559b3fcedb9SMatthias Ringwald    write_indent(fout)
560b3fcedb9SMatthias Ringwald    write_16(fout, size)
561e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
562b3fcedb9SMatthias Ringwald    write_16(fout, handle)
563b3fcedb9SMatthias Ringwald    write_16(fout, 0x2803)
564e22a2612SMatthias Ringwald    write_8(fout, characteristic_properties)
565b3fcedb9SMatthias Ringwald    write_16(fout, handle+1)
566285653b2SMatthias Ringwald    write_uuid(fout, uuid)
567b3fcedb9SMatthias Ringwald    fout.write("\n")
568b3fcedb9SMatthias Ringwald    total_size = total_size + size
569b3fcedb9SMatthias Ringwald
570043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
571043f8832SMatthias Ringwald    database_hash_append_uint16(0x2803)
572043f8832SMatthias Ringwald    database_hash_append_uint8(characteristic_properties)
573043f8832SMatthias Ringwald    database_hash_append_uint16(handle+1)
574043f8832SMatthias Ringwald    database_hash_append_value(uuid)
575043f8832SMatthias Ringwald
5763c5d4072SMatthias Ringwald    handle = handle + 1
5773c5d4072SMatthias Ringwald
578043f8832SMatthias Ringwald    uuid_is_database_hash = len(uuid) == 2 and uuid[0] == 0x2a and uuid[1] == 0x2b
579043f8832SMatthias Ringwald
580b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size
581043f8832SMatthias Ringwald    if uuid_is_database_hash:
582043f8832SMatthias Ringwald        size +=  16
583043f8832SMatthias Ringwald    else:
584b3fcedb9SMatthias Ringwald        if is_string(value):
585b3fcedb9SMatthias Ringwald            size = size + len(value)
586b3fcedb9SMatthias Ringwald        else:
587b3fcedb9SMatthias Ringwald            size = size + len(value.split())
588b3fcedb9SMatthias Ringwald
589e22a2612SMatthias Ringwald    value_flags = att_flags(properties)
5908ea3236cSMatthias Ringwald
5918ea3236cSMatthias Ringwald    # add UUID128 flag for value handle
592b3fcedb9SMatthias Ringwald    if uuid_size == 16:
593e22a2612SMatthias Ringwald        value_flags = value_flags | property_flags['LONG_UUID'];
594b3fcedb9SMatthias Ringwald
595b3fcedb9SMatthias Ringwald    write_indent(fout)
596e5ce8e0eSMilanka Ringwald    properties_string = prettyPrintProperties(parts[2])
597e5ce8e0eSMilanka Ringwald    if "DYNAMIC" in properties_string:
598e5ce8e0eSMilanka Ringwald        fout.write('// 0x%04x VALUE %s - %s\n' % (handle, '-'.join(parts[0:2]), prettyPrintProperties(parts[2])))
599e5ce8e0eSMilanka Ringwald    else:
600e5ce8e0eSMilanka Ringwald        fout.write('// 0x%04x VALUE %s - %s -'"'%s'"'\n' % (
601e5ce8e0eSMilanka Ringwald        handle, '-'.join(parts[0:2]), prettyPrintProperties(parts[2]), value))
602d7ec1d24SMatthias Ringwald
603d7ec1d24SMatthias Ringwald    dump_flags(fout, value_flags)
604d7ec1d24SMatthias Ringwald
605b3fcedb9SMatthias Ringwald    write_indent(fout)
606b3fcedb9SMatthias Ringwald    write_16(fout, size)
607e22a2612SMatthias Ringwald    write_16(fout, value_flags)
608b3fcedb9SMatthias Ringwald    write_16(fout, handle)
609285653b2SMatthias Ringwald    write_uuid(fout, uuid)
610043f8832SMatthias Ringwald    if uuid_is_database_hash:
611043f8832SMatthias Ringwald        write_database_hash(fout)
612043f8832SMatthias Ringwald    else:
613b3fcedb9SMatthias Ringwald        if is_string(value):
614b3fcedb9SMatthias Ringwald            write_string(fout, value)
615b3fcedb9SMatthias Ringwald        else:
616b3fcedb9SMatthias Ringwald            write_sequence(fout,value)
617b3fcedb9SMatthias Ringwald
618b3fcedb9SMatthias Ringwald    fout.write("\n")
619729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
620b3fcedb9SMatthias Ringwald    handle = handle + 1
621b3fcedb9SMatthias Ringwald
622b3fcedb9SMatthias Ringwald    if add_client_characteristic_configuration(properties):
623e22a2612SMatthias Ringwald        # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC
624d7ec1d24SMatthias Ringwald        flags  = write_permissions_and_key_size_flags_from_properties(properties)
625e22a2612SMatthias Ringwald        flags |= property_flags['READ']
626e22a2612SMatthias Ringwald        flags |= property_flags['WRITE']
6279be4aecfSMatthias Ringwald        flags |= property_flags['WRITE_WITHOUT_RESPONSE']
628e22a2612SMatthias Ringwald        flags |= property_flags['DYNAMIC']
629b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
630d7ec1d24SMatthias Ringwald
631b3fcedb9SMatthias Ringwald        write_indent(fout)
632b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle))
633d7ec1d24SMatthias Ringwald
634d7ec1d24SMatthias Ringwald        dump_flags(fout, flags)
635d7ec1d24SMatthias Ringwald
636b3fcedb9SMatthias Ringwald        write_indent(fout)
637b3fcedb9SMatthias Ringwald        write_16(fout, size)
638e22a2612SMatthias Ringwald        write_16(fout, flags)
639b3fcedb9SMatthias Ringwald        write_16(fout, handle)
640b3fcedb9SMatthias Ringwald        write_16(fout, 0x2902)
641b3fcedb9SMatthias Ringwald        write_16(fout, 0)
642b3fcedb9SMatthias Ringwald        fout.write("\n")
643043f8832SMatthias Ringwald
644043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
645043f8832SMatthias Ringwald        database_hash_append_uint16(0x2902)
646043f8832SMatthias Ringwald
647729074c4SMatthias Ringwald        defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
648b3fcedb9SMatthias Ringwald        handle = handle + 1
649b3fcedb9SMatthias Ringwald
650043f8832SMatthias Ringwald
651b3fcedb9SMatthias Ringwald    if properties & property_flags['RELIABLE_WRITE']:
652b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
653b3fcedb9SMatthias Ringwald        write_indent(fout)
654b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle))
655b3fcedb9SMatthias Ringwald        write_indent(fout)
656b3fcedb9SMatthias Ringwald        write_16(fout, size)
657e22a2612SMatthias Ringwald        write_16(fout, read_only_anybody_flags)
658b3fcedb9SMatthias Ringwald        write_16(fout, handle)
659b3fcedb9SMatthias Ringwald        write_16(fout, 0x2900)
660b3fcedb9SMatthias Ringwald        write_16(fout, 1)   # Reliable Write
661b3fcedb9SMatthias Ringwald        fout.write("\n")
662043f8832SMatthias Ringwald
663043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
664043f8832SMatthias Ringwald        database_hash_append_uint16(0x2900)
665043f8832SMatthias Ringwald        database_hash_append_uint16(1)
666043f8832SMatthias Ringwald
667b3fcedb9SMatthias Ringwald        handle = handle + 1
668b3fcedb9SMatthias Ringwald
66917215335SMatthias Ringwalddef parseGenericDynamicDescriptor(fout, parts, uuid, name):
670b3fcedb9SMatthias Ringwald    global handle
671b3fcedb9SMatthias Ringwald    global total_size
672b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
673b3fcedb9SMatthias Ringwald
674b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
675b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
676b3fcedb9SMatthias Ringwald
677e22a2612SMatthias Ringwald    # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY
678d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
679e22a2612SMatthias Ringwald    flags |= property_flags['READ']
680e22a2612SMatthias Ringwald    flags |= property_flags['WRITE']
681e22a2612SMatthias Ringwald    flags |= property_flags['DYNAMIC']
682e22a2612SMatthias Ringwald
683b3fcedb9SMatthias Ringwald    write_indent(fout)
68417215335SMatthias Ringwald    fout.write('// 0x%04x %s-%s\n' % (handle, name, '-'.join(parts[1:])))
685d7ec1d24SMatthias Ringwald
686d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
687d7ec1d24SMatthias Ringwald
688b3fcedb9SMatthias Ringwald    write_indent(fout)
689b3fcedb9SMatthias Ringwald    write_16(fout, size)
690e22a2612SMatthias Ringwald    write_16(fout, flags)
691b3fcedb9SMatthias Ringwald    write_16(fout, handle)
692766e9e27SMatthias Ringwald    write_16(fout, uuid)
693b3fcedb9SMatthias Ringwald    fout.write("\n")
694043f8832SMatthias Ringwald
695043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
69617215335SMatthias Ringwald    database_hash_append_uint16(uuid)
697043f8832SMatthias Ringwald
69817215335SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_%s_HANDLE 0x%04x' % (current_characteristic_uuid_string, name, handle))
699b3fcedb9SMatthias Ringwald    handle = handle + 1
700b3fcedb9SMatthias Ringwald
70117215335SMatthias Ringwalddef parseGenericDynamicReadOnlyDescriptor(fout, parts, uuid, name):
70217215335SMatthias Ringwald    global handle
70317215335SMatthias Ringwald    global total_size
70417215335SMatthias Ringwald    global current_characteristic_uuid_string
70517215335SMatthias Ringwald
70617215335SMatthias Ringwald    properties = parseProperties(parts[1])
70717215335SMatthias Ringwald    size = 2 + 2 + 2 + 2
70817215335SMatthias Ringwald
70917215335SMatthias Ringwald    # use write permissions and encryption key size from attribute value and set READ, DYNAMIC, READ_ANYBODY
71017215335SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
71117215335SMatthias Ringwald    flags |= property_flags['READ']
71217215335SMatthias Ringwald    flags |= property_flags['DYNAMIC']
71317215335SMatthias Ringwald
71417215335SMatthias Ringwald    write_indent(fout)
71517215335SMatthias Ringwald    fout.write('// 0x%04x %s-%s\n' % (handle, name, '-'.join(parts[1:])))
71617215335SMatthias Ringwald
71717215335SMatthias Ringwald    dump_flags(fout, flags)
71817215335SMatthias Ringwald
71917215335SMatthias Ringwald    write_indent(fout)
72017215335SMatthias Ringwald    write_16(fout, size)
72117215335SMatthias Ringwald    write_16(fout, flags)
72217215335SMatthias Ringwald    write_16(fout, handle)
723079990e8SSanaa Hamel    write_16(fout, uuid)
72417215335SMatthias Ringwald    fout.write("\n")
72517215335SMatthias Ringwald
72617215335SMatthias Ringwald    database_hash_append_uint16(handle)
72717215335SMatthias Ringwald    database_hash_append_uint16(uuid)
72817215335SMatthias Ringwald
72917215335SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_%s_HANDLE 0x%04x' % (current_characteristic_uuid_string, name, handle))
73017215335SMatthias Ringwald    handle = handle + 1
73117215335SMatthias Ringwald
73217215335SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts):
73317215335SMatthias Ringwald    parseGenericDynamicDescriptor(fout, parts, 0x2903, 'SERVER_CONFIGURATION')
73417215335SMatthias Ringwald
735b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts):
736b3fcedb9SMatthias Ringwald    global handle
737b3fcedb9SMatthias Ringwald    global total_size
738b3fcedb9SMatthias Ringwald
739e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
740b3fcedb9SMatthias Ringwald
741b3fcedb9SMatthias Ringwald    identifier = parts[1]
742b3fcedb9SMatthias Ringwald    presentation_formats[identifier] = handle
743b3fcedb9SMatthias Ringwald    # print("format '%s' with handle %d\n" % (identifier, handle))
744b3fcedb9SMatthias Ringwald
745b3fcedb9SMatthias Ringwald    format     = parts[2]
746b3fcedb9SMatthias Ringwald    exponent   = parts[3]
747b3fcedb9SMatthias Ringwald    unit       = parseUUID(parts[4])
748b3fcedb9SMatthias Ringwald    name_space = parts[5]
749b3fcedb9SMatthias Ringwald    description = parseUUID(parts[6])
750b3fcedb9SMatthias Ringwald
751b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 7
752b3fcedb9SMatthias Ringwald
753b3fcedb9SMatthias Ringwald    write_indent(fout)
754b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
755b3fcedb9SMatthias Ringwald    write_indent(fout)
756b3fcedb9SMatthias Ringwald    write_16(fout, size)
757e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
758b3fcedb9SMatthias Ringwald    write_16(fout, handle)
759b3fcedb9SMatthias Ringwald    write_16(fout, 0x2904)
760b3fcedb9SMatthias Ringwald    write_sequence(fout, format)
761b3fcedb9SMatthias Ringwald    write_sequence(fout, exponent)
762285653b2SMatthias Ringwald    write_uuid(fout, unit)
763b3fcedb9SMatthias Ringwald    write_sequence(fout, name_space)
764285653b2SMatthias Ringwald    write_uuid(fout, description)
765b3fcedb9SMatthias Ringwald    fout.write("\n")
766043f8832SMatthias Ringwald
767043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
768043f8832SMatthias Ringwald    database_hash_append_uint16(0x2904)
769043f8832SMatthias Ringwald
770b3fcedb9SMatthias Ringwald    handle = handle + 1
771b3fcedb9SMatthias Ringwald
772b3fcedb9SMatthias Ringwald
773b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts):
774b3fcedb9SMatthias Ringwald    global handle
775b3fcedb9SMatthias Ringwald    global total_size
776b3fcedb9SMatthias Ringwald
777e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
778b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2
779b3fcedb9SMatthias Ringwald
780b3fcedb9SMatthias Ringwald    write_indent(fout)
781b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
782b3fcedb9SMatthias Ringwald    write_indent(fout)
783b3fcedb9SMatthias Ringwald    write_16(fout, size)
784e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
785b3fcedb9SMatthias Ringwald    write_16(fout, handle)
786b3fcedb9SMatthias Ringwald    write_16(fout, 0x2905)
787b3fcedb9SMatthias Ringwald    for identifier in parts[1:]:
7888c70002dSMatthias Ringwald        if not identifier in presentation_formats:
7898c70002dSMatthias Ringwald            print(parts)
790b3fcedb9SMatthias Ringwald            print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier)
791b3fcedb9SMatthias Ringwald            sys.exit(1)
7928c70002dSMatthias Ringwald        format_handle = presentation_formats[identifier]
793b3fcedb9SMatthias Ringwald        write_16(fout, format_handle)
794b3fcedb9SMatthias Ringwald    fout.write("\n")
795043f8832SMatthias Ringwald
796043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
797043f8832SMatthias Ringwald    database_hash_append_uint16(0x2905)
798043f8832SMatthias Ringwald
799b3fcedb9SMatthias Ringwald    handle = handle + 1
800b3fcedb9SMatthias Ringwald
80105146de0SMilanka Ringwalddef parseExternalReportReference(fout, parts):
80205146de0SMilanka Ringwald    global handle
80305146de0SMilanka Ringwald    global total_size
80405146de0SMilanka Ringwald
80505146de0SMilanka Ringwald    read_only_anybody_flags = property_flags['READ'];
80605146de0SMilanka Ringwald    size = 2 + 2 + 2 + 2 + 2
80705146de0SMilanka Ringwald
80805146de0SMilanka Ringwald    report_uuid = int(parts[2], 16)
80905146de0SMilanka Ringwald
81005146de0SMilanka Ringwald    write_indent(fout)
81105146de0SMilanka Ringwald    fout.write('// 0x%04x EXTERNAL_REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
81205146de0SMilanka Ringwald    write_indent(fout)
81305146de0SMilanka Ringwald    write_16(fout, size)
81405146de0SMilanka Ringwald    write_16(fout, read_only_anybody_flags)
81505146de0SMilanka Ringwald    write_16(fout, handle)
81605146de0SMilanka Ringwald    write_16(fout, 0x2907)
81705146de0SMilanka Ringwald    write_16(fout, report_uuid)
81805146de0SMilanka Ringwald    fout.write("\n")
81905146de0SMilanka Ringwald    handle = handle + 1
82005146de0SMilanka Ringwald
821b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts):
822b3fcedb9SMatthias Ringwald    global handle
823b3fcedb9SMatthias Ringwald    global total_size
824b3fcedb9SMatthias Ringwald
825e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
826b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1 + 1
827b3fcedb9SMatthias Ringwald
828231a3c5dSMatthias Ringwald    report_id = parts[2]
829231a3c5dSMatthias Ringwald    report_type = parts[3]
830b3fcedb9SMatthias Ringwald
831b3fcedb9SMatthias Ringwald    write_indent(fout)
832b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
833b3fcedb9SMatthias Ringwald    write_indent(fout)
834b3fcedb9SMatthias Ringwald    write_16(fout, size)
835e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
836b3fcedb9SMatthias Ringwald    write_16(fout, handle)
837b3fcedb9SMatthias Ringwald    write_16(fout, 0x2908)
838b3fcedb9SMatthias Ringwald    write_sequence(fout, report_id)
839b3fcedb9SMatthias Ringwald    write_sequence(fout, report_type)
840b3fcedb9SMatthias Ringwald    fout.write("\n")
841b3fcedb9SMatthias Ringwald    handle = handle + 1
842b3fcedb9SMatthias Ringwald
843b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts):
844b3fcedb9SMatthias Ringwald    global handle
845b3fcedb9SMatthias Ringwald    global total_size
846b3fcedb9SMatthias Ringwald
847e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
848b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1
849b3fcedb9SMatthias Ringwald
850b3fcedb9SMatthias Ringwald    no_of_digitals = parts[1]
851b3fcedb9SMatthias Ringwald
852b3fcedb9SMatthias Ringwald    write_indent(fout)
853b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:])))
854b3fcedb9SMatthias Ringwald    write_indent(fout)
855b3fcedb9SMatthias Ringwald    write_16(fout, size)
856e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
857b3fcedb9SMatthias Ringwald    write_16(fout, handle)
858b3fcedb9SMatthias Ringwald    write_16(fout, 0x2909)
859b3fcedb9SMatthias Ringwald    write_sequence(fout, no_of_digitals)
860b3fcedb9SMatthias Ringwald    fout.write("\n")
861b3fcedb9SMatthias Ringwald    handle = handle + 1
862b3fcedb9SMatthias Ringwald
86360b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout):
864b3fcedb9SMatthias Ringwald    global handle
865b3fcedb9SMatthias Ringwald    global total_size
866b3fcedb9SMatthias Ringwald
867b165f97bSMatthias Ringwald    line_count = 0;
868b3fcedb9SMatthias Ringwald    for line in fin:
869b3fcedb9SMatthias Ringwald        line = line.strip("\n\r ")
870b165f97bSMatthias Ringwald        line_count += 1
871b3fcedb9SMatthias Ringwald
872b165f97bSMatthias Ringwald        if line.startswith("//"):
873b165f97bSMatthias Ringwald            fout.write("    //" + line.lstrip('/') + '\n')
874b165f97bSMatthias Ringwald            continue
875b165f97bSMatthias Ringwald
87660b51a4cSMatthias Ringwald        if line.startswith("#import"):
87760b51a4cSMatthias Ringwald            imported_file = ''
878*bae15238SMatthias Ringwald            parts = re.match('#import\\s+<(.*)>\\w*',line)
87960b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
880dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
881*bae15238SMatthias Ringwald            parts = re.match('#import\\s+"(.*)"\\w*',line)
88260b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
883dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
88460b51a4cSMatthias Ringwald            if len(imported_file) == 0:
88560b51a4cSMatthias Ringwald                print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count))
88660b51a4cSMatthias Ringwald                continue
88760b51a4cSMatthias Ringwald
888dbb3997aSMilanka Ringwald            imported_file = getFile( imported_file )
88960b51a4cSMatthias Ringwald            print("Importing %s" % imported_file)
89060b51a4cSMatthias Ringwald            try:
89160b51a4cSMatthias Ringwald                imported_fin = codecs.open (imported_file, encoding='utf-8')
892e5ce8e0eSMilanka Ringwald                fout.write('\n\n    // ' + line + ' -- BEGIN\n')
89360b51a4cSMatthias Ringwald                parseLines(imported_file, imported_fin, fout)
89460b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- END\n')
89560b51a4cSMatthias Ringwald            except IOError as e:
89660b51a4cSMatthias Ringwald                print('ERROR: Import failed. Please check path.')
89760b51a4cSMatthias Ringwald
89860b51a4cSMatthias Ringwald            continue
89960b51a4cSMatthias Ringwald
90060b51a4cSMatthias Ringwald        if line.startswith("#TODO"):
90160b51a4cSMatthias Ringwald            print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count))
902b165f97bSMatthias Ringwald            print ("'%s'" % line)
903b165f97bSMatthias Ringwald            fout.write("// " + line + '\n')
904b3fcedb9SMatthias Ringwald            continue
905b3fcedb9SMatthias Ringwald
906b3fcedb9SMatthias Ringwald        if len(line) == 0:
907b3fcedb9SMatthias Ringwald            continue
908b3fcedb9SMatthias Ringwald
909b3fcedb9SMatthias Ringwald        f = io.StringIO(line)
910b3fcedb9SMatthias Ringwald        parts_list = csv.reader(f, delimiter=',', quotechar='"')
911b3fcedb9SMatthias Ringwald
912b3fcedb9SMatthias Ringwald        for parts in parts_list:
913b3fcedb9SMatthias Ringwald            for index, object in enumerate(parts):
914b3fcedb9SMatthias Ringwald                parts[index] = object.strip().lstrip('"').rstrip('"')
915b3fcedb9SMatthias Ringwald
916b3fcedb9SMatthias Ringwald            if parts[0] == 'PRIMARY_SERVICE':
917b3fcedb9SMatthias Ringwald                parsePrimaryService(fout, parts)
918b3fcedb9SMatthias Ringwald                continue
919b3fcedb9SMatthias Ringwald
920b3fcedb9SMatthias Ringwald            if parts[0] == 'SECONDARY_SERVICE':
921b3fcedb9SMatthias Ringwald                parseSecondaryService(fout, parts)
922b3fcedb9SMatthias Ringwald                continue
923b3fcedb9SMatthias Ringwald
924b3fcedb9SMatthias Ringwald            if parts[0] == 'INCLUDE_SERVICE':
925b3fcedb9SMatthias Ringwald                parseIncludeService(fout, parts)
926b3fcedb9SMatthias Ringwald                continue
927b3fcedb9SMatthias Ringwald
928b3fcedb9SMatthias Ringwald            # 2803
929b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC':
930b3fcedb9SMatthias Ringwald                parseCharacteristic(fout, parts)
931b3fcedb9SMatthias Ringwald                continue
932b3fcedb9SMatthias Ringwald
933b3fcedb9SMatthias Ringwald            # 2900 Characteristic Extended Properties
934b3fcedb9SMatthias Ringwald
935b3fcedb9SMatthias Ringwald            # 2901
936b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
937766e9e27SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x2901, 'USER_DESCRIPTION')
938b3fcedb9SMatthias Ringwald                continue
939b3fcedb9SMatthias Ringwald
940b165f97bSMatthias Ringwald
941b165f97bSMatthias Ringwald            # 2902 Client Characteristic Configuration - automatically included in Characteristic if
942b3fcedb9SMatthias Ringwald            # notification / indication is supported
943231a3c5dSMatthias Ringwald            if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION':
944b165f97bSMatthias Ringwald                continue
945b3fcedb9SMatthias Ringwald
946b3fcedb9SMatthias Ringwald            # 2903
947b3fcedb9SMatthias Ringwald            if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
94817215335SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x2903, 'SERVER_CONFIGURATION')
949b3fcedb9SMatthias Ringwald                continue
950b3fcedb9SMatthias Ringwald
951b3fcedb9SMatthias Ringwald            # 2904
952b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_FORMAT':
953b3fcedb9SMatthias Ringwald                parseCharacteristicFormat(fout, parts)
954b3fcedb9SMatthias Ringwald                continue
955b3fcedb9SMatthias Ringwald
956b3fcedb9SMatthias Ringwald            # 2905
957b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
958b3fcedb9SMatthias Ringwald                parseCharacteristicAggregateFormat(fout, parts)
959b3fcedb9SMatthias Ringwald                continue
960b3fcedb9SMatthias Ringwald
961b3fcedb9SMatthias Ringwald            # 2906
962b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
9636f08f159SMatthias Ringwald                parseGenericDynamicReadOnlyDescriptor(fout, parts, 0x2906, 'VALID_RANGE')
964b3fcedb9SMatthias Ringwald                continue
965b3fcedb9SMatthias Ringwald
966b3fcedb9SMatthias Ringwald            # 2907
967b3fcedb9SMatthias Ringwald            if parts[0] == 'EXTERNAL_REPORT_REFERENCE':
96805146de0SMilanka Ringwald                parseExternalReportReference(fout, parts)
969b3fcedb9SMatthias Ringwald                continue
970b3fcedb9SMatthias Ringwald
971b3fcedb9SMatthias Ringwald            # 2908
972b3fcedb9SMatthias Ringwald            if parts[0] == 'REPORT_REFERENCE':
973b3fcedb9SMatthias Ringwald                parseReportReference(fout, parts)
974b3fcedb9SMatthias Ringwald                continue
975b3fcedb9SMatthias Ringwald
976b3fcedb9SMatthias Ringwald            # 2909
977b3fcedb9SMatthias Ringwald            if parts[0] == 'NUMBER_OF_DIGITALS':
978b3fcedb9SMatthias Ringwald                parseNumberOfDigitals(fout, parts)
979b3fcedb9SMatthias Ringwald                continue
980b3fcedb9SMatthias Ringwald
981b3fcedb9SMatthias Ringwald            # 290A
982b3fcedb9SMatthias Ringwald            if parts[0] == 'VALUE_TRIGGER_SETTING':
9836f08f159SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x290A, 'VALUE_TRIGGER_SETTING')
984b3fcedb9SMatthias Ringwald                continue
985b3fcedb9SMatthias Ringwald
986b3fcedb9SMatthias Ringwald            # 290B
987b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION':
9886f08f159SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x290B, 'ENVIRONMENTAL_SENSING_CONFIGURATION')
989b3fcedb9SMatthias Ringwald                continue
990b3fcedb9SMatthias Ringwald
991b3fcedb9SMatthias Ringwald            # 290C
992b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT':
9936f08f159SMatthias Ringwald                parseGenericDynamicReadOnlyDescriptor(fout, parts, 0x290C, 'ENVIRONMENTAL_SENSING_MEASUREMENT')
994b3fcedb9SMatthias Ringwald                continue
995b3fcedb9SMatthias Ringwald
996b3fcedb9SMatthias Ringwald            # 290D
997b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING':
9986f08f159SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x290D, 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING')
999b3fcedb9SMatthias Ringwald                continue
1000b3fcedb9SMatthias Ringwald
1001b3fcedb9SMatthias Ringwald            print("WARNING: unknown token: %s\n" % (parts[0]))
1002b3fcedb9SMatthias Ringwald
10037050bf34SMatthias Ringwalddef parse(fname_in, fin, fname_out, tool_path, fout):
100460b51a4cSMatthias Ringwald    global handle
100560b51a4cSMatthias Ringwald    global total_size
100660b51a4cSMatthias Ringwald
10077050bf34SMatthias Ringwald    fout.write(header.format(fname_out, fname_in, tool_path))
100860b51a4cSMatthias Ringwald    fout.write('{\n')
1009fd1be25dSMatthias Ringwald    write_indent(fout)
1010fd1be25dSMatthias Ringwald    fout.write('// ATT DB Version\n')
1011fd1be25dSMatthias Ringwald    write_indent(fout)
1012fd1be25dSMatthias Ringwald    fout.write('1,\n')
1013fd1be25dSMatthias Ringwald    fout.write("\n")
101460b51a4cSMatthias Ringwald
101560b51a4cSMatthias Ringwald    parseLines(fname_in, fin, fout)
101660b51a4cSMatthias Ringwald
1017729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
1018b3fcedb9SMatthias Ringwald    write_indent(fout)
1019b3fcedb9SMatthias Ringwald    fout.write("// END\n");
1020b3fcedb9SMatthias Ringwald    write_indent(fout)
1021b3fcedb9SMatthias Ringwald    write_16(fout,0)
1022b3fcedb9SMatthias Ringwald    fout.write("\n")
1023b3fcedb9SMatthias Ringwald    total_size = total_size + 2
1024b3fcedb9SMatthias Ringwald
1025b3fcedb9SMatthias Ringwald    fout.write("}; // total size %u bytes \n" % total_size);
1026b3fcedb9SMatthias Ringwald
1027b3fcedb9SMatthias Ringwalddef listHandles(fout):
1028b3fcedb9SMatthias Ringwald    fout.write('\n\n')
1029b3fcedb9SMatthias Ringwald    fout.write('//\n')
1030729074c4SMatthias Ringwald    fout.write('// list service handle ranges\n')
1031729074c4SMatthias Ringwald    fout.write('//\n')
1032729074c4SMatthias Ringwald    for define in defines_for_services:
1033729074c4SMatthias Ringwald        fout.write(define)
1034729074c4SMatthias Ringwald        fout.write('\n')
1035729074c4SMatthias Ringwald    fout.write('\n')
1036729074c4SMatthias Ringwald    fout.write('//\n')
1037b3fcedb9SMatthias Ringwald    fout.write('// list mapping between characteristics and handles\n')
1038b3fcedb9SMatthias Ringwald    fout.write('//\n')
1039729074c4SMatthias Ringwald    for define in defines_for_characteristics:
1040b3fcedb9SMatthias Ringwald        fout.write(define)
1041b3fcedb9SMatthias Ringwald        fout.write('\n')
1042b3fcedb9SMatthias Ringwald
1043dbb3997aSMilanka Ringwalddef getFile( fileName ):
104478b65b0aSMatthias Ringwald    for d in include_paths:
104578b65b0aSMatthias Ringwald        fullFile = os.path.normpath(d + os.sep + fileName) # because Windows exists
104678b65b0aSMatthias Ringwald        # print("test %s" % fullFile)
1047dbb3997aSMilanka Ringwald        if os.path.isfile( fullFile ) == True:
1048dbb3997aSMilanka Ringwald            return fullFile
1049dbb3997aSMilanka Ringwald    print ("'{0}' not found".format( fileName ))
105078b65b0aSMatthias Ringwald    print ("Include paths: %s" % ", ".join(include_paths))
1051dbb3997aSMilanka Ringwald    exit(-1)
1052dbb3997aSMilanka Ringwald
1053dbb3997aSMilanka Ringwald
1054dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
1055d567aeb3SMatthias Ringwalddefault_includes = [os.path.normpath(path) for path in [
1056d567aeb3SMatthias Ringwald    btstack_root + '/src/',
1057d567aeb3SMatthias Ringwald    btstack_root + '/src/ble/gatt-service/',
1058d567aeb3SMatthias Ringwald    btstack_root + '/src/le-audio/gatt-service/',
1059d567aeb3SMatthias Ringwald    btstack_root + '/src/mesh/gatt-service/'
1060d567aeb3SMatthias Ringwald]]
1061dbb3997aSMilanka Ringwald
1062dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack')
1063dbb3997aSMilanka Ringwald
1064dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes',
1065dbb3997aSMilanka Ringwald        help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes))
1066dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str,
1067dbb3997aSMilanka Ringwald        help='gatt file to be compiled')
1068dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str,
1069dbb3997aSMilanka Ringwald        help='header file to be generated')
1070dbb3997aSMilanka Ringwald
1071dbb3997aSMilanka Ringwaldargs = parser.parse_args()
1072dbb3997aSMilanka Ringwald
107378b65b0aSMatthias Ringwald# add include path arguments
107478b65b0aSMatthias Ringwaldif args.I != None:
107578b65b0aSMatthias Ringwald    for d in args.I:
107678b65b0aSMatthias Ringwald        include_paths.append(os.path.normpath(d[0]))
107778b65b0aSMatthias Ringwald
1078dbb3997aSMilanka Ringwald# append default include paths
107978b65b0aSMatthias Ringwaldinclude_paths.extend(default_includes)
1080dbb3997aSMilanka Ringwald
1081b3fcedb9SMatthias Ringwaldtry:
1082b165f97bSMatthias Ringwald    # read defines from bluetooth_gatt.h
1083dbb3997aSMilanka Ringwald    gen_path = getFile( 'bluetooth_gatt.h' )
1084b165f97bSMatthias Ringwald    bluetooth_gatt = read_defines(gen_path)
1085b165f97bSMatthias Ringwald
1086dbb3997aSMilanka Ringwald    filename = args.hfile
1087dbb3997aSMilanka Ringwald    fin  = codecs.open (args.gattfile, encoding='utf-8')
1088285653b2SMatthias Ringwald
1089285653b2SMatthias Ringwald    # pass 1: create temp .h file
1090d78181d6SMatthias Ringwald    ftemp = tempfile.TemporaryFile(mode='w+t')
1091285653b2SMatthias Ringwald    parse(args.gattfile, fin, filename, sys.argv[0], ftemp)
1092285653b2SMatthias Ringwald    listHandles(ftemp)
1093285653b2SMatthias Ringwald
1094043f8832SMatthias Ringwald    # calc GATT Database Hash
1095043f8832SMatthias Ringwald    db_hash = aes_cmac(bytearray(16), database_hash_message)
1096043f8832SMatthias Ringwald    if isinstance(db_hash, str):
1097043f8832SMatthias Ringwald        # python2
1098043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % ord(i)) for i in db_hash]
1099043f8832SMatthias Ringwald    elif isinstance(db_hash, bytes):
1100043f8832SMatthias Ringwald        # python3
1101043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % i) for i in db_hash]
1102043f8832SMatthias Ringwald    else:
1103043f8832SMatthias Ringwald        print("AES CMAC returns unexpected type %s, abort" % type(db_hash))
1104043f8832SMatthias Ringwald        sys.exit(1)
1105043f8832SMatthias Ringwald    # reverse hash to get little endian
1106043f8832SMatthias Ringwald    db_hash_sequence.reverse()
1107043f8832SMatthias Ringwald    db_hash_string = ', '.join(db_hash_sequence) + ', '
1108043f8832SMatthias Ringwald
1109285653b2SMatthias Ringwald    # pass 2: insert GATT Database Hash
1110b3fcedb9SMatthias Ringwald    fout = open (filename, 'w')
1111285653b2SMatthias Ringwald    ftemp.seek(0)
1112285653b2SMatthias Ringwald    for line in ftemp:
1113043f8832SMatthias Ringwald        fout.write(line.replace('THE-DATABASE-HASH', db_hash_string))
1114b3fcedb9SMatthias Ringwald    fout.close()
1115285653b2SMatthias Ringwald    ftemp.close()
1116285653b2SMatthias Ringwald
1117b165f97bSMatthias Ringwald    print('Created %s' % filename)
1118b3fcedb9SMatthias Ringwald
1119b3fcedb9SMatthias Ringwaldexcept IOError as e:
11205ea34488SWayne Keenan    parser.print_help()
11215ea34488SWayne Keenan    print(e)
1122b3fcedb9SMatthias Ringwald    sys.exit(1)
1123b3fcedb9SMatthias Ringwald
1124b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n')
1125