xref: /btstack/tool/compile_gatt.py (revision d48e511ecb3f1f4bc4d6ac79ddb53d0d5cff7e16)
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 = '''
41*d48e511eSRicardo Quesada// clang-format off
42b3fcedb9SMatthias Ringwald// {0} generated from {1} for BTstack
437050bf34SMatthias Ringwald// it needs to be regenerated when the .gatt file is updated.
447050bf34SMatthias Ringwald
457050bf34SMatthias Ringwald// To generate {0}:
467050bf34SMatthias Ringwald// {2} {1} {0}
477050bf34SMatthias Ringwald
48fd1be25dSMatthias Ringwald// att db format version 1
49b3fcedb9SMatthias Ringwald
50fd1be25dSMatthias Ringwald// binary attribute representation:
51fd1be25dSMatthias Ringwald// - size in bytes (16), flags(16), handle (16), uuid (16/128), value(...)
52b3fcedb9SMatthias Ringwald
53b3fcedb9SMatthias Ringwald#include <stdint.h>
54b3fcedb9SMatthias Ringwald
55fa529fa7SMatthias Ringwald// Reference: https://en.cppreference.com/w/cpp/feature_test
56fa529fa7SMatthias Ringwald#if __cplusplus >= 200704L
57fa529fa7SMatthias Ringwaldconstexpr
58fa529fa7SMatthias Ringwald#endif
59b3fcedb9SMatthias Ringwaldconst uint8_t profile_data[] =
60b3fcedb9SMatthias Ringwald'''
61b3fcedb9SMatthias Ringwald
62b3fcedb9SMatthias Ringwaldprint('''
63dbb3997aSMilanka RingwaldBLE configuration generator for use with BTstack
64dbb3997aSMilanka RingwaldCopyright 2018 BlueKitchen GmbH
65b3fcedb9SMatthias Ringwald''')
66b3fcedb9SMatthias Ringwald
67b3fcedb9SMatthias Ringwaldassigned_uuids = {
68b3fcedb9SMatthias Ringwald    'GAP_SERVICE'          : 0x1800,
69b3fcedb9SMatthias Ringwald    'GATT_SERVICE'         : 0x1801,
70b3fcedb9SMatthias Ringwald    'GAP_DEVICE_NAME'      : 0x2a00,
71b3fcedb9SMatthias Ringwald    'GAP_APPEARANCE'       : 0x2a01,
72b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02,
73b3fcedb9SMatthias Ringwald    'GAP_RECONNECTION_ADDRESS'    : 0x2A03,
74b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04,
75bd8291e9SMatthias Ringwald    'GAP_CENTRAL_ADDRESS_RESOLUTION' : 0x2aa6,
76bd8291e9SMatthias Ringwald    'GAP_RESOLVABLE_PRIVATE_ADDRESS_ONLY' : 0x2AC9,
77bd8291e9SMatthias Ringwald    'GAP_ENCRYPTED_DATA_KEY_MATERIAL' : 0x2B88,
78bd8291e9SMatthias Ringwald    'GAP_LE_GATT_SECURITY_LEVELS' : 0x2BF5,
79b3fcedb9SMatthias Ringwald    'GATT_SERVICE_CHANGED' : 0x2a05,
80c7a36ba4SMatthias Ringwald    'GATT_CLIENT_SUPPORTED_FEATURES' : 0x2b29,
81c7a36ba4SMatthias Ringwald    'GATT_SERVER_SUPPORTED_FEATURES' : 0x2b3a,
82043f8832SMatthias Ringwald    'GATT_DATABASE_HASH' : 0x2b2a
83b3fcedb9SMatthias Ringwald}
84b3fcedb9SMatthias Ringwald
85e72176f8SMatthias Ringwaldsecurity_permsission = ['ANYBODY','ENCRYPTED', 'AUTHENTICATED', 'AUTHORIZED', 'AUTHENTICATED_SC']
86d7ec1d24SMatthias Ringwald
87b3fcedb9SMatthias Ringwaldproperty_flags = {
88eb6072adSMatthias Ringwald    # GATT Characteristic Properties
89b3fcedb9SMatthias Ringwald    'BROADCAST' :                   0x01,
90b3fcedb9SMatthias Ringwald    'READ' :                        0x02,
91b3fcedb9SMatthias Ringwald    'WRITE_WITHOUT_RESPONSE' :      0x04,
92b3fcedb9SMatthias Ringwald    'WRITE' :                       0x08,
93b3fcedb9SMatthias Ringwald    'NOTIFY':                       0x10,
94b3fcedb9SMatthias Ringwald    'INDICATE' :                    0x20,
95b3fcedb9SMatthias Ringwald    'AUTHENTICATED_SIGNED_WRITE' :  0x40,
96b3fcedb9SMatthias Ringwald    'EXTENDED_PROPERTIES' :         0x80,
97b3fcedb9SMatthias Ringwald    # custom BTstack extension
98b3fcedb9SMatthias Ringwald    'DYNAMIC':                      0x100,
99b3fcedb9SMatthias Ringwald    'LONG_UUID':                    0x200,
100e22a2612SMatthias Ringwald
101e22a2612SMatthias Ringwald    # read permissions
102e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_0':        0x400,
103e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_1':        0x800,
104e22a2612SMatthias Ringwald
105e22a2612SMatthias Ringwald    #
106b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_7':       0x6000,
107b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_8':       0x7000,
108b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_9':       0x8000,
109b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_10':      0x9000,
110b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_11':      0xa000,
111b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_12':      0xb000,
112b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_13':      0xc000,
113b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_14':      0xd000,
114b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_15':      0xe000,
115b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_16':      0xf000,
116e22a2612SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_MASK':    0xf000,
117eb6072adSMatthias Ringwald
118b3fcedb9SMatthias Ringwald    # only used by gatt compiler >= 0xffff
119b3fcedb9SMatthias Ringwald    # Extended Properties
120e72176f8SMatthias Ringwald    'RELIABLE_WRITE':              0x00010000,
121e72176f8SMatthias Ringwald    'AUTHENTICATION_REQUIRED':     0x00020000,
122e72176f8SMatthias Ringwald    'AUTHORIZATION_REQUIRED':      0x00040000,
123e72176f8SMatthias Ringwald    'READ_ANYBODY':                0x00080000,
124e72176f8SMatthias Ringwald    'READ_ENCRYPTED':              0x00100000,
125e72176f8SMatthias Ringwald    'READ_AUTHENTICATED':          0x00200000,
126e72176f8SMatthias Ringwald    'READ_AUTHENTICATED_SC':       0x00400000,
127e72176f8SMatthias Ringwald    'READ_AUTHORIZED':             0x00800000,
128e72176f8SMatthias Ringwald    'WRITE_ANYBODY':               0x01000000,
129e72176f8SMatthias Ringwald    'WRITE_ENCRYPTED':             0x02000000,
130e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED':         0x04000000,
131e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED_SC':      0x08000000,
132e72176f8SMatthias Ringwald    'WRITE_AUTHORIZED':            0x10000000,
133eb6072adSMatthias Ringwald
134eb6072adSMatthias Ringwald    # Broadcast, Notify, Indicate, Extended Properties are only used to describe a GATT Characteristic, but are free to use with att_db
135e72176f8SMatthias Ringwald    # - write permissions
136e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_0':      0x01,
137e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_1':      0x10,
138e72176f8SMatthias Ringwald    # - SC required
139e72176f8SMatthias Ringwald    'READ_PERMISSION_SC':          0x20,
140e72176f8SMatthias Ringwald    'WRITE_PERMISSION_SC':         0x80,
141b3fcedb9SMatthias Ringwald}
142b3fcedb9SMatthias Ringwald
143b3fcedb9SMatthias Ringwaldservices = dict()
144b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict()
145b3fcedb9SMatthias Ringwaldpresentation_formats = dict()
146b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = ""
147b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0
148b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = ""
149729074c4SMatthias Ringwalddefines_for_characteristics = []
150729074c4SMatthias Ringwalddefines_for_services = []
15178b65b0aSMatthias Ringwaldinclude_paths = []
152043f8832SMatthias Ringwalddatabase_hash_message = bytearray()
15329ba805bSMatthias Ringwaldservice_counter = {}
154b3fcedb9SMatthias Ringwald
155b3fcedb9SMatthias Ringwaldhandle = 1
156b3fcedb9SMatthias Ringwaldtotal_size = 0
157b3fcedb9SMatthias Ringwald
158043f8832SMatthias Ringwalddef aes_cmac(key, n):
159043f8832SMatthias Ringwald    if have_crypto:
160043f8832SMatthias Ringwald        cobj = CMAC.new(key, ciphermod=AES)
161043f8832SMatthias Ringwald        cobj.update(n)
162043f8832SMatthias Ringwald        return cobj.digest()
163043f8832SMatthias Ringwald    else:
1647490175eSMatthias Ringwald        # return random value
1657490175eSMatthias Ringwald        return os.urandom(16)
166043f8832SMatthias Ringwald
167b165f97bSMatthias Ringwalddef read_defines(infile):
168b165f97bSMatthias Ringwald    defines = dict()
169b165f97bSMatthias Ringwald    with open (infile, 'rt') as fin:
170b165f97bSMatthias Ringwald        for line in fin:
171bae15238SMatthias Ringwald            parts = re.match('#define\\s+(\\w+)\\s+(\\w+)',line)
172b165f97bSMatthias Ringwald            if parts and len(parts.groups()) == 2:
173b165f97bSMatthias Ringwald                (key, value) = parts.groups()
174b165f97bSMatthias Ringwald                defines[key] = int(value, 16)
175b165f97bSMatthias Ringwald    return defines
176b165f97bSMatthias Ringwald
177b3fcedb9SMatthias Ringwalddef keyForUUID(uuid):
178b3fcedb9SMatthias Ringwald    keyUUID = ""
179b3fcedb9SMatthias Ringwald    for i in uuid:
180b3fcedb9SMatthias Ringwald        keyUUID += "%02x" % i
181b3fcedb9SMatthias Ringwald    return keyUUID
182b3fcedb9SMatthias Ringwald
183b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid):
184b3fcedb9SMatthias Ringwald    return uuid.replace('-', '_')
185b3fcedb9SMatthias Ringwald
186b3fcedb9SMatthias Ringwalddef twoByteLEFor(value):
187b3fcedb9SMatthias Ringwald    return [ (value & 0xff), (value >> 8)]
188b3fcedb9SMatthias Ringwald
189b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text):
190b3fcedb9SMatthias 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):
191b3fcedb9SMatthias Ringwald        return True
192b3fcedb9SMatthias Ringwald    return False
193b3fcedb9SMatthias Ringwald
194b3fcedb9SMatthias Ringwalddef parseUUID128(uuid):
195b3fcedb9SMatthias 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)
196b3fcedb9SMatthias Ringwald    uuid_bytes = []
197b3fcedb9SMatthias Ringwald    for i in range(8, 0, -1):
198b3fcedb9SMatthias Ringwald        uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16))
199b3fcedb9SMatthias Ringwald    return uuid_bytes
200b3fcedb9SMatthias Ringwald
201b3fcedb9SMatthias Ringwalddef parseUUID(uuid):
202b3fcedb9SMatthias Ringwald    if uuid in assigned_uuids:
203b3fcedb9SMatthias Ringwald        return twoByteLEFor(assigned_uuids[uuid])
204b165f97bSMatthias Ringwald    uuid_upper = uuid.upper().replace('.','_')
205b165f97bSMatthias Ringwald    if uuid_upper in bluetooth_gatt:
206b165f97bSMatthias Ringwald        return twoByteLEFor(bluetooth_gatt[uuid_upper])
207b3fcedb9SMatthias Ringwald    if is_128bit_uuid(uuid):
208b3fcedb9SMatthias Ringwald        return parseUUID128(uuid)
209b3fcedb9SMatthias Ringwald    uuidInt = int(uuid, 16)
210b3fcedb9SMatthias Ringwald    return twoByteLEFor(uuidInt)
211b3fcedb9SMatthias Ringwald
212b3fcedb9SMatthias Ringwalddef parseProperties(properties):
213b3fcedb9SMatthias Ringwald    value = 0
214b3fcedb9SMatthias Ringwald    parts = properties.split("|")
215b3fcedb9SMatthias Ringwald    for property in parts:
216b3fcedb9SMatthias Ringwald        property = property.strip()
217b3fcedb9SMatthias Ringwald        if property in property_flags:
218b3fcedb9SMatthias Ringwald            value |= property_flags[property]
219b3fcedb9SMatthias Ringwald        else:
220b3fcedb9SMatthias Ringwald            print("WARNING: property %s undefined" % (property))
221e22a2612SMatthias Ringwald
222e5ce8e0eSMilanka Ringwald    return value
223e5ce8e0eSMilanka Ringwald
224e5ce8e0eSMilanka Ringwalddef prettyPrintProperties(properties):
225e5ce8e0eSMilanka Ringwald    value = ""
226e5ce8e0eSMilanka Ringwald    parts = properties.split("|")
227e5ce8e0eSMilanka Ringwald    for property in parts:
228e5ce8e0eSMilanka Ringwald        property = property.strip()
229e5ce8e0eSMilanka Ringwald        if property in property_flags:
230e5ce8e0eSMilanka Ringwald            if value != "":
231e5ce8e0eSMilanka Ringwald                value += " | "
232e5ce8e0eSMilanka Ringwald            value += property
233e5ce8e0eSMilanka Ringwald        else:
234e5ce8e0eSMilanka Ringwald            print("WARNING: property %s undefined" % (property))
235e5ce8e0eSMilanka Ringwald
236e5ce8e0eSMilanka Ringwald    return value
237e5ce8e0eSMilanka Ringwald
238e22a2612SMatthias Ringwald
239e22a2612SMatthias Ringwalddef gatt_characteristic_properties(properties):
240e22a2612SMatthias Ringwald    return properties & 0xff
241e22a2612SMatthias Ringwald
242e22a2612SMatthias Ringwalddef att_flags(properties):
243e72176f8SMatthias Ringwald    # drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Properties (0x80) - not used for flags
244e72176f8SMatthias Ringwald    properties &= 0xffffff4e
245e22a2612SMatthias Ringwald
246e22a2612SMatthias Ringwald    # rw permissions distinct
247e22a2612SMatthias Ringwald    distinct_permissions_used = properties & (
248e22a2612SMatthias Ringwald        property_flags['READ_AUTHORIZED'] |
249e72176f8SMatthias Ringwald        property_flags['READ_AUTHENTICATED_SC'] |
250e22a2612SMatthias Ringwald        property_flags['READ_AUTHENTICATED'] |
251e22a2612SMatthias Ringwald        property_flags['READ_ENCRYPTED'] |
252e22a2612SMatthias Ringwald        property_flags['READ_ANYBODY'] |
253e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHORIZED'] |
254e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED'] |
255e72176f8SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED_SC'] |
256e22a2612SMatthias Ringwald        property_flags['WRITE_ENCRYPTED'] |
257e22a2612SMatthias Ringwald        property_flags['WRITE_ANYBODY']
258e22a2612SMatthias Ringwald    ) != 0
259e22a2612SMatthias Ringwald
260e22a2612SMatthias Ringwald    # post process properties
261e22a2612SMatthias Ringwald    encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0
262e22a2612SMatthias Ringwald
263d7ec1d24SMatthias Ringwald    # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted
264e22a2612SMatthias Ringwald    if encryption_key_size_specified and not distinct_permissions_used:
265e22a2612SMatthias Ringwald        properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED']
266e22a2612SMatthias Ringwald
267d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated
268d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used:
269d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED']
270d7ec1d24SMatthias Ringwald
271d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized
272d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used:
273d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED']
274d7ec1d24SMatthias Ringwald
275d7ec1d24SMatthias Ringwald    # determine read/write security requirements
276d7ec1d24SMatthias Ringwald    read_security_level  = 0
277d7ec1d24SMatthias Ringwald    write_security_level = 0
278e72176f8SMatthias Ringwald    read_requires_sc     = False
279e72176f8SMatthias Ringwald    write_requires_sc    = False
280e22a2612SMatthias Ringwald    if properties & property_flags['READ_AUTHORIZED']:
281d7ec1d24SMatthias Ringwald        read_security_level = 3
282e22a2612SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED']:
283d7ec1d24SMatthias Ringwald        read_security_level = 2
284e72176f8SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED_SC']:
285e72176f8SMatthias Ringwald        read_security_level = 2
286e72176f8SMatthias Ringwald        read_requires_sc = True
287e22a2612SMatthias Ringwald    elif properties & property_flags['READ_ENCRYPTED']:
288d7ec1d24SMatthias Ringwald        read_security_level = 1
289e22a2612SMatthias Ringwald    if properties & property_flags['WRITE_AUTHORIZED']:
290d7ec1d24SMatthias Ringwald        write_security_level = 3
291e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED']:
292d7ec1d24SMatthias Ringwald        write_security_level = 2
293e72176f8SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED_SC']:
294e72176f8SMatthias Ringwald        write_security_level = 2
295e72176f8SMatthias Ringwald        write_requires_sc = True
296e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_ENCRYPTED']:
297d7ec1d24SMatthias Ringwald        write_security_level = 1
298d7ec1d24SMatthias Ringwald
299d7ec1d24SMatthias Ringwald    # map security requirements to flags
300d7ec1d24SMatthias Ringwald    if read_security_level & 2:
301d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_1']
302d7ec1d24SMatthias Ringwald    if read_security_level & 1:
303d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_0']
304e72176f8SMatthias Ringwald    if read_requires_sc:
305e72176f8SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_SC']
306d7ec1d24SMatthias Ringwald    if write_security_level & 2:
307d7ec1d24SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_1']
308d7ec1d24SMatthias Ringwald    if write_security_level & 1:
309e22a2612SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_0']
310e72176f8SMatthias Ringwald    if write_requires_sc:
311e72176f8SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_SC']
312e22a2612SMatthias Ringwald
313e22a2612SMatthias Ringwald    return properties
314e22a2612SMatthias Ringwald
315d7ec1d24SMatthias Ringwalddef write_permissions_and_key_size_flags_from_properties(properties):
316e22a2612SMatthias Ringwald    return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1'])
317b3fcedb9SMatthias Ringwald
318b3fcedb9SMatthias Ringwalddef write_8(fout, value):
319b3fcedb9SMatthias Ringwald    fout.write( "0x%02x, " % (value & 0xff))
320b3fcedb9SMatthias Ringwald
321b3fcedb9SMatthias Ringwalddef write_16(fout, value):
322b3fcedb9SMatthias Ringwald    fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff))
323b3fcedb9SMatthias Ringwald
324285653b2SMatthias Ringwalddef write_uuid(fout, uuid):
325b3fcedb9SMatthias Ringwald    for byte in uuid:
326b3fcedb9SMatthias Ringwald        fout.write( "0x%02x, " % byte)
327b3fcedb9SMatthias Ringwald
328b3fcedb9SMatthias Ringwalddef write_string(fout, text):
329b3fcedb9SMatthias Ringwald    for l in text.lstrip('"').rstrip('"'):
330b3fcedb9SMatthias Ringwald        write_8(fout, ord(l))
331b3fcedb9SMatthias Ringwald
332b3fcedb9SMatthias Ringwalddef write_sequence(fout, text):
333b3fcedb9SMatthias Ringwald    parts = text.split()
334b3fcedb9SMatthias Ringwald    for part in parts:
335b3fcedb9SMatthias Ringwald        fout.write("0x%s, " % (part.strip()))
336b3fcedb9SMatthias Ringwald
337043f8832SMatthias Ringwalddef write_database_hash(fout):
338043f8832SMatthias Ringwald    fout.write("THE-DATABASE-HASH")
339043f8832SMatthias Ringwald
340b3fcedb9SMatthias Ringwalddef write_indent(fout):
341b3fcedb9SMatthias Ringwald    fout.write("    ")
342b3fcedb9SMatthias Ringwald
343d7ec1d24SMatthias Ringwalddef read_permissions_from_flags(flags):
344d7ec1d24SMatthias Ringwald    permissions = 0
345d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_0']:
346d7ec1d24SMatthias Ringwald        permissions |= 1
347d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_1']:
348d7ec1d24SMatthias Ringwald        permissions |= 2
349e72176f8SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_SC'] and permissions == 2:
350e72176f8SMatthias Ringwald        permissions = 4
351d7ec1d24SMatthias Ringwald    return permissions
352d7ec1d24SMatthias Ringwald
353d7ec1d24SMatthias Ringwalddef write_permissions_from_flags(flags):
354d7ec1d24SMatthias Ringwald    permissions = 0
355d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_0']:
356d7ec1d24SMatthias Ringwald        permissions |= 1
357d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_1']:
358d7ec1d24SMatthias Ringwald        permissions |= 2
359e72176f8SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_SC'] and permissions == 2:
360e72176f8SMatthias Ringwald        permissions = 4
361d7ec1d24SMatthias Ringwald    return permissions
362d7ec1d24SMatthias Ringwald
363d7ec1d24SMatthias Ringwalddef encryption_key_size_from_flags(flags):
364d7ec1d24SMatthias Ringwald    encryption_key_size = (flags & 0xf000) >> 12
365d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
366d7ec1d24SMatthias Ringwald        encryption_key_size += 1
367d7ec1d24SMatthias Ringwald    return encryption_key_size
368d7ec1d24SMatthias Ringwald
369b3fcedb9SMatthias Ringwalddef is_string(text):
370b3fcedb9SMatthias Ringwald    for item in text.split(" "):
371b3fcedb9SMatthias Ringwald        if not all(c in string.hexdigits for c in item):
372b3fcedb9SMatthias Ringwald            return True
373b3fcedb9SMatthias Ringwald    return False
374b3fcedb9SMatthias Ringwald
375b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties):
376b3fcedb9SMatthias Ringwald    return properties & (property_flags['NOTIFY'] | property_flags['INDICATE'])
377b3fcedb9SMatthias Ringwald
378729074c4SMatthias Ringwalddef serviceDefinitionComplete(fout):
379729074c4SMatthias Ringwald    global services
380729074c4SMatthias Ringwald    if current_service_uuid_string:
381e5ce8e0eSMilanka Ringwald        # fout.write("\n")
38229ba805bSMatthias Ringwald        # update num instances for this service
38329ba805bSMatthias Ringwald        count = 1
38429ba805bSMatthias Ringwald        if current_service_uuid_string in service_counter:
38529ba805bSMatthias Ringwald            count = service_counter[current_service_uuid_string] + 1
38629ba805bSMatthias Ringwald        service_counter[current_service_uuid_string] = count
38709e90ba6SMilanka Ringwald        # add old defines without service counter for first instance for backward compatibility
38829ba805bSMatthias Ringwald        if count == 1:
389729074c4SMatthias Ringwald            defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle))
390729074c4SMatthias Ringwald            defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1))
39109e90ba6SMilanka Ringwald
39229ba805bSMatthias Ringwald        # unified defines indicating instance
39329ba805bSMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_%02x_START_HANDLE 0x%04x' % (current_service_uuid_string, count, current_service_start_handle))
39429ba805bSMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_%02x_END_HANDLE 0x%04x' % (current_service_uuid_string, count, handle-1))
39509e90ba6SMilanka Ringwald        services[current_service_uuid_string+"_" + str(count)] = [current_service_start_handle, handle - 1, count]
396729074c4SMatthias Ringwald
397d7ec1d24SMatthias Ringwalddef dump_flags(fout, flags):
398d7ec1d24SMatthias Ringwald    global security_permsission
399d7ec1d24SMatthias Ringwald    encryption_key_size = encryption_key_size_from_flags(flags)
400d7ec1d24SMatthias Ringwald    read_permissions    = security_permsission[read_permissions_from_flags(flags)]
401d7ec1d24SMatthias Ringwald    write_permissions   = security_permsission[write_permissions_from_flags(flags)]
402d7ec1d24SMatthias Ringwald    write_indent(fout)
403d7ec1d24SMatthias Ringwald    fout.write('// ')
404d7ec1d24SMatthias Ringwald    first = 1
405d7ec1d24SMatthias Ringwald    if flags & property_flags['READ']:
406d7ec1d24SMatthias Ringwald        fout.write('READ_%s' % read_permissions)
407d7ec1d24SMatthias Ringwald        first = 0
408d7ec1d24SMatthias Ringwald    if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']):
409d7ec1d24SMatthias Ringwald        if not first:
410d7ec1d24SMatthias Ringwald            fout.write(', ')
411d7ec1d24SMatthias Ringwald        first = 0
412d7ec1d24SMatthias Ringwald        fout.write('WRITE_%s' % write_permissions)
413d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
414d7ec1d24SMatthias Ringwald        if not first:
415d7ec1d24SMatthias Ringwald            fout.write(', ')
416d7ec1d24SMatthias Ringwald        first = 0
417d7ec1d24SMatthias Ringwald        fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size)
418d7ec1d24SMatthias Ringwald    fout.write('\n')
419d7ec1d24SMatthias Ringwald
420043f8832SMatthias Ringwalddef database_hash_append_uint8(value):
421043f8832SMatthias Ringwald    global database_hash_message
422043f8832SMatthias Ringwald    database_hash_message.append(value)
423043f8832SMatthias Ringwald
424043f8832SMatthias Ringwalddef database_hash_append_uint16(value):
425043f8832SMatthias Ringwald    global database_hash_message
426043f8832SMatthias Ringwald    database_hash_append_uint8(value & 0xff)
427043f8832SMatthias Ringwald    database_hash_append_uint8((value >> 8) & 0xff)
428043f8832SMatthias Ringwald
429043f8832SMatthias Ringwalddef database_hash_append_value(value):
430043f8832SMatthias Ringwald    global database_hash_message
431043f8832SMatthias Ringwald    for byte in value:
432043f8832SMatthias Ringwald        database_hash_append_uint8(byte)
433043f8832SMatthias Ringwald
434b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type):
435b3fcedb9SMatthias Ringwald    global handle
436b3fcedb9SMatthias Ringwald    global total_size
437b3fcedb9SMatthias Ringwald    global current_service_uuid_string
438b3fcedb9SMatthias Ringwald    global current_service_start_handle
439b3fcedb9SMatthias Ringwald
440729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
441b3fcedb9SMatthias Ringwald
442d7ec1d24SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
443b3fcedb9SMatthias Ringwald
444b3fcedb9SMatthias Ringwald    write_indent(fout)
445b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
446b3fcedb9SMatthias Ringwald
447b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
448b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
449b3fcedb9SMatthias Ringwald
450b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size + 2
451b3fcedb9SMatthias Ringwald
452b3fcedb9SMatthias Ringwald    if service_type == 0x2802:
453b3fcedb9SMatthias Ringwald        size += 4
454b3fcedb9SMatthias Ringwald
455b3fcedb9SMatthias Ringwald    write_indent(fout)
456b3fcedb9SMatthias Ringwald    write_16(fout, size)
457d7ec1d24SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
458b3fcedb9SMatthias Ringwald    write_16(fout, handle)
459b3fcedb9SMatthias Ringwald    write_16(fout, service_type)
460285653b2SMatthias Ringwald    write_uuid(fout, uuid)
461b3fcedb9SMatthias Ringwald    fout.write("\n")
462b3fcedb9SMatthias Ringwald
463043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
464043f8832SMatthias Ringwald    database_hash_append_uint16(service_type)
465043f8832SMatthias Ringwald    database_hash_append_value(uuid)
466043f8832SMatthias Ringwald
467729074c4SMatthias Ringwald    current_service_uuid_string = c_string_for_uuid(parts[1])
468b3fcedb9SMatthias Ringwald    current_service_start_handle = handle
469b3fcedb9SMatthias Ringwald    handle = handle + 1
470b3fcedb9SMatthias Ringwald    total_size = total_size + size
471b3fcedb9SMatthias Ringwald
472b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts):
473b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2800)
474b3fcedb9SMatthias Ringwald
475b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts):
476b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2801)
477b3fcedb9SMatthias Ringwald
478b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts):
479b3fcedb9SMatthias Ringwald    global handle
480b3fcedb9SMatthias Ringwald    global total_size
481b3fcedb9SMatthias Ringwald
482e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
483b3fcedb9SMatthias Ringwald
484b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
485b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
486b3fcedb9SMatthias Ringwald    if uuid_size > 2:
487b3fcedb9SMatthias Ringwald        uuid_size = 0
488b3fcedb9SMatthias Ringwald
489b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 4 + uuid_size
490b3fcedb9SMatthias Ringwald
491729074c4SMatthias Ringwald    keyUUID = c_string_for_uuid(parts[1])
4926a72ce79SMilanka Ringwald    keys_to_delete = []
4936a72ce79SMilanka Ringwald
49409e90ba6SMilanka Ringwald    for (serviceUUID, service) in services.items():
49509e90ba6SMilanka Ringwald        if serviceUUID.startswith(keyUUID):
49609e90ba6SMilanka Ringwald            write_indent(fout)
49709e90ba6SMilanka Ringwald            fout.write('// 0x%04x %s - range [0x%04x, 0x%04x]\n' % (handle, '-'.join(parts), services[serviceUUID][0], services[serviceUUID][1]))
498b3fcedb9SMatthias Ringwald
499b3fcedb9SMatthias Ringwald            write_indent(fout)
500b3fcedb9SMatthias Ringwald            write_16(fout, size)
501e22a2612SMatthias Ringwald            write_16(fout, read_only_anybody_flags)
502b3fcedb9SMatthias Ringwald            write_16(fout, handle)
503b3fcedb9SMatthias Ringwald            write_16(fout, 0x2802)
50409e90ba6SMilanka Ringwald            write_16(fout, services[serviceUUID][0])
50509e90ba6SMilanka Ringwald            write_16(fout, services[serviceUUID][1])
506b3fcedb9SMatthias Ringwald            if uuid_size > 0:
507285653b2SMatthias Ringwald                write_uuid(fout, uuid)
508b3fcedb9SMatthias Ringwald            fout.write("\n")
509b3fcedb9SMatthias Ringwald
510043f8832SMatthias Ringwald            database_hash_append_uint16(handle)
511043f8832SMatthias Ringwald            database_hash_append_uint16(0x2802)
51209e90ba6SMilanka Ringwald            database_hash_append_uint16(services[serviceUUID][0])
51309e90ba6SMilanka Ringwald            database_hash_append_uint16(services[serviceUUID][1])
514043f8832SMatthias Ringwald            if uuid_size > 0:
515043f8832SMatthias Ringwald                database_hash_append_value(uuid)
516043f8832SMatthias Ringwald
5176a72ce79SMilanka Ringwald            keys_to_delete.append(serviceUUID)
5186a72ce79SMilanka Ringwald
519b3fcedb9SMatthias Ringwald            handle = handle + 1
520b3fcedb9SMatthias Ringwald            total_size = total_size + size
521b3fcedb9SMatthias Ringwald
5226a72ce79SMilanka Ringwald    for key in keys_to_delete:
5236a72ce79SMilanka Ringwald        services.pop(key)
5246a72ce79SMilanka Ringwald
5256a72ce79SMilanka Ringwald
526b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts):
527b3fcedb9SMatthias Ringwald    global handle
528b3fcedb9SMatthias Ringwald    global total_size
529b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
530b3fcedb9SMatthias Ringwald    global characteristic_indices
531b3fcedb9SMatthias Ringwald
532e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
533b3fcedb9SMatthias Ringwald
534b3fcedb9SMatthias Ringwald    # enumerate characteristics with same UUID, using optional name tag if available
535b3fcedb9SMatthias Ringwald    current_characteristic_uuid_string = c_string_for_uuid(parts[1]);
536b3fcedb9SMatthias Ringwald    index = 1
537b3fcedb9SMatthias Ringwald    if current_characteristic_uuid_string in characteristic_indices:
538b3fcedb9SMatthias Ringwald        index = characteristic_indices[current_characteristic_uuid_string] + 1
539b3fcedb9SMatthias Ringwald    characteristic_indices[current_characteristic_uuid_string] = index
540b3fcedb9SMatthias Ringwald    if len(parts) > 4:
541b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_')
542b3fcedb9SMatthias Ringwald    else:
543b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += ('_%02x' % index)
544b3fcedb9SMatthias Ringwald
545b3fcedb9SMatthias Ringwald    uuid       = parseUUID(parts[1])
546b3fcedb9SMatthias Ringwald    uuid_size  = len(uuid)
547b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[2])
548b3fcedb9SMatthias Ringwald    value = ', '.join([str(x) for x in parts[3:]])
549b3fcedb9SMatthias Ringwald
550b3fcedb9SMatthias Ringwald    # reliable writes is defined in an extended properties
551b3fcedb9SMatthias Ringwald    if (properties & property_flags['RELIABLE_WRITE']):
552b3fcedb9SMatthias Ringwald        properties = properties | property_flags['EXTENDED_PROPERTIES']
553b3fcedb9SMatthias Ringwald
554b3fcedb9SMatthias Ringwald    write_indent(fout)
555e5ce8e0eSMilanka Ringwald    fout.write('// 0x%04x %s - %s\n' % (handle, '-'.join(parts[0:2]), prettyPrintProperties(parts[2])))
556b3fcedb9SMatthias Ringwald
557e22a2612SMatthias Ringwald
558e22a2612SMatthias Ringwald    characteristic_properties = gatt_characteristic_properties(properties)
559b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (1+2+uuid_size)
560b3fcedb9SMatthias Ringwald    write_indent(fout)
561b3fcedb9SMatthias Ringwald    write_16(fout, size)
562e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
563b3fcedb9SMatthias Ringwald    write_16(fout, handle)
564b3fcedb9SMatthias Ringwald    write_16(fout, 0x2803)
565e22a2612SMatthias Ringwald    write_8(fout, characteristic_properties)
566b3fcedb9SMatthias Ringwald    write_16(fout, handle+1)
567285653b2SMatthias Ringwald    write_uuid(fout, uuid)
568b3fcedb9SMatthias Ringwald    fout.write("\n")
569b3fcedb9SMatthias Ringwald    total_size = total_size + size
570b3fcedb9SMatthias Ringwald
571043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
572043f8832SMatthias Ringwald    database_hash_append_uint16(0x2803)
573043f8832SMatthias Ringwald    database_hash_append_uint8(characteristic_properties)
574043f8832SMatthias Ringwald    database_hash_append_uint16(handle+1)
575043f8832SMatthias Ringwald    database_hash_append_value(uuid)
576043f8832SMatthias Ringwald
5773c5d4072SMatthias Ringwald    handle = handle + 1
5783c5d4072SMatthias Ringwald
579043f8832SMatthias Ringwald    uuid_is_database_hash = len(uuid) == 2 and uuid[0] == 0x2a and uuid[1] == 0x2b
580043f8832SMatthias Ringwald
581b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size
582043f8832SMatthias Ringwald    if uuid_is_database_hash:
583043f8832SMatthias Ringwald        size +=  16
584043f8832SMatthias Ringwald    else:
585b3fcedb9SMatthias Ringwald        if is_string(value):
586b3fcedb9SMatthias Ringwald            size = size + len(value)
587b3fcedb9SMatthias Ringwald        else:
588b3fcedb9SMatthias Ringwald            size = size + len(value.split())
589b3fcedb9SMatthias Ringwald
590e22a2612SMatthias Ringwald    value_flags = att_flags(properties)
5918ea3236cSMatthias Ringwald
5928ea3236cSMatthias Ringwald    # add UUID128 flag for value handle
593b3fcedb9SMatthias Ringwald    if uuid_size == 16:
594e22a2612SMatthias Ringwald        value_flags = value_flags | property_flags['LONG_UUID'];
595b3fcedb9SMatthias Ringwald
596b3fcedb9SMatthias Ringwald    write_indent(fout)
597e5ce8e0eSMilanka Ringwald    properties_string = prettyPrintProperties(parts[2])
598e5ce8e0eSMilanka Ringwald    if "DYNAMIC" in properties_string:
599e5ce8e0eSMilanka Ringwald        fout.write('// 0x%04x VALUE %s - %s\n' % (handle, '-'.join(parts[0:2]), prettyPrintProperties(parts[2])))
600e5ce8e0eSMilanka Ringwald    else:
601e5ce8e0eSMilanka Ringwald        fout.write('// 0x%04x VALUE %s - %s -'"'%s'"'\n' % (
602e5ce8e0eSMilanka Ringwald        handle, '-'.join(parts[0:2]), prettyPrintProperties(parts[2]), value))
603d7ec1d24SMatthias Ringwald
604d7ec1d24SMatthias Ringwald    dump_flags(fout, value_flags)
605d7ec1d24SMatthias Ringwald
606b3fcedb9SMatthias Ringwald    write_indent(fout)
607b3fcedb9SMatthias Ringwald    write_16(fout, size)
608e22a2612SMatthias Ringwald    write_16(fout, value_flags)
609b3fcedb9SMatthias Ringwald    write_16(fout, handle)
610285653b2SMatthias Ringwald    write_uuid(fout, uuid)
611043f8832SMatthias Ringwald    if uuid_is_database_hash:
612043f8832SMatthias Ringwald        write_database_hash(fout)
613043f8832SMatthias Ringwald    else:
614b3fcedb9SMatthias Ringwald        if is_string(value):
615b3fcedb9SMatthias Ringwald            write_string(fout, value)
616b3fcedb9SMatthias Ringwald        else:
617b3fcedb9SMatthias Ringwald            write_sequence(fout,value)
618b3fcedb9SMatthias Ringwald
619b3fcedb9SMatthias Ringwald    fout.write("\n")
620729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
621b3fcedb9SMatthias Ringwald    handle = handle + 1
622b3fcedb9SMatthias Ringwald
623b3fcedb9SMatthias Ringwald    if add_client_characteristic_configuration(properties):
624e22a2612SMatthias Ringwald        # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC
625d7ec1d24SMatthias Ringwald        flags  = write_permissions_and_key_size_flags_from_properties(properties)
626e22a2612SMatthias Ringwald        flags |= property_flags['READ']
627e22a2612SMatthias Ringwald        flags |= property_flags['WRITE']
6289be4aecfSMatthias Ringwald        flags |= property_flags['WRITE_WITHOUT_RESPONSE']
629e22a2612SMatthias Ringwald        flags |= property_flags['DYNAMIC']
630b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
631d7ec1d24SMatthias Ringwald
632b3fcedb9SMatthias Ringwald        write_indent(fout)
633b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle))
634d7ec1d24SMatthias Ringwald
635d7ec1d24SMatthias Ringwald        dump_flags(fout, flags)
636d7ec1d24SMatthias Ringwald
637b3fcedb9SMatthias Ringwald        write_indent(fout)
638b3fcedb9SMatthias Ringwald        write_16(fout, size)
639e22a2612SMatthias Ringwald        write_16(fout, flags)
640b3fcedb9SMatthias Ringwald        write_16(fout, handle)
641b3fcedb9SMatthias Ringwald        write_16(fout, 0x2902)
642b3fcedb9SMatthias Ringwald        write_16(fout, 0)
643b3fcedb9SMatthias Ringwald        fout.write("\n")
644043f8832SMatthias Ringwald
645043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
646043f8832SMatthias Ringwald        database_hash_append_uint16(0x2902)
647043f8832SMatthias Ringwald
648729074c4SMatthias Ringwald        defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
649b3fcedb9SMatthias Ringwald        handle = handle + 1
650b3fcedb9SMatthias Ringwald
651043f8832SMatthias Ringwald
652b3fcedb9SMatthias Ringwald    if properties & property_flags['RELIABLE_WRITE']:
653b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
654b3fcedb9SMatthias Ringwald        write_indent(fout)
655b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle))
656b3fcedb9SMatthias Ringwald        write_indent(fout)
657b3fcedb9SMatthias Ringwald        write_16(fout, size)
658e22a2612SMatthias Ringwald        write_16(fout, read_only_anybody_flags)
659b3fcedb9SMatthias Ringwald        write_16(fout, handle)
660b3fcedb9SMatthias Ringwald        write_16(fout, 0x2900)
661b3fcedb9SMatthias Ringwald        write_16(fout, 1)   # Reliable Write
662b3fcedb9SMatthias Ringwald        fout.write("\n")
663043f8832SMatthias Ringwald
664043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
665043f8832SMatthias Ringwald        database_hash_append_uint16(0x2900)
666043f8832SMatthias Ringwald        database_hash_append_uint16(1)
667043f8832SMatthias Ringwald
668b3fcedb9SMatthias Ringwald        handle = handle + 1
669b3fcedb9SMatthias Ringwald
67017215335SMatthias Ringwalddef parseGenericDynamicDescriptor(fout, parts, uuid, name):
671b3fcedb9SMatthias Ringwald    global handle
672b3fcedb9SMatthias Ringwald    global total_size
673b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
674b3fcedb9SMatthias Ringwald
675b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
676b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
677b3fcedb9SMatthias Ringwald
678e22a2612SMatthias Ringwald    # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY
679d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
680e22a2612SMatthias Ringwald    flags |= property_flags['READ']
681e22a2612SMatthias Ringwald    flags |= property_flags['WRITE']
682e22a2612SMatthias Ringwald    flags |= property_flags['DYNAMIC']
683e22a2612SMatthias Ringwald
684b3fcedb9SMatthias Ringwald    write_indent(fout)
68517215335SMatthias Ringwald    fout.write('// 0x%04x %s-%s\n' % (handle, name, '-'.join(parts[1:])))
686d7ec1d24SMatthias Ringwald
687d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
688d7ec1d24SMatthias Ringwald
689b3fcedb9SMatthias Ringwald    write_indent(fout)
690b3fcedb9SMatthias Ringwald    write_16(fout, size)
691e22a2612SMatthias Ringwald    write_16(fout, flags)
692b3fcedb9SMatthias Ringwald    write_16(fout, handle)
693766e9e27SMatthias Ringwald    write_16(fout, uuid)
694b3fcedb9SMatthias Ringwald    fout.write("\n")
695043f8832SMatthias Ringwald
696043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
69717215335SMatthias Ringwald    database_hash_append_uint16(uuid)
698043f8832SMatthias Ringwald
69917215335SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_%s_HANDLE 0x%04x' % (current_characteristic_uuid_string, name, handle))
700b3fcedb9SMatthias Ringwald    handle = handle + 1
701b3fcedb9SMatthias Ringwald
70217215335SMatthias Ringwalddef parseGenericDynamicReadOnlyDescriptor(fout, parts, uuid, name):
70317215335SMatthias Ringwald    global handle
70417215335SMatthias Ringwald    global total_size
70517215335SMatthias Ringwald    global current_characteristic_uuid_string
70617215335SMatthias Ringwald
70717215335SMatthias Ringwald    properties = parseProperties(parts[1])
70817215335SMatthias Ringwald    size = 2 + 2 + 2 + 2
70917215335SMatthias Ringwald
71017215335SMatthias Ringwald    # use write permissions and encryption key size from attribute value and set READ, DYNAMIC, READ_ANYBODY
71117215335SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
71217215335SMatthias Ringwald    flags |= property_flags['READ']
71317215335SMatthias Ringwald    flags |= property_flags['DYNAMIC']
71417215335SMatthias Ringwald
71517215335SMatthias Ringwald    write_indent(fout)
71617215335SMatthias Ringwald    fout.write('// 0x%04x %s-%s\n' % (handle, name, '-'.join(parts[1:])))
71717215335SMatthias Ringwald
71817215335SMatthias Ringwald    dump_flags(fout, flags)
71917215335SMatthias Ringwald
72017215335SMatthias Ringwald    write_indent(fout)
72117215335SMatthias Ringwald    write_16(fout, size)
72217215335SMatthias Ringwald    write_16(fout, flags)
72317215335SMatthias Ringwald    write_16(fout, handle)
724079990e8SSanaa Hamel    write_16(fout, uuid)
72517215335SMatthias Ringwald    fout.write("\n")
72617215335SMatthias Ringwald
72717215335SMatthias Ringwald    database_hash_append_uint16(handle)
72817215335SMatthias Ringwald    database_hash_append_uint16(uuid)
72917215335SMatthias Ringwald
73017215335SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_%s_HANDLE 0x%04x' % (current_characteristic_uuid_string, name, handle))
73117215335SMatthias Ringwald    handle = handle + 1
73217215335SMatthias Ringwald
73317215335SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts):
73417215335SMatthias Ringwald    parseGenericDynamicDescriptor(fout, parts, 0x2903, 'SERVER_CONFIGURATION')
73517215335SMatthias Ringwald
736b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts):
737b3fcedb9SMatthias Ringwald    global handle
738b3fcedb9SMatthias Ringwald    global total_size
739b3fcedb9SMatthias Ringwald
740e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
741b3fcedb9SMatthias Ringwald
742b3fcedb9SMatthias Ringwald    identifier = parts[1]
743b3fcedb9SMatthias Ringwald    presentation_formats[identifier] = handle
744b3fcedb9SMatthias Ringwald    # print("format '%s' with handle %d\n" % (identifier, handle))
745b3fcedb9SMatthias Ringwald
746b3fcedb9SMatthias Ringwald    format     = parts[2]
747b3fcedb9SMatthias Ringwald    exponent   = parts[3]
748b3fcedb9SMatthias Ringwald    unit       = parseUUID(parts[4])
749b3fcedb9SMatthias Ringwald    name_space = parts[5]
750b3fcedb9SMatthias Ringwald    description = parseUUID(parts[6])
751b3fcedb9SMatthias Ringwald
752b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 7
753b3fcedb9SMatthias Ringwald
754b3fcedb9SMatthias Ringwald    write_indent(fout)
755b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
756b3fcedb9SMatthias Ringwald    write_indent(fout)
757b3fcedb9SMatthias Ringwald    write_16(fout, size)
758e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
759b3fcedb9SMatthias Ringwald    write_16(fout, handle)
760b3fcedb9SMatthias Ringwald    write_16(fout, 0x2904)
761b3fcedb9SMatthias Ringwald    write_sequence(fout, format)
762b3fcedb9SMatthias Ringwald    write_sequence(fout, exponent)
763285653b2SMatthias Ringwald    write_uuid(fout, unit)
764b3fcedb9SMatthias Ringwald    write_sequence(fout, name_space)
765285653b2SMatthias Ringwald    write_uuid(fout, description)
766b3fcedb9SMatthias Ringwald    fout.write("\n")
767043f8832SMatthias Ringwald
768043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
769043f8832SMatthias Ringwald    database_hash_append_uint16(0x2904)
770043f8832SMatthias Ringwald
771b3fcedb9SMatthias Ringwald    handle = handle + 1
772b3fcedb9SMatthias Ringwald
773b3fcedb9SMatthias Ringwald
774b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts):
775b3fcedb9SMatthias Ringwald    global handle
776b3fcedb9SMatthias Ringwald    global total_size
777b3fcedb9SMatthias Ringwald
778e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
779b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2
780b3fcedb9SMatthias Ringwald
781b3fcedb9SMatthias Ringwald    write_indent(fout)
782b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
783b3fcedb9SMatthias Ringwald    write_indent(fout)
784b3fcedb9SMatthias Ringwald    write_16(fout, size)
785e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
786b3fcedb9SMatthias Ringwald    write_16(fout, handle)
787b3fcedb9SMatthias Ringwald    write_16(fout, 0x2905)
788b3fcedb9SMatthias Ringwald    for identifier in parts[1:]:
7898c70002dSMatthias Ringwald        if not identifier in presentation_formats:
7908c70002dSMatthias Ringwald            print(parts)
791b3fcedb9SMatthias Ringwald            print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier)
792b3fcedb9SMatthias Ringwald            sys.exit(1)
7938c70002dSMatthias Ringwald        format_handle = presentation_formats[identifier]
794b3fcedb9SMatthias Ringwald        write_16(fout, format_handle)
795b3fcedb9SMatthias Ringwald    fout.write("\n")
796043f8832SMatthias Ringwald
797043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
798043f8832SMatthias Ringwald    database_hash_append_uint16(0x2905)
799043f8832SMatthias Ringwald
800b3fcedb9SMatthias Ringwald    handle = handle + 1
801b3fcedb9SMatthias Ringwald
80205146de0SMilanka Ringwalddef parseExternalReportReference(fout, parts):
80305146de0SMilanka Ringwald    global handle
80405146de0SMilanka Ringwald    global total_size
80505146de0SMilanka Ringwald
80605146de0SMilanka Ringwald    read_only_anybody_flags = property_flags['READ'];
80705146de0SMilanka Ringwald    size = 2 + 2 + 2 + 2 + 2
80805146de0SMilanka Ringwald
80905146de0SMilanka Ringwald    report_uuid = int(parts[2], 16)
81005146de0SMilanka Ringwald
81105146de0SMilanka Ringwald    write_indent(fout)
81205146de0SMilanka Ringwald    fout.write('// 0x%04x EXTERNAL_REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
81305146de0SMilanka Ringwald    write_indent(fout)
81405146de0SMilanka Ringwald    write_16(fout, size)
81505146de0SMilanka Ringwald    write_16(fout, read_only_anybody_flags)
81605146de0SMilanka Ringwald    write_16(fout, handle)
81705146de0SMilanka Ringwald    write_16(fout, 0x2907)
81805146de0SMilanka Ringwald    write_16(fout, report_uuid)
81905146de0SMilanka Ringwald    fout.write("\n")
82005146de0SMilanka Ringwald    handle = handle + 1
82105146de0SMilanka Ringwald
822b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts):
823b3fcedb9SMatthias Ringwald    global handle
824b3fcedb9SMatthias Ringwald    global total_size
825b3fcedb9SMatthias Ringwald
826e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
827b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1 + 1
828b3fcedb9SMatthias Ringwald
829231a3c5dSMatthias Ringwald    report_id = parts[2]
830231a3c5dSMatthias Ringwald    report_type = parts[3]
831b3fcedb9SMatthias Ringwald
832b3fcedb9SMatthias Ringwald    write_indent(fout)
833b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
834b3fcedb9SMatthias Ringwald    write_indent(fout)
835b3fcedb9SMatthias Ringwald    write_16(fout, size)
836e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
837b3fcedb9SMatthias Ringwald    write_16(fout, handle)
838b3fcedb9SMatthias Ringwald    write_16(fout, 0x2908)
839b3fcedb9SMatthias Ringwald    write_sequence(fout, report_id)
840b3fcedb9SMatthias Ringwald    write_sequence(fout, report_type)
841b3fcedb9SMatthias Ringwald    fout.write("\n")
842b3fcedb9SMatthias Ringwald    handle = handle + 1
843b3fcedb9SMatthias Ringwald
844b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts):
845b3fcedb9SMatthias Ringwald    global handle
846b3fcedb9SMatthias Ringwald    global total_size
847b3fcedb9SMatthias Ringwald
848e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
849b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1
850b3fcedb9SMatthias Ringwald
851b3fcedb9SMatthias Ringwald    no_of_digitals = parts[1]
852b3fcedb9SMatthias Ringwald
853b3fcedb9SMatthias Ringwald    write_indent(fout)
854b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:])))
855b3fcedb9SMatthias Ringwald    write_indent(fout)
856b3fcedb9SMatthias Ringwald    write_16(fout, size)
857e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
858b3fcedb9SMatthias Ringwald    write_16(fout, handle)
859b3fcedb9SMatthias Ringwald    write_16(fout, 0x2909)
860b3fcedb9SMatthias Ringwald    write_sequence(fout, no_of_digitals)
861b3fcedb9SMatthias Ringwald    fout.write("\n")
862b3fcedb9SMatthias Ringwald    handle = handle + 1
863b3fcedb9SMatthias Ringwald
86460b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout):
865b3fcedb9SMatthias Ringwald    global handle
866b3fcedb9SMatthias Ringwald    global total_size
867b3fcedb9SMatthias Ringwald
868b165f97bSMatthias Ringwald    line_count = 0;
869b3fcedb9SMatthias Ringwald    for line in fin:
870b3fcedb9SMatthias Ringwald        line = line.strip("\n\r ")
871b165f97bSMatthias Ringwald        line_count += 1
872b3fcedb9SMatthias Ringwald
873b165f97bSMatthias Ringwald        if line.startswith("//"):
874b165f97bSMatthias Ringwald            fout.write("    //" + line.lstrip('/') + '\n')
875b165f97bSMatthias Ringwald            continue
876b165f97bSMatthias Ringwald
87760b51a4cSMatthias Ringwald        if line.startswith("#import"):
87860b51a4cSMatthias Ringwald            imported_file = ''
879bae15238SMatthias Ringwald            parts = re.match('#import\\s+<(.*)>\\w*',line)
88060b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
881dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
882bae15238SMatthias Ringwald            parts = re.match('#import\\s+"(.*)"\\w*',line)
88360b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
884dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
88560b51a4cSMatthias Ringwald            if len(imported_file) == 0:
88660b51a4cSMatthias Ringwald                print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count))
88760b51a4cSMatthias Ringwald                continue
88860b51a4cSMatthias Ringwald
889dbb3997aSMilanka Ringwald            imported_file = getFile( imported_file )
89060b51a4cSMatthias Ringwald            print("Importing %s" % imported_file)
89160b51a4cSMatthias Ringwald            try:
89260b51a4cSMatthias Ringwald                imported_fin = codecs.open (imported_file, encoding='utf-8')
893e5ce8e0eSMilanka Ringwald                fout.write('\n\n    // ' + line + ' -- BEGIN\n')
89460b51a4cSMatthias Ringwald                parseLines(imported_file, imported_fin, fout)
89560b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- END\n')
89660b51a4cSMatthias Ringwald            except IOError as e:
89760b51a4cSMatthias Ringwald                print('ERROR: Import failed. Please check path.')
89860b51a4cSMatthias Ringwald
89960b51a4cSMatthias Ringwald            continue
90060b51a4cSMatthias Ringwald
90160b51a4cSMatthias Ringwald        if line.startswith("#TODO"):
90260b51a4cSMatthias Ringwald            print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count))
903b165f97bSMatthias Ringwald            print ("'%s'" % line)
904b165f97bSMatthias Ringwald            fout.write("// " + line + '\n')
905b3fcedb9SMatthias Ringwald            continue
906b3fcedb9SMatthias Ringwald
907b3fcedb9SMatthias Ringwald        if len(line) == 0:
908b3fcedb9SMatthias Ringwald            continue
909b3fcedb9SMatthias Ringwald
910b3fcedb9SMatthias Ringwald        f = io.StringIO(line)
911b3fcedb9SMatthias Ringwald        parts_list = csv.reader(f, delimiter=',', quotechar='"')
912b3fcedb9SMatthias Ringwald
913b3fcedb9SMatthias Ringwald        for parts in parts_list:
914b3fcedb9SMatthias Ringwald            for index, object in enumerate(parts):
915b3fcedb9SMatthias Ringwald                parts[index] = object.strip().lstrip('"').rstrip('"')
916b3fcedb9SMatthias Ringwald
917b3fcedb9SMatthias Ringwald            if parts[0] == 'PRIMARY_SERVICE':
918b3fcedb9SMatthias Ringwald                parsePrimaryService(fout, parts)
919b3fcedb9SMatthias Ringwald                continue
920b3fcedb9SMatthias Ringwald
921b3fcedb9SMatthias Ringwald            if parts[0] == 'SECONDARY_SERVICE':
922b3fcedb9SMatthias Ringwald                parseSecondaryService(fout, parts)
923b3fcedb9SMatthias Ringwald                continue
924b3fcedb9SMatthias Ringwald
925b3fcedb9SMatthias Ringwald            if parts[0] == 'INCLUDE_SERVICE':
926b3fcedb9SMatthias Ringwald                parseIncludeService(fout, parts)
927b3fcedb9SMatthias Ringwald                continue
928b3fcedb9SMatthias Ringwald
929b3fcedb9SMatthias Ringwald            # 2803
930b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC':
931b3fcedb9SMatthias Ringwald                parseCharacteristic(fout, parts)
932b3fcedb9SMatthias Ringwald                continue
933b3fcedb9SMatthias Ringwald
934b3fcedb9SMatthias Ringwald            # 2900 Characteristic Extended Properties
935b3fcedb9SMatthias Ringwald
936b3fcedb9SMatthias Ringwald            # 2901
937b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
938766e9e27SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x2901, 'USER_DESCRIPTION')
939b3fcedb9SMatthias Ringwald                continue
940b3fcedb9SMatthias Ringwald
941b165f97bSMatthias Ringwald
942b165f97bSMatthias Ringwald            # 2902 Client Characteristic Configuration - automatically included in Characteristic if
943b3fcedb9SMatthias Ringwald            # notification / indication is supported
944231a3c5dSMatthias Ringwald            if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION':
945b165f97bSMatthias Ringwald                continue
946b3fcedb9SMatthias Ringwald
947b3fcedb9SMatthias Ringwald            # 2903
948b3fcedb9SMatthias Ringwald            if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
94917215335SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x2903, 'SERVER_CONFIGURATION')
950b3fcedb9SMatthias Ringwald                continue
951b3fcedb9SMatthias Ringwald
952b3fcedb9SMatthias Ringwald            # 2904
953b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_FORMAT':
954b3fcedb9SMatthias Ringwald                parseCharacteristicFormat(fout, parts)
955b3fcedb9SMatthias Ringwald                continue
956b3fcedb9SMatthias Ringwald
957b3fcedb9SMatthias Ringwald            # 2905
958b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
959b3fcedb9SMatthias Ringwald                parseCharacteristicAggregateFormat(fout, parts)
960b3fcedb9SMatthias Ringwald                continue
961b3fcedb9SMatthias Ringwald
962b3fcedb9SMatthias Ringwald            # 2906
963b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
9646f08f159SMatthias Ringwald                parseGenericDynamicReadOnlyDescriptor(fout, parts, 0x2906, 'VALID_RANGE')
965b3fcedb9SMatthias Ringwald                continue
966b3fcedb9SMatthias Ringwald
967b3fcedb9SMatthias Ringwald            # 2907
968b3fcedb9SMatthias Ringwald            if parts[0] == 'EXTERNAL_REPORT_REFERENCE':
96905146de0SMilanka Ringwald                parseExternalReportReference(fout, parts)
970b3fcedb9SMatthias Ringwald                continue
971b3fcedb9SMatthias Ringwald
972b3fcedb9SMatthias Ringwald            # 2908
973b3fcedb9SMatthias Ringwald            if parts[0] == 'REPORT_REFERENCE':
974b3fcedb9SMatthias Ringwald                parseReportReference(fout, parts)
975b3fcedb9SMatthias Ringwald                continue
976b3fcedb9SMatthias Ringwald
977b3fcedb9SMatthias Ringwald            # 2909
978b3fcedb9SMatthias Ringwald            if parts[0] == 'NUMBER_OF_DIGITALS':
979b3fcedb9SMatthias Ringwald                parseNumberOfDigitals(fout, parts)
980b3fcedb9SMatthias Ringwald                continue
981b3fcedb9SMatthias Ringwald
982b3fcedb9SMatthias Ringwald            # 290A
983b3fcedb9SMatthias Ringwald            if parts[0] == 'VALUE_TRIGGER_SETTING':
9846f08f159SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x290A, 'VALUE_TRIGGER_SETTING')
985b3fcedb9SMatthias Ringwald                continue
986b3fcedb9SMatthias Ringwald
987b3fcedb9SMatthias Ringwald            # 290B
988b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION':
9896f08f159SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x290B, 'ENVIRONMENTAL_SENSING_CONFIGURATION')
990b3fcedb9SMatthias Ringwald                continue
991b3fcedb9SMatthias Ringwald
992b3fcedb9SMatthias Ringwald            # 290C
993b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT':
9946f08f159SMatthias Ringwald                parseGenericDynamicReadOnlyDescriptor(fout, parts, 0x290C, 'ENVIRONMENTAL_SENSING_MEASUREMENT')
995b3fcedb9SMatthias Ringwald                continue
996b3fcedb9SMatthias Ringwald
997b3fcedb9SMatthias Ringwald            # 290D
998b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING':
9996f08f159SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x290D, 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING')
1000b3fcedb9SMatthias Ringwald                continue
1001b3fcedb9SMatthias Ringwald
1002b3fcedb9SMatthias Ringwald            print("WARNING: unknown token: %s\n" % (parts[0]))
1003b3fcedb9SMatthias Ringwald
10047050bf34SMatthias Ringwalddef parse(fname_in, fin, fname_out, tool_path, fout):
100560b51a4cSMatthias Ringwald    global handle
100660b51a4cSMatthias Ringwald    global total_size
100760b51a4cSMatthias Ringwald
10087050bf34SMatthias Ringwald    fout.write(header.format(fname_out, fname_in, tool_path))
100960b51a4cSMatthias Ringwald    fout.write('{\n')
1010fd1be25dSMatthias Ringwald    write_indent(fout)
1011fd1be25dSMatthias Ringwald    fout.write('// ATT DB Version\n')
1012fd1be25dSMatthias Ringwald    write_indent(fout)
1013fd1be25dSMatthias Ringwald    fout.write('1,\n')
1014fd1be25dSMatthias Ringwald    fout.write("\n")
101560b51a4cSMatthias Ringwald
101660b51a4cSMatthias Ringwald    parseLines(fname_in, fin, fout)
101760b51a4cSMatthias Ringwald
1018729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
1019b3fcedb9SMatthias Ringwald    write_indent(fout)
1020b3fcedb9SMatthias Ringwald    fout.write("// END\n");
1021b3fcedb9SMatthias Ringwald    write_indent(fout)
1022b3fcedb9SMatthias Ringwald    write_16(fout,0)
1023b3fcedb9SMatthias Ringwald    fout.write("\n")
1024b3fcedb9SMatthias Ringwald    total_size = total_size + 2
1025b3fcedb9SMatthias Ringwald
1026b3fcedb9SMatthias Ringwald    fout.write("}; // total size %u bytes \n" % total_size);
1027b3fcedb9SMatthias Ringwald
1028b3fcedb9SMatthias Ringwalddef listHandles(fout):
1029b3fcedb9SMatthias Ringwald    fout.write('\n\n')
1030b3fcedb9SMatthias Ringwald    fout.write('//\n')
1031729074c4SMatthias Ringwald    fout.write('// list service handle ranges\n')
1032729074c4SMatthias Ringwald    fout.write('//\n')
1033729074c4SMatthias Ringwald    for define in defines_for_services:
1034729074c4SMatthias Ringwald        fout.write(define)
1035729074c4SMatthias Ringwald        fout.write('\n')
1036729074c4SMatthias Ringwald    fout.write('\n')
1037729074c4SMatthias Ringwald    fout.write('//\n')
1038b3fcedb9SMatthias Ringwald    fout.write('// list mapping between characteristics and handles\n')
1039b3fcedb9SMatthias Ringwald    fout.write('//\n')
1040729074c4SMatthias Ringwald    for define in defines_for_characteristics:
1041b3fcedb9SMatthias Ringwald        fout.write(define)
1042b3fcedb9SMatthias Ringwald        fout.write('\n')
1043b3fcedb9SMatthias Ringwald
1044dbb3997aSMilanka Ringwalddef getFile( fileName ):
104578b65b0aSMatthias Ringwald    for d in include_paths:
104678b65b0aSMatthias Ringwald        fullFile = os.path.normpath(d + os.sep + fileName) # because Windows exists
104778b65b0aSMatthias Ringwald        # print("test %s" % fullFile)
1048dbb3997aSMilanka Ringwald        if os.path.isfile( fullFile ) == True:
1049dbb3997aSMilanka Ringwald            return fullFile
1050dbb3997aSMilanka Ringwald    print ("'{0}' not found".format( fileName ))
105178b65b0aSMatthias Ringwald    print ("Include paths: %s" % ", ".join(include_paths))
1052dbb3997aSMilanka Ringwald    exit(-1)
1053dbb3997aSMilanka Ringwald
1054dbb3997aSMilanka Ringwald
1055dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
1056d567aeb3SMatthias Ringwalddefault_includes = [os.path.normpath(path) for path in [
1057d567aeb3SMatthias Ringwald    btstack_root + '/src/',
1058d567aeb3SMatthias Ringwald    btstack_root + '/src/ble/gatt-service/',
1059d567aeb3SMatthias Ringwald    btstack_root + '/src/le-audio/gatt-service/',
1060d567aeb3SMatthias Ringwald    btstack_root + '/src/mesh/gatt-service/'
1061d567aeb3SMatthias Ringwald]]
1062dbb3997aSMilanka Ringwald
1063dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack')
1064dbb3997aSMilanka Ringwald
1065dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes',
1066dbb3997aSMilanka Ringwald        help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes))
1067dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str,
1068dbb3997aSMilanka Ringwald        help='gatt file to be compiled')
1069dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str,
1070dbb3997aSMilanka Ringwald        help='header file to be generated')
1071dbb3997aSMilanka Ringwald
1072dbb3997aSMilanka Ringwaldargs = parser.parse_args()
1073dbb3997aSMilanka Ringwald
107478b65b0aSMatthias Ringwald# add include path arguments
107578b65b0aSMatthias Ringwaldif args.I != None:
107678b65b0aSMatthias Ringwald    for d in args.I:
107778b65b0aSMatthias Ringwald        include_paths.append(os.path.normpath(d[0]))
107878b65b0aSMatthias Ringwald
1079dbb3997aSMilanka Ringwald# append default include paths
108078b65b0aSMatthias Ringwaldinclude_paths.extend(default_includes)
1081dbb3997aSMilanka Ringwald
1082b3fcedb9SMatthias Ringwaldtry:
1083b165f97bSMatthias Ringwald    # read defines from bluetooth_gatt.h
1084dbb3997aSMilanka Ringwald    gen_path = getFile( 'bluetooth_gatt.h' )
1085b165f97bSMatthias Ringwald    bluetooth_gatt = read_defines(gen_path)
1086b165f97bSMatthias Ringwald
1087dbb3997aSMilanka Ringwald    filename = args.hfile
1088dbb3997aSMilanka Ringwald    fin  = codecs.open (args.gattfile, encoding='utf-8')
1089285653b2SMatthias Ringwald
1090285653b2SMatthias Ringwald    # pass 1: create temp .h file
1091d78181d6SMatthias Ringwald    ftemp = tempfile.TemporaryFile(mode='w+t')
1092285653b2SMatthias Ringwald    parse(args.gattfile, fin, filename, sys.argv[0], ftemp)
1093285653b2SMatthias Ringwald    listHandles(ftemp)
1094285653b2SMatthias Ringwald
1095043f8832SMatthias Ringwald    # calc GATT Database Hash
1096043f8832SMatthias Ringwald    db_hash = aes_cmac(bytearray(16), database_hash_message)
1097043f8832SMatthias Ringwald    if isinstance(db_hash, str):
1098043f8832SMatthias Ringwald        # python2
1099043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % ord(i)) for i in db_hash]
1100043f8832SMatthias Ringwald    elif isinstance(db_hash, bytes):
1101043f8832SMatthias Ringwald        # python3
1102043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % i) for i in db_hash]
1103043f8832SMatthias Ringwald    else:
1104043f8832SMatthias Ringwald        print("AES CMAC returns unexpected type %s, abort" % type(db_hash))
1105043f8832SMatthias Ringwald        sys.exit(1)
1106043f8832SMatthias Ringwald    # reverse hash to get little endian
1107043f8832SMatthias Ringwald    db_hash_sequence.reverse()
1108043f8832SMatthias Ringwald    db_hash_string = ', '.join(db_hash_sequence) + ', '
1109043f8832SMatthias Ringwald
1110285653b2SMatthias Ringwald    # pass 2: insert GATT Database Hash
1111b3fcedb9SMatthias Ringwald    fout = open (filename, 'w')
1112285653b2SMatthias Ringwald    ftemp.seek(0)
1113285653b2SMatthias Ringwald    for line in ftemp:
1114043f8832SMatthias Ringwald        fout.write(line.replace('THE-DATABASE-HASH', db_hash_string))
1115b3fcedb9SMatthias Ringwald    fout.close()
1116285653b2SMatthias Ringwald    ftemp.close()
1117285653b2SMatthias Ringwald
1118b165f97bSMatthias Ringwald    print('Created %s' % filename)
1119b3fcedb9SMatthias Ringwald
1120b3fcedb9SMatthias Ringwaldexcept IOError as e:
11215ea34488SWayne Keenan    parser.print_help()
11225ea34488SWayne Keenan    print(e)
1123b3fcedb9SMatthias Ringwald    sys.exit(1)
1124b3fcedb9SMatthias Ringwald
1125b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n')
1126