xref: /btstack/tool/compile_gatt.py (revision fa529fa70ecd60b9bf447a172e5b96a75741e08d)
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)")
38ced5a857SMatthias Ringwald            print("[!] Please install PyCryptodome, e.g. 'pip install pycryptodomex' or 'pip 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
54*fa529fa7SMatthias Ringwald// Reference: https://en.cppreference.com/w/cpp/feature_test
55*fa529fa7SMatthias Ringwald#if __cplusplus >= 200704L
56*fa529fa7SMatthias Ringwaldconstexpr
57*fa529fa7SMatthias 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,
75043f8832SMatthias Ringwald    'GATT_DATABASE_HASH' : 0x2b2a
76b3fcedb9SMatthias Ringwald}
77b3fcedb9SMatthias Ringwald
78e72176f8SMatthias Ringwaldsecurity_permsission = ['ANYBODY','ENCRYPTED', 'AUTHENTICATED', 'AUTHORIZED', 'AUTHENTICATED_SC']
79d7ec1d24SMatthias Ringwald
80b3fcedb9SMatthias Ringwaldproperty_flags = {
81eb6072adSMatthias Ringwald    # GATT Characteristic Properties
82b3fcedb9SMatthias Ringwald    'BROADCAST' :                   0x01,
83b3fcedb9SMatthias Ringwald    'READ' :                        0x02,
84b3fcedb9SMatthias Ringwald    'WRITE_WITHOUT_RESPONSE' :      0x04,
85b3fcedb9SMatthias Ringwald    'WRITE' :                       0x08,
86b3fcedb9SMatthias Ringwald    'NOTIFY':                       0x10,
87b3fcedb9SMatthias Ringwald    'INDICATE' :                    0x20,
88b3fcedb9SMatthias Ringwald    'AUTHENTICATED_SIGNED_WRITE' :  0x40,
89b3fcedb9SMatthias Ringwald    'EXTENDED_PROPERTIES' :         0x80,
90b3fcedb9SMatthias Ringwald    # custom BTstack extension
91b3fcedb9SMatthias Ringwald    'DYNAMIC':                      0x100,
92b3fcedb9SMatthias Ringwald    'LONG_UUID':                    0x200,
93e22a2612SMatthias Ringwald
94e22a2612SMatthias Ringwald    # read permissions
95e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_0':        0x400,
96e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_1':        0x800,
97e22a2612SMatthias Ringwald
98e22a2612SMatthias Ringwald    #
99b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_7':       0x6000,
100b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_8':       0x7000,
101b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_9':       0x8000,
102b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_10':      0x9000,
103b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_11':      0xa000,
104b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_12':      0xb000,
105b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_13':      0xc000,
106b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_14':      0xd000,
107b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_15':      0xe000,
108b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_16':      0xf000,
109e22a2612SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_MASK':    0xf000,
110eb6072adSMatthias Ringwald
111b3fcedb9SMatthias Ringwald    # only used by gatt compiler >= 0xffff
112b3fcedb9SMatthias Ringwald    # Extended Properties
113e72176f8SMatthias Ringwald    'RELIABLE_WRITE':              0x00010000,
114e72176f8SMatthias Ringwald    'AUTHENTICATION_REQUIRED':     0x00020000,
115e72176f8SMatthias Ringwald    'AUTHORIZATION_REQUIRED':      0x00040000,
116e72176f8SMatthias Ringwald    'READ_ANYBODY':                0x00080000,
117e72176f8SMatthias Ringwald    'READ_ENCRYPTED':              0x00100000,
118e72176f8SMatthias Ringwald    'READ_AUTHENTICATED':          0x00200000,
119e72176f8SMatthias Ringwald    'READ_AUTHENTICATED_SC':       0x00400000,
120e72176f8SMatthias Ringwald    'READ_AUTHORIZED':             0x00800000,
121e72176f8SMatthias Ringwald    'WRITE_ANYBODY':               0x01000000,
122e72176f8SMatthias Ringwald    'WRITE_ENCRYPTED':             0x02000000,
123e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED':         0x04000000,
124e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED_SC':      0x08000000,
125e72176f8SMatthias Ringwald    'WRITE_AUTHORIZED':            0x10000000,
126eb6072adSMatthias Ringwald
127eb6072adSMatthias Ringwald    # Broadcast, Notify, Indicate, Extended Properties are only used to describe a GATT Characteristic, but are free to use with att_db
128e72176f8SMatthias Ringwald    # - write permissions
129e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_0':      0x01,
130e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_1':      0x10,
131e72176f8SMatthias Ringwald    # - SC required
132e72176f8SMatthias Ringwald    'READ_PERMISSION_SC':          0x20,
133e72176f8SMatthias Ringwald    'WRITE_PERMISSION_SC':         0x80,
134b3fcedb9SMatthias Ringwald}
135b3fcedb9SMatthias Ringwald
136b3fcedb9SMatthias Ringwaldservices = dict()
137b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict()
138b3fcedb9SMatthias Ringwaldpresentation_formats = dict()
139b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = ""
140b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0
141b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = ""
142729074c4SMatthias Ringwalddefines_for_characteristics = []
143729074c4SMatthias Ringwalddefines_for_services = []
14478b65b0aSMatthias Ringwaldinclude_paths = []
145043f8832SMatthias Ringwalddatabase_hash_message = bytearray()
146b3fcedb9SMatthias Ringwald
147b3fcedb9SMatthias Ringwaldhandle = 1
148b3fcedb9SMatthias Ringwaldtotal_size = 0
149b3fcedb9SMatthias Ringwald
150043f8832SMatthias Ringwalddef aes_cmac(key, n):
151043f8832SMatthias Ringwald    if have_crypto:
152043f8832SMatthias Ringwald        cobj = CMAC.new(key, ciphermod=AES)
153043f8832SMatthias Ringwald        cobj.update(n)
154043f8832SMatthias Ringwald        return cobj.digest()
155043f8832SMatthias Ringwald    else:
1567490175eSMatthias Ringwald        # return random value
1577490175eSMatthias Ringwald        return os.urandom(16)
158043f8832SMatthias Ringwald
159b165f97bSMatthias Ringwalddef read_defines(infile):
160b165f97bSMatthias Ringwald    defines = dict()
161b165f97bSMatthias Ringwald    with open (infile, 'rt') as fin:
162b165f97bSMatthias Ringwald        for line in fin:
163b165f97bSMatthias Ringwald            parts = re.match('#define\s+(\w+)\s+(\w+)',line)
164b165f97bSMatthias Ringwald            if parts and len(parts.groups()) == 2:
165b165f97bSMatthias Ringwald                (key, value) = parts.groups()
166b165f97bSMatthias Ringwald                defines[key] = int(value, 16)
167b165f97bSMatthias Ringwald    return defines
168b165f97bSMatthias Ringwald
169b3fcedb9SMatthias Ringwalddef keyForUUID(uuid):
170b3fcedb9SMatthias Ringwald    keyUUID = ""
171b3fcedb9SMatthias Ringwald    for i in uuid:
172b3fcedb9SMatthias Ringwald        keyUUID += "%02x" % i
173b3fcedb9SMatthias Ringwald    return keyUUID
174b3fcedb9SMatthias Ringwald
175b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid):
176b3fcedb9SMatthias Ringwald    return uuid.replace('-', '_')
177b3fcedb9SMatthias Ringwald
178b3fcedb9SMatthias Ringwalddef twoByteLEFor(value):
179b3fcedb9SMatthias Ringwald    return [ (value & 0xff), (value >> 8)]
180b3fcedb9SMatthias Ringwald
181b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text):
182b3fcedb9SMatthias 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):
183b3fcedb9SMatthias Ringwald        return True
184b3fcedb9SMatthias Ringwald    return False
185b3fcedb9SMatthias Ringwald
186b3fcedb9SMatthias Ringwalddef parseUUID128(uuid):
187b3fcedb9SMatthias 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)
188b3fcedb9SMatthias Ringwald    uuid_bytes = []
189b3fcedb9SMatthias Ringwald    for i in range(8, 0, -1):
190b3fcedb9SMatthias Ringwald        uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16))
191b3fcedb9SMatthias Ringwald    return uuid_bytes
192b3fcedb9SMatthias Ringwald
193b3fcedb9SMatthias Ringwalddef parseUUID(uuid):
194b3fcedb9SMatthias Ringwald    if uuid in assigned_uuids:
195b3fcedb9SMatthias Ringwald        return twoByteLEFor(assigned_uuids[uuid])
196b165f97bSMatthias Ringwald    uuid_upper = uuid.upper().replace('.','_')
197b165f97bSMatthias Ringwald    if uuid_upper in bluetooth_gatt:
198b165f97bSMatthias Ringwald        return twoByteLEFor(bluetooth_gatt[uuid_upper])
199b3fcedb9SMatthias Ringwald    if is_128bit_uuid(uuid):
200b3fcedb9SMatthias Ringwald        return parseUUID128(uuid)
201b3fcedb9SMatthias Ringwald    uuidInt = int(uuid, 16)
202b3fcedb9SMatthias Ringwald    return twoByteLEFor(uuidInt)
203b3fcedb9SMatthias Ringwald
204b3fcedb9SMatthias Ringwalddef parseProperties(properties):
205b3fcedb9SMatthias Ringwald    value = 0
206b3fcedb9SMatthias Ringwald    parts = properties.split("|")
207b3fcedb9SMatthias Ringwald    for property in parts:
208b3fcedb9SMatthias Ringwald        property = property.strip()
209b3fcedb9SMatthias Ringwald        if property in property_flags:
210b3fcedb9SMatthias Ringwald            value |= property_flags[property]
211b3fcedb9SMatthias Ringwald        else:
212b3fcedb9SMatthias Ringwald            print("WARNING: property %s undefined" % (property))
213e22a2612SMatthias Ringwald
214e22a2612SMatthias Ringwald    return value;
215e22a2612SMatthias Ringwald
216e22a2612SMatthias Ringwalddef gatt_characteristic_properties(properties):
217e22a2612SMatthias Ringwald    return properties & 0xff
218e22a2612SMatthias Ringwald
219e22a2612SMatthias Ringwalddef att_flags(properties):
220e72176f8SMatthias Ringwald    # drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Properties (0x80) - not used for flags
221e72176f8SMatthias Ringwald    properties &= 0xffffff4e
222e22a2612SMatthias Ringwald
223e22a2612SMatthias Ringwald    # rw permissions distinct
224e22a2612SMatthias Ringwald    distinct_permissions_used = properties & (
225e22a2612SMatthias Ringwald        property_flags['READ_AUTHORIZED'] |
226e72176f8SMatthias Ringwald        property_flags['READ_AUTHENTICATED_SC'] |
227e22a2612SMatthias Ringwald        property_flags['READ_AUTHENTICATED'] |
228e22a2612SMatthias Ringwald        property_flags['READ_ENCRYPTED'] |
229e22a2612SMatthias Ringwald        property_flags['READ_ANYBODY'] |
230e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHORIZED'] |
231e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED'] |
232e72176f8SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED_SC'] |
233e22a2612SMatthias Ringwald        property_flags['WRITE_ENCRYPTED'] |
234e22a2612SMatthias Ringwald        property_flags['WRITE_ANYBODY']
235e22a2612SMatthias Ringwald    ) != 0
236e22a2612SMatthias Ringwald
237e22a2612SMatthias Ringwald    # post process properties
238e22a2612SMatthias Ringwald    encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0
239e22a2612SMatthias Ringwald
240d7ec1d24SMatthias Ringwald    # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted
241e22a2612SMatthias Ringwald    if encryption_key_size_specified and not distinct_permissions_used:
242e22a2612SMatthias Ringwald        properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED']
243e22a2612SMatthias Ringwald
244d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated
245d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used:
246d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED']
247d7ec1d24SMatthias Ringwald
248d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized
249d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used:
250d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED']
251d7ec1d24SMatthias Ringwald
252d7ec1d24SMatthias Ringwald    # determine read/write security requirements
253d7ec1d24SMatthias Ringwald    read_security_level  = 0
254d7ec1d24SMatthias Ringwald    write_security_level = 0
255e72176f8SMatthias Ringwald    read_requires_sc     = False
256e72176f8SMatthias Ringwald    write_requires_sc    = False
257e22a2612SMatthias Ringwald    if properties & property_flags['READ_AUTHORIZED']:
258d7ec1d24SMatthias Ringwald        read_security_level = 3
259e22a2612SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED']:
260d7ec1d24SMatthias Ringwald        read_security_level = 2
261e72176f8SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED_SC']:
262e72176f8SMatthias Ringwald        read_security_level = 2
263e72176f8SMatthias Ringwald        read_requires_sc = True
264e22a2612SMatthias Ringwald    elif properties & property_flags['READ_ENCRYPTED']:
265d7ec1d24SMatthias Ringwald        read_security_level = 1
266e22a2612SMatthias Ringwald    if properties & property_flags['WRITE_AUTHORIZED']:
267d7ec1d24SMatthias Ringwald        write_security_level = 3
268e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED']:
269d7ec1d24SMatthias Ringwald        write_security_level = 2
270e72176f8SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED_SC']:
271e72176f8SMatthias Ringwald        write_security_level = 2
272e72176f8SMatthias Ringwald        write_requires_sc = True
273e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_ENCRYPTED']:
274d7ec1d24SMatthias Ringwald        write_security_level = 1
275d7ec1d24SMatthias Ringwald
276d7ec1d24SMatthias Ringwald    # map security requirements to flags
277d7ec1d24SMatthias Ringwald    if read_security_level & 2:
278d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_1']
279d7ec1d24SMatthias Ringwald    if read_security_level & 1:
280d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_0']
281e72176f8SMatthias Ringwald    if read_requires_sc:
282e72176f8SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_SC']
283d7ec1d24SMatthias Ringwald    if write_security_level & 2:
284d7ec1d24SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_1']
285d7ec1d24SMatthias Ringwald    if write_security_level & 1:
286e22a2612SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_0']
287e72176f8SMatthias Ringwald    if write_requires_sc:
288e72176f8SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_SC']
289e22a2612SMatthias Ringwald
290e22a2612SMatthias Ringwald    return properties
291e22a2612SMatthias Ringwald
292d7ec1d24SMatthias Ringwalddef write_permissions_and_key_size_flags_from_properties(properties):
293e22a2612SMatthias Ringwald    return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1'])
294b3fcedb9SMatthias Ringwald
295b3fcedb9SMatthias Ringwalddef write_8(fout, value):
296b3fcedb9SMatthias Ringwald    fout.write( "0x%02x, " % (value & 0xff))
297b3fcedb9SMatthias Ringwald
298b3fcedb9SMatthias Ringwalddef write_16(fout, value):
299b3fcedb9SMatthias Ringwald    fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff))
300b3fcedb9SMatthias Ringwald
301285653b2SMatthias Ringwalddef write_uuid(fout, uuid):
302b3fcedb9SMatthias Ringwald    for byte in uuid:
303b3fcedb9SMatthias Ringwald        fout.write( "0x%02x, " % byte)
304b3fcedb9SMatthias Ringwald
305b3fcedb9SMatthias Ringwalddef write_string(fout, text):
306b3fcedb9SMatthias Ringwald    for l in text.lstrip('"').rstrip('"'):
307b3fcedb9SMatthias Ringwald        write_8(fout, ord(l))
308b3fcedb9SMatthias Ringwald
309b3fcedb9SMatthias Ringwalddef write_sequence(fout, text):
310b3fcedb9SMatthias Ringwald    parts = text.split()
311b3fcedb9SMatthias Ringwald    for part in parts:
312b3fcedb9SMatthias Ringwald        fout.write("0x%s, " % (part.strip()))
313b3fcedb9SMatthias Ringwald
314043f8832SMatthias Ringwalddef write_database_hash(fout):
315043f8832SMatthias Ringwald    fout.write("THE-DATABASE-HASH")
316043f8832SMatthias Ringwald
317b3fcedb9SMatthias Ringwalddef write_indent(fout):
318b3fcedb9SMatthias Ringwald    fout.write("    ")
319b3fcedb9SMatthias Ringwald
320d7ec1d24SMatthias Ringwalddef read_permissions_from_flags(flags):
321d7ec1d24SMatthias Ringwald    permissions = 0
322d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_0']:
323d7ec1d24SMatthias Ringwald        permissions |= 1
324d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_1']:
325d7ec1d24SMatthias Ringwald        permissions |= 2
326e72176f8SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_SC'] and permissions == 2:
327e72176f8SMatthias Ringwald        permissions = 4
328d7ec1d24SMatthias Ringwald    return permissions
329d7ec1d24SMatthias Ringwald
330d7ec1d24SMatthias Ringwalddef write_permissions_from_flags(flags):
331d7ec1d24SMatthias Ringwald    permissions = 0
332d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_0']:
333d7ec1d24SMatthias Ringwald        permissions |= 1
334d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_1']:
335d7ec1d24SMatthias Ringwald        permissions |= 2
336e72176f8SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_SC'] and permissions == 2:
337e72176f8SMatthias Ringwald        permissions = 4
338d7ec1d24SMatthias Ringwald    return permissions
339d7ec1d24SMatthias Ringwald
340d7ec1d24SMatthias Ringwalddef encryption_key_size_from_flags(flags):
341d7ec1d24SMatthias Ringwald    encryption_key_size = (flags & 0xf000) >> 12
342d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
343d7ec1d24SMatthias Ringwald        encryption_key_size += 1
344d7ec1d24SMatthias Ringwald    return encryption_key_size
345d7ec1d24SMatthias Ringwald
346b3fcedb9SMatthias Ringwalddef is_string(text):
347b3fcedb9SMatthias Ringwald    for item in text.split(" "):
348b3fcedb9SMatthias Ringwald        if not all(c in string.hexdigits for c in item):
349b3fcedb9SMatthias Ringwald            return True
350b3fcedb9SMatthias Ringwald    return False
351b3fcedb9SMatthias Ringwald
352b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties):
353b3fcedb9SMatthias Ringwald    return properties & (property_flags['NOTIFY'] | property_flags['INDICATE'])
354b3fcedb9SMatthias Ringwald
355729074c4SMatthias Ringwalddef serviceDefinitionComplete(fout):
356729074c4SMatthias Ringwald    global services
357729074c4SMatthias Ringwald    if current_service_uuid_string:
358729074c4SMatthias Ringwald        fout.write("\n")
359729074c4SMatthias Ringwald        # print("append service %s = [%d, %d]" % (current_characteristic_uuid_string, current_service_start_handle, handle-1))
360729074c4SMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle))
361729074c4SMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1))
362729074c4SMatthias Ringwald        services[current_service_uuid_string] = [current_service_start_handle, handle-1]
363729074c4SMatthias Ringwald
364d7ec1d24SMatthias Ringwalddef dump_flags(fout, flags):
365d7ec1d24SMatthias Ringwald    global security_permsission
366d7ec1d24SMatthias Ringwald    encryption_key_size = encryption_key_size_from_flags(flags)
367d7ec1d24SMatthias Ringwald    read_permissions    = security_permsission[read_permissions_from_flags(flags)]
368d7ec1d24SMatthias Ringwald    write_permissions   = security_permsission[write_permissions_from_flags(flags)]
369d7ec1d24SMatthias Ringwald    write_indent(fout)
370d7ec1d24SMatthias Ringwald    fout.write('// ')
371d7ec1d24SMatthias Ringwald    first = 1
372d7ec1d24SMatthias Ringwald    if flags & property_flags['READ']:
373d7ec1d24SMatthias Ringwald        fout.write('READ_%s' % read_permissions)
374d7ec1d24SMatthias Ringwald        first = 0
375d7ec1d24SMatthias Ringwald    if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']):
376d7ec1d24SMatthias Ringwald        if not first:
377d7ec1d24SMatthias Ringwald            fout.write(', ')
378d7ec1d24SMatthias Ringwald        first = 0
379d7ec1d24SMatthias Ringwald        fout.write('WRITE_%s' % write_permissions)
380d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
381d7ec1d24SMatthias Ringwald        if not first:
382d7ec1d24SMatthias Ringwald            fout.write(', ')
383d7ec1d24SMatthias Ringwald        first = 0
384d7ec1d24SMatthias Ringwald        fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size)
385d7ec1d24SMatthias Ringwald    fout.write('\n')
386d7ec1d24SMatthias Ringwald
387043f8832SMatthias Ringwalddef database_hash_append_uint8(value):
388043f8832SMatthias Ringwald    global database_hash_message
389043f8832SMatthias Ringwald    database_hash_message.append(value)
390043f8832SMatthias Ringwald
391043f8832SMatthias Ringwalddef database_hash_append_uint16(value):
392043f8832SMatthias Ringwald    global database_hash_message
393043f8832SMatthias Ringwald    database_hash_append_uint8(value & 0xff)
394043f8832SMatthias Ringwald    database_hash_append_uint8((value >> 8) & 0xff)
395043f8832SMatthias Ringwald
396043f8832SMatthias Ringwalddef database_hash_append_value(value):
397043f8832SMatthias Ringwald    global database_hash_message
398043f8832SMatthias Ringwald    for byte in value:
399043f8832SMatthias Ringwald        database_hash_append_uint8(byte)
400043f8832SMatthias Ringwald
401b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type):
402b3fcedb9SMatthias Ringwald    global handle
403b3fcedb9SMatthias Ringwald    global total_size
404b3fcedb9SMatthias Ringwald    global current_service_uuid_string
405b3fcedb9SMatthias Ringwald    global current_service_start_handle
406b3fcedb9SMatthias Ringwald
407729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
408b3fcedb9SMatthias Ringwald
409d7ec1d24SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
410b3fcedb9SMatthias Ringwald
411b3fcedb9SMatthias Ringwald    write_indent(fout)
412b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
413b3fcedb9SMatthias Ringwald
414b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
415b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
416b3fcedb9SMatthias Ringwald
417b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size + 2
418b3fcedb9SMatthias Ringwald
419b3fcedb9SMatthias Ringwald    if service_type == 0x2802:
420b3fcedb9SMatthias Ringwald        size += 4
421b3fcedb9SMatthias Ringwald
422b3fcedb9SMatthias Ringwald    write_indent(fout)
423b3fcedb9SMatthias Ringwald    write_16(fout, size)
424d7ec1d24SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
425b3fcedb9SMatthias Ringwald    write_16(fout, handle)
426b3fcedb9SMatthias Ringwald    write_16(fout, service_type)
427285653b2SMatthias Ringwald    write_uuid(fout, uuid)
428b3fcedb9SMatthias Ringwald    fout.write("\n")
429b3fcedb9SMatthias Ringwald
430043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
431043f8832SMatthias Ringwald    database_hash_append_uint16(service_type)
432043f8832SMatthias Ringwald    database_hash_append_value(uuid)
433043f8832SMatthias Ringwald
434729074c4SMatthias Ringwald    current_service_uuid_string = c_string_for_uuid(parts[1])
435b3fcedb9SMatthias Ringwald    current_service_start_handle = handle
436b3fcedb9SMatthias Ringwald    handle = handle + 1
437b3fcedb9SMatthias Ringwald    total_size = total_size + size
438b3fcedb9SMatthias Ringwald
439b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts):
440b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2800)
441b3fcedb9SMatthias Ringwald
442b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts):
443b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2801)
444b3fcedb9SMatthias Ringwald
445b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts):
446b3fcedb9SMatthias Ringwald    global handle
447b3fcedb9SMatthias Ringwald    global total_size
448b3fcedb9SMatthias Ringwald
449e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
450b3fcedb9SMatthias Ringwald
451b3fcedb9SMatthias Ringwald    write_indent(fout)
452b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
453b3fcedb9SMatthias Ringwald
454b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
455b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
456b3fcedb9SMatthias Ringwald    if uuid_size > 2:
457b3fcedb9SMatthias Ringwald        uuid_size = 0
458729074c4SMatthias Ringwald    # print("Include Service ", c_string_for_uuid(uuid))
459b3fcedb9SMatthias Ringwald
460b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 4 + uuid_size
461b3fcedb9SMatthias Ringwald
462729074c4SMatthias Ringwald    keyUUID = c_string_for_uuid(parts[1])
463b3fcedb9SMatthias Ringwald
464b3fcedb9SMatthias Ringwald    write_indent(fout)
465b3fcedb9SMatthias Ringwald    write_16(fout, size)
466e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
467b3fcedb9SMatthias Ringwald    write_16(fout, handle)
468b3fcedb9SMatthias Ringwald    write_16(fout, 0x2802)
469b3fcedb9SMatthias Ringwald    write_16(fout, services[keyUUID][0])
470b3fcedb9SMatthias Ringwald    write_16(fout, services[keyUUID][1])
471b3fcedb9SMatthias Ringwald    if uuid_size > 0:
472285653b2SMatthias Ringwald        write_uuid(fout, uuid)
473b3fcedb9SMatthias Ringwald    fout.write("\n")
474b3fcedb9SMatthias Ringwald
475043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
476043f8832SMatthias Ringwald    database_hash_append_uint16(0x2802)
477043f8832SMatthias Ringwald    database_hash_append_uint16(services[keyUUID][0])
478043f8832SMatthias Ringwald    database_hash_append_uint16(services[keyUUID][1])
479043f8832SMatthias Ringwald    if uuid_size > 0:
480043f8832SMatthias Ringwald        database_hash_append_value(uuid)
481043f8832SMatthias Ringwald
482b3fcedb9SMatthias Ringwald    handle = handle + 1
483b3fcedb9SMatthias Ringwald    total_size = total_size + size
484b3fcedb9SMatthias Ringwald
485b3fcedb9SMatthias Ringwald
486b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts):
487b3fcedb9SMatthias Ringwald    global handle
488b3fcedb9SMatthias Ringwald    global total_size
489b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
490b3fcedb9SMatthias Ringwald    global characteristic_indices
491b3fcedb9SMatthias Ringwald
492e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
493b3fcedb9SMatthias Ringwald
494b3fcedb9SMatthias Ringwald    # enumerate characteristics with same UUID, using optional name tag if available
495b3fcedb9SMatthias Ringwald    current_characteristic_uuid_string = c_string_for_uuid(parts[1]);
496b3fcedb9SMatthias Ringwald    index = 1
497b3fcedb9SMatthias Ringwald    if current_characteristic_uuid_string in characteristic_indices:
498b3fcedb9SMatthias Ringwald        index = characteristic_indices[current_characteristic_uuid_string] + 1
499b3fcedb9SMatthias Ringwald    characteristic_indices[current_characteristic_uuid_string] = index
500b3fcedb9SMatthias Ringwald    if len(parts) > 4:
501b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_')
502b3fcedb9SMatthias Ringwald    else:
503b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += ('_%02x' % index)
504b3fcedb9SMatthias Ringwald
505b3fcedb9SMatthias Ringwald    uuid       = parseUUID(parts[1])
506b3fcedb9SMatthias Ringwald    uuid_size  = len(uuid)
507b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[2])
508b3fcedb9SMatthias Ringwald    value = ', '.join([str(x) for x in parts[3:]])
509b3fcedb9SMatthias Ringwald
510b3fcedb9SMatthias Ringwald    # reliable writes is defined in an extended properties
511b3fcedb9SMatthias Ringwald    if (properties & property_flags['RELIABLE_WRITE']):
512b3fcedb9SMatthias Ringwald        properties = properties | property_flags['EXTENDED_PROPERTIES']
513b3fcedb9SMatthias Ringwald
514b3fcedb9SMatthias Ringwald    write_indent(fout)
515b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3])))
516b3fcedb9SMatthias Ringwald
517e22a2612SMatthias Ringwald
518e22a2612SMatthias Ringwald    characteristic_properties = gatt_characteristic_properties(properties)
519b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (1+2+uuid_size)
520b3fcedb9SMatthias Ringwald    write_indent(fout)
521b3fcedb9SMatthias Ringwald    write_16(fout, size)
522e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
523b3fcedb9SMatthias Ringwald    write_16(fout, handle)
524b3fcedb9SMatthias Ringwald    write_16(fout, 0x2803)
525e22a2612SMatthias Ringwald    write_8(fout, characteristic_properties)
526b3fcedb9SMatthias Ringwald    write_16(fout, handle+1)
527285653b2SMatthias Ringwald    write_uuid(fout, uuid)
528b3fcedb9SMatthias Ringwald    fout.write("\n")
529b3fcedb9SMatthias Ringwald    handle = handle + 1
530b3fcedb9SMatthias Ringwald    total_size = total_size + size
531b3fcedb9SMatthias Ringwald
532043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
533043f8832SMatthias Ringwald    database_hash_append_uint16(0x2803)
534043f8832SMatthias Ringwald    database_hash_append_uint8(characteristic_properties)
535043f8832SMatthias Ringwald    database_hash_append_uint16(handle+1)
536043f8832SMatthias Ringwald    database_hash_append_value(uuid)
537043f8832SMatthias Ringwald
538043f8832SMatthias Ringwald    uuid_is_database_hash = len(uuid) == 2 and uuid[0] == 0x2a and uuid[1] == 0x2b
539043f8832SMatthias Ringwald
540b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size
541043f8832SMatthias Ringwald    if uuid_is_database_hash:
542043f8832SMatthias Ringwald        size +=  16
543043f8832SMatthias Ringwald    else:
544b3fcedb9SMatthias Ringwald        if is_string(value):
545b3fcedb9SMatthias Ringwald            size = size + len(value)
546b3fcedb9SMatthias Ringwald        else:
547b3fcedb9SMatthias Ringwald            size = size + len(value.split())
548b3fcedb9SMatthias Ringwald
549e22a2612SMatthias Ringwald    value_flags = att_flags(properties)
5508ea3236cSMatthias Ringwald
5518ea3236cSMatthias Ringwald    # add UUID128 flag for value handle
552b3fcedb9SMatthias Ringwald    if uuid_size == 16:
553e22a2612SMatthias Ringwald        value_flags = value_flags | property_flags['LONG_UUID'];
554b3fcedb9SMatthias Ringwald
555b3fcedb9SMatthias Ringwald    write_indent(fout)
556b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value))
557d7ec1d24SMatthias Ringwald
558d7ec1d24SMatthias Ringwald    dump_flags(fout, value_flags)
559d7ec1d24SMatthias Ringwald
560b3fcedb9SMatthias Ringwald    write_indent(fout)
561b3fcedb9SMatthias Ringwald    write_16(fout, size)
562e22a2612SMatthias Ringwald    write_16(fout, value_flags)
563b3fcedb9SMatthias Ringwald    write_16(fout, handle)
564285653b2SMatthias Ringwald    write_uuid(fout, uuid)
565043f8832SMatthias Ringwald    if uuid_is_database_hash:
566043f8832SMatthias Ringwald        write_database_hash(fout)
567043f8832SMatthias Ringwald    else:
568b3fcedb9SMatthias Ringwald        if is_string(value):
569b3fcedb9SMatthias Ringwald            write_string(fout, value)
570b3fcedb9SMatthias Ringwald        else:
571b3fcedb9SMatthias Ringwald            write_sequence(fout,value)
572b3fcedb9SMatthias Ringwald
573b3fcedb9SMatthias Ringwald    fout.write("\n")
574729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
575b3fcedb9SMatthias Ringwald    handle = handle + 1
576b3fcedb9SMatthias Ringwald
577b3fcedb9SMatthias Ringwald    if add_client_characteristic_configuration(properties):
578e22a2612SMatthias Ringwald        # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC
579d7ec1d24SMatthias Ringwald        flags  = write_permissions_and_key_size_flags_from_properties(properties)
580e22a2612SMatthias Ringwald        flags |= property_flags['READ']
581e22a2612SMatthias Ringwald        flags |= property_flags['WRITE']
5829be4aecfSMatthias Ringwald        flags |= property_flags['WRITE_WITHOUT_RESPONSE']
583e22a2612SMatthias Ringwald        flags |= property_flags['DYNAMIC']
584b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
585d7ec1d24SMatthias Ringwald
586b3fcedb9SMatthias Ringwald        write_indent(fout)
587b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle))
588d7ec1d24SMatthias Ringwald
589d7ec1d24SMatthias Ringwald        dump_flags(fout, flags)
590d7ec1d24SMatthias Ringwald
591b3fcedb9SMatthias Ringwald        write_indent(fout)
592b3fcedb9SMatthias Ringwald        write_16(fout, size)
593e22a2612SMatthias Ringwald        write_16(fout, flags)
594b3fcedb9SMatthias Ringwald        write_16(fout, handle)
595b3fcedb9SMatthias Ringwald        write_16(fout, 0x2902)
596b3fcedb9SMatthias Ringwald        write_16(fout, 0)
597b3fcedb9SMatthias Ringwald        fout.write("\n")
598043f8832SMatthias Ringwald
599043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
600043f8832SMatthias Ringwald        database_hash_append_uint16(0x2902)
601043f8832SMatthias Ringwald
602729074c4SMatthias Ringwald        defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
603b3fcedb9SMatthias Ringwald        handle = handle + 1
604b3fcedb9SMatthias Ringwald
605043f8832SMatthias Ringwald
606b3fcedb9SMatthias Ringwald    if properties & property_flags['RELIABLE_WRITE']:
607b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
608b3fcedb9SMatthias Ringwald        write_indent(fout)
609b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle))
610b3fcedb9SMatthias Ringwald        write_indent(fout)
611b3fcedb9SMatthias Ringwald        write_16(fout, size)
612e22a2612SMatthias Ringwald        write_16(fout, read_only_anybody_flags)
613b3fcedb9SMatthias Ringwald        write_16(fout, handle)
614b3fcedb9SMatthias Ringwald        write_16(fout, 0x2900)
615b3fcedb9SMatthias Ringwald        write_16(fout, 1)   # Reliable Write
616b3fcedb9SMatthias Ringwald        fout.write("\n")
617043f8832SMatthias Ringwald
618043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
619043f8832SMatthias Ringwald        database_hash_append_uint16(0x2900)
620043f8832SMatthias Ringwald        database_hash_append_uint16(1)
621043f8832SMatthias Ringwald
622b3fcedb9SMatthias Ringwald        handle = handle + 1
623b3fcedb9SMatthias Ringwald
624b3fcedb9SMatthias Ringwalddef parseCharacteristicUserDescription(fout, parts):
625b3fcedb9SMatthias Ringwald    global handle
626b3fcedb9SMatthias Ringwald    global total_size
627b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
628b3fcedb9SMatthias Ringwald
629b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
630b3fcedb9SMatthias Ringwald    value      = parts[2]
631b3fcedb9SMatthias Ringwald
632b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
633b3fcedb9SMatthias Ringwald    if is_string(value):
634b7647eb6SMatthias Ringwald        size = size + len(value)
635b3fcedb9SMatthias Ringwald    else:
636b3fcedb9SMatthias Ringwald        size = size + len(value.split())
637b3fcedb9SMatthias Ringwald
638e22a2612SMatthias Ringwald    # use write, write permissions and encryption key size from attribute value and set READ_ANYBODY
639d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
640e22a2612SMatthias Ringwald    flags |= properties & property_flags['WRITE']
641e22a2612SMatthias Ringwald    flags |= property_flags['READ']
642e22a2612SMatthias Ringwald
643b3fcedb9SMatthias Ringwald    write_indent(fout)
644b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:])))
645d7ec1d24SMatthias Ringwald
646d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
647d7ec1d24SMatthias Ringwald
648b3fcedb9SMatthias Ringwald    write_indent(fout)
649b3fcedb9SMatthias Ringwald    write_16(fout, size)
650e22a2612SMatthias Ringwald    write_16(fout, flags)
651b3fcedb9SMatthias Ringwald    write_16(fout, handle)
652b3fcedb9SMatthias Ringwald    write_16(fout, 0x2901)
653b3fcedb9SMatthias Ringwald    if is_string(value):
654b3fcedb9SMatthias Ringwald        write_string(fout, value)
655b3fcedb9SMatthias Ringwald    else:
656b3fcedb9SMatthias Ringwald        write_sequence(fout,value)
657b3fcedb9SMatthias Ringwald    fout.write("\n")
658043f8832SMatthias Ringwald
659043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
660043f8832SMatthias Ringwald    database_hash_append_uint16(0x2901)
661043f8832SMatthias Ringwald
662729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
663b3fcedb9SMatthias Ringwald    handle = handle + 1
664b3fcedb9SMatthias Ringwald
665b3fcedb9SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts):
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)
680b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.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)
688b3fcedb9SMatthias Ringwald    write_16(fout, 0x2903)
689b3fcedb9SMatthias Ringwald    fout.write("\n")
690043f8832SMatthias Ringwald
691043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
692043f8832SMatthias Ringwald    database_hash_append_uint16(0x2903)
693043f8832SMatthias Ringwald
694729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
695b3fcedb9SMatthias Ringwald    handle = handle + 1
696b3fcedb9SMatthias Ringwald
697b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts):
698b3fcedb9SMatthias Ringwald    global handle
699b3fcedb9SMatthias Ringwald    global total_size
700b3fcedb9SMatthias Ringwald
701e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
702b3fcedb9SMatthias Ringwald
703b3fcedb9SMatthias Ringwald    identifier = parts[1]
704b3fcedb9SMatthias Ringwald    presentation_formats[identifier] = handle
705b3fcedb9SMatthias Ringwald    # print("format '%s' with handle %d\n" % (identifier, handle))
706b3fcedb9SMatthias Ringwald
707b3fcedb9SMatthias Ringwald    format     = parts[2]
708b3fcedb9SMatthias Ringwald    exponent   = parts[3]
709b3fcedb9SMatthias Ringwald    unit       = parseUUID(parts[4])
710b3fcedb9SMatthias Ringwald    name_space = parts[5]
711b3fcedb9SMatthias Ringwald    description = parseUUID(parts[6])
712b3fcedb9SMatthias Ringwald
713b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 7
714b3fcedb9SMatthias Ringwald
715b3fcedb9SMatthias Ringwald    write_indent(fout)
716b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
717b3fcedb9SMatthias Ringwald    write_indent(fout)
718b3fcedb9SMatthias Ringwald    write_16(fout, size)
719e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
720b3fcedb9SMatthias Ringwald    write_16(fout, handle)
721b3fcedb9SMatthias Ringwald    write_16(fout, 0x2904)
722b3fcedb9SMatthias Ringwald    write_sequence(fout, format)
723b3fcedb9SMatthias Ringwald    write_sequence(fout, exponent)
724285653b2SMatthias Ringwald    write_uuid(fout, unit)
725b3fcedb9SMatthias Ringwald    write_sequence(fout, name_space)
726285653b2SMatthias Ringwald    write_uuid(fout, description)
727b3fcedb9SMatthias Ringwald    fout.write("\n")
728043f8832SMatthias Ringwald
729043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
730043f8832SMatthias Ringwald    database_hash_append_uint16(0x2904)
731043f8832SMatthias Ringwald
732b3fcedb9SMatthias Ringwald    handle = handle + 1
733b3fcedb9SMatthias Ringwald
734b3fcedb9SMatthias Ringwald
735b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts):
736b3fcedb9SMatthias Ringwald    global handle
737b3fcedb9SMatthias Ringwald    global total_size
738b3fcedb9SMatthias Ringwald
739e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
740b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2
741b3fcedb9SMatthias Ringwald
742b3fcedb9SMatthias Ringwald    write_indent(fout)
743b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
744b3fcedb9SMatthias Ringwald    write_indent(fout)
745b3fcedb9SMatthias Ringwald    write_16(fout, size)
746e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
747b3fcedb9SMatthias Ringwald    write_16(fout, handle)
748b3fcedb9SMatthias Ringwald    write_16(fout, 0x2905)
749b3fcedb9SMatthias Ringwald    for identifier in parts[1:]:
750b3fcedb9SMatthias Ringwald        format_handle = presentation_formats[identifier]
751b3fcedb9SMatthias Ringwald        if format == 0:
752b3fcedb9SMatthias Ringwald            print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier)
753b3fcedb9SMatthias Ringwald            sys.exit(1)
754b3fcedb9SMatthias Ringwald        write_16(fout, format_handle)
755b3fcedb9SMatthias Ringwald    fout.write("\n")
756043f8832SMatthias Ringwald
757043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
758043f8832SMatthias Ringwald    database_hash_append_uint16(0x2905)
759043f8832SMatthias Ringwald
760b3fcedb9SMatthias Ringwald    handle = handle + 1
761b3fcedb9SMatthias Ringwald
762b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts):
763b3fcedb9SMatthias Ringwald    global handle
764b3fcedb9SMatthias Ringwald    global total_size
765b3fcedb9SMatthias Ringwald
766e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
767b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1 + 1
768b3fcedb9SMatthias Ringwald
769231a3c5dSMatthias Ringwald    report_id = parts[2]
770231a3c5dSMatthias Ringwald    report_type = parts[3]
771b3fcedb9SMatthias Ringwald
772b3fcedb9SMatthias Ringwald    write_indent(fout)
773b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
774b3fcedb9SMatthias Ringwald    write_indent(fout)
775b3fcedb9SMatthias Ringwald    write_16(fout, size)
776e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
777b3fcedb9SMatthias Ringwald    write_16(fout, handle)
778b3fcedb9SMatthias Ringwald    write_16(fout, 0x2908)
779b3fcedb9SMatthias Ringwald    write_sequence(fout, report_id)
780b3fcedb9SMatthias Ringwald    write_sequence(fout, report_type)
781b3fcedb9SMatthias Ringwald    fout.write("\n")
782b3fcedb9SMatthias Ringwald    handle = handle + 1
783b3fcedb9SMatthias Ringwald
784b3fcedb9SMatthias Ringwald
785b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts):
786b3fcedb9SMatthias Ringwald    global handle
787b3fcedb9SMatthias Ringwald    global total_size
788b3fcedb9SMatthias Ringwald
789e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
790b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1
791b3fcedb9SMatthias Ringwald
792b3fcedb9SMatthias Ringwald    no_of_digitals = parts[1]
793b3fcedb9SMatthias Ringwald
794b3fcedb9SMatthias Ringwald    write_indent(fout)
795b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:])))
796b3fcedb9SMatthias Ringwald    write_indent(fout)
797b3fcedb9SMatthias Ringwald    write_16(fout, size)
798e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
799b3fcedb9SMatthias Ringwald    write_16(fout, handle)
800b3fcedb9SMatthias Ringwald    write_16(fout, 0x2909)
801b3fcedb9SMatthias Ringwald    write_sequence(fout, no_of_digitals)
802b3fcedb9SMatthias Ringwald    fout.write("\n")
803b3fcedb9SMatthias Ringwald    handle = handle + 1
804b3fcedb9SMatthias Ringwald
80560b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout):
806b3fcedb9SMatthias Ringwald    global handle
807b3fcedb9SMatthias Ringwald    global total_size
808b3fcedb9SMatthias Ringwald
809b165f97bSMatthias Ringwald    line_count = 0;
810b3fcedb9SMatthias Ringwald    for line in fin:
811b3fcedb9SMatthias Ringwald        line = line.strip("\n\r ")
812b165f97bSMatthias Ringwald        line_count += 1
813b3fcedb9SMatthias Ringwald
814b165f97bSMatthias Ringwald        if line.startswith("//"):
815b165f97bSMatthias Ringwald            fout.write("    //" + line.lstrip('/') + '\n')
816b165f97bSMatthias Ringwald            continue
817b165f97bSMatthias Ringwald
81860b51a4cSMatthias Ringwald        if line.startswith("#import"):
81960b51a4cSMatthias Ringwald            imported_file = ''
82060b51a4cSMatthias Ringwald            parts = re.match('#import\s+<(.*)>\w*',line)
82160b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
822dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
82360b51a4cSMatthias Ringwald            parts = re.match('#import\s+"(.*)"\w*',line)
82460b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
825dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
82660b51a4cSMatthias Ringwald            if len(imported_file) == 0:
82760b51a4cSMatthias Ringwald                print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count))
82860b51a4cSMatthias Ringwald                continue
82960b51a4cSMatthias Ringwald
830dbb3997aSMilanka Ringwald            imported_file = getFile( imported_file )
83160b51a4cSMatthias Ringwald            print("Importing %s" % imported_file)
83260b51a4cSMatthias Ringwald            try:
83360b51a4cSMatthias Ringwald                imported_fin = codecs.open (imported_file, encoding='utf-8')
83460b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- BEGIN\n')
83560b51a4cSMatthias Ringwald                parseLines(imported_file, imported_fin, fout)
83660b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- END\n')
83760b51a4cSMatthias Ringwald            except IOError as e:
83860b51a4cSMatthias Ringwald                print('ERROR: Import failed. Please check path.')
83960b51a4cSMatthias Ringwald
84060b51a4cSMatthias Ringwald            continue
84160b51a4cSMatthias Ringwald
84260b51a4cSMatthias Ringwald        if line.startswith("#TODO"):
84360b51a4cSMatthias Ringwald            print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count))
844b165f97bSMatthias Ringwald            print ("'%s'" % line)
845b165f97bSMatthias Ringwald            fout.write("// " + line + '\n')
846b3fcedb9SMatthias Ringwald            continue
847b3fcedb9SMatthias Ringwald
848b3fcedb9SMatthias Ringwald        if len(line) == 0:
849b3fcedb9SMatthias Ringwald            continue
850b3fcedb9SMatthias Ringwald
851b3fcedb9SMatthias Ringwald        f = io.StringIO(line)
852b3fcedb9SMatthias Ringwald        parts_list = csv.reader(f, delimiter=',', quotechar='"')
853b3fcedb9SMatthias Ringwald
854b3fcedb9SMatthias Ringwald        for parts in parts_list:
855b3fcedb9SMatthias Ringwald            for index, object in enumerate(parts):
856b3fcedb9SMatthias Ringwald                parts[index] = object.strip().lstrip('"').rstrip('"')
857b3fcedb9SMatthias Ringwald
858b3fcedb9SMatthias Ringwald            if parts[0] == 'PRIMARY_SERVICE':
859b3fcedb9SMatthias Ringwald                parsePrimaryService(fout, parts)
860b3fcedb9SMatthias Ringwald                continue
861b3fcedb9SMatthias Ringwald
862b3fcedb9SMatthias Ringwald            if parts[0] == 'SECONDARY_SERVICE':
863b3fcedb9SMatthias Ringwald                parseSecondaryService(fout, parts)
864b3fcedb9SMatthias Ringwald                continue
865b3fcedb9SMatthias Ringwald
866b3fcedb9SMatthias Ringwald            if parts[0] == 'INCLUDE_SERVICE':
867b3fcedb9SMatthias Ringwald                parseIncludeService(fout, parts)
868b3fcedb9SMatthias Ringwald                continue
869b3fcedb9SMatthias Ringwald
870b3fcedb9SMatthias Ringwald            # 2803
871b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC':
872b3fcedb9SMatthias Ringwald                parseCharacteristic(fout, parts)
873b3fcedb9SMatthias Ringwald                continue
874b3fcedb9SMatthias Ringwald
875b3fcedb9SMatthias Ringwald            # 2900 Characteristic Extended Properties
876b3fcedb9SMatthias Ringwald
877b3fcedb9SMatthias Ringwald            # 2901
878b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
879b3fcedb9SMatthias Ringwald                parseCharacteristicUserDescription(fout, parts)
880b3fcedb9SMatthias Ringwald                continue
881b3fcedb9SMatthias Ringwald
882b165f97bSMatthias Ringwald
883b165f97bSMatthias Ringwald            # 2902 Client Characteristic Configuration - automatically included in Characteristic if
884b3fcedb9SMatthias Ringwald            # notification / indication is supported
885231a3c5dSMatthias Ringwald            if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION':
886b165f97bSMatthias Ringwald                continue
887b3fcedb9SMatthias Ringwald
888b3fcedb9SMatthias Ringwald            # 2903
889b3fcedb9SMatthias Ringwald            if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
890b3fcedb9SMatthias Ringwald                parseServerCharacteristicConfiguration(fout, parts)
891b3fcedb9SMatthias Ringwald                continue
892b3fcedb9SMatthias Ringwald
893b3fcedb9SMatthias Ringwald            # 2904
894b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_FORMAT':
895b3fcedb9SMatthias Ringwald                parseCharacteristicFormat(fout, parts)
896b3fcedb9SMatthias Ringwald                continue
897b3fcedb9SMatthias Ringwald
898b3fcedb9SMatthias Ringwald            # 2905
899b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
900b3fcedb9SMatthias Ringwald                parseCharacteristicAggregateFormat(fout, parts)
901b3fcedb9SMatthias Ringwald                continue
902b3fcedb9SMatthias Ringwald
903b3fcedb9SMatthias Ringwald            # 2906
904b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
905b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
906b3fcedb9SMatthias Ringwald                continue
907b3fcedb9SMatthias Ringwald
908b3fcedb9SMatthias Ringwald            # 2907
909b3fcedb9SMatthias Ringwald            if parts[0] == 'EXTERNAL_REPORT_REFERENCE':
910b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
911b3fcedb9SMatthias Ringwald                continue
912b3fcedb9SMatthias Ringwald
913b3fcedb9SMatthias Ringwald            # 2908
914b3fcedb9SMatthias Ringwald            if parts[0] == 'REPORT_REFERENCE':
915b3fcedb9SMatthias Ringwald                parseReportReference(fout, parts)
916b3fcedb9SMatthias Ringwald                continue
917b3fcedb9SMatthias Ringwald
918b3fcedb9SMatthias Ringwald            # 2909
919b3fcedb9SMatthias Ringwald            if parts[0] == 'NUMBER_OF_DIGITALS':
920b3fcedb9SMatthias Ringwald                parseNumberOfDigitals(fout, parts)
921b3fcedb9SMatthias Ringwald                continue
922b3fcedb9SMatthias Ringwald
923b3fcedb9SMatthias Ringwald            # 290A
924b3fcedb9SMatthias Ringwald            if parts[0] == 'VALUE_TRIGGER_SETTING':
925b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
926b3fcedb9SMatthias Ringwald                continue
927b3fcedb9SMatthias Ringwald
928b3fcedb9SMatthias Ringwald            # 290B
929b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION':
930b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
931b3fcedb9SMatthias Ringwald                continue
932b3fcedb9SMatthias Ringwald
933b3fcedb9SMatthias Ringwald            # 290C
934b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT':
935b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
936b3fcedb9SMatthias Ringwald                continue
937b3fcedb9SMatthias Ringwald
938b3fcedb9SMatthias Ringwald            # 290D
939b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING':
940b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
941b3fcedb9SMatthias Ringwald                continue
942b3fcedb9SMatthias Ringwald
943b3fcedb9SMatthias Ringwald            # 2906
944b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
945b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
946b3fcedb9SMatthias Ringwald                continue
947b3fcedb9SMatthias Ringwald
948b3fcedb9SMatthias Ringwald            print("WARNING: unknown token: %s\n" % (parts[0]))
949b3fcedb9SMatthias Ringwald
9507050bf34SMatthias Ringwalddef parse(fname_in, fin, fname_out, tool_path, fout):
95160b51a4cSMatthias Ringwald    global handle
95260b51a4cSMatthias Ringwald    global total_size
95360b51a4cSMatthias Ringwald
9547050bf34SMatthias Ringwald    fout.write(header.format(fname_out, fname_in, tool_path))
95560b51a4cSMatthias Ringwald    fout.write('{\n')
956fd1be25dSMatthias Ringwald    write_indent(fout)
957fd1be25dSMatthias Ringwald    fout.write('// ATT DB Version\n')
958fd1be25dSMatthias Ringwald    write_indent(fout)
959fd1be25dSMatthias Ringwald    fout.write('1,\n')
960fd1be25dSMatthias Ringwald    fout.write("\n")
96160b51a4cSMatthias Ringwald
96260b51a4cSMatthias Ringwald    parseLines(fname_in, fin, fout)
96360b51a4cSMatthias Ringwald
964729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
965b3fcedb9SMatthias Ringwald    write_indent(fout)
966b3fcedb9SMatthias Ringwald    fout.write("// END\n");
967b3fcedb9SMatthias Ringwald    write_indent(fout)
968b3fcedb9SMatthias Ringwald    write_16(fout,0)
969b3fcedb9SMatthias Ringwald    fout.write("\n")
970b3fcedb9SMatthias Ringwald    total_size = total_size + 2
971b3fcedb9SMatthias Ringwald
972b3fcedb9SMatthias Ringwald    fout.write("}; // total size %u bytes \n" % total_size);
973b3fcedb9SMatthias Ringwald
974b3fcedb9SMatthias Ringwalddef listHandles(fout):
975b3fcedb9SMatthias Ringwald    fout.write('\n\n')
976b3fcedb9SMatthias Ringwald    fout.write('//\n')
977729074c4SMatthias Ringwald    fout.write('// list service handle ranges\n')
978729074c4SMatthias Ringwald    fout.write('//\n')
979729074c4SMatthias Ringwald    for define in defines_for_services:
980729074c4SMatthias Ringwald        fout.write(define)
981729074c4SMatthias Ringwald        fout.write('\n')
982729074c4SMatthias Ringwald    fout.write('\n')
983729074c4SMatthias Ringwald    fout.write('//\n')
984b3fcedb9SMatthias Ringwald    fout.write('// list mapping between characteristics and handles\n')
985b3fcedb9SMatthias Ringwald    fout.write('//\n')
986729074c4SMatthias Ringwald    for define in defines_for_characteristics:
987b3fcedb9SMatthias Ringwald        fout.write(define)
988b3fcedb9SMatthias Ringwald        fout.write('\n')
989b3fcedb9SMatthias Ringwald
990dbb3997aSMilanka Ringwalddef getFile( fileName ):
99178b65b0aSMatthias Ringwald    for d in include_paths:
99278b65b0aSMatthias Ringwald        fullFile = os.path.normpath(d + os.sep + fileName) # because Windows exists
99378b65b0aSMatthias Ringwald        # print("test %s" % fullFile)
994dbb3997aSMilanka Ringwald        if os.path.isfile( fullFile ) == True:
995dbb3997aSMilanka Ringwald            return fullFile
996dbb3997aSMilanka Ringwald    print ("'{0}' not found".format( fileName ))
99778b65b0aSMatthias Ringwald    print ("Include paths: %s" % ", ".join(include_paths))
998dbb3997aSMilanka Ringwald    exit(-1)
999dbb3997aSMilanka Ringwald
1000dbb3997aSMilanka Ringwald
1001dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
100278b65b0aSMatthias Ringwalddefault_includes = [os.path.normpath(path) for path in [ btstack_root + '/src/', btstack_root + '/src/ble/gatt-service/']]
1003dbb3997aSMilanka Ringwald
1004dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack')
1005dbb3997aSMilanka Ringwald
1006dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes',
1007dbb3997aSMilanka Ringwald        help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes))
1008dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str,
1009dbb3997aSMilanka Ringwald        help='gatt file to be compiled')
1010dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str,
1011dbb3997aSMilanka Ringwald        help='header file to be generated')
1012dbb3997aSMilanka Ringwald
1013dbb3997aSMilanka Ringwaldargs = parser.parse_args()
1014dbb3997aSMilanka Ringwald
101578b65b0aSMatthias Ringwald# add include path arguments
101678b65b0aSMatthias Ringwaldif args.I != None:
101778b65b0aSMatthias Ringwald    for d in args.I:
101878b65b0aSMatthias Ringwald        include_paths.append(os.path.normpath(d[0]))
101978b65b0aSMatthias Ringwald
1020dbb3997aSMilanka Ringwald# append default include paths
102178b65b0aSMatthias Ringwaldinclude_paths.extend(default_includes)
1022dbb3997aSMilanka Ringwald
1023b3fcedb9SMatthias Ringwaldtry:
1024b165f97bSMatthias Ringwald    # read defines from bluetooth_gatt.h
1025dbb3997aSMilanka Ringwald    gen_path = getFile( 'bluetooth_gatt.h' )
1026b165f97bSMatthias Ringwald    bluetooth_gatt = read_defines(gen_path)
1027b165f97bSMatthias Ringwald
1028dbb3997aSMilanka Ringwald    filename = args.hfile
1029dbb3997aSMilanka Ringwald    fin  = codecs.open (args.gattfile, encoding='utf-8')
1030285653b2SMatthias Ringwald
1031285653b2SMatthias Ringwald    # pass 1: create temp .h file
1032d78181d6SMatthias Ringwald    ftemp = tempfile.TemporaryFile(mode='w+t')
1033285653b2SMatthias Ringwald    parse(args.gattfile, fin, filename, sys.argv[0], ftemp)
1034285653b2SMatthias Ringwald    listHandles(ftemp)
1035285653b2SMatthias Ringwald
1036043f8832SMatthias Ringwald    # calc GATT Database Hash
1037043f8832SMatthias Ringwald    db_hash = aes_cmac(bytearray(16), database_hash_message)
1038043f8832SMatthias Ringwald    if isinstance(db_hash, str):
1039043f8832SMatthias Ringwald        # python2
1040043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % ord(i)) for i in db_hash]
1041043f8832SMatthias Ringwald    elif isinstance(db_hash, bytes):
1042043f8832SMatthias Ringwald        # python3
1043043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % i) for i in db_hash]
1044043f8832SMatthias Ringwald    else:
1045043f8832SMatthias Ringwald        print("AES CMAC returns unexpected type %s, abort" % type(db_hash))
1046043f8832SMatthias Ringwald        sys.exit(1)
1047043f8832SMatthias Ringwald    # reverse hash to get little endian
1048043f8832SMatthias Ringwald    db_hash_sequence.reverse()
1049043f8832SMatthias Ringwald    db_hash_string = ', '.join(db_hash_sequence) + ', '
1050043f8832SMatthias Ringwald
1051285653b2SMatthias Ringwald    # pass 2: insert GATT Database Hash
1052b3fcedb9SMatthias Ringwald    fout = open (filename, 'w')
1053285653b2SMatthias Ringwald    ftemp.seek(0)
1054285653b2SMatthias Ringwald    for line in ftemp:
1055043f8832SMatthias Ringwald        fout.write(line.replace('THE-DATABASE-HASH', db_hash_string))
1056b3fcedb9SMatthias Ringwald    fout.close()
1057285653b2SMatthias Ringwald    ftemp.close()
1058285653b2SMatthias Ringwald
1059b165f97bSMatthias Ringwald    print('Created %s' % filename)
1060b3fcedb9SMatthias Ringwald
1061b3fcedb9SMatthias Ringwaldexcept IOError as e:
1062e22a2612SMatthias Ringwald
1063b3fcedb9SMatthias Ringwald    print(usage)
1064b3fcedb9SMatthias Ringwald    sys.exit(1)
1065b3fcedb9SMatthias Ringwald
1066b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n')
1067