xref: /btstack/tool/compile_gatt.py (revision c7a36ba4a1c52b4d2da501f725bc79dd32e65642)
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,
74b3fcedb9SMatthias Ringwald    'GATT_SERVICE_CHANGED' : 0x2a05,
75*c7a36ba4SMatthias Ringwald    'GATT_CLIENT_SUPPORTED_FEATURES' : 0x2b29,
76*c7a36ba4SMatthias Ringwald    'GATT_SERVER_SUPPORTED_FEATURES' : 0x2b3a,
77043f8832SMatthias Ringwald    'GATT_DATABASE_HASH' : 0x2b2a
78b3fcedb9SMatthias Ringwald}
79b3fcedb9SMatthias Ringwald
80e72176f8SMatthias Ringwaldsecurity_permsission = ['ANYBODY','ENCRYPTED', 'AUTHENTICATED', 'AUTHORIZED', 'AUTHENTICATED_SC']
81d7ec1d24SMatthias Ringwald
82b3fcedb9SMatthias Ringwaldproperty_flags = {
83eb6072adSMatthias Ringwald    # GATT Characteristic Properties
84b3fcedb9SMatthias Ringwald    'BROADCAST' :                   0x01,
85b3fcedb9SMatthias Ringwald    'READ' :                        0x02,
86b3fcedb9SMatthias Ringwald    'WRITE_WITHOUT_RESPONSE' :      0x04,
87b3fcedb9SMatthias Ringwald    'WRITE' :                       0x08,
88b3fcedb9SMatthias Ringwald    'NOTIFY':                       0x10,
89b3fcedb9SMatthias Ringwald    'INDICATE' :                    0x20,
90b3fcedb9SMatthias Ringwald    'AUTHENTICATED_SIGNED_WRITE' :  0x40,
91b3fcedb9SMatthias Ringwald    'EXTENDED_PROPERTIES' :         0x80,
92b3fcedb9SMatthias Ringwald    # custom BTstack extension
93b3fcedb9SMatthias Ringwald    'DYNAMIC':                      0x100,
94b3fcedb9SMatthias Ringwald    'LONG_UUID':                    0x200,
95e22a2612SMatthias Ringwald
96e22a2612SMatthias Ringwald    # read permissions
97e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_0':        0x400,
98e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_1':        0x800,
99e22a2612SMatthias Ringwald
100e22a2612SMatthias Ringwald    #
101b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_7':       0x6000,
102b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_8':       0x7000,
103b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_9':       0x8000,
104b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_10':      0x9000,
105b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_11':      0xa000,
106b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_12':      0xb000,
107b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_13':      0xc000,
108b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_14':      0xd000,
109b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_15':      0xe000,
110b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_16':      0xf000,
111e22a2612SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_MASK':    0xf000,
112eb6072adSMatthias Ringwald
113b3fcedb9SMatthias Ringwald    # only used by gatt compiler >= 0xffff
114b3fcedb9SMatthias Ringwald    # Extended Properties
115e72176f8SMatthias Ringwald    'RELIABLE_WRITE':              0x00010000,
116e72176f8SMatthias Ringwald    'AUTHENTICATION_REQUIRED':     0x00020000,
117e72176f8SMatthias Ringwald    'AUTHORIZATION_REQUIRED':      0x00040000,
118e72176f8SMatthias Ringwald    'READ_ANYBODY':                0x00080000,
119e72176f8SMatthias Ringwald    'READ_ENCRYPTED':              0x00100000,
120e72176f8SMatthias Ringwald    'READ_AUTHENTICATED':          0x00200000,
121e72176f8SMatthias Ringwald    'READ_AUTHENTICATED_SC':       0x00400000,
122e72176f8SMatthias Ringwald    'READ_AUTHORIZED':             0x00800000,
123e72176f8SMatthias Ringwald    'WRITE_ANYBODY':               0x01000000,
124e72176f8SMatthias Ringwald    'WRITE_ENCRYPTED':             0x02000000,
125e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED':         0x04000000,
126e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED_SC':      0x08000000,
127e72176f8SMatthias Ringwald    'WRITE_AUTHORIZED':            0x10000000,
128eb6072adSMatthias Ringwald
129eb6072adSMatthias Ringwald    # Broadcast, Notify, Indicate, Extended Properties are only used to describe a GATT Characteristic, but are free to use with att_db
130e72176f8SMatthias Ringwald    # - write permissions
131e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_0':      0x01,
132e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_1':      0x10,
133e72176f8SMatthias Ringwald    # - SC required
134e72176f8SMatthias Ringwald    'READ_PERMISSION_SC':          0x20,
135e72176f8SMatthias Ringwald    'WRITE_PERMISSION_SC':         0x80,
136b3fcedb9SMatthias Ringwald}
137b3fcedb9SMatthias Ringwald
138b3fcedb9SMatthias Ringwaldservices = dict()
139b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict()
140b3fcedb9SMatthias Ringwaldpresentation_formats = dict()
141b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = ""
142b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0
143b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = ""
144729074c4SMatthias Ringwalddefines_for_characteristics = []
145729074c4SMatthias Ringwalddefines_for_services = []
14678b65b0aSMatthias Ringwaldinclude_paths = []
147043f8832SMatthias Ringwalddatabase_hash_message = bytearray()
14829ba805bSMatthias Ringwaldservice_counter = {}
149b3fcedb9SMatthias Ringwald
150b3fcedb9SMatthias Ringwaldhandle = 1
151b3fcedb9SMatthias Ringwaldtotal_size = 0
152b3fcedb9SMatthias Ringwald
153043f8832SMatthias Ringwalddef aes_cmac(key, n):
154043f8832SMatthias Ringwald    if have_crypto:
155043f8832SMatthias Ringwald        cobj = CMAC.new(key, ciphermod=AES)
156043f8832SMatthias Ringwald        cobj.update(n)
157043f8832SMatthias Ringwald        return cobj.digest()
158043f8832SMatthias Ringwald    else:
1597490175eSMatthias Ringwald        # return random value
1607490175eSMatthias Ringwald        return os.urandom(16)
161043f8832SMatthias Ringwald
162b165f97bSMatthias Ringwalddef read_defines(infile):
163b165f97bSMatthias Ringwald    defines = dict()
164b165f97bSMatthias Ringwald    with open (infile, 'rt') as fin:
165b165f97bSMatthias Ringwald        for line in fin:
166b165f97bSMatthias Ringwald            parts = re.match('#define\s+(\w+)\s+(\w+)',line)
167b165f97bSMatthias Ringwald            if parts and len(parts.groups()) == 2:
168b165f97bSMatthias Ringwald                (key, value) = parts.groups()
169b165f97bSMatthias Ringwald                defines[key] = int(value, 16)
170b165f97bSMatthias Ringwald    return defines
171b165f97bSMatthias Ringwald
172b3fcedb9SMatthias Ringwalddef keyForUUID(uuid):
173b3fcedb9SMatthias Ringwald    keyUUID = ""
174b3fcedb9SMatthias Ringwald    for i in uuid:
175b3fcedb9SMatthias Ringwald        keyUUID += "%02x" % i
176b3fcedb9SMatthias Ringwald    return keyUUID
177b3fcedb9SMatthias Ringwald
178b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid):
179b3fcedb9SMatthias Ringwald    return uuid.replace('-', '_')
180b3fcedb9SMatthias Ringwald
181b3fcedb9SMatthias Ringwalddef twoByteLEFor(value):
182b3fcedb9SMatthias Ringwald    return [ (value & 0xff), (value >> 8)]
183b3fcedb9SMatthias Ringwald
184b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text):
185b3fcedb9SMatthias 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):
186b3fcedb9SMatthias Ringwald        return True
187b3fcedb9SMatthias Ringwald    return False
188b3fcedb9SMatthias Ringwald
189b3fcedb9SMatthias Ringwalddef parseUUID128(uuid):
190b3fcedb9SMatthias 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)
191b3fcedb9SMatthias Ringwald    uuid_bytes = []
192b3fcedb9SMatthias Ringwald    for i in range(8, 0, -1):
193b3fcedb9SMatthias Ringwald        uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16))
194b3fcedb9SMatthias Ringwald    return uuid_bytes
195b3fcedb9SMatthias Ringwald
196b3fcedb9SMatthias Ringwalddef parseUUID(uuid):
197b3fcedb9SMatthias Ringwald    if uuid in assigned_uuids:
198b3fcedb9SMatthias Ringwald        return twoByteLEFor(assigned_uuids[uuid])
199b165f97bSMatthias Ringwald    uuid_upper = uuid.upper().replace('.','_')
200b165f97bSMatthias Ringwald    if uuid_upper in bluetooth_gatt:
201b165f97bSMatthias Ringwald        return twoByteLEFor(bluetooth_gatt[uuid_upper])
202b3fcedb9SMatthias Ringwald    if is_128bit_uuid(uuid):
203b3fcedb9SMatthias Ringwald        return parseUUID128(uuid)
204b3fcedb9SMatthias Ringwald    uuidInt = int(uuid, 16)
205b3fcedb9SMatthias Ringwald    return twoByteLEFor(uuidInt)
206b3fcedb9SMatthias Ringwald
207b3fcedb9SMatthias Ringwalddef parseProperties(properties):
208b3fcedb9SMatthias Ringwald    value = 0
209b3fcedb9SMatthias Ringwald    parts = properties.split("|")
210b3fcedb9SMatthias Ringwald    for property in parts:
211b3fcedb9SMatthias Ringwald        property = property.strip()
212b3fcedb9SMatthias Ringwald        if property in property_flags:
213b3fcedb9SMatthias Ringwald            value |= property_flags[property]
214b3fcedb9SMatthias Ringwald        else:
215b3fcedb9SMatthias Ringwald            print("WARNING: property %s undefined" % (property))
216e22a2612SMatthias Ringwald
217e5ce8e0eSMilanka Ringwald    return value
218e5ce8e0eSMilanka Ringwald
219e5ce8e0eSMilanka Ringwalddef prettyPrintProperties(properties):
220e5ce8e0eSMilanka Ringwald    value = ""
221e5ce8e0eSMilanka Ringwald    parts = properties.split("|")
222e5ce8e0eSMilanka Ringwald    for property in parts:
223e5ce8e0eSMilanka Ringwald        property = property.strip()
224e5ce8e0eSMilanka Ringwald        if property in property_flags:
225e5ce8e0eSMilanka Ringwald            if value != "":
226e5ce8e0eSMilanka Ringwald                value += " | "
227e5ce8e0eSMilanka Ringwald            value += property
228e5ce8e0eSMilanka Ringwald        else:
229e5ce8e0eSMilanka Ringwald            print("WARNING: property %s undefined" % (property))
230e5ce8e0eSMilanka Ringwald
231e5ce8e0eSMilanka Ringwald    return value
232e5ce8e0eSMilanka Ringwald
233e22a2612SMatthias Ringwald
234e22a2612SMatthias Ringwalddef gatt_characteristic_properties(properties):
235e22a2612SMatthias Ringwald    return properties & 0xff
236e22a2612SMatthias Ringwald
237e22a2612SMatthias Ringwalddef att_flags(properties):
238e72176f8SMatthias Ringwald    # drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Properties (0x80) - not used for flags
239e72176f8SMatthias Ringwald    properties &= 0xffffff4e
240e22a2612SMatthias Ringwald
241e22a2612SMatthias Ringwald    # rw permissions distinct
242e22a2612SMatthias Ringwald    distinct_permissions_used = properties & (
243e22a2612SMatthias Ringwald        property_flags['READ_AUTHORIZED'] |
244e72176f8SMatthias Ringwald        property_flags['READ_AUTHENTICATED_SC'] |
245e22a2612SMatthias Ringwald        property_flags['READ_AUTHENTICATED'] |
246e22a2612SMatthias Ringwald        property_flags['READ_ENCRYPTED'] |
247e22a2612SMatthias Ringwald        property_flags['READ_ANYBODY'] |
248e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHORIZED'] |
249e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED'] |
250e72176f8SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED_SC'] |
251e22a2612SMatthias Ringwald        property_flags['WRITE_ENCRYPTED'] |
252e22a2612SMatthias Ringwald        property_flags['WRITE_ANYBODY']
253e22a2612SMatthias Ringwald    ) != 0
254e22a2612SMatthias Ringwald
255e22a2612SMatthias Ringwald    # post process properties
256e22a2612SMatthias Ringwald    encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0
257e22a2612SMatthias Ringwald
258d7ec1d24SMatthias Ringwald    # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted
259e22a2612SMatthias Ringwald    if encryption_key_size_specified and not distinct_permissions_used:
260e22a2612SMatthias Ringwald        properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED']
261e22a2612SMatthias Ringwald
262d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated
263d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used:
264d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED']
265d7ec1d24SMatthias Ringwald
266d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized
267d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used:
268d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED']
269d7ec1d24SMatthias Ringwald
270d7ec1d24SMatthias Ringwald    # determine read/write security requirements
271d7ec1d24SMatthias Ringwald    read_security_level  = 0
272d7ec1d24SMatthias Ringwald    write_security_level = 0
273e72176f8SMatthias Ringwald    read_requires_sc     = False
274e72176f8SMatthias Ringwald    write_requires_sc    = False
275e22a2612SMatthias Ringwald    if properties & property_flags['READ_AUTHORIZED']:
276d7ec1d24SMatthias Ringwald        read_security_level = 3
277e22a2612SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED']:
278d7ec1d24SMatthias Ringwald        read_security_level = 2
279e72176f8SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED_SC']:
280e72176f8SMatthias Ringwald        read_security_level = 2
281e72176f8SMatthias Ringwald        read_requires_sc = True
282e22a2612SMatthias Ringwald    elif properties & property_flags['READ_ENCRYPTED']:
283d7ec1d24SMatthias Ringwald        read_security_level = 1
284e22a2612SMatthias Ringwald    if properties & property_flags['WRITE_AUTHORIZED']:
285d7ec1d24SMatthias Ringwald        write_security_level = 3
286e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED']:
287d7ec1d24SMatthias Ringwald        write_security_level = 2
288e72176f8SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED_SC']:
289e72176f8SMatthias Ringwald        write_security_level = 2
290e72176f8SMatthias Ringwald        write_requires_sc = True
291e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_ENCRYPTED']:
292d7ec1d24SMatthias Ringwald        write_security_level = 1
293d7ec1d24SMatthias Ringwald
294d7ec1d24SMatthias Ringwald    # map security requirements to flags
295d7ec1d24SMatthias Ringwald    if read_security_level & 2:
296d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_1']
297d7ec1d24SMatthias Ringwald    if read_security_level & 1:
298d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_0']
299e72176f8SMatthias Ringwald    if read_requires_sc:
300e72176f8SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_SC']
301d7ec1d24SMatthias Ringwald    if write_security_level & 2:
302d7ec1d24SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_1']
303d7ec1d24SMatthias Ringwald    if write_security_level & 1:
304e22a2612SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_0']
305e72176f8SMatthias Ringwald    if write_requires_sc:
306e72176f8SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_SC']
307e22a2612SMatthias Ringwald
308e22a2612SMatthias Ringwald    return properties
309e22a2612SMatthias Ringwald
310d7ec1d24SMatthias Ringwalddef write_permissions_and_key_size_flags_from_properties(properties):
311e22a2612SMatthias Ringwald    return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1'])
312b3fcedb9SMatthias Ringwald
313b3fcedb9SMatthias Ringwalddef write_8(fout, value):
314b3fcedb9SMatthias Ringwald    fout.write( "0x%02x, " % (value & 0xff))
315b3fcedb9SMatthias Ringwald
316b3fcedb9SMatthias Ringwalddef write_16(fout, value):
317b3fcedb9SMatthias Ringwald    fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff))
318b3fcedb9SMatthias Ringwald
319285653b2SMatthias Ringwalddef write_uuid(fout, uuid):
320b3fcedb9SMatthias Ringwald    for byte in uuid:
321b3fcedb9SMatthias Ringwald        fout.write( "0x%02x, " % byte)
322b3fcedb9SMatthias Ringwald
323b3fcedb9SMatthias Ringwalddef write_string(fout, text):
324b3fcedb9SMatthias Ringwald    for l in text.lstrip('"').rstrip('"'):
325b3fcedb9SMatthias Ringwald        write_8(fout, ord(l))
326b3fcedb9SMatthias Ringwald
327b3fcedb9SMatthias Ringwalddef write_sequence(fout, text):
328b3fcedb9SMatthias Ringwald    parts = text.split()
329b3fcedb9SMatthias Ringwald    for part in parts:
330b3fcedb9SMatthias Ringwald        fout.write("0x%s, " % (part.strip()))
331b3fcedb9SMatthias Ringwald
332043f8832SMatthias Ringwalddef write_database_hash(fout):
333043f8832SMatthias Ringwald    fout.write("THE-DATABASE-HASH")
334043f8832SMatthias Ringwald
335b3fcedb9SMatthias Ringwalddef write_indent(fout):
336b3fcedb9SMatthias Ringwald    fout.write("    ")
337b3fcedb9SMatthias Ringwald
338d7ec1d24SMatthias Ringwalddef read_permissions_from_flags(flags):
339d7ec1d24SMatthias Ringwald    permissions = 0
340d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_0']:
341d7ec1d24SMatthias Ringwald        permissions |= 1
342d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_1']:
343d7ec1d24SMatthias Ringwald        permissions |= 2
344e72176f8SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_SC'] and permissions == 2:
345e72176f8SMatthias Ringwald        permissions = 4
346d7ec1d24SMatthias Ringwald    return permissions
347d7ec1d24SMatthias Ringwald
348d7ec1d24SMatthias Ringwalddef write_permissions_from_flags(flags):
349d7ec1d24SMatthias Ringwald    permissions = 0
350d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_0']:
351d7ec1d24SMatthias Ringwald        permissions |= 1
352d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_1']:
353d7ec1d24SMatthias Ringwald        permissions |= 2
354e72176f8SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_SC'] and permissions == 2:
355e72176f8SMatthias Ringwald        permissions = 4
356d7ec1d24SMatthias Ringwald    return permissions
357d7ec1d24SMatthias Ringwald
358d7ec1d24SMatthias Ringwalddef encryption_key_size_from_flags(flags):
359d7ec1d24SMatthias Ringwald    encryption_key_size = (flags & 0xf000) >> 12
360d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
361d7ec1d24SMatthias Ringwald        encryption_key_size += 1
362d7ec1d24SMatthias Ringwald    return encryption_key_size
363d7ec1d24SMatthias Ringwald
364b3fcedb9SMatthias Ringwalddef is_string(text):
365b3fcedb9SMatthias Ringwald    for item in text.split(" "):
366b3fcedb9SMatthias Ringwald        if not all(c in string.hexdigits for c in item):
367b3fcedb9SMatthias Ringwald            return True
368b3fcedb9SMatthias Ringwald    return False
369b3fcedb9SMatthias Ringwald
370b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties):
371b3fcedb9SMatthias Ringwald    return properties & (property_flags['NOTIFY'] | property_flags['INDICATE'])
372b3fcedb9SMatthias Ringwald
373729074c4SMatthias Ringwalddef serviceDefinitionComplete(fout):
374729074c4SMatthias Ringwald    global services
375729074c4SMatthias Ringwald    if current_service_uuid_string:
376e5ce8e0eSMilanka Ringwald        # fout.write("\n")
37729ba805bSMatthias Ringwald        # update num instances for this service
37829ba805bSMatthias Ringwald        count = 1
37929ba805bSMatthias Ringwald        if current_service_uuid_string in service_counter:
38029ba805bSMatthias Ringwald            count = service_counter[current_service_uuid_string] + 1
38129ba805bSMatthias Ringwald        service_counter[current_service_uuid_string] = count
38209e90ba6SMilanka Ringwald        # add old defines without service counter for first instance for backward compatibility
38329ba805bSMatthias Ringwald        if count == 1:
384729074c4SMatthias Ringwald            defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle))
385729074c4SMatthias Ringwald            defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1))
38609e90ba6SMilanka Ringwald
38729ba805bSMatthias Ringwald        # unified defines indicating instance
38829ba805bSMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_%02x_START_HANDLE 0x%04x' % (current_service_uuid_string, count, current_service_start_handle))
38929ba805bSMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_%02x_END_HANDLE 0x%04x' % (current_service_uuid_string, count, handle-1))
39009e90ba6SMilanka Ringwald        services[current_service_uuid_string+"_" + str(count)] = [current_service_start_handle, handle - 1, count]
391729074c4SMatthias Ringwald
392d7ec1d24SMatthias Ringwalddef dump_flags(fout, flags):
393d7ec1d24SMatthias Ringwald    global security_permsission
394d7ec1d24SMatthias Ringwald    encryption_key_size = encryption_key_size_from_flags(flags)
395d7ec1d24SMatthias Ringwald    read_permissions    = security_permsission[read_permissions_from_flags(flags)]
396d7ec1d24SMatthias Ringwald    write_permissions   = security_permsission[write_permissions_from_flags(flags)]
397d7ec1d24SMatthias Ringwald    write_indent(fout)
398d7ec1d24SMatthias Ringwald    fout.write('// ')
399d7ec1d24SMatthias Ringwald    first = 1
400d7ec1d24SMatthias Ringwald    if flags & property_flags['READ']:
401d7ec1d24SMatthias Ringwald        fout.write('READ_%s' % read_permissions)
402d7ec1d24SMatthias Ringwald        first = 0
403d7ec1d24SMatthias Ringwald    if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']):
404d7ec1d24SMatthias Ringwald        if not first:
405d7ec1d24SMatthias Ringwald            fout.write(', ')
406d7ec1d24SMatthias Ringwald        first = 0
407d7ec1d24SMatthias Ringwald        fout.write('WRITE_%s' % write_permissions)
408d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
409d7ec1d24SMatthias Ringwald        if not first:
410d7ec1d24SMatthias Ringwald            fout.write(', ')
411d7ec1d24SMatthias Ringwald        first = 0
412d7ec1d24SMatthias Ringwald        fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size)
413d7ec1d24SMatthias Ringwald    fout.write('\n')
414d7ec1d24SMatthias Ringwald
415043f8832SMatthias Ringwalddef database_hash_append_uint8(value):
416043f8832SMatthias Ringwald    global database_hash_message
417043f8832SMatthias Ringwald    database_hash_message.append(value)
418043f8832SMatthias Ringwald
419043f8832SMatthias Ringwalddef database_hash_append_uint16(value):
420043f8832SMatthias Ringwald    global database_hash_message
421043f8832SMatthias Ringwald    database_hash_append_uint8(value & 0xff)
422043f8832SMatthias Ringwald    database_hash_append_uint8((value >> 8) & 0xff)
423043f8832SMatthias Ringwald
424043f8832SMatthias Ringwalddef database_hash_append_value(value):
425043f8832SMatthias Ringwald    global database_hash_message
426043f8832SMatthias Ringwald    for byte in value:
427043f8832SMatthias Ringwald        database_hash_append_uint8(byte)
428043f8832SMatthias Ringwald
429b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type):
430b3fcedb9SMatthias Ringwald    global handle
431b3fcedb9SMatthias Ringwald    global total_size
432b3fcedb9SMatthias Ringwald    global current_service_uuid_string
433b3fcedb9SMatthias Ringwald    global current_service_start_handle
434b3fcedb9SMatthias Ringwald
435729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
436b3fcedb9SMatthias Ringwald
437d7ec1d24SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
438b3fcedb9SMatthias Ringwald
439b3fcedb9SMatthias Ringwald    write_indent(fout)
440b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
441b3fcedb9SMatthias Ringwald
442b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
443b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
444b3fcedb9SMatthias Ringwald
445b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size + 2
446b3fcedb9SMatthias Ringwald
447b3fcedb9SMatthias Ringwald    if service_type == 0x2802:
448b3fcedb9SMatthias Ringwald        size += 4
449b3fcedb9SMatthias Ringwald
450b3fcedb9SMatthias Ringwald    write_indent(fout)
451b3fcedb9SMatthias Ringwald    write_16(fout, size)
452d7ec1d24SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
453b3fcedb9SMatthias Ringwald    write_16(fout, handle)
454b3fcedb9SMatthias Ringwald    write_16(fout, service_type)
455285653b2SMatthias Ringwald    write_uuid(fout, uuid)
456b3fcedb9SMatthias Ringwald    fout.write("\n")
457b3fcedb9SMatthias Ringwald
458043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
459043f8832SMatthias Ringwald    database_hash_append_uint16(service_type)
460043f8832SMatthias Ringwald    database_hash_append_value(uuid)
461043f8832SMatthias Ringwald
462729074c4SMatthias Ringwald    current_service_uuid_string = c_string_for_uuid(parts[1])
463b3fcedb9SMatthias Ringwald    current_service_start_handle = handle
464b3fcedb9SMatthias Ringwald    handle = handle + 1
465b3fcedb9SMatthias Ringwald    total_size = total_size + size
466b3fcedb9SMatthias Ringwald
467b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts):
468b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2800)
469b3fcedb9SMatthias Ringwald
470b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts):
471b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2801)
472b3fcedb9SMatthias Ringwald
473b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts):
474b3fcedb9SMatthias Ringwald    global handle
475b3fcedb9SMatthias Ringwald    global total_size
476b3fcedb9SMatthias Ringwald
477e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
478b3fcedb9SMatthias Ringwald
479b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
480b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
481b3fcedb9SMatthias Ringwald    if uuid_size > 2:
482b3fcedb9SMatthias Ringwald        uuid_size = 0
483b3fcedb9SMatthias Ringwald
484b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 4 + uuid_size
485b3fcedb9SMatthias Ringwald
486729074c4SMatthias Ringwald    keyUUID = c_string_for_uuid(parts[1])
4876a72ce79SMilanka Ringwald    keys_to_delete = []
4886a72ce79SMilanka Ringwald
48909e90ba6SMilanka Ringwald    for (serviceUUID, service) in services.items():
49009e90ba6SMilanka Ringwald        if serviceUUID.startswith(keyUUID):
49109e90ba6SMilanka Ringwald            write_indent(fout)
49209e90ba6SMilanka Ringwald            fout.write('// 0x%04x %s - range [0x%04x, 0x%04x]\n' % (handle, '-'.join(parts), services[serviceUUID][0], services[serviceUUID][1]))
493b3fcedb9SMatthias Ringwald
494b3fcedb9SMatthias Ringwald            write_indent(fout)
495b3fcedb9SMatthias Ringwald            write_16(fout, size)
496e22a2612SMatthias Ringwald            write_16(fout, read_only_anybody_flags)
497b3fcedb9SMatthias Ringwald            write_16(fout, handle)
498b3fcedb9SMatthias Ringwald            write_16(fout, 0x2802)
49909e90ba6SMilanka Ringwald            write_16(fout, services[serviceUUID][0])
50009e90ba6SMilanka Ringwald            write_16(fout, services[serviceUUID][1])
501b3fcedb9SMatthias Ringwald            if uuid_size > 0:
502285653b2SMatthias Ringwald                write_uuid(fout, uuid)
503b3fcedb9SMatthias Ringwald            fout.write("\n")
504b3fcedb9SMatthias Ringwald
505043f8832SMatthias Ringwald            database_hash_append_uint16(handle)
506043f8832SMatthias Ringwald            database_hash_append_uint16(0x2802)
50709e90ba6SMilanka Ringwald            database_hash_append_uint16(services[serviceUUID][0])
50809e90ba6SMilanka Ringwald            database_hash_append_uint16(services[serviceUUID][1])
509043f8832SMatthias Ringwald            if uuid_size > 0:
510043f8832SMatthias Ringwald                database_hash_append_value(uuid)
511043f8832SMatthias Ringwald
5126a72ce79SMilanka Ringwald            keys_to_delete.append(serviceUUID)
5136a72ce79SMilanka Ringwald
514b3fcedb9SMatthias Ringwald            handle = handle + 1
515b3fcedb9SMatthias Ringwald            total_size = total_size + size
516b3fcedb9SMatthias Ringwald
5176a72ce79SMilanka Ringwald    for key in keys_to_delete:
5186a72ce79SMilanka Ringwald        services.pop(key)
5196a72ce79SMilanka Ringwald
5206a72ce79SMilanka Ringwald
521b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts):
522b3fcedb9SMatthias Ringwald    global handle
523b3fcedb9SMatthias Ringwald    global total_size
524b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
525b3fcedb9SMatthias Ringwald    global characteristic_indices
526b3fcedb9SMatthias Ringwald
527e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
528b3fcedb9SMatthias Ringwald
529b3fcedb9SMatthias Ringwald    # enumerate characteristics with same UUID, using optional name tag if available
530b3fcedb9SMatthias Ringwald    current_characteristic_uuid_string = c_string_for_uuid(parts[1]);
531b3fcedb9SMatthias Ringwald    index = 1
532b3fcedb9SMatthias Ringwald    if current_characteristic_uuid_string in characteristic_indices:
533b3fcedb9SMatthias Ringwald        index = characteristic_indices[current_characteristic_uuid_string] + 1
534b3fcedb9SMatthias Ringwald    characteristic_indices[current_characteristic_uuid_string] = index
535b3fcedb9SMatthias Ringwald    if len(parts) > 4:
536b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_')
537b3fcedb9SMatthias Ringwald    else:
538b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += ('_%02x' % index)
539b3fcedb9SMatthias Ringwald
540b3fcedb9SMatthias Ringwald    uuid       = parseUUID(parts[1])
541b3fcedb9SMatthias Ringwald    uuid_size  = len(uuid)
542b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[2])
543b3fcedb9SMatthias Ringwald    value = ', '.join([str(x) for x in parts[3:]])
544b3fcedb9SMatthias Ringwald
545b3fcedb9SMatthias Ringwald    # reliable writes is defined in an extended properties
546b3fcedb9SMatthias Ringwald    if (properties & property_flags['RELIABLE_WRITE']):
547b3fcedb9SMatthias Ringwald        properties = properties | property_flags['EXTENDED_PROPERTIES']
548b3fcedb9SMatthias Ringwald
549b3fcedb9SMatthias Ringwald    write_indent(fout)
550e5ce8e0eSMilanka Ringwald    fout.write('// 0x%04x %s - %s\n' % (handle, '-'.join(parts[0:2]), prettyPrintProperties(parts[2])))
551b3fcedb9SMatthias Ringwald
552e22a2612SMatthias Ringwald
553e22a2612SMatthias Ringwald    characteristic_properties = gatt_characteristic_properties(properties)
554b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (1+2+uuid_size)
555b3fcedb9SMatthias Ringwald    write_indent(fout)
556b3fcedb9SMatthias Ringwald    write_16(fout, size)
557e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
558b3fcedb9SMatthias Ringwald    write_16(fout, handle)
559b3fcedb9SMatthias Ringwald    write_16(fout, 0x2803)
560e22a2612SMatthias Ringwald    write_8(fout, characteristic_properties)
561b3fcedb9SMatthias Ringwald    write_16(fout, handle+1)
562285653b2SMatthias Ringwald    write_uuid(fout, uuid)
563b3fcedb9SMatthias Ringwald    fout.write("\n")
564b3fcedb9SMatthias Ringwald    total_size = total_size + size
565b3fcedb9SMatthias Ringwald
566043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
567043f8832SMatthias Ringwald    database_hash_append_uint16(0x2803)
568043f8832SMatthias Ringwald    database_hash_append_uint8(characteristic_properties)
569043f8832SMatthias Ringwald    database_hash_append_uint16(handle+1)
570043f8832SMatthias Ringwald    database_hash_append_value(uuid)
571043f8832SMatthias Ringwald
5723c5d4072SMatthias Ringwald    handle = handle + 1
5733c5d4072SMatthias Ringwald
574043f8832SMatthias Ringwald    uuid_is_database_hash = len(uuid) == 2 and uuid[0] == 0x2a and uuid[1] == 0x2b
575043f8832SMatthias Ringwald
576b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size
577043f8832SMatthias Ringwald    if uuid_is_database_hash:
578043f8832SMatthias Ringwald        size +=  16
579043f8832SMatthias Ringwald    else:
580b3fcedb9SMatthias Ringwald        if is_string(value):
581b3fcedb9SMatthias Ringwald            size = size + len(value)
582b3fcedb9SMatthias Ringwald        else:
583b3fcedb9SMatthias Ringwald            size = size + len(value.split())
584b3fcedb9SMatthias Ringwald
585e22a2612SMatthias Ringwald    value_flags = att_flags(properties)
5868ea3236cSMatthias Ringwald
5878ea3236cSMatthias Ringwald    # add UUID128 flag for value handle
588b3fcedb9SMatthias Ringwald    if uuid_size == 16:
589e22a2612SMatthias Ringwald        value_flags = value_flags | property_flags['LONG_UUID'];
590b3fcedb9SMatthias Ringwald
591b3fcedb9SMatthias Ringwald    write_indent(fout)
592e5ce8e0eSMilanka Ringwald    properties_string = prettyPrintProperties(parts[2])
593e5ce8e0eSMilanka Ringwald    if "DYNAMIC" in properties_string:
594e5ce8e0eSMilanka Ringwald        fout.write('// 0x%04x VALUE %s - %s\n' % (handle, '-'.join(parts[0:2]), prettyPrintProperties(parts[2])))
595e5ce8e0eSMilanka Ringwald    else:
596e5ce8e0eSMilanka Ringwald        fout.write('// 0x%04x VALUE %s - %s -'"'%s'"'\n' % (
597e5ce8e0eSMilanka Ringwald        handle, '-'.join(parts[0:2]), prettyPrintProperties(parts[2]), value))
598d7ec1d24SMatthias Ringwald
599d7ec1d24SMatthias Ringwald    dump_flags(fout, value_flags)
600d7ec1d24SMatthias Ringwald
601b3fcedb9SMatthias Ringwald    write_indent(fout)
602b3fcedb9SMatthias Ringwald    write_16(fout, size)
603e22a2612SMatthias Ringwald    write_16(fout, value_flags)
604b3fcedb9SMatthias Ringwald    write_16(fout, handle)
605285653b2SMatthias Ringwald    write_uuid(fout, uuid)
606043f8832SMatthias Ringwald    if uuid_is_database_hash:
607043f8832SMatthias Ringwald        write_database_hash(fout)
608043f8832SMatthias Ringwald    else:
609b3fcedb9SMatthias Ringwald        if is_string(value):
610b3fcedb9SMatthias Ringwald            write_string(fout, value)
611b3fcedb9SMatthias Ringwald        else:
612b3fcedb9SMatthias Ringwald            write_sequence(fout,value)
613b3fcedb9SMatthias Ringwald
614b3fcedb9SMatthias Ringwald    fout.write("\n")
615729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
616b3fcedb9SMatthias Ringwald    handle = handle + 1
617b3fcedb9SMatthias Ringwald
618b3fcedb9SMatthias Ringwald    if add_client_characteristic_configuration(properties):
619e22a2612SMatthias Ringwald        # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC
620d7ec1d24SMatthias Ringwald        flags  = write_permissions_and_key_size_flags_from_properties(properties)
621e22a2612SMatthias Ringwald        flags |= property_flags['READ']
622e22a2612SMatthias Ringwald        flags |= property_flags['WRITE']
6239be4aecfSMatthias Ringwald        flags |= property_flags['WRITE_WITHOUT_RESPONSE']
624e22a2612SMatthias Ringwald        flags |= property_flags['DYNAMIC']
625b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
626d7ec1d24SMatthias Ringwald
627b3fcedb9SMatthias Ringwald        write_indent(fout)
628b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle))
629d7ec1d24SMatthias Ringwald
630d7ec1d24SMatthias Ringwald        dump_flags(fout, flags)
631d7ec1d24SMatthias Ringwald
632b3fcedb9SMatthias Ringwald        write_indent(fout)
633b3fcedb9SMatthias Ringwald        write_16(fout, size)
634e22a2612SMatthias Ringwald        write_16(fout, flags)
635b3fcedb9SMatthias Ringwald        write_16(fout, handle)
636b3fcedb9SMatthias Ringwald        write_16(fout, 0x2902)
637b3fcedb9SMatthias Ringwald        write_16(fout, 0)
638b3fcedb9SMatthias Ringwald        fout.write("\n")
639043f8832SMatthias Ringwald
640043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
641043f8832SMatthias Ringwald        database_hash_append_uint16(0x2902)
642043f8832SMatthias Ringwald
643729074c4SMatthias Ringwald        defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
644b3fcedb9SMatthias Ringwald        handle = handle + 1
645b3fcedb9SMatthias Ringwald
646043f8832SMatthias Ringwald
647b3fcedb9SMatthias Ringwald    if properties & property_flags['RELIABLE_WRITE']:
648b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
649b3fcedb9SMatthias Ringwald        write_indent(fout)
650b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle))
651b3fcedb9SMatthias Ringwald        write_indent(fout)
652b3fcedb9SMatthias Ringwald        write_16(fout, size)
653e22a2612SMatthias Ringwald        write_16(fout, read_only_anybody_flags)
654b3fcedb9SMatthias Ringwald        write_16(fout, handle)
655b3fcedb9SMatthias Ringwald        write_16(fout, 0x2900)
656b3fcedb9SMatthias Ringwald        write_16(fout, 1)   # Reliable Write
657b3fcedb9SMatthias Ringwald        fout.write("\n")
658043f8832SMatthias Ringwald
659043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
660043f8832SMatthias Ringwald        database_hash_append_uint16(0x2900)
661043f8832SMatthias Ringwald        database_hash_append_uint16(1)
662043f8832SMatthias Ringwald
663b3fcedb9SMatthias Ringwald        handle = handle + 1
664b3fcedb9SMatthias Ringwald
66517215335SMatthias Ringwalddef parseGenericDynamicDescriptor(fout, parts, uuid, name):
666b3fcedb9SMatthias Ringwald    global handle
667b3fcedb9SMatthias Ringwald    global total_size
668b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
669b3fcedb9SMatthias Ringwald
670b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
671b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
672b3fcedb9SMatthias Ringwald
673e22a2612SMatthias Ringwald    # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY
674d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
675e22a2612SMatthias Ringwald    flags |= property_flags['READ']
676e22a2612SMatthias Ringwald    flags |= property_flags['WRITE']
677e22a2612SMatthias Ringwald    flags |= property_flags['DYNAMIC']
678e22a2612SMatthias Ringwald
679b3fcedb9SMatthias Ringwald    write_indent(fout)
68017215335SMatthias Ringwald    fout.write('// 0x%04x %s-%s\n' % (handle, name, '-'.join(parts[1:])))
681d7ec1d24SMatthias Ringwald
682d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
683d7ec1d24SMatthias Ringwald
684b3fcedb9SMatthias Ringwald    write_indent(fout)
685b3fcedb9SMatthias Ringwald    write_16(fout, size)
686e22a2612SMatthias Ringwald    write_16(fout, flags)
687b3fcedb9SMatthias Ringwald    write_16(fout, handle)
688766e9e27SMatthias Ringwald    write_16(fout, uuid)
689b3fcedb9SMatthias Ringwald    fout.write("\n")
690043f8832SMatthias Ringwald
691043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
69217215335SMatthias Ringwald    database_hash_append_uint16(uuid)
693043f8832SMatthias Ringwald
69417215335SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_%s_HANDLE 0x%04x' % (current_characteristic_uuid_string, name, handle))
695b3fcedb9SMatthias Ringwald    handle = handle + 1
696b3fcedb9SMatthias Ringwald
69717215335SMatthias Ringwalddef parseGenericDynamicReadOnlyDescriptor(fout, parts, uuid, name):
69817215335SMatthias Ringwald    global handle
69917215335SMatthias Ringwald    global total_size
70017215335SMatthias Ringwald    global current_characteristic_uuid_string
70117215335SMatthias Ringwald
70217215335SMatthias Ringwald    properties = parseProperties(parts[1])
70317215335SMatthias Ringwald    size = 2 + 2 + 2 + 2
70417215335SMatthias Ringwald
70517215335SMatthias Ringwald    # use write permissions and encryption key size from attribute value and set READ, DYNAMIC, READ_ANYBODY
70617215335SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
70717215335SMatthias Ringwald    flags |= property_flags['READ']
70817215335SMatthias Ringwald    flags |= property_flags['DYNAMIC']
70917215335SMatthias Ringwald
71017215335SMatthias Ringwald    write_indent(fout)
71117215335SMatthias Ringwald    fout.write('// 0x%04x %s-%s\n' % (handle, name, '-'.join(parts[1:])))
71217215335SMatthias Ringwald
71317215335SMatthias Ringwald    dump_flags(fout, flags)
71417215335SMatthias Ringwald
71517215335SMatthias Ringwald    write_indent(fout)
71617215335SMatthias Ringwald    write_16(fout, size)
71717215335SMatthias Ringwald    write_16(fout, flags)
71817215335SMatthias Ringwald    write_16(fout, handle)
719079990e8SSanaa Hamel    write_16(fout, uuid)
72017215335SMatthias Ringwald    fout.write("\n")
72117215335SMatthias Ringwald
72217215335SMatthias Ringwald    database_hash_append_uint16(handle)
72317215335SMatthias Ringwald    database_hash_append_uint16(uuid)
72417215335SMatthias Ringwald
72517215335SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_%s_HANDLE 0x%04x' % (current_characteristic_uuid_string, name, handle))
72617215335SMatthias Ringwald    handle = handle + 1
72717215335SMatthias Ringwald
72817215335SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts):
72917215335SMatthias Ringwald    parseGenericDynamicDescriptor(fout, parts, 0x2903, 'SERVER_CONFIGURATION')
73017215335SMatthias Ringwald
731b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts):
732b3fcedb9SMatthias Ringwald    global handle
733b3fcedb9SMatthias Ringwald    global total_size
734b3fcedb9SMatthias Ringwald
735e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
736b3fcedb9SMatthias Ringwald
737b3fcedb9SMatthias Ringwald    identifier = parts[1]
738b3fcedb9SMatthias Ringwald    presentation_formats[identifier] = handle
739b3fcedb9SMatthias Ringwald    # print("format '%s' with handle %d\n" % (identifier, handle))
740b3fcedb9SMatthias Ringwald
741b3fcedb9SMatthias Ringwald    format     = parts[2]
742b3fcedb9SMatthias Ringwald    exponent   = parts[3]
743b3fcedb9SMatthias Ringwald    unit       = parseUUID(parts[4])
744b3fcedb9SMatthias Ringwald    name_space = parts[5]
745b3fcedb9SMatthias Ringwald    description = parseUUID(parts[6])
746b3fcedb9SMatthias Ringwald
747b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 7
748b3fcedb9SMatthias Ringwald
749b3fcedb9SMatthias Ringwald    write_indent(fout)
750b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
751b3fcedb9SMatthias Ringwald    write_indent(fout)
752b3fcedb9SMatthias Ringwald    write_16(fout, size)
753e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
754b3fcedb9SMatthias Ringwald    write_16(fout, handle)
755b3fcedb9SMatthias Ringwald    write_16(fout, 0x2904)
756b3fcedb9SMatthias Ringwald    write_sequence(fout, format)
757b3fcedb9SMatthias Ringwald    write_sequence(fout, exponent)
758285653b2SMatthias Ringwald    write_uuid(fout, unit)
759b3fcedb9SMatthias Ringwald    write_sequence(fout, name_space)
760285653b2SMatthias Ringwald    write_uuid(fout, description)
761b3fcedb9SMatthias Ringwald    fout.write("\n")
762043f8832SMatthias Ringwald
763043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
764043f8832SMatthias Ringwald    database_hash_append_uint16(0x2904)
765043f8832SMatthias Ringwald
766b3fcedb9SMatthias Ringwald    handle = handle + 1
767b3fcedb9SMatthias Ringwald
768b3fcedb9SMatthias Ringwald
769b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts):
770b3fcedb9SMatthias Ringwald    global handle
771b3fcedb9SMatthias Ringwald    global total_size
772b3fcedb9SMatthias Ringwald
773e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
774b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2
775b3fcedb9SMatthias Ringwald
776b3fcedb9SMatthias Ringwald    write_indent(fout)
777b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
778b3fcedb9SMatthias Ringwald    write_indent(fout)
779b3fcedb9SMatthias Ringwald    write_16(fout, size)
780e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
781b3fcedb9SMatthias Ringwald    write_16(fout, handle)
782b3fcedb9SMatthias Ringwald    write_16(fout, 0x2905)
783b3fcedb9SMatthias Ringwald    for identifier in parts[1:]:
7848c70002dSMatthias Ringwald        if not identifier in presentation_formats:
7858c70002dSMatthias Ringwald            print(parts)
786b3fcedb9SMatthias Ringwald            print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier)
787b3fcedb9SMatthias Ringwald            sys.exit(1)
7888c70002dSMatthias Ringwald        format_handle = presentation_formats[identifier]
789b3fcedb9SMatthias Ringwald        write_16(fout, format_handle)
790b3fcedb9SMatthias Ringwald    fout.write("\n")
791043f8832SMatthias Ringwald
792043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
793043f8832SMatthias Ringwald    database_hash_append_uint16(0x2905)
794043f8832SMatthias Ringwald
795b3fcedb9SMatthias Ringwald    handle = handle + 1
796b3fcedb9SMatthias Ringwald
79705146de0SMilanka Ringwalddef parseExternalReportReference(fout, parts):
79805146de0SMilanka Ringwald    global handle
79905146de0SMilanka Ringwald    global total_size
80005146de0SMilanka Ringwald
80105146de0SMilanka Ringwald    read_only_anybody_flags = property_flags['READ'];
80205146de0SMilanka Ringwald    size = 2 + 2 + 2 + 2 + 2
80305146de0SMilanka Ringwald
80405146de0SMilanka Ringwald    report_uuid = int(parts[2], 16)
80505146de0SMilanka Ringwald
80605146de0SMilanka Ringwald    write_indent(fout)
80705146de0SMilanka Ringwald    fout.write('// 0x%04x EXTERNAL_REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
80805146de0SMilanka Ringwald    write_indent(fout)
80905146de0SMilanka Ringwald    write_16(fout, size)
81005146de0SMilanka Ringwald    write_16(fout, read_only_anybody_flags)
81105146de0SMilanka Ringwald    write_16(fout, handle)
81205146de0SMilanka Ringwald    write_16(fout, 0x2907)
81305146de0SMilanka Ringwald    write_16(fout, report_uuid)
81405146de0SMilanka Ringwald    fout.write("\n")
81505146de0SMilanka Ringwald    handle = handle + 1
81605146de0SMilanka Ringwald
817b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts):
818b3fcedb9SMatthias Ringwald    global handle
819b3fcedb9SMatthias Ringwald    global total_size
820b3fcedb9SMatthias Ringwald
821e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
822b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1 + 1
823b3fcedb9SMatthias Ringwald
824231a3c5dSMatthias Ringwald    report_id = parts[2]
825231a3c5dSMatthias Ringwald    report_type = parts[3]
826b3fcedb9SMatthias Ringwald
827b3fcedb9SMatthias Ringwald    write_indent(fout)
828b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
829b3fcedb9SMatthias Ringwald    write_indent(fout)
830b3fcedb9SMatthias Ringwald    write_16(fout, size)
831e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
832b3fcedb9SMatthias Ringwald    write_16(fout, handle)
833b3fcedb9SMatthias Ringwald    write_16(fout, 0x2908)
834b3fcedb9SMatthias Ringwald    write_sequence(fout, report_id)
835b3fcedb9SMatthias Ringwald    write_sequence(fout, report_type)
836b3fcedb9SMatthias Ringwald    fout.write("\n")
837b3fcedb9SMatthias Ringwald    handle = handle + 1
838b3fcedb9SMatthias Ringwald
839b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts):
840b3fcedb9SMatthias Ringwald    global handle
841b3fcedb9SMatthias Ringwald    global total_size
842b3fcedb9SMatthias Ringwald
843e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
844b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1
845b3fcedb9SMatthias Ringwald
846b3fcedb9SMatthias Ringwald    no_of_digitals = parts[1]
847b3fcedb9SMatthias Ringwald
848b3fcedb9SMatthias Ringwald    write_indent(fout)
849b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:])))
850b3fcedb9SMatthias Ringwald    write_indent(fout)
851b3fcedb9SMatthias Ringwald    write_16(fout, size)
852e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
853b3fcedb9SMatthias Ringwald    write_16(fout, handle)
854b3fcedb9SMatthias Ringwald    write_16(fout, 0x2909)
855b3fcedb9SMatthias Ringwald    write_sequence(fout, no_of_digitals)
856b3fcedb9SMatthias Ringwald    fout.write("\n")
857b3fcedb9SMatthias Ringwald    handle = handle + 1
858b3fcedb9SMatthias Ringwald
85960b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout):
860b3fcedb9SMatthias Ringwald    global handle
861b3fcedb9SMatthias Ringwald    global total_size
862b3fcedb9SMatthias Ringwald
863b165f97bSMatthias Ringwald    line_count = 0;
864b3fcedb9SMatthias Ringwald    for line in fin:
865b3fcedb9SMatthias Ringwald        line = line.strip("\n\r ")
866b165f97bSMatthias Ringwald        line_count += 1
867b3fcedb9SMatthias Ringwald
868b165f97bSMatthias Ringwald        if line.startswith("//"):
869b165f97bSMatthias Ringwald            fout.write("    //" + line.lstrip('/') + '\n')
870b165f97bSMatthias Ringwald            continue
871b165f97bSMatthias Ringwald
87260b51a4cSMatthias Ringwald        if line.startswith("#import"):
87360b51a4cSMatthias Ringwald            imported_file = ''
87460b51a4cSMatthias Ringwald            parts = re.match('#import\s+<(.*)>\w*',line)
87560b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
876dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
87760b51a4cSMatthias Ringwald            parts = re.match('#import\s+"(.*)"\w*',line)
87860b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
879dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
88060b51a4cSMatthias Ringwald            if len(imported_file) == 0:
88160b51a4cSMatthias Ringwald                print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count))
88260b51a4cSMatthias Ringwald                continue
88360b51a4cSMatthias Ringwald
884dbb3997aSMilanka Ringwald            imported_file = getFile( imported_file )
88560b51a4cSMatthias Ringwald            print("Importing %s" % imported_file)
88660b51a4cSMatthias Ringwald            try:
88760b51a4cSMatthias Ringwald                imported_fin = codecs.open (imported_file, encoding='utf-8')
888e5ce8e0eSMilanka Ringwald                fout.write('\n\n    // ' + line + ' -- BEGIN\n')
88960b51a4cSMatthias Ringwald                parseLines(imported_file, imported_fin, fout)
89060b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- END\n')
89160b51a4cSMatthias Ringwald            except IOError as e:
89260b51a4cSMatthias Ringwald                print('ERROR: Import failed. Please check path.')
89360b51a4cSMatthias Ringwald
89460b51a4cSMatthias Ringwald            continue
89560b51a4cSMatthias Ringwald
89660b51a4cSMatthias Ringwald        if line.startswith("#TODO"):
89760b51a4cSMatthias Ringwald            print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count))
898b165f97bSMatthias Ringwald            print ("'%s'" % line)
899b165f97bSMatthias Ringwald            fout.write("// " + line + '\n')
900b3fcedb9SMatthias Ringwald            continue
901b3fcedb9SMatthias Ringwald
902b3fcedb9SMatthias Ringwald        if len(line) == 0:
903b3fcedb9SMatthias Ringwald            continue
904b3fcedb9SMatthias Ringwald
905b3fcedb9SMatthias Ringwald        f = io.StringIO(line)
906b3fcedb9SMatthias Ringwald        parts_list = csv.reader(f, delimiter=',', quotechar='"')
907b3fcedb9SMatthias Ringwald
908b3fcedb9SMatthias Ringwald        for parts in parts_list:
909b3fcedb9SMatthias Ringwald            for index, object in enumerate(parts):
910b3fcedb9SMatthias Ringwald                parts[index] = object.strip().lstrip('"').rstrip('"')
911b3fcedb9SMatthias Ringwald
912b3fcedb9SMatthias Ringwald            if parts[0] == 'PRIMARY_SERVICE':
913b3fcedb9SMatthias Ringwald                parsePrimaryService(fout, parts)
914b3fcedb9SMatthias Ringwald                continue
915b3fcedb9SMatthias Ringwald
916b3fcedb9SMatthias Ringwald            if parts[0] == 'SECONDARY_SERVICE':
917b3fcedb9SMatthias Ringwald                parseSecondaryService(fout, parts)
918b3fcedb9SMatthias Ringwald                continue
919b3fcedb9SMatthias Ringwald
920b3fcedb9SMatthias Ringwald            if parts[0] == 'INCLUDE_SERVICE':
921b3fcedb9SMatthias Ringwald                parseIncludeService(fout, parts)
922b3fcedb9SMatthias Ringwald                continue
923b3fcedb9SMatthias Ringwald
924b3fcedb9SMatthias Ringwald            # 2803
925b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC':
926b3fcedb9SMatthias Ringwald                parseCharacteristic(fout, parts)
927b3fcedb9SMatthias Ringwald                continue
928b3fcedb9SMatthias Ringwald
929b3fcedb9SMatthias Ringwald            # 2900 Characteristic Extended Properties
930b3fcedb9SMatthias Ringwald
931b3fcedb9SMatthias Ringwald            # 2901
932b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
933766e9e27SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x2901, 'USER_DESCRIPTION')
934b3fcedb9SMatthias Ringwald                continue
935b3fcedb9SMatthias Ringwald
936b165f97bSMatthias Ringwald
937b165f97bSMatthias Ringwald            # 2902 Client Characteristic Configuration - automatically included in Characteristic if
938b3fcedb9SMatthias Ringwald            # notification / indication is supported
939231a3c5dSMatthias Ringwald            if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION':
940b165f97bSMatthias Ringwald                continue
941b3fcedb9SMatthias Ringwald
942b3fcedb9SMatthias Ringwald            # 2903
943b3fcedb9SMatthias Ringwald            if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
94417215335SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x2903, 'SERVER_CONFIGURATION')
945b3fcedb9SMatthias Ringwald                continue
946b3fcedb9SMatthias Ringwald
947b3fcedb9SMatthias Ringwald            # 2904
948b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_FORMAT':
949b3fcedb9SMatthias Ringwald                parseCharacteristicFormat(fout, parts)
950b3fcedb9SMatthias Ringwald                continue
951b3fcedb9SMatthias Ringwald
952b3fcedb9SMatthias Ringwald            # 2905
953b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
954b3fcedb9SMatthias Ringwald                parseCharacteristicAggregateFormat(fout, parts)
955b3fcedb9SMatthias Ringwald                continue
956b3fcedb9SMatthias Ringwald
957b3fcedb9SMatthias Ringwald            # 2906
958b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
9596f08f159SMatthias Ringwald                parseGenericDynamicReadOnlyDescriptor(fout, parts, 0x2906, 'VALID_RANGE')
960b3fcedb9SMatthias Ringwald                continue
961b3fcedb9SMatthias Ringwald
962b3fcedb9SMatthias Ringwald            # 2907
963b3fcedb9SMatthias Ringwald            if parts[0] == 'EXTERNAL_REPORT_REFERENCE':
96405146de0SMilanka Ringwald                parseExternalReportReference(fout, parts)
965b3fcedb9SMatthias Ringwald                continue
966b3fcedb9SMatthias Ringwald
967b3fcedb9SMatthias Ringwald            # 2908
968b3fcedb9SMatthias Ringwald            if parts[0] == 'REPORT_REFERENCE':
969b3fcedb9SMatthias Ringwald                parseReportReference(fout, parts)
970b3fcedb9SMatthias Ringwald                continue
971b3fcedb9SMatthias Ringwald
972b3fcedb9SMatthias Ringwald            # 2909
973b3fcedb9SMatthias Ringwald            if parts[0] == 'NUMBER_OF_DIGITALS':
974b3fcedb9SMatthias Ringwald                parseNumberOfDigitals(fout, parts)
975b3fcedb9SMatthias Ringwald                continue
976b3fcedb9SMatthias Ringwald
977b3fcedb9SMatthias Ringwald            # 290A
978b3fcedb9SMatthias Ringwald            if parts[0] == 'VALUE_TRIGGER_SETTING':
9796f08f159SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x290A, 'VALUE_TRIGGER_SETTING')
980b3fcedb9SMatthias Ringwald                continue
981b3fcedb9SMatthias Ringwald
982b3fcedb9SMatthias Ringwald            # 290B
983b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION':
9846f08f159SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x290B, 'ENVIRONMENTAL_SENSING_CONFIGURATION')
985b3fcedb9SMatthias Ringwald                continue
986b3fcedb9SMatthias Ringwald
987b3fcedb9SMatthias Ringwald            # 290C
988b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT':
9896f08f159SMatthias Ringwald                parseGenericDynamicReadOnlyDescriptor(fout, parts, 0x290C, 'ENVIRONMENTAL_SENSING_MEASUREMENT')
990b3fcedb9SMatthias Ringwald                continue
991b3fcedb9SMatthias Ringwald
992b3fcedb9SMatthias Ringwald            # 290D
993b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING':
9946f08f159SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x290D, 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING')
995b3fcedb9SMatthias Ringwald                continue
996b3fcedb9SMatthias Ringwald
997b3fcedb9SMatthias Ringwald            print("WARNING: unknown token: %s\n" % (parts[0]))
998b3fcedb9SMatthias Ringwald
9997050bf34SMatthias Ringwalddef parse(fname_in, fin, fname_out, tool_path, fout):
100060b51a4cSMatthias Ringwald    global handle
100160b51a4cSMatthias Ringwald    global total_size
100260b51a4cSMatthias Ringwald
10037050bf34SMatthias Ringwald    fout.write(header.format(fname_out, fname_in, tool_path))
100460b51a4cSMatthias Ringwald    fout.write('{\n')
1005fd1be25dSMatthias Ringwald    write_indent(fout)
1006fd1be25dSMatthias Ringwald    fout.write('// ATT DB Version\n')
1007fd1be25dSMatthias Ringwald    write_indent(fout)
1008fd1be25dSMatthias Ringwald    fout.write('1,\n')
1009fd1be25dSMatthias Ringwald    fout.write("\n")
101060b51a4cSMatthias Ringwald
101160b51a4cSMatthias Ringwald    parseLines(fname_in, fin, fout)
101260b51a4cSMatthias Ringwald
1013729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
1014b3fcedb9SMatthias Ringwald    write_indent(fout)
1015b3fcedb9SMatthias Ringwald    fout.write("// END\n");
1016b3fcedb9SMatthias Ringwald    write_indent(fout)
1017b3fcedb9SMatthias Ringwald    write_16(fout,0)
1018b3fcedb9SMatthias Ringwald    fout.write("\n")
1019b3fcedb9SMatthias Ringwald    total_size = total_size + 2
1020b3fcedb9SMatthias Ringwald
1021b3fcedb9SMatthias Ringwald    fout.write("}; // total size %u bytes \n" % total_size);
1022b3fcedb9SMatthias Ringwald
1023b3fcedb9SMatthias Ringwalddef listHandles(fout):
1024b3fcedb9SMatthias Ringwald    fout.write('\n\n')
1025b3fcedb9SMatthias Ringwald    fout.write('//\n')
1026729074c4SMatthias Ringwald    fout.write('// list service handle ranges\n')
1027729074c4SMatthias Ringwald    fout.write('//\n')
1028729074c4SMatthias Ringwald    for define in defines_for_services:
1029729074c4SMatthias Ringwald        fout.write(define)
1030729074c4SMatthias Ringwald        fout.write('\n')
1031729074c4SMatthias Ringwald    fout.write('\n')
1032729074c4SMatthias Ringwald    fout.write('//\n')
1033b3fcedb9SMatthias Ringwald    fout.write('// list mapping between characteristics and handles\n')
1034b3fcedb9SMatthias Ringwald    fout.write('//\n')
1035729074c4SMatthias Ringwald    for define in defines_for_characteristics:
1036b3fcedb9SMatthias Ringwald        fout.write(define)
1037b3fcedb9SMatthias Ringwald        fout.write('\n')
1038b3fcedb9SMatthias Ringwald
1039dbb3997aSMilanka Ringwalddef getFile( fileName ):
104078b65b0aSMatthias Ringwald    for d in include_paths:
104178b65b0aSMatthias Ringwald        fullFile = os.path.normpath(d + os.sep + fileName) # because Windows exists
104278b65b0aSMatthias Ringwald        # print("test %s" % fullFile)
1043dbb3997aSMilanka Ringwald        if os.path.isfile( fullFile ) == True:
1044dbb3997aSMilanka Ringwald            return fullFile
1045dbb3997aSMilanka Ringwald    print ("'{0}' not found".format( fileName ))
104678b65b0aSMatthias Ringwald    print ("Include paths: %s" % ", ".join(include_paths))
1047dbb3997aSMilanka Ringwald    exit(-1)
1048dbb3997aSMilanka Ringwald
1049dbb3997aSMilanka Ringwald
1050dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
1051d567aeb3SMatthias Ringwalddefault_includes = [os.path.normpath(path) for path in [
1052d567aeb3SMatthias Ringwald    btstack_root + '/src/',
1053d567aeb3SMatthias Ringwald    btstack_root + '/src/ble/gatt-service/',
1054d567aeb3SMatthias Ringwald    btstack_root + '/src/le-audio/gatt-service/',
1055d567aeb3SMatthias Ringwald    btstack_root + '/src/mesh/gatt-service/'
1056d567aeb3SMatthias Ringwald]]
1057dbb3997aSMilanka Ringwald
1058dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack')
1059dbb3997aSMilanka Ringwald
1060dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes',
1061dbb3997aSMilanka Ringwald        help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes))
1062dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str,
1063dbb3997aSMilanka Ringwald        help='gatt file to be compiled')
1064dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str,
1065dbb3997aSMilanka Ringwald        help='header file to be generated')
1066dbb3997aSMilanka Ringwald
1067dbb3997aSMilanka Ringwaldargs = parser.parse_args()
1068dbb3997aSMilanka Ringwald
106978b65b0aSMatthias Ringwald# add include path arguments
107078b65b0aSMatthias Ringwaldif args.I != None:
107178b65b0aSMatthias Ringwald    for d in args.I:
107278b65b0aSMatthias Ringwald        include_paths.append(os.path.normpath(d[0]))
107378b65b0aSMatthias Ringwald
1074dbb3997aSMilanka Ringwald# append default include paths
107578b65b0aSMatthias Ringwaldinclude_paths.extend(default_includes)
1076dbb3997aSMilanka Ringwald
1077b3fcedb9SMatthias Ringwaldtry:
1078b165f97bSMatthias Ringwald    # read defines from bluetooth_gatt.h
1079dbb3997aSMilanka Ringwald    gen_path = getFile( 'bluetooth_gatt.h' )
1080b165f97bSMatthias Ringwald    bluetooth_gatt = read_defines(gen_path)
1081b165f97bSMatthias Ringwald
1082dbb3997aSMilanka Ringwald    filename = args.hfile
1083dbb3997aSMilanka Ringwald    fin  = codecs.open (args.gattfile, encoding='utf-8')
1084285653b2SMatthias Ringwald
1085285653b2SMatthias Ringwald    # pass 1: create temp .h file
1086d78181d6SMatthias Ringwald    ftemp = tempfile.TemporaryFile(mode='w+t')
1087285653b2SMatthias Ringwald    parse(args.gattfile, fin, filename, sys.argv[0], ftemp)
1088285653b2SMatthias Ringwald    listHandles(ftemp)
1089285653b2SMatthias Ringwald
1090043f8832SMatthias Ringwald    # calc GATT Database Hash
1091043f8832SMatthias Ringwald    db_hash = aes_cmac(bytearray(16), database_hash_message)
1092043f8832SMatthias Ringwald    if isinstance(db_hash, str):
1093043f8832SMatthias Ringwald        # python2
1094043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % ord(i)) for i in db_hash]
1095043f8832SMatthias Ringwald    elif isinstance(db_hash, bytes):
1096043f8832SMatthias Ringwald        # python3
1097043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % i) for i in db_hash]
1098043f8832SMatthias Ringwald    else:
1099043f8832SMatthias Ringwald        print("AES CMAC returns unexpected type %s, abort" % type(db_hash))
1100043f8832SMatthias Ringwald        sys.exit(1)
1101043f8832SMatthias Ringwald    # reverse hash to get little endian
1102043f8832SMatthias Ringwald    db_hash_sequence.reverse()
1103043f8832SMatthias Ringwald    db_hash_string = ', '.join(db_hash_sequence) + ', '
1104043f8832SMatthias Ringwald
1105285653b2SMatthias Ringwald    # pass 2: insert GATT Database Hash
1106b3fcedb9SMatthias Ringwald    fout = open (filename, 'w')
1107285653b2SMatthias Ringwald    ftemp.seek(0)
1108285653b2SMatthias Ringwald    for line in ftemp:
1109043f8832SMatthias Ringwald        fout.write(line.replace('THE-DATABASE-HASH', db_hash_string))
1110b3fcedb9SMatthias Ringwald    fout.close()
1111285653b2SMatthias Ringwald    ftemp.close()
1112285653b2SMatthias Ringwald
1113b165f97bSMatthias Ringwald    print('Created %s' % filename)
1114b3fcedb9SMatthias Ringwald
1115b3fcedb9SMatthias Ringwaldexcept IOError as e:
1116e22a2612SMatthias Ringwald
1117b3fcedb9SMatthias Ringwald    print(usage)
1118b3fcedb9SMatthias Ringwald    sys.exit(1)
1119b3fcedb9SMatthias Ringwald
1120b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n')
1121