xref: /btstack/tool/compile_gatt.py (revision 6f08f1597c660340a253920dbb3d9b573dac60f8)
14783d256SMatheus Garbelini#!/usr/bin/env python3
2b3fcedb9SMatthias Ringwald#
3dbb3997aSMilanka Ringwald# BLE GATT configuration generator for use with BTstack
4043f8832SMatthias Ringwald# Copyright 2019 BlueKitchen GmbH
5b3fcedb9SMatthias Ringwald#
6b3fcedb9SMatthias Ringwald# Format of input file:
7b3fcedb9SMatthias Ringwald# PRIMARY_SERVICE, SERVICE_UUID
8b3fcedb9SMatthias Ringwald# CHARACTERISTIC, ATTRIBUTE_TYPE_UUID, [READ | WRITE | DYNAMIC], VALUE
9b3fcedb9SMatthias Ringwald
10043f8832SMatthias Ringwald# dependencies:
11043f8832SMatthias Ringwald# - pip3 install pycryptodomex
12ced5a857SMatthias Ringwald# alternatively, the pycryptodome package can be used instead
13ced5a857SMatthias Ringwald# - pip3 install pycryptodome
14043f8832SMatthias Ringwald
15b3fcedb9SMatthias Ringwaldimport codecs
16b165f97bSMatthias Ringwaldimport csv
17b165f97bSMatthias Ringwaldimport io
18b165f97bSMatthias Ringwaldimport os
19b165f97bSMatthias Ringwaldimport re
20b165f97bSMatthias Ringwaldimport string
2160b51a4cSMatthias Ringwaldimport sys
22dbb3997aSMilanka Ringwaldimport argparse
23285653b2SMatthias Ringwaldimport tempfile
24b3fcedb9SMatthias Ringwald
25ced5a857SMatthias Ringwaldhave_crypto = True
26ced5a857SMatthias Ringwald# try to import PyCryptodome independent from PyCrypto
27043f8832SMatthias Ringwaldtry:
28379d3aceSMatthias Ringwald    from Cryptodome.Cipher import AES
29043f8832SMatthias Ringwald    from Cryptodome.Hash import CMAC
30ced5a857SMatthias Ringwaldexcept ImportError:
31ced5a857SMatthias Ringwald    # fallback: try to import PyCryptodome as (an almost drop-in) replacement for the PyCrypto library
32ced5a857SMatthias Ringwald    try:
33ced5a857SMatthias Ringwald        from Crypto.Cipher import AES
34ced5a857SMatthias Ringwald        from Crypto.Hash import CMAC
35043f8832SMatthias Ringwald    except ImportError:
36043f8832SMatthias Ringwald        have_crypto = False
377490175eSMatthias Ringwald        print("\n[!] PyCryptodome required to calculate GATT Database Hash but not installed (using random value instead)")
386ccd8248SMilanka Ringwald        print("[!] Please install PyCryptodome, e.g. 'pip3 install pycryptodomex' or 'pip3 install pycryptodome'\n")
39043f8832SMatthias Ringwald
40b3fcedb9SMatthias Ringwaldheader = '''
41b3fcedb9SMatthias Ringwald// {0} generated from {1} for BTstack
427050bf34SMatthias Ringwald// it needs to be regenerated when the .gatt file is updated.
437050bf34SMatthias Ringwald
447050bf34SMatthias Ringwald// To generate {0}:
457050bf34SMatthias Ringwald// {2} {1} {0}
467050bf34SMatthias Ringwald
47fd1be25dSMatthias Ringwald// att db format version 1
48b3fcedb9SMatthias Ringwald
49fd1be25dSMatthias Ringwald// binary attribute representation:
50fd1be25dSMatthias Ringwald// - size in bytes (16), flags(16), handle (16), uuid (16/128), value(...)
51b3fcedb9SMatthias Ringwald
52b3fcedb9SMatthias Ringwald#include <stdint.h>
53b3fcedb9SMatthias Ringwald
54fa529fa7SMatthias Ringwald// Reference: https://en.cppreference.com/w/cpp/feature_test
55fa529fa7SMatthias Ringwald#if __cplusplus >= 200704L
56fa529fa7SMatthias Ringwaldconstexpr
57fa529fa7SMatthias Ringwald#endif
58b3fcedb9SMatthias Ringwaldconst uint8_t profile_data[] =
59b3fcedb9SMatthias Ringwald'''
60b3fcedb9SMatthias Ringwald
61b3fcedb9SMatthias Ringwaldprint('''
62dbb3997aSMilanka RingwaldBLE configuration generator for use with BTstack
63dbb3997aSMilanka RingwaldCopyright 2018 BlueKitchen GmbH
64b3fcedb9SMatthias Ringwald''')
65b3fcedb9SMatthias Ringwald
66b3fcedb9SMatthias Ringwaldassigned_uuids = {
67b3fcedb9SMatthias Ringwald    'GAP_SERVICE'          : 0x1800,
68b3fcedb9SMatthias Ringwald    'GATT_SERVICE'         : 0x1801,
69b3fcedb9SMatthias Ringwald    'GAP_DEVICE_NAME'      : 0x2a00,
70b3fcedb9SMatthias Ringwald    'GAP_APPEARANCE'       : 0x2a01,
71b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02,
72b3fcedb9SMatthias Ringwald    'GAP_RECONNECTION_ADDRESS'    : 0x2A03,
73b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04,
74b3fcedb9SMatthias Ringwald    'GATT_SERVICE_CHANGED' : 0x2a05,
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()
14629ba805bSMatthias Ringwaldservice_counter = {}
147b3fcedb9SMatthias Ringwald
148b3fcedb9SMatthias Ringwaldhandle = 1
149b3fcedb9SMatthias Ringwaldtotal_size = 0
150b3fcedb9SMatthias Ringwald
151043f8832SMatthias Ringwalddef aes_cmac(key, n):
152043f8832SMatthias Ringwald    if have_crypto:
153043f8832SMatthias Ringwald        cobj = CMAC.new(key, ciphermod=AES)
154043f8832SMatthias Ringwald        cobj.update(n)
155043f8832SMatthias Ringwald        return cobj.digest()
156043f8832SMatthias Ringwald    else:
1577490175eSMatthias Ringwald        # return random value
1587490175eSMatthias Ringwald        return os.urandom(16)
159043f8832SMatthias Ringwald
160b165f97bSMatthias Ringwalddef read_defines(infile):
161b165f97bSMatthias Ringwald    defines = dict()
162b165f97bSMatthias Ringwald    with open (infile, 'rt') as fin:
163b165f97bSMatthias Ringwald        for line in fin:
164b165f97bSMatthias Ringwald            parts = re.match('#define\s+(\w+)\s+(\w+)',line)
165b165f97bSMatthias Ringwald            if parts and len(parts.groups()) == 2:
166b165f97bSMatthias Ringwald                (key, value) = parts.groups()
167b165f97bSMatthias Ringwald                defines[key] = int(value, 16)
168b165f97bSMatthias Ringwald    return defines
169b165f97bSMatthias Ringwald
170b3fcedb9SMatthias Ringwalddef keyForUUID(uuid):
171b3fcedb9SMatthias Ringwald    keyUUID = ""
172b3fcedb9SMatthias Ringwald    for i in uuid:
173b3fcedb9SMatthias Ringwald        keyUUID += "%02x" % i
174b3fcedb9SMatthias Ringwald    return keyUUID
175b3fcedb9SMatthias Ringwald
176b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid):
177b3fcedb9SMatthias Ringwald    return uuid.replace('-', '_')
178b3fcedb9SMatthias Ringwald
179b3fcedb9SMatthias Ringwalddef twoByteLEFor(value):
180b3fcedb9SMatthias Ringwald    return [ (value & 0xff), (value >> 8)]
181b3fcedb9SMatthias Ringwald
182b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text):
183b3fcedb9SMatthias 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):
184b3fcedb9SMatthias Ringwald        return True
185b3fcedb9SMatthias Ringwald    return False
186b3fcedb9SMatthias Ringwald
187b3fcedb9SMatthias Ringwalddef parseUUID128(uuid):
188b3fcedb9SMatthias 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)
189b3fcedb9SMatthias Ringwald    uuid_bytes = []
190b3fcedb9SMatthias Ringwald    for i in range(8, 0, -1):
191b3fcedb9SMatthias Ringwald        uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16))
192b3fcedb9SMatthias Ringwald    return uuid_bytes
193b3fcedb9SMatthias Ringwald
194b3fcedb9SMatthias Ringwalddef parseUUID(uuid):
195b3fcedb9SMatthias Ringwald    if uuid in assigned_uuids:
196b3fcedb9SMatthias Ringwald        return twoByteLEFor(assigned_uuids[uuid])
197b165f97bSMatthias Ringwald    uuid_upper = uuid.upper().replace('.','_')
198b165f97bSMatthias Ringwald    if uuid_upper in bluetooth_gatt:
199b165f97bSMatthias Ringwald        return twoByteLEFor(bluetooth_gatt[uuid_upper])
200b3fcedb9SMatthias Ringwald    if is_128bit_uuid(uuid):
201b3fcedb9SMatthias Ringwald        return parseUUID128(uuid)
202b3fcedb9SMatthias Ringwald    uuidInt = int(uuid, 16)
203b3fcedb9SMatthias Ringwald    return twoByteLEFor(uuidInt)
204b3fcedb9SMatthias Ringwald
205b3fcedb9SMatthias Ringwalddef parseProperties(properties):
206b3fcedb9SMatthias Ringwald    value = 0
207b3fcedb9SMatthias Ringwald    parts = properties.split("|")
208b3fcedb9SMatthias Ringwald    for property in parts:
209b3fcedb9SMatthias Ringwald        property = property.strip()
210b3fcedb9SMatthias Ringwald        if property in property_flags:
211b3fcedb9SMatthias Ringwald            value |= property_flags[property]
212b3fcedb9SMatthias Ringwald        else:
213b3fcedb9SMatthias Ringwald            print("WARNING: property %s undefined" % (property))
214e22a2612SMatthias Ringwald
215e22a2612SMatthias Ringwald    return value;
216e22a2612SMatthias Ringwald
217e22a2612SMatthias Ringwalddef gatt_characteristic_properties(properties):
218e22a2612SMatthias Ringwald    return properties & 0xff
219e22a2612SMatthias Ringwald
220e22a2612SMatthias Ringwalddef att_flags(properties):
221e72176f8SMatthias Ringwald    # drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Properties (0x80) - not used for flags
222e72176f8SMatthias Ringwald    properties &= 0xffffff4e
223e22a2612SMatthias Ringwald
224e22a2612SMatthias Ringwald    # rw permissions distinct
225e22a2612SMatthias Ringwald    distinct_permissions_used = properties & (
226e22a2612SMatthias Ringwald        property_flags['READ_AUTHORIZED'] |
227e72176f8SMatthias Ringwald        property_flags['READ_AUTHENTICATED_SC'] |
228e22a2612SMatthias Ringwald        property_flags['READ_AUTHENTICATED'] |
229e22a2612SMatthias Ringwald        property_flags['READ_ENCRYPTED'] |
230e22a2612SMatthias Ringwald        property_flags['READ_ANYBODY'] |
231e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHORIZED'] |
232e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED'] |
233e72176f8SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED_SC'] |
234e22a2612SMatthias Ringwald        property_flags['WRITE_ENCRYPTED'] |
235e22a2612SMatthias Ringwald        property_flags['WRITE_ANYBODY']
236e22a2612SMatthias Ringwald    ) != 0
237e22a2612SMatthias Ringwald
238e22a2612SMatthias Ringwald    # post process properties
239e22a2612SMatthias Ringwald    encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0
240e22a2612SMatthias Ringwald
241d7ec1d24SMatthias Ringwald    # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted
242e22a2612SMatthias Ringwald    if encryption_key_size_specified and not distinct_permissions_used:
243e22a2612SMatthias Ringwald        properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED']
244e22a2612SMatthias Ringwald
245d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated
246d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used:
247d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED']
248d7ec1d24SMatthias Ringwald
249d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized
250d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used:
251d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED']
252d7ec1d24SMatthias Ringwald
253d7ec1d24SMatthias Ringwald    # determine read/write security requirements
254d7ec1d24SMatthias Ringwald    read_security_level  = 0
255d7ec1d24SMatthias Ringwald    write_security_level = 0
256e72176f8SMatthias Ringwald    read_requires_sc     = False
257e72176f8SMatthias Ringwald    write_requires_sc    = False
258e22a2612SMatthias Ringwald    if properties & property_flags['READ_AUTHORIZED']:
259d7ec1d24SMatthias Ringwald        read_security_level = 3
260e22a2612SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED']:
261d7ec1d24SMatthias Ringwald        read_security_level = 2
262e72176f8SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED_SC']:
263e72176f8SMatthias Ringwald        read_security_level = 2
264e72176f8SMatthias Ringwald        read_requires_sc = True
265e22a2612SMatthias Ringwald    elif properties & property_flags['READ_ENCRYPTED']:
266d7ec1d24SMatthias Ringwald        read_security_level = 1
267e22a2612SMatthias Ringwald    if properties & property_flags['WRITE_AUTHORIZED']:
268d7ec1d24SMatthias Ringwald        write_security_level = 3
269e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED']:
270d7ec1d24SMatthias Ringwald        write_security_level = 2
271e72176f8SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED_SC']:
272e72176f8SMatthias Ringwald        write_security_level = 2
273e72176f8SMatthias Ringwald        write_requires_sc = True
274e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_ENCRYPTED']:
275d7ec1d24SMatthias Ringwald        write_security_level = 1
276d7ec1d24SMatthias Ringwald
277d7ec1d24SMatthias Ringwald    # map security requirements to flags
278d7ec1d24SMatthias Ringwald    if read_security_level & 2:
279d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_1']
280d7ec1d24SMatthias Ringwald    if read_security_level & 1:
281d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_0']
282e72176f8SMatthias Ringwald    if read_requires_sc:
283e72176f8SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_SC']
284d7ec1d24SMatthias Ringwald    if write_security_level & 2:
285d7ec1d24SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_1']
286d7ec1d24SMatthias Ringwald    if write_security_level & 1:
287e22a2612SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_0']
288e72176f8SMatthias Ringwald    if write_requires_sc:
289e72176f8SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_SC']
290e22a2612SMatthias Ringwald
291e22a2612SMatthias Ringwald    return properties
292e22a2612SMatthias Ringwald
293d7ec1d24SMatthias Ringwalddef write_permissions_and_key_size_flags_from_properties(properties):
294e22a2612SMatthias Ringwald    return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1'])
295b3fcedb9SMatthias Ringwald
296b3fcedb9SMatthias Ringwalddef write_8(fout, value):
297b3fcedb9SMatthias Ringwald    fout.write( "0x%02x, " % (value & 0xff))
298b3fcedb9SMatthias Ringwald
299b3fcedb9SMatthias Ringwalddef write_16(fout, value):
300b3fcedb9SMatthias Ringwald    fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff))
301b3fcedb9SMatthias Ringwald
302285653b2SMatthias Ringwalddef write_uuid(fout, uuid):
303b3fcedb9SMatthias Ringwald    for byte in uuid:
304b3fcedb9SMatthias Ringwald        fout.write( "0x%02x, " % byte)
305b3fcedb9SMatthias Ringwald
306b3fcedb9SMatthias Ringwalddef write_string(fout, text):
307b3fcedb9SMatthias Ringwald    for l in text.lstrip('"').rstrip('"'):
308b3fcedb9SMatthias Ringwald        write_8(fout, ord(l))
309b3fcedb9SMatthias Ringwald
310b3fcedb9SMatthias Ringwalddef write_sequence(fout, text):
311b3fcedb9SMatthias Ringwald    parts = text.split()
312b3fcedb9SMatthias Ringwald    for part in parts:
313b3fcedb9SMatthias Ringwald        fout.write("0x%s, " % (part.strip()))
314b3fcedb9SMatthias Ringwald
315043f8832SMatthias Ringwalddef write_database_hash(fout):
316043f8832SMatthias Ringwald    fout.write("THE-DATABASE-HASH")
317043f8832SMatthias Ringwald
318b3fcedb9SMatthias Ringwalddef write_indent(fout):
319b3fcedb9SMatthias Ringwald    fout.write("    ")
320b3fcedb9SMatthias Ringwald
321d7ec1d24SMatthias Ringwalddef read_permissions_from_flags(flags):
322d7ec1d24SMatthias Ringwald    permissions = 0
323d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_0']:
324d7ec1d24SMatthias Ringwald        permissions |= 1
325d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_1']:
326d7ec1d24SMatthias Ringwald        permissions |= 2
327e72176f8SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_SC'] and permissions == 2:
328e72176f8SMatthias Ringwald        permissions = 4
329d7ec1d24SMatthias Ringwald    return permissions
330d7ec1d24SMatthias Ringwald
331d7ec1d24SMatthias Ringwalddef write_permissions_from_flags(flags):
332d7ec1d24SMatthias Ringwald    permissions = 0
333d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_0']:
334d7ec1d24SMatthias Ringwald        permissions |= 1
335d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_1']:
336d7ec1d24SMatthias Ringwald        permissions |= 2
337e72176f8SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_SC'] and permissions == 2:
338e72176f8SMatthias Ringwald        permissions = 4
339d7ec1d24SMatthias Ringwald    return permissions
340d7ec1d24SMatthias Ringwald
341d7ec1d24SMatthias Ringwalddef encryption_key_size_from_flags(flags):
342d7ec1d24SMatthias Ringwald    encryption_key_size = (flags & 0xf000) >> 12
343d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
344d7ec1d24SMatthias Ringwald        encryption_key_size += 1
345d7ec1d24SMatthias Ringwald    return encryption_key_size
346d7ec1d24SMatthias Ringwald
347b3fcedb9SMatthias Ringwalddef is_string(text):
348b3fcedb9SMatthias Ringwald    for item in text.split(" "):
349b3fcedb9SMatthias Ringwald        if not all(c in string.hexdigits for c in item):
350b3fcedb9SMatthias Ringwald            return True
351b3fcedb9SMatthias Ringwald    return False
352b3fcedb9SMatthias Ringwald
353b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties):
354b3fcedb9SMatthias Ringwald    return properties & (property_flags['NOTIFY'] | property_flags['INDICATE'])
355b3fcedb9SMatthias Ringwald
356729074c4SMatthias Ringwalddef serviceDefinitionComplete(fout):
357729074c4SMatthias Ringwald    global services
358729074c4SMatthias Ringwald    if current_service_uuid_string:
359729074c4SMatthias Ringwald        fout.write("\n")
36029ba805bSMatthias Ringwald        # update num instances for this service
36129ba805bSMatthias Ringwald        count = 1
36229ba805bSMatthias Ringwald        if current_service_uuid_string in service_counter:
36329ba805bSMatthias Ringwald            count = service_counter[current_service_uuid_string] + 1
36429ba805bSMatthias Ringwald        service_counter[current_service_uuid_string] = count
36529ba805bSMatthias Ringwald        # add old defines without service counter for first instance
36629ba805bSMatthias Ringwald        if count == 1:
367729074c4SMatthias Ringwald            defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle))
368729074c4SMatthias Ringwald            defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1))
369729074c4SMatthias Ringwald            services[current_service_uuid_string] = [current_service_start_handle, handle-1]
37029ba805bSMatthias Ringwald        # unified defines indicating instance
37129ba805bSMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_%02x_START_HANDLE 0x%04x' % (current_service_uuid_string, count, current_service_start_handle))
37229ba805bSMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_%02x_END_HANDLE 0x%04x' % (current_service_uuid_string, count, handle-1))
37329ba805bSMatthias Ringwald
374729074c4SMatthias Ringwald
375d7ec1d24SMatthias Ringwalddef dump_flags(fout, flags):
376d7ec1d24SMatthias Ringwald    global security_permsission
377d7ec1d24SMatthias Ringwald    encryption_key_size = encryption_key_size_from_flags(flags)
378d7ec1d24SMatthias Ringwald    read_permissions    = security_permsission[read_permissions_from_flags(flags)]
379d7ec1d24SMatthias Ringwald    write_permissions   = security_permsission[write_permissions_from_flags(flags)]
380d7ec1d24SMatthias Ringwald    write_indent(fout)
381d7ec1d24SMatthias Ringwald    fout.write('// ')
382d7ec1d24SMatthias Ringwald    first = 1
383d7ec1d24SMatthias Ringwald    if flags & property_flags['READ']:
384d7ec1d24SMatthias Ringwald        fout.write('READ_%s' % read_permissions)
385d7ec1d24SMatthias Ringwald        first = 0
386d7ec1d24SMatthias Ringwald    if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']):
387d7ec1d24SMatthias Ringwald        if not first:
388d7ec1d24SMatthias Ringwald            fout.write(', ')
389d7ec1d24SMatthias Ringwald        first = 0
390d7ec1d24SMatthias Ringwald        fout.write('WRITE_%s' % write_permissions)
391d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
392d7ec1d24SMatthias Ringwald        if not first:
393d7ec1d24SMatthias Ringwald            fout.write(', ')
394d7ec1d24SMatthias Ringwald        first = 0
395d7ec1d24SMatthias Ringwald        fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size)
396d7ec1d24SMatthias Ringwald    fout.write('\n')
397d7ec1d24SMatthias Ringwald
398043f8832SMatthias Ringwalddef database_hash_append_uint8(value):
399043f8832SMatthias Ringwald    global database_hash_message
400043f8832SMatthias Ringwald    database_hash_message.append(value)
401043f8832SMatthias Ringwald
402043f8832SMatthias Ringwalddef database_hash_append_uint16(value):
403043f8832SMatthias Ringwald    global database_hash_message
404043f8832SMatthias Ringwald    database_hash_append_uint8(value & 0xff)
405043f8832SMatthias Ringwald    database_hash_append_uint8((value >> 8) & 0xff)
406043f8832SMatthias Ringwald
407043f8832SMatthias Ringwalddef database_hash_append_value(value):
408043f8832SMatthias Ringwald    global database_hash_message
409043f8832SMatthias Ringwald    for byte in value:
410043f8832SMatthias Ringwald        database_hash_append_uint8(byte)
411043f8832SMatthias Ringwald
412b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type):
413b3fcedb9SMatthias Ringwald    global handle
414b3fcedb9SMatthias Ringwald    global total_size
415b3fcedb9SMatthias Ringwald    global current_service_uuid_string
416b3fcedb9SMatthias Ringwald    global current_service_start_handle
417b3fcedb9SMatthias Ringwald
418729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
419b3fcedb9SMatthias Ringwald
420d7ec1d24SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
421b3fcedb9SMatthias Ringwald
422b3fcedb9SMatthias Ringwald    write_indent(fout)
423b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
424b3fcedb9SMatthias Ringwald
425b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
426b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
427b3fcedb9SMatthias Ringwald
428b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size + 2
429b3fcedb9SMatthias Ringwald
430b3fcedb9SMatthias Ringwald    if service_type == 0x2802:
431b3fcedb9SMatthias Ringwald        size += 4
432b3fcedb9SMatthias Ringwald
433b3fcedb9SMatthias Ringwald    write_indent(fout)
434b3fcedb9SMatthias Ringwald    write_16(fout, size)
435d7ec1d24SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
436b3fcedb9SMatthias Ringwald    write_16(fout, handle)
437b3fcedb9SMatthias Ringwald    write_16(fout, service_type)
438285653b2SMatthias Ringwald    write_uuid(fout, uuid)
439b3fcedb9SMatthias Ringwald    fout.write("\n")
440b3fcedb9SMatthias Ringwald
441043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
442043f8832SMatthias Ringwald    database_hash_append_uint16(service_type)
443043f8832SMatthias Ringwald    database_hash_append_value(uuid)
444043f8832SMatthias Ringwald
445729074c4SMatthias Ringwald    current_service_uuid_string = c_string_for_uuid(parts[1])
446b3fcedb9SMatthias Ringwald    current_service_start_handle = handle
447b3fcedb9SMatthias Ringwald    handle = handle + 1
448b3fcedb9SMatthias Ringwald    total_size = total_size + size
449b3fcedb9SMatthias Ringwald
450b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts):
451b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2800)
452b3fcedb9SMatthias Ringwald
453b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts):
454b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2801)
455b3fcedb9SMatthias Ringwald
456b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts):
457b3fcedb9SMatthias Ringwald    global handle
458b3fcedb9SMatthias Ringwald    global total_size
459b3fcedb9SMatthias Ringwald
460e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
461b3fcedb9SMatthias Ringwald
462b3fcedb9SMatthias Ringwald    write_indent(fout)
463b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
464b3fcedb9SMatthias Ringwald
465b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
466b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
467b3fcedb9SMatthias Ringwald    if uuid_size > 2:
468b3fcedb9SMatthias Ringwald        uuid_size = 0
469729074c4SMatthias Ringwald    # print("Include Service ", c_string_for_uuid(uuid))
470b3fcedb9SMatthias Ringwald
471b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 4 + uuid_size
472b3fcedb9SMatthias Ringwald
473729074c4SMatthias Ringwald    keyUUID = c_string_for_uuid(parts[1])
474b3fcedb9SMatthias Ringwald
475b3fcedb9SMatthias Ringwald    write_indent(fout)
476b3fcedb9SMatthias Ringwald    write_16(fout, size)
477e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
478b3fcedb9SMatthias Ringwald    write_16(fout, handle)
479b3fcedb9SMatthias Ringwald    write_16(fout, 0x2802)
480b3fcedb9SMatthias Ringwald    write_16(fout, services[keyUUID][0])
481b3fcedb9SMatthias Ringwald    write_16(fout, services[keyUUID][1])
482b3fcedb9SMatthias Ringwald    if uuid_size > 0:
483285653b2SMatthias Ringwald        write_uuid(fout, uuid)
484b3fcedb9SMatthias Ringwald    fout.write("\n")
485b3fcedb9SMatthias Ringwald
486043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
487043f8832SMatthias Ringwald    database_hash_append_uint16(0x2802)
488043f8832SMatthias Ringwald    database_hash_append_uint16(services[keyUUID][0])
489043f8832SMatthias Ringwald    database_hash_append_uint16(services[keyUUID][1])
490043f8832SMatthias Ringwald    if uuid_size > 0:
491043f8832SMatthias Ringwald        database_hash_append_value(uuid)
492043f8832SMatthias Ringwald
493b3fcedb9SMatthias Ringwald    handle = handle + 1
494b3fcedb9SMatthias Ringwald    total_size = total_size + size
495b3fcedb9SMatthias Ringwald
496b3fcedb9SMatthias Ringwald
497b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts):
498b3fcedb9SMatthias Ringwald    global handle
499b3fcedb9SMatthias Ringwald    global total_size
500b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
501b3fcedb9SMatthias Ringwald    global characteristic_indices
502b3fcedb9SMatthias Ringwald
503e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
504b3fcedb9SMatthias Ringwald
505b3fcedb9SMatthias Ringwald    # enumerate characteristics with same UUID, using optional name tag if available
506b3fcedb9SMatthias Ringwald    current_characteristic_uuid_string = c_string_for_uuid(parts[1]);
507b3fcedb9SMatthias Ringwald    index = 1
508b3fcedb9SMatthias Ringwald    if current_characteristic_uuid_string in characteristic_indices:
509b3fcedb9SMatthias Ringwald        index = characteristic_indices[current_characteristic_uuid_string] + 1
510b3fcedb9SMatthias Ringwald    characteristic_indices[current_characteristic_uuid_string] = index
511b3fcedb9SMatthias Ringwald    if len(parts) > 4:
512b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_')
513b3fcedb9SMatthias Ringwald    else:
514b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += ('_%02x' % index)
515b3fcedb9SMatthias Ringwald
516b3fcedb9SMatthias Ringwald    uuid       = parseUUID(parts[1])
517b3fcedb9SMatthias Ringwald    uuid_size  = len(uuid)
518b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[2])
519b3fcedb9SMatthias Ringwald    value = ', '.join([str(x) for x in parts[3:]])
520b3fcedb9SMatthias Ringwald
521b3fcedb9SMatthias Ringwald    # reliable writes is defined in an extended properties
522b3fcedb9SMatthias Ringwald    if (properties & property_flags['RELIABLE_WRITE']):
523b3fcedb9SMatthias Ringwald        properties = properties | property_flags['EXTENDED_PROPERTIES']
524b3fcedb9SMatthias Ringwald
525b3fcedb9SMatthias Ringwald    write_indent(fout)
526b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3])))
527b3fcedb9SMatthias Ringwald
528e22a2612SMatthias Ringwald
529e22a2612SMatthias Ringwald    characteristic_properties = gatt_characteristic_properties(properties)
530b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (1+2+uuid_size)
531b3fcedb9SMatthias Ringwald    write_indent(fout)
532b3fcedb9SMatthias Ringwald    write_16(fout, size)
533e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
534b3fcedb9SMatthias Ringwald    write_16(fout, handle)
535b3fcedb9SMatthias Ringwald    write_16(fout, 0x2803)
536e22a2612SMatthias Ringwald    write_8(fout, characteristic_properties)
537b3fcedb9SMatthias Ringwald    write_16(fout, handle+1)
538285653b2SMatthias Ringwald    write_uuid(fout, uuid)
539b3fcedb9SMatthias Ringwald    fout.write("\n")
540b3fcedb9SMatthias Ringwald    handle = handle + 1
541b3fcedb9SMatthias Ringwald    total_size = total_size + size
542b3fcedb9SMatthias Ringwald
543043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
544043f8832SMatthias Ringwald    database_hash_append_uint16(0x2803)
545043f8832SMatthias Ringwald    database_hash_append_uint8(characteristic_properties)
546043f8832SMatthias Ringwald    database_hash_append_uint16(handle+1)
547043f8832SMatthias Ringwald    database_hash_append_value(uuid)
548043f8832SMatthias Ringwald
549043f8832SMatthias Ringwald    uuid_is_database_hash = len(uuid) == 2 and uuid[0] == 0x2a and uuid[1] == 0x2b
550043f8832SMatthias Ringwald
551b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size
552043f8832SMatthias Ringwald    if uuid_is_database_hash:
553043f8832SMatthias Ringwald        size +=  16
554043f8832SMatthias Ringwald    else:
555b3fcedb9SMatthias Ringwald        if is_string(value):
556b3fcedb9SMatthias Ringwald            size = size + len(value)
557b3fcedb9SMatthias Ringwald        else:
558b3fcedb9SMatthias Ringwald            size = size + len(value.split())
559b3fcedb9SMatthias Ringwald
560e22a2612SMatthias Ringwald    value_flags = att_flags(properties)
5618ea3236cSMatthias Ringwald
5628ea3236cSMatthias Ringwald    # add UUID128 flag for value handle
563b3fcedb9SMatthias Ringwald    if uuid_size == 16:
564e22a2612SMatthias Ringwald        value_flags = value_flags | property_flags['LONG_UUID'];
565b3fcedb9SMatthias Ringwald
566b3fcedb9SMatthias Ringwald    write_indent(fout)
567b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value))
568d7ec1d24SMatthias Ringwald
569d7ec1d24SMatthias Ringwald    dump_flags(fout, value_flags)
570d7ec1d24SMatthias Ringwald
571b3fcedb9SMatthias Ringwald    write_indent(fout)
572b3fcedb9SMatthias Ringwald    write_16(fout, size)
573e22a2612SMatthias Ringwald    write_16(fout, value_flags)
574b3fcedb9SMatthias Ringwald    write_16(fout, handle)
575285653b2SMatthias Ringwald    write_uuid(fout, uuid)
576043f8832SMatthias Ringwald    if uuid_is_database_hash:
577043f8832SMatthias Ringwald        write_database_hash(fout)
578043f8832SMatthias Ringwald    else:
579b3fcedb9SMatthias Ringwald        if is_string(value):
580b3fcedb9SMatthias Ringwald            write_string(fout, value)
581b3fcedb9SMatthias Ringwald        else:
582b3fcedb9SMatthias Ringwald            write_sequence(fout,value)
583b3fcedb9SMatthias Ringwald
584b3fcedb9SMatthias Ringwald    fout.write("\n")
585729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
586b3fcedb9SMatthias Ringwald    handle = handle + 1
587b3fcedb9SMatthias Ringwald
588b3fcedb9SMatthias Ringwald    if add_client_characteristic_configuration(properties):
589e22a2612SMatthias Ringwald        # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC
590d7ec1d24SMatthias Ringwald        flags  = write_permissions_and_key_size_flags_from_properties(properties)
591e22a2612SMatthias Ringwald        flags |= property_flags['READ']
592e22a2612SMatthias Ringwald        flags |= property_flags['WRITE']
5939be4aecfSMatthias Ringwald        flags |= property_flags['WRITE_WITHOUT_RESPONSE']
594e22a2612SMatthias Ringwald        flags |= property_flags['DYNAMIC']
595b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
596d7ec1d24SMatthias Ringwald
597b3fcedb9SMatthias Ringwald        write_indent(fout)
598b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle))
599d7ec1d24SMatthias Ringwald
600d7ec1d24SMatthias Ringwald        dump_flags(fout, flags)
601d7ec1d24SMatthias Ringwald
602b3fcedb9SMatthias Ringwald        write_indent(fout)
603b3fcedb9SMatthias Ringwald        write_16(fout, size)
604e22a2612SMatthias Ringwald        write_16(fout, flags)
605b3fcedb9SMatthias Ringwald        write_16(fout, handle)
606b3fcedb9SMatthias Ringwald        write_16(fout, 0x2902)
607b3fcedb9SMatthias Ringwald        write_16(fout, 0)
608b3fcedb9SMatthias Ringwald        fout.write("\n")
609043f8832SMatthias Ringwald
610043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
611043f8832SMatthias Ringwald        database_hash_append_uint16(0x2902)
612043f8832SMatthias Ringwald
613729074c4SMatthias Ringwald        defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
614b3fcedb9SMatthias Ringwald        handle = handle + 1
615b3fcedb9SMatthias Ringwald
616043f8832SMatthias Ringwald
617b3fcedb9SMatthias Ringwald    if properties & property_flags['RELIABLE_WRITE']:
618b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
619b3fcedb9SMatthias Ringwald        write_indent(fout)
620b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle))
621b3fcedb9SMatthias Ringwald        write_indent(fout)
622b3fcedb9SMatthias Ringwald        write_16(fout, size)
623e22a2612SMatthias Ringwald        write_16(fout, read_only_anybody_flags)
624b3fcedb9SMatthias Ringwald        write_16(fout, handle)
625b3fcedb9SMatthias Ringwald        write_16(fout, 0x2900)
626b3fcedb9SMatthias Ringwald        write_16(fout, 1)   # Reliable Write
627b3fcedb9SMatthias Ringwald        fout.write("\n")
628043f8832SMatthias Ringwald
629043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
630043f8832SMatthias Ringwald        database_hash_append_uint16(0x2900)
631043f8832SMatthias Ringwald        database_hash_append_uint16(1)
632043f8832SMatthias Ringwald
633b3fcedb9SMatthias Ringwald        handle = handle + 1
634b3fcedb9SMatthias Ringwald
635b3fcedb9SMatthias Ringwalddef parseCharacteristicUserDescription(fout, parts):
636b3fcedb9SMatthias Ringwald    global handle
637b3fcedb9SMatthias Ringwald    global total_size
638b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
639b3fcedb9SMatthias Ringwald
640b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
641b3fcedb9SMatthias Ringwald    value      = parts[2]
642b3fcedb9SMatthias Ringwald
643b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
644b3fcedb9SMatthias Ringwald    if is_string(value):
645b7647eb6SMatthias Ringwald        size = size + len(value)
646b3fcedb9SMatthias Ringwald    else:
647b3fcedb9SMatthias Ringwald        size = size + len(value.split())
648b3fcedb9SMatthias Ringwald
649e22a2612SMatthias Ringwald    # use write, write permissions and encryption key size from attribute value and set READ_ANYBODY
650d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
651e22a2612SMatthias Ringwald    flags |= properties & property_flags['WRITE']
652e22a2612SMatthias Ringwald    flags |= property_flags['READ']
653e22a2612SMatthias Ringwald
654b3fcedb9SMatthias Ringwald    write_indent(fout)
655b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:])))
656d7ec1d24SMatthias Ringwald
657d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
658d7ec1d24SMatthias Ringwald
659b3fcedb9SMatthias Ringwald    write_indent(fout)
660b3fcedb9SMatthias Ringwald    write_16(fout, size)
661e22a2612SMatthias Ringwald    write_16(fout, flags)
662b3fcedb9SMatthias Ringwald    write_16(fout, handle)
663b3fcedb9SMatthias Ringwald    write_16(fout, 0x2901)
664b3fcedb9SMatthias Ringwald    if is_string(value):
665b3fcedb9SMatthias Ringwald        write_string(fout, value)
666b3fcedb9SMatthias Ringwald    else:
667b3fcedb9SMatthias Ringwald        write_sequence(fout,value)
668b3fcedb9SMatthias Ringwald    fout.write("\n")
669043f8832SMatthias Ringwald
670043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
671043f8832SMatthias Ringwald    database_hash_append_uint16(0x2901)
672043f8832SMatthias Ringwald
673729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
674b3fcedb9SMatthias Ringwald    handle = handle + 1
675b3fcedb9SMatthias Ringwald
67617215335SMatthias Ringwalddef parseGenericDynamicDescriptor(fout, parts, uuid, name):
677b3fcedb9SMatthias Ringwald    global handle
678b3fcedb9SMatthias Ringwald    global total_size
679b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
680b3fcedb9SMatthias Ringwald
681b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
682b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
683b3fcedb9SMatthias Ringwald
684e22a2612SMatthias Ringwald    # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY
685d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
686e22a2612SMatthias Ringwald    flags |= property_flags['READ']
687e22a2612SMatthias Ringwald    flags |= property_flags['WRITE']
688e22a2612SMatthias Ringwald    flags |= property_flags['DYNAMIC']
689e22a2612SMatthias Ringwald
690b3fcedb9SMatthias Ringwald    write_indent(fout)
69117215335SMatthias Ringwald    fout.write('// 0x%04x %s-%s\n' % (handle, name, '-'.join(parts[1:])))
692d7ec1d24SMatthias Ringwald
693d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
694d7ec1d24SMatthias Ringwald
695b3fcedb9SMatthias Ringwald    write_indent(fout)
696b3fcedb9SMatthias Ringwald    write_16(fout, size)
697e22a2612SMatthias Ringwald    write_16(fout, flags)
698b3fcedb9SMatthias Ringwald    write_16(fout, handle)
699b3fcedb9SMatthias Ringwald    write_16(fout, 0x2903)
700b3fcedb9SMatthias Ringwald    fout.write("\n")
701043f8832SMatthias Ringwald
702043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
70317215335SMatthias Ringwald    database_hash_append_uint16(uuid)
704043f8832SMatthias Ringwald
70517215335SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_%s_HANDLE 0x%04x' % (current_characteristic_uuid_string, name, handle))
706b3fcedb9SMatthias Ringwald    handle = handle + 1
707b3fcedb9SMatthias Ringwald
70817215335SMatthias Ringwalddef parseGenericDynamicReadOnlyDescriptor(fout, parts, uuid, name):
70917215335SMatthias Ringwald    global handle
71017215335SMatthias Ringwald    global total_size
71117215335SMatthias Ringwald    global current_characteristic_uuid_string
71217215335SMatthias Ringwald
71317215335SMatthias Ringwald    properties = parseProperties(parts[1])
71417215335SMatthias Ringwald    size = 2 + 2 + 2 + 2
71517215335SMatthias Ringwald
71617215335SMatthias Ringwald    # use write permissions and encryption key size from attribute value and set READ, DYNAMIC, READ_ANYBODY
71717215335SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
71817215335SMatthias Ringwald    flags |= property_flags['READ']
71917215335SMatthias Ringwald    flags |= property_flags['DYNAMIC']
72017215335SMatthias Ringwald
72117215335SMatthias Ringwald    write_indent(fout)
72217215335SMatthias Ringwald    fout.write('// 0x%04x %s-%s\n' % (handle, name, '-'.join(parts[1:])))
72317215335SMatthias Ringwald
72417215335SMatthias Ringwald    dump_flags(fout, flags)
72517215335SMatthias Ringwald
72617215335SMatthias Ringwald    write_indent(fout)
72717215335SMatthias Ringwald    write_16(fout, size)
72817215335SMatthias Ringwald    write_16(fout, flags)
72917215335SMatthias Ringwald    write_16(fout, handle)
73017215335SMatthias Ringwald    write_16(fout, 0x2903)
73117215335SMatthias Ringwald    fout.write("\n")
73217215335SMatthias Ringwald
73317215335SMatthias Ringwald    database_hash_append_uint16(handle)
73417215335SMatthias Ringwald    database_hash_append_uint16(uuid)
73517215335SMatthias Ringwald
73617215335SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_%s_HANDLE 0x%04x' % (current_characteristic_uuid_string, name, handle))
73717215335SMatthias Ringwald    handle = handle + 1
73817215335SMatthias Ringwald
73917215335SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts):
74017215335SMatthias Ringwald    parseGenericDynamicDescriptor(fout, parts, 0x2903, 'SERVER_CONFIGURATION')
74117215335SMatthias Ringwald
742b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts):
743b3fcedb9SMatthias Ringwald    global handle
744b3fcedb9SMatthias Ringwald    global total_size
745b3fcedb9SMatthias Ringwald
746e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
747b3fcedb9SMatthias Ringwald
748b3fcedb9SMatthias Ringwald    identifier = parts[1]
749b3fcedb9SMatthias Ringwald    presentation_formats[identifier] = handle
750b3fcedb9SMatthias Ringwald    # print("format '%s' with handle %d\n" % (identifier, handle))
751b3fcedb9SMatthias Ringwald
752b3fcedb9SMatthias Ringwald    format     = parts[2]
753b3fcedb9SMatthias Ringwald    exponent   = parts[3]
754b3fcedb9SMatthias Ringwald    unit       = parseUUID(parts[4])
755b3fcedb9SMatthias Ringwald    name_space = parts[5]
756b3fcedb9SMatthias Ringwald    description = parseUUID(parts[6])
757b3fcedb9SMatthias Ringwald
758b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 7
759b3fcedb9SMatthias Ringwald
760b3fcedb9SMatthias Ringwald    write_indent(fout)
761b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
762b3fcedb9SMatthias Ringwald    write_indent(fout)
763b3fcedb9SMatthias Ringwald    write_16(fout, size)
764e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
765b3fcedb9SMatthias Ringwald    write_16(fout, handle)
766b3fcedb9SMatthias Ringwald    write_16(fout, 0x2904)
767b3fcedb9SMatthias Ringwald    write_sequence(fout, format)
768b3fcedb9SMatthias Ringwald    write_sequence(fout, exponent)
769285653b2SMatthias Ringwald    write_uuid(fout, unit)
770b3fcedb9SMatthias Ringwald    write_sequence(fout, name_space)
771285653b2SMatthias Ringwald    write_uuid(fout, description)
772b3fcedb9SMatthias Ringwald    fout.write("\n")
773043f8832SMatthias Ringwald
774043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
775043f8832SMatthias Ringwald    database_hash_append_uint16(0x2904)
776043f8832SMatthias Ringwald
777b3fcedb9SMatthias Ringwald    handle = handle + 1
778b3fcedb9SMatthias Ringwald
779b3fcedb9SMatthias Ringwald
780b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts):
781b3fcedb9SMatthias Ringwald    global handle
782b3fcedb9SMatthias Ringwald    global total_size
783b3fcedb9SMatthias Ringwald
784e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
785b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2
786b3fcedb9SMatthias Ringwald
787b3fcedb9SMatthias Ringwald    write_indent(fout)
788b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
789b3fcedb9SMatthias Ringwald    write_indent(fout)
790b3fcedb9SMatthias Ringwald    write_16(fout, size)
791e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
792b3fcedb9SMatthias Ringwald    write_16(fout, handle)
793b3fcedb9SMatthias Ringwald    write_16(fout, 0x2905)
794b3fcedb9SMatthias Ringwald    for identifier in parts[1:]:
795b3fcedb9SMatthias Ringwald        format_handle = presentation_formats[identifier]
796b3fcedb9SMatthias Ringwald        if format == 0:
797b3fcedb9SMatthias Ringwald            print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier)
798b3fcedb9SMatthias Ringwald            sys.exit(1)
799b3fcedb9SMatthias Ringwald        write_16(fout, format_handle)
800b3fcedb9SMatthias Ringwald    fout.write("\n")
801043f8832SMatthias Ringwald
802043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
803043f8832SMatthias Ringwald    database_hash_append_uint16(0x2905)
804043f8832SMatthias Ringwald
805b3fcedb9SMatthias Ringwald    handle = handle + 1
806b3fcedb9SMatthias Ringwald
807b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts):
808b3fcedb9SMatthias Ringwald    global handle
809b3fcedb9SMatthias Ringwald    global total_size
810b3fcedb9SMatthias Ringwald
811e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
812b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1 + 1
813b3fcedb9SMatthias Ringwald
814231a3c5dSMatthias Ringwald    report_id = parts[2]
815231a3c5dSMatthias Ringwald    report_type = parts[3]
816b3fcedb9SMatthias Ringwald
817b3fcedb9SMatthias Ringwald    write_indent(fout)
818b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
819b3fcedb9SMatthias Ringwald    write_indent(fout)
820b3fcedb9SMatthias Ringwald    write_16(fout, size)
821e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
822b3fcedb9SMatthias Ringwald    write_16(fout, handle)
823b3fcedb9SMatthias Ringwald    write_16(fout, 0x2908)
824b3fcedb9SMatthias Ringwald    write_sequence(fout, report_id)
825b3fcedb9SMatthias Ringwald    write_sequence(fout, report_type)
826b3fcedb9SMatthias Ringwald    fout.write("\n")
827b3fcedb9SMatthias Ringwald    handle = handle + 1
828b3fcedb9SMatthias Ringwald
829b3fcedb9SMatthias Ringwald
830b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts):
831b3fcedb9SMatthias Ringwald    global handle
832b3fcedb9SMatthias Ringwald    global total_size
833b3fcedb9SMatthias Ringwald
834e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
835b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1
836b3fcedb9SMatthias Ringwald
837b3fcedb9SMatthias Ringwald    no_of_digitals = parts[1]
838b3fcedb9SMatthias Ringwald
839b3fcedb9SMatthias Ringwald    write_indent(fout)
840b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:])))
841b3fcedb9SMatthias Ringwald    write_indent(fout)
842b3fcedb9SMatthias Ringwald    write_16(fout, size)
843e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
844b3fcedb9SMatthias Ringwald    write_16(fout, handle)
845b3fcedb9SMatthias Ringwald    write_16(fout, 0x2909)
846b3fcedb9SMatthias Ringwald    write_sequence(fout, no_of_digitals)
847b3fcedb9SMatthias Ringwald    fout.write("\n")
848b3fcedb9SMatthias Ringwald    handle = handle + 1
849b3fcedb9SMatthias Ringwald
85060b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout):
851b3fcedb9SMatthias Ringwald    global handle
852b3fcedb9SMatthias Ringwald    global total_size
853b3fcedb9SMatthias Ringwald
854b165f97bSMatthias Ringwald    line_count = 0;
855b3fcedb9SMatthias Ringwald    for line in fin:
856b3fcedb9SMatthias Ringwald        line = line.strip("\n\r ")
857b165f97bSMatthias Ringwald        line_count += 1
858b3fcedb9SMatthias Ringwald
859b165f97bSMatthias Ringwald        if line.startswith("//"):
860b165f97bSMatthias Ringwald            fout.write("    //" + line.lstrip('/') + '\n')
861b165f97bSMatthias Ringwald            continue
862b165f97bSMatthias Ringwald
86360b51a4cSMatthias Ringwald        if line.startswith("#import"):
86460b51a4cSMatthias Ringwald            imported_file = ''
86560b51a4cSMatthias Ringwald            parts = re.match('#import\s+<(.*)>\w*',line)
86660b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
867dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
86860b51a4cSMatthias Ringwald            parts = re.match('#import\s+"(.*)"\w*',line)
86960b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
870dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
87160b51a4cSMatthias Ringwald            if len(imported_file) == 0:
87260b51a4cSMatthias Ringwald                print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count))
87360b51a4cSMatthias Ringwald                continue
87460b51a4cSMatthias Ringwald
875dbb3997aSMilanka Ringwald            imported_file = getFile( imported_file )
87660b51a4cSMatthias Ringwald            print("Importing %s" % imported_file)
87760b51a4cSMatthias Ringwald            try:
87860b51a4cSMatthias Ringwald                imported_fin = codecs.open (imported_file, encoding='utf-8')
87960b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- BEGIN\n')
88060b51a4cSMatthias Ringwald                parseLines(imported_file, imported_fin, fout)
88160b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- END\n')
88260b51a4cSMatthias Ringwald            except IOError as e:
88360b51a4cSMatthias Ringwald                print('ERROR: Import failed. Please check path.')
88460b51a4cSMatthias Ringwald
88560b51a4cSMatthias Ringwald            continue
88660b51a4cSMatthias Ringwald
88760b51a4cSMatthias Ringwald        if line.startswith("#TODO"):
88860b51a4cSMatthias Ringwald            print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count))
889b165f97bSMatthias Ringwald            print ("'%s'" % line)
890b165f97bSMatthias Ringwald            fout.write("// " + line + '\n')
891b3fcedb9SMatthias Ringwald            continue
892b3fcedb9SMatthias Ringwald
893b3fcedb9SMatthias Ringwald        if len(line) == 0:
894b3fcedb9SMatthias Ringwald            continue
895b3fcedb9SMatthias Ringwald
896b3fcedb9SMatthias Ringwald        f = io.StringIO(line)
897b3fcedb9SMatthias Ringwald        parts_list = csv.reader(f, delimiter=',', quotechar='"')
898b3fcedb9SMatthias Ringwald
899b3fcedb9SMatthias Ringwald        for parts in parts_list:
900b3fcedb9SMatthias Ringwald            for index, object in enumerate(parts):
901b3fcedb9SMatthias Ringwald                parts[index] = object.strip().lstrip('"').rstrip('"')
902b3fcedb9SMatthias Ringwald
903b3fcedb9SMatthias Ringwald            if parts[0] == 'PRIMARY_SERVICE':
904b3fcedb9SMatthias Ringwald                parsePrimaryService(fout, parts)
905b3fcedb9SMatthias Ringwald                continue
906b3fcedb9SMatthias Ringwald
907b3fcedb9SMatthias Ringwald            if parts[0] == 'SECONDARY_SERVICE':
908b3fcedb9SMatthias Ringwald                parseSecondaryService(fout, parts)
909b3fcedb9SMatthias Ringwald                continue
910b3fcedb9SMatthias Ringwald
911b3fcedb9SMatthias Ringwald            if parts[0] == 'INCLUDE_SERVICE':
912b3fcedb9SMatthias Ringwald                parseIncludeService(fout, parts)
913b3fcedb9SMatthias Ringwald                continue
914b3fcedb9SMatthias Ringwald
915b3fcedb9SMatthias Ringwald            # 2803
916b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC':
917b3fcedb9SMatthias Ringwald                parseCharacteristic(fout, parts)
918b3fcedb9SMatthias Ringwald                continue
919b3fcedb9SMatthias Ringwald
920b3fcedb9SMatthias Ringwald            # 2900 Characteristic Extended Properties
921b3fcedb9SMatthias Ringwald
922b3fcedb9SMatthias Ringwald            # 2901
923b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
924b3fcedb9SMatthias Ringwald                parseCharacteristicUserDescription(fout, parts)
925b3fcedb9SMatthias Ringwald                continue
926b3fcedb9SMatthias Ringwald
927b165f97bSMatthias Ringwald
928b165f97bSMatthias Ringwald            # 2902 Client Characteristic Configuration - automatically included in Characteristic if
929b3fcedb9SMatthias Ringwald            # notification / indication is supported
930231a3c5dSMatthias Ringwald            if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION':
931b165f97bSMatthias Ringwald                continue
932b3fcedb9SMatthias Ringwald
933b3fcedb9SMatthias Ringwald            # 2903
934b3fcedb9SMatthias Ringwald            if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
93517215335SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x2903, 'SERVER_CONFIGURATION')
936b3fcedb9SMatthias Ringwald                continue
937b3fcedb9SMatthias Ringwald
938b3fcedb9SMatthias Ringwald            # 2904
939b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_FORMAT':
940b3fcedb9SMatthias Ringwald                parseCharacteristicFormat(fout, parts)
941b3fcedb9SMatthias Ringwald                continue
942b3fcedb9SMatthias Ringwald
943b3fcedb9SMatthias Ringwald            # 2905
944b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
945b3fcedb9SMatthias Ringwald                parseCharacteristicAggregateFormat(fout, parts)
946b3fcedb9SMatthias Ringwald                continue
947b3fcedb9SMatthias Ringwald
948b3fcedb9SMatthias Ringwald            # 2906
949b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
950*6f08f159SMatthias Ringwald                parseGenericDynamicReadOnlyDescriptor(fout, parts, 0x2906, 'VALID_RANGE')
951b3fcedb9SMatthias Ringwald                continue
952b3fcedb9SMatthias Ringwald
953b3fcedb9SMatthias Ringwald            # 2907
954b3fcedb9SMatthias Ringwald            if parts[0] == 'EXTERNAL_REPORT_REFERENCE':
955b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
956b3fcedb9SMatthias Ringwald                continue
957b3fcedb9SMatthias Ringwald
958b3fcedb9SMatthias Ringwald            # 2908
959b3fcedb9SMatthias Ringwald            if parts[0] == 'REPORT_REFERENCE':
960b3fcedb9SMatthias Ringwald                parseReportReference(fout, parts)
961b3fcedb9SMatthias Ringwald                continue
962b3fcedb9SMatthias Ringwald
963b3fcedb9SMatthias Ringwald            # 2909
964b3fcedb9SMatthias Ringwald            if parts[0] == 'NUMBER_OF_DIGITALS':
965b3fcedb9SMatthias Ringwald                parseNumberOfDigitals(fout, parts)
966b3fcedb9SMatthias Ringwald                continue
967b3fcedb9SMatthias Ringwald
968b3fcedb9SMatthias Ringwald            # 290A
969b3fcedb9SMatthias Ringwald            if parts[0] == 'VALUE_TRIGGER_SETTING':
970*6f08f159SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x290A, 'VALUE_TRIGGER_SETTING')
971b3fcedb9SMatthias Ringwald                continue
972b3fcedb9SMatthias Ringwald
973b3fcedb9SMatthias Ringwald            # 290B
974b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION':
975*6f08f159SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x290B, 'ENVIRONMENTAL_SENSING_CONFIGURATION')
976b3fcedb9SMatthias Ringwald                continue
977b3fcedb9SMatthias Ringwald
978b3fcedb9SMatthias Ringwald            # 290C
979b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT':
980*6f08f159SMatthias Ringwald                parseGenericDynamicReadOnlyDescriptor(fout, parts, 0x290C, 'ENVIRONMENTAL_SENSING_MEASUREMENT')
981b3fcedb9SMatthias Ringwald                continue
982b3fcedb9SMatthias Ringwald
983b3fcedb9SMatthias Ringwald            # 290D
984b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING':
985*6f08f159SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x290D, 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING')
986b3fcedb9SMatthias Ringwald                continue
987b3fcedb9SMatthias Ringwald
988b3fcedb9SMatthias Ringwald            print("WARNING: unknown token: %s\n" % (parts[0]))
989b3fcedb9SMatthias Ringwald
9907050bf34SMatthias Ringwalddef parse(fname_in, fin, fname_out, tool_path, fout):
99160b51a4cSMatthias Ringwald    global handle
99260b51a4cSMatthias Ringwald    global total_size
99360b51a4cSMatthias Ringwald
9947050bf34SMatthias Ringwald    fout.write(header.format(fname_out, fname_in, tool_path))
99560b51a4cSMatthias Ringwald    fout.write('{\n')
996fd1be25dSMatthias Ringwald    write_indent(fout)
997fd1be25dSMatthias Ringwald    fout.write('// ATT DB Version\n')
998fd1be25dSMatthias Ringwald    write_indent(fout)
999fd1be25dSMatthias Ringwald    fout.write('1,\n')
1000fd1be25dSMatthias Ringwald    fout.write("\n")
100160b51a4cSMatthias Ringwald
100260b51a4cSMatthias Ringwald    parseLines(fname_in, fin, fout)
100360b51a4cSMatthias Ringwald
1004729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
1005b3fcedb9SMatthias Ringwald    write_indent(fout)
1006b3fcedb9SMatthias Ringwald    fout.write("// END\n");
1007b3fcedb9SMatthias Ringwald    write_indent(fout)
1008b3fcedb9SMatthias Ringwald    write_16(fout,0)
1009b3fcedb9SMatthias Ringwald    fout.write("\n")
1010b3fcedb9SMatthias Ringwald    total_size = total_size + 2
1011b3fcedb9SMatthias Ringwald
1012b3fcedb9SMatthias Ringwald    fout.write("}; // total size %u bytes \n" % total_size);
1013b3fcedb9SMatthias Ringwald
1014b3fcedb9SMatthias Ringwalddef listHandles(fout):
1015b3fcedb9SMatthias Ringwald    fout.write('\n\n')
1016b3fcedb9SMatthias Ringwald    fout.write('//\n')
1017729074c4SMatthias Ringwald    fout.write('// list service handle ranges\n')
1018729074c4SMatthias Ringwald    fout.write('//\n')
1019729074c4SMatthias Ringwald    for define in defines_for_services:
1020729074c4SMatthias Ringwald        fout.write(define)
1021729074c4SMatthias Ringwald        fout.write('\n')
1022729074c4SMatthias Ringwald    fout.write('\n')
1023729074c4SMatthias Ringwald    fout.write('//\n')
1024b3fcedb9SMatthias Ringwald    fout.write('// list mapping between characteristics and handles\n')
1025b3fcedb9SMatthias Ringwald    fout.write('//\n')
1026729074c4SMatthias Ringwald    for define in defines_for_characteristics:
1027b3fcedb9SMatthias Ringwald        fout.write(define)
1028b3fcedb9SMatthias Ringwald        fout.write('\n')
1029b3fcedb9SMatthias Ringwald
1030dbb3997aSMilanka Ringwalddef getFile( fileName ):
103178b65b0aSMatthias Ringwald    for d in include_paths:
103278b65b0aSMatthias Ringwald        fullFile = os.path.normpath(d + os.sep + fileName) # because Windows exists
103378b65b0aSMatthias Ringwald        # print("test %s" % fullFile)
1034dbb3997aSMilanka Ringwald        if os.path.isfile( fullFile ) == True:
1035dbb3997aSMilanka Ringwald            return fullFile
1036dbb3997aSMilanka Ringwald    print ("'{0}' not found".format( fileName ))
103778b65b0aSMatthias Ringwald    print ("Include paths: %s" % ", ".join(include_paths))
1038dbb3997aSMilanka Ringwald    exit(-1)
1039dbb3997aSMilanka Ringwald
1040dbb3997aSMilanka Ringwald
1041dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
104278b65b0aSMatthias Ringwalddefault_includes = [os.path.normpath(path) for path in [ btstack_root + '/src/', btstack_root + '/src/ble/gatt-service/']]
1043dbb3997aSMilanka Ringwald
1044dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack')
1045dbb3997aSMilanka Ringwald
1046dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes',
1047dbb3997aSMilanka Ringwald        help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes))
1048dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str,
1049dbb3997aSMilanka Ringwald        help='gatt file to be compiled')
1050dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str,
1051dbb3997aSMilanka Ringwald        help='header file to be generated')
1052dbb3997aSMilanka Ringwald
1053dbb3997aSMilanka Ringwaldargs = parser.parse_args()
1054dbb3997aSMilanka Ringwald
105578b65b0aSMatthias Ringwald# add include path arguments
105678b65b0aSMatthias Ringwaldif args.I != None:
105778b65b0aSMatthias Ringwald    for d in args.I:
105878b65b0aSMatthias Ringwald        include_paths.append(os.path.normpath(d[0]))
105978b65b0aSMatthias Ringwald
1060dbb3997aSMilanka Ringwald# append default include paths
106178b65b0aSMatthias Ringwaldinclude_paths.extend(default_includes)
1062dbb3997aSMilanka Ringwald
1063b3fcedb9SMatthias Ringwaldtry:
1064b165f97bSMatthias Ringwald    # read defines from bluetooth_gatt.h
1065dbb3997aSMilanka Ringwald    gen_path = getFile( 'bluetooth_gatt.h' )
1066b165f97bSMatthias Ringwald    bluetooth_gatt = read_defines(gen_path)
1067b165f97bSMatthias Ringwald
1068dbb3997aSMilanka Ringwald    filename = args.hfile
1069dbb3997aSMilanka Ringwald    fin  = codecs.open (args.gattfile, encoding='utf-8')
1070285653b2SMatthias Ringwald
1071285653b2SMatthias Ringwald    # pass 1: create temp .h file
1072d78181d6SMatthias Ringwald    ftemp = tempfile.TemporaryFile(mode='w+t')
1073285653b2SMatthias Ringwald    parse(args.gattfile, fin, filename, sys.argv[0], ftemp)
1074285653b2SMatthias Ringwald    listHandles(ftemp)
1075285653b2SMatthias Ringwald
1076043f8832SMatthias Ringwald    # calc GATT Database Hash
1077043f8832SMatthias Ringwald    db_hash = aes_cmac(bytearray(16), database_hash_message)
1078043f8832SMatthias Ringwald    if isinstance(db_hash, str):
1079043f8832SMatthias Ringwald        # python2
1080043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % ord(i)) for i in db_hash]
1081043f8832SMatthias Ringwald    elif isinstance(db_hash, bytes):
1082043f8832SMatthias Ringwald        # python3
1083043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % i) for i in db_hash]
1084043f8832SMatthias Ringwald    else:
1085043f8832SMatthias Ringwald        print("AES CMAC returns unexpected type %s, abort" % type(db_hash))
1086043f8832SMatthias Ringwald        sys.exit(1)
1087043f8832SMatthias Ringwald    # reverse hash to get little endian
1088043f8832SMatthias Ringwald    db_hash_sequence.reverse()
1089043f8832SMatthias Ringwald    db_hash_string = ', '.join(db_hash_sequence) + ', '
1090043f8832SMatthias Ringwald
1091285653b2SMatthias Ringwald    # pass 2: insert GATT Database Hash
1092b3fcedb9SMatthias Ringwald    fout = open (filename, 'w')
1093285653b2SMatthias Ringwald    ftemp.seek(0)
1094285653b2SMatthias Ringwald    for line in ftemp:
1095043f8832SMatthias Ringwald        fout.write(line.replace('THE-DATABASE-HASH', db_hash_string))
1096b3fcedb9SMatthias Ringwald    fout.close()
1097285653b2SMatthias Ringwald    ftemp.close()
1098285653b2SMatthias Ringwald
1099b165f97bSMatthias Ringwald    print('Created %s' % filename)
1100b3fcedb9SMatthias Ringwald
1101b3fcedb9SMatthias Ringwaldexcept IOError as e:
1102e22a2612SMatthias Ringwald
1103b3fcedb9SMatthias Ringwald    print(usage)
1104b3fcedb9SMatthias Ringwald    sys.exit(1)
1105b3fcedb9SMatthias Ringwald
1106b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n')
1107