xref: /btstack/tool/compile_gatt.py (revision ced5a857a4247f7eaffbe9bc3bff2d5bde76efb7)
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
12*ced5a857SMatthias Ringwald# alternatively, the pycryptodome package can be used instead
13*ced5a857SMatthias 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
25*ced5a857SMatthias Ringwaldhave_crypto = True
26*ced5a857SMatthias Ringwald# try to import PyCryptodome independent from PyCrypto
27043f8832SMatthias Ringwaldtry:
28379d3aceSMatthias Ringwald    from Cryptodome.Cipher import AES
29043f8832SMatthias Ringwald    from Cryptodome.Hash import CMAC
30*ced5a857SMatthias Ringwaldexcept ImportError:
31*ced5a857SMatthias Ringwald    # fallback: try to import PyCryptodome as (an almost drop-in) replacement for the PyCrypto library
32*ced5a857SMatthias Ringwald    try:
33*ced5a857SMatthias Ringwald        from Crypto.Cipher import AES
34*ced5a857SMatthias 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)")
38*ced5a857SMatthias 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
54b3fcedb9SMatthias Ringwaldconst uint8_t profile_data[] =
55b3fcedb9SMatthias Ringwald'''
56b3fcedb9SMatthias Ringwald
57b3fcedb9SMatthias Ringwaldprint('''
58dbb3997aSMilanka RingwaldBLE configuration generator for use with BTstack
59dbb3997aSMilanka RingwaldCopyright 2018 BlueKitchen GmbH
60b3fcedb9SMatthias Ringwald''')
61b3fcedb9SMatthias Ringwald
62b3fcedb9SMatthias Ringwaldassigned_uuids = {
63b3fcedb9SMatthias Ringwald    'GAP_SERVICE'          : 0x1800,
64b3fcedb9SMatthias Ringwald    'GATT_SERVICE'         : 0x1801,
65b3fcedb9SMatthias Ringwald    'GAP_DEVICE_NAME'      : 0x2a00,
66b3fcedb9SMatthias Ringwald    'GAP_APPEARANCE'       : 0x2a01,
67b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02,
68b3fcedb9SMatthias Ringwald    'GAP_RECONNECTION_ADDRESS'    : 0x2A03,
69b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04,
70b3fcedb9SMatthias Ringwald    'GATT_SERVICE_CHANGED' : 0x2a05,
71043f8832SMatthias Ringwald    'GATT_DATABASE_HASH' : 0x2b2a
72b3fcedb9SMatthias Ringwald}
73b3fcedb9SMatthias Ringwald
74e72176f8SMatthias Ringwaldsecurity_permsission = ['ANYBODY','ENCRYPTED', 'AUTHENTICATED', 'AUTHORIZED', 'AUTHENTICATED_SC']
75d7ec1d24SMatthias Ringwald
76b3fcedb9SMatthias Ringwaldproperty_flags = {
77eb6072adSMatthias Ringwald    # GATT Characteristic Properties
78b3fcedb9SMatthias Ringwald    'BROADCAST' :                   0x01,
79b3fcedb9SMatthias Ringwald    'READ' :                        0x02,
80b3fcedb9SMatthias Ringwald    'WRITE_WITHOUT_RESPONSE' :      0x04,
81b3fcedb9SMatthias Ringwald    'WRITE' :                       0x08,
82b3fcedb9SMatthias Ringwald    'NOTIFY':                       0x10,
83b3fcedb9SMatthias Ringwald    'INDICATE' :                    0x20,
84b3fcedb9SMatthias Ringwald    'AUTHENTICATED_SIGNED_WRITE' :  0x40,
85b3fcedb9SMatthias Ringwald    'EXTENDED_PROPERTIES' :         0x80,
86b3fcedb9SMatthias Ringwald    # custom BTstack extension
87b3fcedb9SMatthias Ringwald    'DYNAMIC':                      0x100,
88b3fcedb9SMatthias Ringwald    'LONG_UUID':                    0x200,
89e22a2612SMatthias Ringwald
90e22a2612SMatthias Ringwald    # read permissions
91e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_0':        0x400,
92e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_1':        0x800,
93e22a2612SMatthias Ringwald
94e22a2612SMatthias Ringwald    #
95b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_7':       0x6000,
96b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_8':       0x7000,
97b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_9':       0x8000,
98b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_10':      0x9000,
99b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_11':      0xa000,
100b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_12':      0xb000,
101b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_13':      0xc000,
102b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_14':      0xd000,
103b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_15':      0xe000,
104b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_16':      0xf000,
105e22a2612SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_MASK':    0xf000,
106eb6072adSMatthias Ringwald
107b3fcedb9SMatthias Ringwald    # only used by gatt compiler >= 0xffff
108b3fcedb9SMatthias Ringwald    # Extended Properties
109e72176f8SMatthias Ringwald    'RELIABLE_WRITE':              0x00010000,
110e72176f8SMatthias Ringwald    'AUTHENTICATION_REQUIRED':     0x00020000,
111e72176f8SMatthias Ringwald    'AUTHORIZATION_REQUIRED':      0x00040000,
112e72176f8SMatthias Ringwald    'READ_ANYBODY':                0x00080000,
113e72176f8SMatthias Ringwald    'READ_ENCRYPTED':              0x00100000,
114e72176f8SMatthias Ringwald    'READ_AUTHENTICATED':          0x00200000,
115e72176f8SMatthias Ringwald    'READ_AUTHENTICATED_SC':       0x00400000,
116e72176f8SMatthias Ringwald    'READ_AUTHORIZED':             0x00800000,
117e72176f8SMatthias Ringwald    'WRITE_ANYBODY':               0x01000000,
118e72176f8SMatthias Ringwald    'WRITE_ENCRYPTED':             0x02000000,
119e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED':         0x04000000,
120e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED_SC':      0x08000000,
121e72176f8SMatthias Ringwald    'WRITE_AUTHORIZED':            0x10000000,
122eb6072adSMatthias Ringwald
123eb6072adSMatthias Ringwald    # Broadcast, Notify, Indicate, Extended Properties are only used to describe a GATT Characteristic, but are free to use with att_db
124e72176f8SMatthias Ringwald    # - write permissions
125e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_0':      0x01,
126e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_1':      0x10,
127e72176f8SMatthias Ringwald    # - SC required
128e72176f8SMatthias Ringwald    'READ_PERMISSION_SC':          0x20,
129e72176f8SMatthias Ringwald    'WRITE_PERMISSION_SC':         0x80,
130b3fcedb9SMatthias Ringwald}
131b3fcedb9SMatthias Ringwald
132b3fcedb9SMatthias Ringwaldservices = dict()
133b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict()
134b3fcedb9SMatthias Ringwaldpresentation_formats = dict()
135b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = ""
136b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0
137b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = ""
138729074c4SMatthias Ringwalddefines_for_characteristics = []
139729074c4SMatthias Ringwalddefines_for_services = []
14078b65b0aSMatthias Ringwaldinclude_paths = []
141043f8832SMatthias Ringwalddatabase_hash_message = bytearray()
142b3fcedb9SMatthias Ringwald
143b3fcedb9SMatthias Ringwaldhandle = 1
144b3fcedb9SMatthias Ringwaldtotal_size = 0
145b3fcedb9SMatthias Ringwald
146043f8832SMatthias Ringwalddef aes_cmac(key, n):
147043f8832SMatthias Ringwald    if have_crypto:
148043f8832SMatthias Ringwald        cobj = CMAC.new(key, ciphermod=AES)
149043f8832SMatthias Ringwald        cobj.update(n)
150043f8832SMatthias Ringwald        return cobj.digest()
151043f8832SMatthias Ringwald    else:
1527490175eSMatthias Ringwald        # return random value
1537490175eSMatthias Ringwald        return os.urandom(16)
154043f8832SMatthias Ringwald
155b165f97bSMatthias Ringwalddef read_defines(infile):
156b165f97bSMatthias Ringwald    defines = dict()
157b165f97bSMatthias Ringwald    with open (infile, 'rt') as fin:
158b165f97bSMatthias Ringwald        for line in fin:
159b165f97bSMatthias Ringwald            parts = re.match('#define\s+(\w+)\s+(\w+)',line)
160b165f97bSMatthias Ringwald            if parts and len(parts.groups()) == 2:
161b165f97bSMatthias Ringwald                (key, value) = parts.groups()
162b165f97bSMatthias Ringwald                defines[key] = int(value, 16)
163b165f97bSMatthias Ringwald    return defines
164b165f97bSMatthias Ringwald
165b3fcedb9SMatthias Ringwalddef keyForUUID(uuid):
166b3fcedb9SMatthias Ringwald    keyUUID = ""
167b3fcedb9SMatthias Ringwald    for i in uuid:
168b3fcedb9SMatthias Ringwald        keyUUID += "%02x" % i
169b3fcedb9SMatthias Ringwald    return keyUUID
170b3fcedb9SMatthias Ringwald
171b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid):
172b3fcedb9SMatthias Ringwald    return uuid.replace('-', '_')
173b3fcedb9SMatthias Ringwald
174b3fcedb9SMatthias Ringwalddef twoByteLEFor(value):
175b3fcedb9SMatthias Ringwald    return [ (value & 0xff), (value >> 8)]
176b3fcedb9SMatthias Ringwald
177b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text):
178b3fcedb9SMatthias 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):
179b3fcedb9SMatthias Ringwald        return True
180b3fcedb9SMatthias Ringwald    return False
181b3fcedb9SMatthias Ringwald
182b3fcedb9SMatthias Ringwalddef parseUUID128(uuid):
183b3fcedb9SMatthias 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)
184b3fcedb9SMatthias Ringwald    uuid_bytes = []
185b3fcedb9SMatthias Ringwald    for i in range(8, 0, -1):
186b3fcedb9SMatthias Ringwald        uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16))
187b3fcedb9SMatthias Ringwald    return uuid_bytes
188b3fcedb9SMatthias Ringwald
189b3fcedb9SMatthias Ringwalddef parseUUID(uuid):
190b3fcedb9SMatthias Ringwald    if uuid in assigned_uuids:
191b3fcedb9SMatthias Ringwald        return twoByteLEFor(assigned_uuids[uuid])
192b165f97bSMatthias Ringwald    uuid_upper = uuid.upper().replace('.','_')
193b165f97bSMatthias Ringwald    if uuid_upper in bluetooth_gatt:
194b165f97bSMatthias Ringwald        return twoByteLEFor(bluetooth_gatt[uuid_upper])
195b3fcedb9SMatthias Ringwald    if is_128bit_uuid(uuid):
196b3fcedb9SMatthias Ringwald        return parseUUID128(uuid)
197b3fcedb9SMatthias Ringwald    uuidInt = int(uuid, 16)
198b3fcedb9SMatthias Ringwald    return twoByteLEFor(uuidInt)
199b3fcedb9SMatthias Ringwald
200b3fcedb9SMatthias Ringwalddef parseProperties(properties):
201b3fcedb9SMatthias Ringwald    value = 0
202b3fcedb9SMatthias Ringwald    parts = properties.split("|")
203b3fcedb9SMatthias Ringwald    for property in parts:
204b3fcedb9SMatthias Ringwald        property = property.strip()
205b3fcedb9SMatthias Ringwald        if property in property_flags:
206b3fcedb9SMatthias Ringwald            value |= property_flags[property]
207b3fcedb9SMatthias Ringwald        else:
208b3fcedb9SMatthias Ringwald            print("WARNING: property %s undefined" % (property))
209e22a2612SMatthias Ringwald
210e22a2612SMatthias Ringwald    return value;
211e22a2612SMatthias Ringwald
212e22a2612SMatthias Ringwalddef gatt_characteristic_properties(properties):
213e22a2612SMatthias Ringwald    return properties & 0xff
214e22a2612SMatthias Ringwald
215e22a2612SMatthias Ringwalddef att_flags(properties):
216e72176f8SMatthias Ringwald    # drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Properties (0x80) - not used for flags
217e72176f8SMatthias Ringwald    properties &= 0xffffff4e
218e22a2612SMatthias Ringwald
219e22a2612SMatthias Ringwald    # rw permissions distinct
220e22a2612SMatthias Ringwald    distinct_permissions_used = properties & (
221e22a2612SMatthias Ringwald        property_flags['READ_AUTHORIZED'] |
222e72176f8SMatthias Ringwald        property_flags['READ_AUTHENTICATED_SC'] |
223e22a2612SMatthias Ringwald        property_flags['READ_AUTHENTICATED'] |
224e22a2612SMatthias Ringwald        property_flags['READ_ENCRYPTED'] |
225e22a2612SMatthias Ringwald        property_flags['READ_ANYBODY'] |
226e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHORIZED'] |
227e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED'] |
228e72176f8SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED_SC'] |
229e22a2612SMatthias Ringwald        property_flags['WRITE_ENCRYPTED'] |
230e22a2612SMatthias Ringwald        property_flags['WRITE_ANYBODY']
231e22a2612SMatthias Ringwald    ) != 0
232e22a2612SMatthias Ringwald
233e22a2612SMatthias Ringwald    # post process properties
234e22a2612SMatthias Ringwald    encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0
235e22a2612SMatthias Ringwald
236d7ec1d24SMatthias Ringwald    # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted
237e22a2612SMatthias Ringwald    if encryption_key_size_specified and not distinct_permissions_used:
238e22a2612SMatthias Ringwald        properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED']
239e22a2612SMatthias Ringwald
240d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated
241d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used:
242d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED']
243d7ec1d24SMatthias Ringwald
244d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized
245d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used:
246d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED']
247d7ec1d24SMatthias Ringwald
248d7ec1d24SMatthias Ringwald    # determine read/write security requirements
249d7ec1d24SMatthias Ringwald    read_security_level  = 0
250d7ec1d24SMatthias Ringwald    write_security_level = 0
251e72176f8SMatthias Ringwald    read_requires_sc     = False
252e72176f8SMatthias Ringwald    write_requires_sc    = False
253e22a2612SMatthias Ringwald    if properties & property_flags['READ_AUTHORIZED']:
254d7ec1d24SMatthias Ringwald        read_security_level = 3
255e22a2612SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED']:
256d7ec1d24SMatthias Ringwald        read_security_level = 2
257e72176f8SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED_SC']:
258e72176f8SMatthias Ringwald        read_security_level = 2
259e72176f8SMatthias Ringwald        read_requires_sc = True
260e22a2612SMatthias Ringwald    elif properties & property_flags['READ_ENCRYPTED']:
261d7ec1d24SMatthias Ringwald        read_security_level = 1
262e22a2612SMatthias Ringwald    if properties & property_flags['WRITE_AUTHORIZED']:
263d7ec1d24SMatthias Ringwald        write_security_level = 3
264e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED']:
265d7ec1d24SMatthias Ringwald        write_security_level = 2
266e72176f8SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED_SC']:
267e72176f8SMatthias Ringwald        write_security_level = 2
268e72176f8SMatthias Ringwald        write_requires_sc = True
269e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_ENCRYPTED']:
270d7ec1d24SMatthias Ringwald        write_security_level = 1
271d7ec1d24SMatthias Ringwald
272d7ec1d24SMatthias Ringwald    # map security requirements to flags
273d7ec1d24SMatthias Ringwald    if read_security_level & 2:
274d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_1']
275d7ec1d24SMatthias Ringwald    if read_security_level & 1:
276d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_0']
277e72176f8SMatthias Ringwald    if read_requires_sc:
278e72176f8SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_SC']
279d7ec1d24SMatthias Ringwald    if write_security_level & 2:
280d7ec1d24SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_1']
281d7ec1d24SMatthias Ringwald    if write_security_level & 1:
282e22a2612SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_0']
283e72176f8SMatthias Ringwald    if write_requires_sc:
284e72176f8SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_SC']
285e22a2612SMatthias Ringwald
286e22a2612SMatthias Ringwald    return properties
287e22a2612SMatthias Ringwald
288d7ec1d24SMatthias Ringwalddef write_permissions_and_key_size_flags_from_properties(properties):
289e22a2612SMatthias Ringwald    return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1'])
290b3fcedb9SMatthias Ringwald
291b3fcedb9SMatthias Ringwalddef write_8(fout, value):
292b3fcedb9SMatthias Ringwald    fout.write( "0x%02x, " % (value & 0xff))
293b3fcedb9SMatthias Ringwald
294b3fcedb9SMatthias Ringwalddef write_16(fout, value):
295b3fcedb9SMatthias Ringwald    fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff))
296b3fcedb9SMatthias Ringwald
297285653b2SMatthias Ringwalddef write_uuid(fout, uuid):
298b3fcedb9SMatthias Ringwald    for byte in uuid:
299b3fcedb9SMatthias Ringwald        fout.write( "0x%02x, " % byte)
300b3fcedb9SMatthias Ringwald
301b3fcedb9SMatthias Ringwalddef write_string(fout, text):
302b3fcedb9SMatthias Ringwald    for l in text.lstrip('"').rstrip('"'):
303b3fcedb9SMatthias Ringwald        write_8(fout, ord(l))
304b3fcedb9SMatthias Ringwald
305b3fcedb9SMatthias Ringwalddef write_sequence(fout, text):
306b3fcedb9SMatthias Ringwald    parts = text.split()
307b3fcedb9SMatthias Ringwald    for part in parts:
308b3fcedb9SMatthias Ringwald        fout.write("0x%s, " % (part.strip()))
309b3fcedb9SMatthias Ringwald
310043f8832SMatthias Ringwalddef write_database_hash(fout):
311043f8832SMatthias Ringwald    fout.write("THE-DATABASE-HASH")
312043f8832SMatthias Ringwald
313b3fcedb9SMatthias Ringwalddef write_indent(fout):
314b3fcedb9SMatthias Ringwald    fout.write("    ")
315b3fcedb9SMatthias Ringwald
316d7ec1d24SMatthias Ringwalddef read_permissions_from_flags(flags):
317d7ec1d24SMatthias Ringwald    permissions = 0
318d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_0']:
319d7ec1d24SMatthias Ringwald        permissions |= 1
320d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_1']:
321d7ec1d24SMatthias Ringwald        permissions |= 2
322e72176f8SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_SC'] and permissions == 2:
323e72176f8SMatthias Ringwald        permissions = 4
324d7ec1d24SMatthias Ringwald    return permissions
325d7ec1d24SMatthias Ringwald
326d7ec1d24SMatthias Ringwalddef write_permissions_from_flags(flags):
327d7ec1d24SMatthias Ringwald    permissions = 0
328d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_0']:
329d7ec1d24SMatthias Ringwald        permissions |= 1
330d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_1']:
331d7ec1d24SMatthias Ringwald        permissions |= 2
332e72176f8SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_SC'] and permissions == 2:
333e72176f8SMatthias Ringwald        permissions = 4
334d7ec1d24SMatthias Ringwald    return permissions
335d7ec1d24SMatthias Ringwald
336d7ec1d24SMatthias Ringwalddef encryption_key_size_from_flags(flags):
337d7ec1d24SMatthias Ringwald    encryption_key_size = (flags & 0xf000) >> 12
338d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
339d7ec1d24SMatthias Ringwald        encryption_key_size += 1
340d7ec1d24SMatthias Ringwald    return encryption_key_size
341d7ec1d24SMatthias Ringwald
342b3fcedb9SMatthias Ringwalddef is_string(text):
343b3fcedb9SMatthias Ringwald    for item in text.split(" "):
344b3fcedb9SMatthias Ringwald        if not all(c in string.hexdigits for c in item):
345b3fcedb9SMatthias Ringwald            return True
346b3fcedb9SMatthias Ringwald    return False
347b3fcedb9SMatthias Ringwald
348b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties):
349b3fcedb9SMatthias Ringwald    return properties & (property_flags['NOTIFY'] | property_flags['INDICATE'])
350b3fcedb9SMatthias Ringwald
351729074c4SMatthias Ringwalddef serviceDefinitionComplete(fout):
352729074c4SMatthias Ringwald    global services
353729074c4SMatthias Ringwald    if current_service_uuid_string:
354729074c4SMatthias Ringwald        fout.write("\n")
355729074c4SMatthias Ringwald        # print("append service %s = [%d, %d]" % (current_characteristic_uuid_string, current_service_start_handle, handle-1))
356729074c4SMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle))
357729074c4SMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1))
358729074c4SMatthias Ringwald        services[current_service_uuid_string] = [current_service_start_handle, handle-1]
359729074c4SMatthias Ringwald
360d7ec1d24SMatthias Ringwalddef dump_flags(fout, flags):
361d7ec1d24SMatthias Ringwald    global security_permsission
362d7ec1d24SMatthias Ringwald    encryption_key_size = encryption_key_size_from_flags(flags)
363d7ec1d24SMatthias Ringwald    read_permissions    = security_permsission[read_permissions_from_flags(flags)]
364d7ec1d24SMatthias Ringwald    write_permissions   = security_permsission[write_permissions_from_flags(flags)]
365d7ec1d24SMatthias Ringwald    write_indent(fout)
366d7ec1d24SMatthias Ringwald    fout.write('// ')
367d7ec1d24SMatthias Ringwald    first = 1
368d7ec1d24SMatthias Ringwald    if flags & property_flags['READ']:
369d7ec1d24SMatthias Ringwald        fout.write('READ_%s' % read_permissions)
370d7ec1d24SMatthias Ringwald        first = 0
371d7ec1d24SMatthias Ringwald    if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']):
372d7ec1d24SMatthias Ringwald        if not first:
373d7ec1d24SMatthias Ringwald            fout.write(', ')
374d7ec1d24SMatthias Ringwald        first = 0
375d7ec1d24SMatthias Ringwald        fout.write('WRITE_%s' % write_permissions)
376d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
377d7ec1d24SMatthias Ringwald        if not first:
378d7ec1d24SMatthias Ringwald            fout.write(', ')
379d7ec1d24SMatthias Ringwald        first = 0
380d7ec1d24SMatthias Ringwald        fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size)
381d7ec1d24SMatthias Ringwald    fout.write('\n')
382d7ec1d24SMatthias Ringwald
383043f8832SMatthias Ringwalddef database_hash_append_uint8(value):
384043f8832SMatthias Ringwald    global database_hash_message
385043f8832SMatthias Ringwald    database_hash_message.append(value)
386043f8832SMatthias Ringwald
387043f8832SMatthias Ringwalddef database_hash_append_uint16(value):
388043f8832SMatthias Ringwald    global database_hash_message
389043f8832SMatthias Ringwald    database_hash_append_uint8(value & 0xff)
390043f8832SMatthias Ringwald    database_hash_append_uint8((value >> 8) & 0xff)
391043f8832SMatthias Ringwald
392043f8832SMatthias Ringwalddef database_hash_append_value(value):
393043f8832SMatthias Ringwald    global database_hash_message
394043f8832SMatthias Ringwald    for byte in value:
395043f8832SMatthias Ringwald        database_hash_append_uint8(byte)
396043f8832SMatthias Ringwald
397b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type):
398b3fcedb9SMatthias Ringwald    global handle
399b3fcedb9SMatthias Ringwald    global total_size
400b3fcedb9SMatthias Ringwald    global current_service_uuid_string
401b3fcedb9SMatthias Ringwald    global current_service_start_handle
402b3fcedb9SMatthias Ringwald
403729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
404b3fcedb9SMatthias Ringwald
405d7ec1d24SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
406b3fcedb9SMatthias Ringwald
407b3fcedb9SMatthias Ringwald    write_indent(fout)
408b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
409b3fcedb9SMatthias Ringwald
410b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
411b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
412b3fcedb9SMatthias Ringwald
413b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size + 2
414b3fcedb9SMatthias Ringwald
415b3fcedb9SMatthias Ringwald    if service_type == 0x2802:
416b3fcedb9SMatthias Ringwald        size += 4
417b3fcedb9SMatthias Ringwald
418b3fcedb9SMatthias Ringwald    write_indent(fout)
419b3fcedb9SMatthias Ringwald    write_16(fout, size)
420d7ec1d24SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
421b3fcedb9SMatthias Ringwald    write_16(fout, handle)
422b3fcedb9SMatthias Ringwald    write_16(fout, service_type)
423285653b2SMatthias Ringwald    write_uuid(fout, uuid)
424b3fcedb9SMatthias Ringwald    fout.write("\n")
425b3fcedb9SMatthias Ringwald
426043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
427043f8832SMatthias Ringwald    database_hash_append_uint16(service_type)
428043f8832SMatthias Ringwald    database_hash_append_value(uuid)
429043f8832SMatthias Ringwald
430729074c4SMatthias Ringwald    current_service_uuid_string = c_string_for_uuid(parts[1])
431b3fcedb9SMatthias Ringwald    current_service_start_handle = handle
432b3fcedb9SMatthias Ringwald    handle = handle + 1
433b3fcedb9SMatthias Ringwald    total_size = total_size + size
434b3fcedb9SMatthias Ringwald
435b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts):
436b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2800)
437b3fcedb9SMatthias Ringwald
438b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts):
439b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2801)
440b3fcedb9SMatthias Ringwald
441b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts):
442b3fcedb9SMatthias Ringwald    global handle
443b3fcedb9SMatthias Ringwald    global total_size
444b3fcedb9SMatthias Ringwald
445e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
446b3fcedb9SMatthias Ringwald
447b3fcedb9SMatthias Ringwald    write_indent(fout)
448b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
449b3fcedb9SMatthias Ringwald
450b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
451b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
452b3fcedb9SMatthias Ringwald    if uuid_size > 2:
453b3fcedb9SMatthias Ringwald        uuid_size = 0
454729074c4SMatthias Ringwald    # print("Include Service ", c_string_for_uuid(uuid))
455b3fcedb9SMatthias Ringwald
456b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 4 + uuid_size
457b3fcedb9SMatthias Ringwald
458729074c4SMatthias Ringwald    keyUUID = c_string_for_uuid(parts[1])
459b3fcedb9SMatthias Ringwald
460b3fcedb9SMatthias Ringwald    write_indent(fout)
461b3fcedb9SMatthias Ringwald    write_16(fout, size)
462e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
463b3fcedb9SMatthias Ringwald    write_16(fout, handle)
464b3fcedb9SMatthias Ringwald    write_16(fout, 0x2802)
465b3fcedb9SMatthias Ringwald    write_16(fout, services[keyUUID][0])
466b3fcedb9SMatthias Ringwald    write_16(fout, services[keyUUID][1])
467b3fcedb9SMatthias Ringwald    if uuid_size > 0:
468285653b2SMatthias Ringwald        write_uuid(fout, uuid)
469b3fcedb9SMatthias Ringwald    fout.write("\n")
470b3fcedb9SMatthias Ringwald
471043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
472043f8832SMatthias Ringwald    database_hash_append_uint16(0x2802)
473043f8832SMatthias Ringwald    database_hash_append_uint16(services[keyUUID][0])
474043f8832SMatthias Ringwald    database_hash_append_uint16(services[keyUUID][1])
475043f8832SMatthias Ringwald    if uuid_size > 0:
476043f8832SMatthias Ringwald        database_hash_append_value(uuid)
477043f8832SMatthias Ringwald
478b3fcedb9SMatthias Ringwald    handle = handle + 1
479b3fcedb9SMatthias Ringwald    total_size = total_size + size
480b3fcedb9SMatthias Ringwald
481b3fcedb9SMatthias Ringwald
482b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts):
483b3fcedb9SMatthias Ringwald    global handle
484b3fcedb9SMatthias Ringwald    global total_size
485b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
486b3fcedb9SMatthias Ringwald    global characteristic_indices
487b3fcedb9SMatthias Ringwald
488e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
489b3fcedb9SMatthias Ringwald
490b3fcedb9SMatthias Ringwald    # enumerate characteristics with same UUID, using optional name tag if available
491b3fcedb9SMatthias Ringwald    current_characteristic_uuid_string = c_string_for_uuid(parts[1]);
492b3fcedb9SMatthias Ringwald    index = 1
493b3fcedb9SMatthias Ringwald    if current_characteristic_uuid_string in characteristic_indices:
494b3fcedb9SMatthias Ringwald        index = characteristic_indices[current_characteristic_uuid_string] + 1
495b3fcedb9SMatthias Ringwald    characteristic_indices[current_characteristic_uuid_string] = index
496b3fcedb9SMatthias Ringwald    if len(parts) > 4:
497b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_')
498b3fcedb9SMatthias Ringwald    else:
499b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += ('_%02x' % index)
500b3fcedb9SMatthias Ringwald
501b3fcedb9SMatthias Ringwald    uuid       = parseUUID(parts[1])
502b3fcedb9SMatthias Ringwald    uuid_size  = len(uuid)
503b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[2])
504b3fcedb9SMatthias Ringwald    value = ', '.join([str(x) for x in parts[3:]])
505b3fcedb9SMatthias Ringwald
506b3fcedb9SMatthias Ringwald    # reliable writes is defined in an extended properties
507b3fcedb9SMatthias Ringwald    if (properties & property_flags['RELIABLE_WRITE']):
508b3fcedb9SMatthias Ringwald        properties = properties | property_flags['EXTENDED_PROPERTIES']
509b3fcedb9SMatthias Ringwald
510b3fcedb9SMatthias Ringwald    write_indent(fout)
511b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3])))
512b3fcedb9SMatthias Ringwald
513e22a2612SMatthias Ringwald
514e22a2612SMatthias Ringwald    characteristic_properties = gatt_characteristic_properties(properties)
515b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (1+2+uuid_size)
516b3fcedb9SMatthias Ringwald    write_indent(fout)
517b3fcedb9SMatthias Ringwald    write_16(fout, size)
518e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
519b3fcedb9SMatthias Ringwald    write_16(fout, handle)
520b3fcedb9SMatthias Ringwald    write_16(fout, 0x2803)
521e22a2612SMatthias Ringwald    write_8(fout, characteristic_properties)
522b3fcedb9SMatthias Ringwald    write_16(fout, handle+1)
523285653b2SMatthias Ringwald    write_uuid(fout, uuid)
524b3fcedb9SMatthias Ringwald    fout.write("\n")
525b3fcedb9SMatthias Ringwald    handle = handle + 1
526b3fcedb9SMatthias Ringwald    total_size = total_size + size
527b3fcedb9SMatthias Ringwald
528043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
529043f8832SMatthias Ringwald    database_hash_append_uint16(0x2803)
530043f8832SMatthias Ringwald    database_hash_append_uint8(characteristic_properties)
531043f8832SMatthias Ringwald    database_hash_append_uint16(handle+1)
532043f8832SMatthias Ringwald    database_hash_append_value(uuid)
533043f8832SMatthias Ringwald
534043f8832SMatthias Ringwald    uuid_is_database_hash = len(uuid) == 2 and uuid[0] == 0x2a and uuid[1] == 0x2b
535043f8832SMatthias Ringwald
536b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size
537043f8832SMatthias Ringwald    if uuid_is_database_hash:
538043f8832SMatthias Ringwald        size +=  16
539043f8832SMatthias Ringwald    else:
540b3fcedb9SMatthias Ringwald        if is_string(value):
541b3fcedb9SMatthias Ringwald            size = size + len(value)
542b3fcedb9SMatthias Ringwald        else:
543b3fcedb9SMatthias Ringwald            size = size + len(value.split())
544b3fcedb9SMatthias Ringwald
545e22a2612SMatthias Ringwald    value_flags = att_flags(properties)
5468ea3236cSMatthias Ringwald
5478ea3236cSMatthias Ringwald    # add UUID128 flag for value handle
548b3fcedb9SMatthias Ringwald    if uuid_size == 16:
549e22a2612SMatthias Ringwald        value_flags = value_flags | property_flags['LONG_UUID'];
550b3fcedb9SMatthias Ringwald
551b3fcedb9SMatthias Ringwald    write_indent(fout)
552b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value))
553d7ec1d24SMatthias Ringwald
554d7ec1d24SMatthias Ringwald    dump_flags(fout, value_flags)
555d7ec1d24SMatthias Ringwald
556b3fcedb9SMatthias Ringwald    write_indent(fout)
557b3fcedb9SMatthias Ringwald    write_16(fout, size)
558e22a2612SMatthias Ringwald    write_16(fout, value_flags)
559b3fcedb9SMatthias Ringwald    write_16(fout, handle)
560285653b2SMatthias Ringwald    write_uuid(fout, uuid)
561043f8832SMatthias Ringwald    if uuid_is_database_hash:
562043f8832SMatthias Ringwald        write_database_hash(fout)
563043f8832SMatthias Ringwald    else:
564b3fcedb9SMatthias Ringwald        if is_string(value):
565b3fcedb9SMatthias Ringwald            write_string(fout, value)
566b3fcedb9SMatthias Ringwald        else:
567b3fcedb9SMatthias Ringwald            write_sequence(fout,value)
568b3fcedb9SMatthias Ringwald
569b3fcedb9SMatthias Ringwald    fout.write("\n")
570729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
571b3fcedb9SMatthias Ringwald    handle = handle + 1
572b3fcedb9SMatthias Ringwald
573b3fcedb9SMatthias Ringwald    if add_client_characteristic_configuration(properties):
574e22a2612SMatthias Ringwald        # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC
575d7ec1d24SMatthias Ringwald        flags  = write_permissions_and_key_size_flags_from_properties(properties)
576e22a2612SMatthias Ringwald        flags |= property_flags['READ']
577e22a2612SMatthias Ringwald        flags |= property_flags['WRITE']
5789be4aecfSMatthias Ringwald        flags |= property_flags['WRITE_WITHOUT_RESPONSE']
579e22a2612SMatthias Ringwald        flags |= property_flags['DYNAMIC']
580b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
581d7ec1d24SMatthias Ringwald
582b3fcedb9SMatthias Ringwald        write_indent(fout)
583b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle))
584d7ec1d24SMatthias Ringwald
585d7ec1d24SMatthias Ringwald        dump_flags(fout, flags)
586d7ec1d24SMatthias Ringwald
587b3fcedb9SMatthias Ringwald        write_indent(fout)
588b3fcedb9SMatthias Ringwald        write_16(fout, size)
589e22a2612SMatthias Ringwald        write_16(fout, flags)
590b3fcedb9SMatthias Ringwald        write_16(fout, handle)
591b3fcedb9SMatthias Ringwald        write_16(fout, 0x2902)
592b3fcedb9SMatthias Ringwald        write_16(fout, 0)
593b3fcedb9SMatthias Ringwald        fout.write("\n")
594043f8832SMatthias Ringwald
595043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
596043f8832SMatthias Ringwald        database_hash_append_uint16(0x2902)
597043f8832SMatthias Ringwald
598729074c4SMatthias Ringwald        defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
599b3fcedb9SMatthias Ringwald        handle = handle + 1
600b3fcedb9SMatthias Ringwald
601043f8832SMatthias Ringwald
602b3fcedb9SMatthias Ringwald    if properties & property_flags['RELIABLE_WRITE']:
603b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
604b3fcedb9SMatthias Ringwald        write_indent(fout)
605b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle))
606b3fcedb9SMatthias Ringwald        write_indent(fout)
607b3fcedb9SMatthias Ringwald        write_16(fout, size)
608e22a2612SMatthias Ringwald        write_16(fout, read_only_anybody_flags)
609b3fcedb9SMatthias Ringwald        write_16(fout, handle)
610b3fcedb9SMatthias Ringwald        write_16(fout, 0x2900)
611b3fcedb9SMatthias Ringwald        write_16(fout, 1)   # Reliable Write
612b3fcedb9SMatthias Ringwald        fout.write("\n")
613043f8832SMatthias Ringwald
614043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
615043f8832SMatthias Ringwald        database_hash_append_uint16(0x2900)
616043f8832SMatthias Ringwald        database_hash_append_uint16(1)
617043f8832SMatthias Ringwald
618b3fcedb9SMatthias Ringwald        handle = handle + 1
619b3fcedb9SMatthias Ringwald
620b3fcedb9SMatthias Ringwalddef parseCharacteristicUserDescription(fout, parts):
621b3fcedb9SMatthias Ringwald    global handle
622b3fcedb9SMatthias Ringwald    global total_size
623b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
624b3fcedb9SMatthias Ringwald
625b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
626b3fcedb9SMatthias Ringwald    value      = parts[2]
627b3fcedb9SMatthias Ringwald
628b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
629b3fcedb9SMatthias Ringwald    if is_string(value):
630b7647eb6SMatthias Ringwald        size = size + len(value)
631b3fcedb9SMatthias Ringwald    else:
632b3fcedb9SMatthias Ringwald        size = size + len(value.split())
633b3fcedb9SMatthias Ringwald
634e22a2612SMatthias Ringwald    # use write, write permissions and encryption key size from attribute value and set READ_ANYBODY
635d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
636e22a2612SMatthias Ringwald    flags |= properties & property_flags['WRITE']
637e22a2612SMatthias Ringwald    flags |= property_flags['READ']
638e22a2612SMatthias Ringwald
639b3fcedb9SMatthias Ringwald    write_indent(fout)
640b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:])))
641d7ec1d24SMatthias Ringwald
642d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
643d7ec1d24SMatthias Ringwald
644b3fcedb9SMatthias Ringwald    write_indent(fout)
645b3fcedb9SMatthias Ringwald    write_16(fout, size)
646e22a2612SMatthias Ringwald    write_16(fout, flags)
647b3fcedb9SMatthias Ringwald    write_16(fout, handle)
648b3fcedb9SMatthias Ringwald    write_16(fout, 0x2901)
649b3fcedb9SMatthias Ringwald    if is_string(value):
650b3fcedb9SMatthias Ringwald        write_string(fout, value)
651b3fcedb9SMatthias Ringwald    else:
652b3fcedb9SMatthias Ringwald        write_sequence(fout,value)
653b3fcedb9SMatthias Ringwald    fout.write("\n")
654043f8832SMatthias Ringwald
655043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
656043f8832SMatthias Ringwald    database_hash_append_uint16(0x2901)
657043f8832SMatthias Ringwald
658729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
659b3fcedb9SMatthias Ringwald    handle = handle + 1
660b3fcedb9SMatthias Ringwald
661b3fcedb9SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts):
662b3fcedb9SMatthias Ringwald    global handle
663b3fcedb9SMatthias Ringwald    global total_size
664b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
665b3fcedb9SMatthias Ringwald
666b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
667b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
668b3fcedb9SMatthias Ringwald
669e22a2612SMatthias Ringwald    # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY
670d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
671e22a2612SMatthias Ringwald    flags |= property_flags['READ']
672e22a2612SMatthias Ringwald    flags |= property_flags['WRITE']
673e22a2612SMatthias Ringwald    flags |= property_flags['DYNAMIC']
674e22a2612SMatthias Ringwald
675b3fcedb9SMatthias Ringwald    write_indent(fout)
676b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:])))
677d7ec1d24SMatthias Ringwald
678d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
679d7ec1d24SMatthias Ringwald
680b3fcedb9SMatthias Ringwald    write_indent(fout)
681b3fcedb9SMatthias Ringwald    write_16(fout, size)
682e22a2612SMatthias Ringwald    write_16(fout, flags)
683b3fcedb9SMatthias Ringwald    write_16(fout, handle)
684b3fcedb9SMatthias Ringwald    write_16(fout, 0x2903)
685b3fcedb9SMatthias Ringwald    fout.write("\n")
686043f8832SMatthias Ringwald
687043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
688043f8832SMatthias Ringwald    database_hash_append_uint16(0x2903)
689043f8832SMatthias Ringwald
690729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
691b3fcedb9SMatthias Ringwald    handle = handle + 1
692b3fcedb9SMatthias Ringwald
693b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts):
694b3fcedb9SMatthias Ringwald    global handle
695b3fcedb9SMatthias Ringwald    global total_size
696b3fcedb9SMatthias Ringwald
697e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
698b3fcedb9SMatthias Ringwald
699b3fcedb9SMatthias Ringwald    identifier = parts[1]
700b3fcedb9SMatthias Ringwald    presentation_formats[identifier] = handle
701b3fcedb9SMatthias Ringwald    # print("format '%s' with handle %d\n" % (identifier, handle))
702b3fcedb9SMatthias Ringwald
703b3fcedb9SMatthias Ringwald    format     = parts[2]
704b3fcedb9SMatthias Ringwald    exponent   = parts[3]
705b3fcedb9SMatthias Ringwald    unit       = parseUUID(parts[4])
706b3fcedb9SMatthias Ringwald    name_space = parts[5]
707b3fcedb9SMatthias Ringwald    description = parseUUID(parts[6])
708b3fcedb9SMatthias Ringwald
709b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 7
710b3fcedb9SMatthias Ringwald
711b3fcedb9SMatthias Ringwald    write_indent(fout)
712b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
713b3fcedb9SMatthias Ringwald    write_indent(fout)
714b3fcedb9SMatthias Ringwald    write_16(fout, size)
715e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
716b3fcedb9SMatthias Ringwald    write_16(fout, handle)
717b3fcedb9SMatthias Ringwald    write_16(fout, 0x2904)
718b3fcedb9SMatthias Ringwald    write_sequence(fout, format)
719b3fcedb9SMatthias Ringwald    write_sequence(fout, exponent)
720285653b2SMatthias Ringwald    write_uuid(fout, unit)
721b3fcedb9SMatthias Ringwald    write_sequence(fout, name_space)
722285653b2SMatthias Ringwald    write_uuid(fout, description)
723b3fcedb9SMatthias Ringwald    fout.write("\n")
724043f8832SMatthias Ringwald
725043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
726043f8832SMatthias Ringwald    database_hash_append_uint16(0x2904)
727043f8832SMatthias Ringwald
728b3fcedb9SMatthias Ringwald    handle = handle + 1
729b3fcedb9SMatthias Ringwald
730b3fcedb9SMatthias Ringwald
731b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts):
732b3fcedb9SMatthias Ringwald    global handle
733b3fcedb9SMatthias Ringwald    global total_size
734b3fcedb9SMatthias Ringwald
735e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
736b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2
737b3fcedb9SMatthias Ringwald
738b3fcedb9SMatthias Ringwald    write_indent(fout)
739b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
740b3fcedb9SMatthias Ringwald    write_indent(fout)
741b3fcedb9SMatthias Ringwald    write_16(fout, size)
742e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
743b3fcedb9SMatthias Ringwald    write_16(fout, handle)
744b3fcedb9SMatthias Ringwald    write_16(fout, 0x2905)
745b3fcedb9SMatthias Ringwald    for identifier in parts[1:]:
746b3fcedb9SMatthias Ringwald        format_handle = presentation_formats[identifier]
747b3fcedb9SMatthias Ringwald        if format == 0:
748b3fcedb9SMatthias Ringwald            print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier)
749b3fcedb9SMatthias Ringwald            sys.exit(1)
750b3fcedb9SMatthias Ringwald        write_16(fout, format_handle)
751b3fcedb9SMatthias Ringwald    fout.write("\n")
752043f8832SMatthias Ringwald
753043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
754043f8832SMatthias Ringwald    database_hash_append_uint16(0x2905)
755043f8832SMatthias Ringwald
756b3fcedb9SMatthias Ringwald    handle = handle + 1
757b3fcedb9SMatthias Ringwald
758b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts):
759b3fcedb9SMatthias Ringwald    global handle
760b3fcedb9SMatthias Ringwald    global total_size
761b3fcedb9SMatthias Ringwald
762e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
763b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1 + 1
764b3fcedb9SMatthias Ringwald
765231a3c5dSMatthias Ringwald    report_id = parts[2]
766231a3c5dSMatthias Ringwald    report_type = parts[3]
767b3fcedb9SMatthias Ringwald
768b3fcedb9SMatthias Ringwald    write_indent(fout)
769b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
770b3fcedb9SMatthias Ringwald    write_indent(fout)
771b3fcedb9SMatthias Ringwald    write_16(fout, size)
772e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
773b3fcedb9SMatthias Ringwald    write_16(fout, handle)
774b3fcedb9SMatthias Ringwald    write_16(fout, 0x2908)
775b3fcedb9SMatthias Ringwald    write_sequence(fout, report_id)
776b3fcedb9SMatthias Ringwald    write_sequence(fout, report_type)
777b3fcedb9SMatthias Ringwald    fout.write("\n")
778b3fcedb9SMatthias Ringwald    handle = handle + 1
779b3fcedb9SMatthias Ringwald
780b3fcedb9SMatthias Ringwald
781b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts):
782b3fcedb9SMatthias Ringwald    global handle
783b3fcedb9SMatthias Ringwald    global total_size
784b3fcedb9SMatthias Ringwald
785e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
786b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1
787b3fcedb9SMatthias Ringwald
788b3fcedb9SMatthias Ringwald    no_of_digitals = parts[1]
789b3fcedb9SMatthias Ringwald
790b3fcedb9SMatthias Ringwald    write_indent(fout)
791b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:])))
792b3fcedb9SMatthias Ringwald    write_indent(fout)
793b3fcedb9SMatthias Ringwald    write_16(fout, size)
794e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
795b3fcedb9SMatthias Ringwald    write_16(fout, handle)
796b3fcedb9SMatthias Ringwald    write_16(fout, 0x2909)
797b3fcedb9SMatthias Ringwald    write_sequence(fout, no_of_digitals)
798b3fcedb9SMatthias Ringwald    fout.write("\n")
799b3fcedb9SMatthias Ringwald    handle = handle + 1
800b3fcedb9SMatthias Ringwald
80160b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout):
802b3fcedb9SMatthias Ringwald    global handle
803b3fcedb9SMatthias Ringwald    global total_size
804b3fcedb9SMatthias Ringwald
805b165f97bSMatthias Ringwald    line_count = 0;
806b3fcedb9SMatthias Ringwald    for line in fin:
807b3fcedb9SMatthias Ringwald        line = line.strip("\n\r ")
808b165f97bSMatthias Ringwald        line_count += 1
809b3fcedb9SMatthias Ringwald
810b165f97bSMatthias Ringwald        if line.startswith("//"):
811b165f97bSMatthias Ringwald            fout.write("    //" + line.lstrip('/') + '\n')
812b165f97bSMatthias Ringwald            continue
813b165f97bSMatthias Ringwald
81460b51a4cSMatthias Ringwald        if line.startswith("#import"):
81560b51a4cSMatthias Ringwald            imported_file = ''
81660b51a4cSMatthias Ringwald            parts = re.match('#import\s+<(.*)>\w*',line)
81760b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
818dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
81960b51a4cSMatthias Ringwald            parts = re.match('#import\s+"(.*)"\w*',line)
82060b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
821dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
82260b51a4cSMatthias Ringwald            if len(imported_file) == 0:
82360b51a4cSMatthias Ringwald                print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count))
82460b51a4cSMatthias Ringwald                continue
82560b51a4cSMatthias Ringwald
826dbb3997aSMilanka Ringwald            imported_file = getFile( imported_file )
82760b51a4cSMatthias Ringwald            print("Importing %s" % imported_file)
82860b51a4cSMatthias Ringwald            try:
82960b51a4cSMatthias Ringwald                imported_fin = codecs.open (imported_file, encoding='utf-8')
83060b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- BEGIN\n')
83160b51a4cSMatthias Ringwald                parseLines(imported_file, imported_fin, fout)
83260b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- END\n')
83360b51a4cSMatthias Ringwald            except IOError as e:
83460b51a4cSMatthias Ringwald                print('ERROR: Import failed. Please check path.')
83560b51a4cSMatthias Ringwald
83660b51a4cSMatthias Ringwald            continue
83760b51a4cSMatthias Ringwald
83860b51a4cSMatthias Ringwald        if line.startswith("#TODO"):
83960b51a4cSMatthias Ringwald            print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count))
840b165f97bSMatthias Ringwald            print ("'%s'" % line)
841b165f97bSMatthias Ringwald            fout.write("// " + line + '\n')
842b3fcedb9SMatthias Ringwald            continue
843b3fcedb9SMatthias Ringwald
844b3fcedb9SMatthias Ringwald        if len(line) == 0:
845b3fcedb9SMatthias Ringwald            continue
846b3fcedb9SMatthias Ringwald
847b3fcedb9SMatthias Ringwald        f = io.StringIO(line)
848b3fcedb9SMatthias Ringwald        parts_list = csv.reader(f, delimiter=',', quotechar='"')
849b3fcedb9SMatthias Ringwald
850b3fcedb9SMatthias Ringwald        for parts in parts_list:
851b3fcedb9SMatthias Ringwald            for index, object in enumerate(parts):
852b3fcedb9SMatthias Ringwald                parts[index] = object.strip().lstrip('"').rstrip('"')
853b3fcedb9SMatthias Ringwald
854b3fcedb9SMatthias Ringwald            if parts[0] == 'PRIMARY_SERVICE':
855b3fcedb9SMatthias Ringwald                parsePrimaryService(fout, parts)
856b3fcedb9SMatthias Ringwald                continue
857b3fcedb9SMatthias Ringwald
858b3fcedb9SMatthias Ringwald            if parts[0] == 'SECONDARY_SERVICE':
859b3fcedb9SMatthias Ringwald                parseSecondaryService(fout, parts)
860b3fcedb9SMatthias Ringwald                continue
861b3fcedb9SMatthias Ringwald
862b3fcedb9SMatthias Ringwald            if parts[0] == 'INCLUDE_SERVICE':
863b3fcedb9SMatthias Ringwald                parseIncludeService(fout, parts)
864b3fcedb9SMatthias Ringwald                continue
865b3fcedb9SMatthias Ringwald
866b3fcedb9SMatthias Ringwald            # 2803
867b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC':
868b3fcedb9SMatthias Ringwald                parseCharacteristic(fout, parts)
869b3fcedb9SMatthias Ringwald                continue
870b3fcedb9SMatthias Ringwald
871b3fcedb9SMatthias Ringwald            # 2900 Characteristic Extended Properties
872b3fcedb9SMatthias Ringwald
873b3fcedb9SMatthias Ringwald            # 2901
874b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
875b3fcedb9SMatthias Ringwald                parseCharacteristicUserDescription(fout, parts)
876b3fcedb9SMatthias Ringwald                continue
877b3fcedb9SMatthias Ringwald
878b165f97bSMatthias Ringwald
879b165f97bSMatthias Ringwald            # 2902 Client Characteristic Configuration - automatically included in Characteristic if
880b3fcedb9SMatthias Ringwald            # notification / indication is supported
881231a3c5dSMatthias Ringwald            if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION':
882b165f97bSMatthias Ringwald                continue
883b3fcedb9SMatthias Ringwald
884b3fcedb9SMatthias Ringwald            # 2903
885b3fcedb9SMatthias Ringwald            if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
886b3fcedb9SMatthias Ringwald                parseServerCharacteristicConfiguration(fout, parts)
887b3fcedb9SMatthias Ringwald                continue
888b3fcedb9SMatthias Ringwald
889b3fcedb9SMatthias Ringwald            # 2904
890b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_FORMAT':
891b3fcedb9SMatthias Ringwald                parseCharacteristicFormat(fout, parts)
892b3fcedb9SMatthias Ringwald                continue
893b3fcedb9SMatthias Ringwald
894b3fcedb9SMatthias Ringwald            # 2905
895b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
896b3fcedb9SMatthias Ringwald                parseCharacteristicAggregateFormat(fout, parts)
897b3fcedb9SMatthias Ringwald                continue
898b3fcedb9SMatthias Ringwald
899b3fcedb9SMatthias Ringwald            # 2906
900b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
901b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
902b3fcedb9SMatthias Ringwald                continue
903b3fcedb9SMatthias Ringwald
904b3fcedb9SMatthias Ringwald            # 2907
905b3fcedb9SMatthias Ringwald            if parts[0] == 'EXTERNAL_REPORT_REFERENCE':
906b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
907b3fcedb9SMatthias Ringwald                continue
908b3fcedb9SMatthias Ringwald
909b3fcedb9SMatthias Ringwald            # 2908
910b3fcedb9SMatthias Ringwald            if parts[0] == 'REPORT_REFERENCE':
911b3fcedb9SMatthias Ringwald                parseReportReference(fout, parts)
912b3fcedb9SMatthias Ringwald                continue
913b3fcedb9SMatthias Ringwald
914b3fcedb9SMatthias Ringwald            # 2909
915b3fcedb9SMatthias Ringwald            if parts[0] == 'NUMBER_OF_DIGITALS':
916b3fcedb9SMatthias Ringwald                parseNumberOfDigitals(fout, parts)
917b3fcedb9SMatthias Ringwald                continue
918b3fcedb9SMatthias Ringwald
919b3fcedb9SMatthias Ringwald            # 290A
920b3fcedb9SMatthias Ringwald            if parts[0] == 'VALUE_TRIGGER_SETTING':
921b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
922b3fcedb9SMatthias Ringwald                continue
923b3fcedb9SMatthias Ringwald
924b3fcedb9SMatthias Ringwald            # 290B
925b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION':
926b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
927b3fcedb9SMatthias Ringwald                continue
928b3fcedb9SMatthias Ringwald
929b3fcedb9SMatthias Ringwald            # 290C
930b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT':
931b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
932b3fcedb9SMatthias Ringwald                continue
933b3fcedb9SMatthias Ringwald
934b3fcedb9SMatthias Ringwald            # 290D
935b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING':
936b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
937b3fcedb9SMatthias Ringwald                continue
938b3fcedb9SMatthias Ringwald
939b3fcedb9SMatthias Ringwald            # 2906
940b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
941b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
942b3fcedb9SMatthias Ringwald                continue
943b3fcedb9SMatthias Ringwald
944b3fcedb9SMatthias Ringwald            print("WARNING: unknown token: %s\n" % (parts[0]))
945b3fcedb9SMatthias Ringwald
9467050bf34SMatthias Ringwalddef parse(fname_in, fin, fname_out, tool_path, fout):
94760b51a4cSMatthias Ringwald    global handle
94860b51a4cSMatthias Ringwald    global total_size
94960b51a4cSMatthias Ringwald
9507050bf34SMatthias Ringwald    fout.write(header.format(fname_out, fname_in, tool_path))
95160b51a4cSMatthias Ringwald    fout.write('{\n')
952fd1be25dSMatthias Ringwald    write_indent(fout)
953fd1be25dSMatthias Ringwald    fout.write('// ATT DB Version\n')
954fd1be25dSMatthias Ringwald    write_indent(fout)
955fd1be25dSMatthias Ringwald    fout.write('1,\n')
956fd1be25dSMatthias Ringwald    fout.write("\n")
95760b51a4cSMatthias Ringwald
95860b51a4cSMatthias Ringwald    parseLines(fname_in, fin, fout)
95960b51a4cSMatthias Ringwald
960729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
961b3fcedb9SMatthias Ringwald    write_indent(fout)
962b3fcedb9SMatthias Ringwald    fout.write("// END\n");
963b3fcedb9SMatthias Ringwald    write_indent(fout)
964b3fcedb9SMatthias Ringwald    write_16(fout,0)
965b3fcedb9SMatthias Ringwald    fout.write("\n")
966b3fcedb9SMatthias Ringwald    total_size = total_size + 2
967b3fcedb9SMatthias Ringwald
968b3fcedb9SMatthias Ringwald    fout.write("}; // total size %u bytes \n" % total_size);
969b3fcedb9SMatthias Ringwald
970b3fcedb9SMatthias Ringwalddef listHandles(fout):
971b3fcedb9SMatthias Ringwald    fout.write('\n\n')
972b3fcedb9SMatthias Ringwald    fout.write('//\n')
973729074c4SMatthias Ringwald    fout.write('// list service handle ranges\n')
974729074c4SMatthias Ringwald    fout.write('//\n')
975729074c4SMatthias Ringwald    for define in defines_for_services:
976729074c4SMatthias Ringwald        fout.write(define)
977729074c4SMatthias Ringwald        fout.write('\n')
978729074c4SMatthias Ringwald    fout.write('\n')
979729074c4SMatthias Ringwald    fout.write('//\n')
980b3fcedb9SMatthias Ringwald    fout.write('// list mapping between characteristics and handles\n')
981b3fcedb9SMatthias Ringwald    fout.write('//\n')
982729074c4SMatthias Ringwald    for define in defines_for_characteristics:
983b3fcedb9SMatthias Ringwald        fout.write(define)
984b3fcedb9SMatthias Ringwald        fout.write('\n')
985b3fcedb9SMatthias Ringwald
986dbb3997aSMilanka Ringwalddef getFile( fileName ):
98778b65b0aSMatthias Ringwald    for d in include_paths:
98878b65b0aSMatthias Ringwald        fullFile = os.path.normpath(d + os.sep + fileName) # because Windows exists
98978b65b0aSMatthias Ringwald        # print("test %s" % fullFile)
990dbb3997aSMilanka Ringwald        if os.path.isfile( fullFile ) == True:
991dbb3997aSMilanka Ringwald            return fullFile
992dbb3997aSMilanka Ringwald    print ("'{0}' not found".format( fileName ))
99378b65b0aSMatthias Ringwald    print ("Include paths: %s" % ", ".join(include_paths))
994dbb3997aSMilanka Ringwald    exit(-1)
995dbb3997aSMilanka Ringwald
996dbb3997aSMilanka Ringwald
997dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
99878b65b0aSMatthias Ringwalddefault_includes = [os.path.normpath(path) for path in [ btstack_root + '/src/', btstack_root + '/src/ble/gatt-service/']]
999dbb3997aSMilanka Ringwald
1000dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack')
1001dbb3997aSMilanka Ringwald
1002dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes',
1003dbb3997aSMilanka Ringwald        help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes))
1004dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str,
1005dbb3997aSMilanka Ringwald        help='gatt file to be compiled')
1006dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str,
1007dbb3997aSMilanka Ringwald        help='header file to be generated')
1008dbb3997aSMilanka Ringwald
1009dbb3997aSMilanka Ringwaldargs = parser.parse_args()
1010dbb3997aSMilanka Ringwald
101178b65b0aSMatthias Ringwald# add include path arguments
101278b65b0aSMatthias Ringwaldif args.I != None:
101378b65b0aSMatthias Ringwald    for d in args.I:
101478b65b0aSMatthias Ringwald        include_paths.append(os.path.normpath(d[0]))
101578b65b0aSMatthias Ringwald
1016dbb3997aSMilanka Ringwald# append default include paths
101778b65b0aSMatthias Ringwaldinclude_paths.extend(default_includes)
1018dbb3997aSMilanka Ringwald
1019b3fcedb9SMatthias Ringwaldtry:
1020b165f97bSMatthias Ringwald    # read defines from bluetooth_gatt.h
1021dbb3997aSMilanka Ringwald    gen_path = getFile( 'bluetooth_gatt.h' )
1022b165f97bSMatthias Ringwald    bluetooth_gatt = read_defines(gen_path)
1023b165f97bSMatthias Ringwald
1024dbb3997aSMilanka Ringwald    filename = args.hfile
1025dbb3997aSMilanka Ringwald    fin  = codecs.open (args.gattfile, encoding='utf-8')
1026285653b2SMatthias Ringwald
1027285653b2SMatthias Ringwald    # pass 1: create temp .h file
1028d78181d6SMatthias Ringwald    ftemp = tempfile.TemporaryFile(mode='w+t')
1029285653b2SMatthias Ringwald    parse(args.gattfile, fin, filename, sys.argv[0], ftemp)
1030285653b2SMatthias Ringwald    listHandles(ftemp)
1031285653b2SMatthias Ringwald
1032043f8832SMatthias Ringwald    # calc GATT Database Hash
1033043f8832SMatthias Ringwald    db_hash = aes_cmac(bytearray(16), database_hash_message)
1034043f8832SMatthias Ringwald    if isinstance(db_hash, str):
1035043f8832SMatthias Ringwald        # python2
1036043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % ord(i)) for i in db_hash]
1037043f8832SMatthias Ringwald    elif isinstance(db_hash, bytes):
1038043f8832SMatthias Ringwald        # python3
1039043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % i) for i in db_hash]
1040043f8832SMatthias Ringwald    else:
1041043f8832SMatthias Ringwald        print("AES CMAC returns unexpected type %s, abort" % type(db_hash))
1042043f8832SMatthias Ringwald        sys.exit(1)
1043043f8832SMatthias Ringwald    # reverse hash to get little endian
1044043f8832SMatthias Ringwald    db_hash_sequence.reverse()
1045043f8832SMatthias Ringwald    db_hash_string = ', '.join(db_hash_sequence) + ', '
1046043f8832SMatthias Ringwald
1047285653b2SMatthias Ringwald    # pass 2: insert GATT Database Hash
1048b3fcedb9SMatthias Ringwald    fout = open (filename, 'w')
1049285653b2SMatthias Ringwald    ftemp.seek(0)
1050285653b2SMatthias Ringwald    for line in ftemp:
1051043f8832SMatthias Ringwald        fout.write(line.replace('THE-DATABASE-HASH', db_hash_string))
1052b3fcedb9SMatthias Ringwald    fout.close()
1053285653b2SMatthias Ringwald    ftemp.close()
1054285653b2SMatthias Ringwald
1055b165f97bSMatthias Ringwald    print('Created %s' % filename)
1056b3fcedb9SMatthias Ringwald
1057b3fcedb9SMatthias Ringwaldexcept IOError as e:
1058e22a2612SMatthias Ringwald
1059b3fcedb9SMatthias Ringwald    print(usage)
1060b3fcedb9SMatthias Ringwald    sys.exit(1)
1061b3fcedb9SMatthias Ringwald
1062b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n')
1063