xref: /btstack/tool/compile_gatt.py (revision d567aeb32398f2a708611c446bcd9bad85a152cd)
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
215e5ce8e0eSMilanka Ringwald    return value
216e5ce8e0eSMilanka Ringwald
217e5ce8e0eSMilanka Ringwalddef prettyPrintProperties(properties):
218e5ce8e0eSMilanka Ringwald    value = ""
219e5ce8e0eSMilanka Ringwald    parts = properties.split("|")
220e5ce8e0eSMilanka Ringwald    for property in parts:
221e5ce8e0eSMilanka Ringwald        property = property.strip()
222e5ce8e0eSMilanka Ringwald        if property in property_flags:
223e5ce8e0eSMilanka Ringwald            if value != "":
224e5ce8e0eSMilanka Ringwald                value += " | "
225e5ce8e0eSMilanka Ringwald            value += property
226e5ce8e0eSMilanka Ringwald        else:
227e5ce8e0eSMilanka Ringwald            print("WARNING: property %s undefined" % (property))
228e5ce8e0eSMilanka Ringwald
229e5ce8e0eSMilanka Ringwald    return value
230e5ce8e0eSMilanka Ringwald
231e22a2612SMatthias Ringwald
232e22a2612SMatthias Ringwalddef gatt_characteristic_properties(properties):
233e22a2612SMatthias Ringwald    return properties & 0xff
234e22a2612SMatthias Ringwald
235e22a2612SMatthias Ringwalddef att_flags(properties):
236e72176f8SMatthias Ringwald    # drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Properties (0x80) - not used for flags
237e72176f8SMatthias Ringwald    properties &= 0xffffff4e
238e22a2612SMatthias Ringwald
239e22a2612SMatthias Ringwald    # rw permissions distinct
240e22a2612SMatthias Ringwald    distinct_permissions_used = properties & (
241e22a2612SMatthias Ringwald        property_flags['READ_AUTHORIZED'] |
242e72176f8SMatthias Ringwald        property_flags['READ_AUTHENTICATED_SC'] |
243e22a2612SMatthias Ringwald        property_flags['READ_AUTHENTICATED'] |
244e22a2612SMatthias Ringwald        property_flags['READ_ENCRYPTED'] |
245e22a2612SMatthias Ringwald        property_flags['READ_ANYBODY'] |
246e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHORIZED'] |
247e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED'] |
248e72176f8SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED_SC'] |
249e22a2612SMatthias Ringwald        property_flags['WRITE_ENCRYPTED'] |
250e22a2612SMatthias Ringwald        property_flags['WRITE_ANYBODY']
251e22a2612SMatthias Ringwald    ) != 0
252e22a2612SMatthias Ringwald
253e22a2612SMatthias Ringwald    # post process properties
254e22a2612SMatthias Ringwald    encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0
255e22a2612SMatthias Ringwald
256d7ec1d24SMatthias Ringwald    # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted
257e22a2612SMatthias Ringwald    if encryption_key_size_specified and not distinct_permissions_used:
258e22a2612SMatthias Ringwald        properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED']
259e22a2612SMatthias Ringwald
260d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated
261d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used:
262d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED']
263d7ec1d24SMatthias Ringwald
264d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized
265d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used:
266d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED']
267d7ec1d24SMatthias Ringwald
268d7ec1d24SMatthias Ringwald    # determine read/write security requirements
269d7ec1d24SMatthias Ringwald    read_security_level  = 0
270d7ec1d24SMatthias Ringwald    write_security_level = 0
271e72176f8SMatthias Ringwald    read_requires_sc     = False
272e72176f8SMatthias Ringwald    write_requires_sc    = False
273e22a2612SMatthias Ringwald    if properties & property_flags['READ_AUTHORIZED']:
274d7ec1d24SMatthias Ringwald        read_security_level = 3
275e22a2612SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED']:
276d7ec1d24SMatthias Ringwald        read_security_level = 2
277e72176f8SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED_SC']:
278e72176f8SMatthias Ringwald        read_security_level = 2
279e72176f8SMatthias Ringwald        read_requires_sc = True
280e22a2612SMatthias Ringwald    elif properties & property_flags['READ_ENCRYPTED']:
281d7ec1d24SMatthias Ringwald        read_security_level = 1
282e22a2612SMatthias Ringwald    if properties & property_flags['WRITE_AUTHORIZED']:
283d7ec1d24SMatthias Ringwald        write_security_level = 3
284e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED']:
285d7ec1d24SMatthias Ringwald        write_security_level = 2
286e72176f8SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED_SC']:
287e72176f8SMatthias Ringwald        write_security_level = 2
288e72176f8SMatthias Ringwald        write_requires_sc = True
289e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_ENCRYPTED']:
290d7ec1d24SMatthias Ringwald        write_security_level = 1
291d7ec1d24SMatthias Ringwald
292d7ec1d24SMatthias Ringwald    # map security requirements to flags
293d7ec1d24SMatthias Ringwald    if read_security_level & 2:
294d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_1']
295d7ec1d24SMatthias Ringwald    if read_security_level & 1:
296d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_0']
297e72176f8SMatthias Ringwald    if read_requires_sc:
298e72176f8SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_SC']
299d7ec1d24SMatthias Ringwald    if write_security_level & 2:
300d7ec1d24SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_1']
301d7ec1d24SMatthias Ringwald    if write_security_level & 1:
302e22a2612SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_0']
303e72176f8SMatthias Ringwald    if write_requires_sc:
304e72176f8SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_SC']
305e22a2612SMatthias Ringwald
306e22a2612SMatthias Ringwald    return properties
307e22a2612SMatthias Ringwald
308d7ec1d24SMatthias Ringwalddef write_permissions_and_key_size_flags_from_properties(properties):
309e22a2612SMatthias Ringwald    return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1'])
310b3fcedb9SMatthias Ringwald
311b3fcedb9SMatthias Ringwalddef write_8(fout, value):
312b3fcedb9SMatthias Ringwald    fout.write( "0x%02x, " % (value & 0xff))
313b3fcedb9SMatthias Ringwald
314b3fcedb9SMatthias Ringwalddef write_16(fout, value):
315b3fcedb9SMatthias Ringwald    fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff))
316b3fcedb9SMatthias Ringwald
317285653b2SMatthias Ringwalddef write_uuid(fout, uuid):
318b3fcedb9SMatthias Ringwald    for byte in uuid:
319b3fcedb9SMatthias Ringwald        fout.write( "0x%02x, " % byte)
320b3fcedb9SMatthias Ringwald
321b3fcedb9SMatthias Ringwalddef write_string(fout, text):
322b3fcedb9SMatthias Ringwald    for l in text.lstrip('"').rstrip('"'):
323b3fcedb9SMatthias Ringwald        write_8(fout, ord(l))
324b3fcedb9SMatthias Ringwald
325b3fcedb9SMatthias Ringwalddef write_sequence(fout, text):
326b3fcedb9SMatthias Ringwald    parts = text.split()
327b3fcedb9SMatthias Ringwald    for part in parts:
328b3fcedb9SMatthias Ringwald        fout.write("0x%s, " % (part.strip()))
329b3fcedb9SMatthias Ringwald
330043f8832SMatthias Ringwalddef write_database_hash(fout):
331043f8832SMatthias Ringwald    fout.write("THE-DATABASE-HASH")
332043f8832SMatthias Ringwald
333b3fcedb9SMatthias Ringwalddef write_indent(fout):
334b3fcedb9SMatthias Ringwald    fout.write("    ")
335b3fcedb9SMatthias Ringwald
336d7ec1d24SMatthias Ringwalddef read_permissions_from_flags(flags):
337d7ec1d24SMatthias Ringwald    permissions = 0
338d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_0']:
339d7ec1d24SMatthias Ringwald        permissions |= 1
340d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_1']:
341d7ec1d24SMatthias Ringwald        permissions |= 2
342e72176f8SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_SC'] and permissions == 2:
343e72176f8SMatthias Ringwald        permissions = 4
344d7ec1d24SMatthias Ringwald    return permissions
345d7ec1d24SMatthias Ringwald
346d7ec1d24SMatthias Ringwalddef write_permissions_from_flags(flags):
347d7ec1d24SMatthias Ringwald    permissions = 0
348d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_0']:
349d7ec1d24SMatthias Ringwald        permissions |= 1
350d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_1']:
351d7ec1d24SMatthias Ringwald        permissions |= 2
352e72176f8SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_SC'] and permissions == 2:
353e72176f8SMatthias Ringwald        permissions = 4
354d7ec1d24SMatthias Ringwald    return permissions
355d7ec1d24SMatthias Ringwald
356d7ec1d24SMatthias Ringwalddef encryption_key_size_from_flags(flags):
357d7ec1d24SMatthias Ringwald    encryption_key_size = (flags & 0xf000) >> 12
358d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
359d7ec1d24SMatthias Ringwald        encryption_key_size += 1
360d7ec1d24SMatthias Ringwald    return encryption_key_size
361d7ec1d24SMatthias Ringwald
362b3fcedb9SMatthias Ringwalddef is_string(text):
363b3fcedb9SMatthias Ringwald    for item in text.split(" "):
364b3fcedb9SMatthias Ringwald        if not all(c in string.hexdigits for c in item):
365b3fcedb9SMatthias Ringwald            return True
366b3fcedb9SMatthias Ringwald    return False
367b3fcedb9SMatthias Ringwald
368b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties):
369b3fcedb9SMatthias Ringwald    return properties & (property_flags['NOTIFY'] | property_flags['INDICATE'])
370b3fcedb9SMatthias Ringwald
371729074c4SMatthias Ringwalddef serviceDefinitionComplete(fout):
372729074c4SMatthias Ringwald    global services
373729074c4SMatthias Ringwald    if current_service_uuid_string:
374e5ce8e0eSMilanka Ringwald        # fout.write("\n")
37529ba805bSMatthias Ringwald        # update num instances for this service
37629ba805bSMatthias Ringwald        count = 1
37729ba805bSMatthias Ringwald        if current_service_uuid_string in service_counter:
37829ba805bSMatthias Ringwald            count = service_counter[current_service_uuid_string] + 1
37929ba805bSMatthias Ringwald        service_counter[current_service_uuid_string] = count
38009e90ba6SMilanka Ringwald        # add old defines without service counter for first instance for backward compatibility
38129ba805bSMatthias Ringwald        if count == 1:
382729074c4SMatthias Ringwald            defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle))
383729074c4SMatthias Ringwald            defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1))
38409e90ba6SMilanka Ringwald
38529ba805bSMatthias Ringwald        # unified defines indicating instance
38629ba805bSMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_%02x_START_HANDLE 0x%04x' % (current_service_uuid_string, count, current_service_start_handle))
38729ba805bSMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_%02x_END_HANDLE 0x%04x' % (current_service_uuid_string, count, handle-1))
38809e90ba6SMilanka Ringwald        services[current_service_uuid_string+"_" + str(count)] = [current_service_start_handle, handle - 1, count]
389729074c4SMatthias Ringwald
390d7ec1d24SMatthias Ringwalddef dump_flags(fout, flags):
391d7ec1d24SMatthias Ringwald    global security_permsission
392d7ec1d24SMatthias Ringwald    encryption_key_size = encryption_key_size_from_flags(flags)
393d7ec1d24SMatthias Ringwald    read_permissions    = security_permsission[read_permissions_from_flags(flags)]
394d7ec1d24SMatthias Ringwald    write_permissions   = security_permsission[write_permissions_from_flags(flags)]
395d7ec1d24SMatthias Ringwald    write_indent(fout)
396d7ec1d24SMatthias Ringwald    fout.write('// ')
397d7ec1d24SMatthias Ringwald    first = 1
398d7ec1d24SMatthias Ringwald    if flags & property_flags['READ']:
399d7ec1d24SMatthias Ringwald        fout.write('READ_%s' % read_permissions)
400d7ec1d24SMatthias Ringwald        first = 0
401d7ec1d24SMatthias Ringwald    if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']):
402d7ec1d24SMatthias Ringwald        if not first:
403d7ec1d24SMatthias Ringwald            fout.write(', ')
404d7ec1d24SMatthias Ringwald        first = 0
405d7ec1d24SMatthias Ringwald        fout.write('WRITE_%s' % write_permissions)
406d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
407d7ec1d24SMatthias Ringwald        if not first:
408d7ec1d24SMatthias Ringwald            fout.write(', ')
409d7ec1d24SMatthias Ringwald        first = 0
410d7ec1d24SMatthias Ringwald        fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size)
411d7ec1d24SMatthias Ringwald    fout.write('\n')
412d7ec1d24SMatthias Ringwald
413043f8832SMatthias Ringwalddef database_hash_append_uint8(value):
414043f8832SMatthias Ringwald    global database_hash_message
415043f8832SMatthias Ringwald    database_hash_message.append(value)
416043f8832SMatthias Ringwald
417043f8832SMatthias Ringwalddef database_hash_append_uint16(value):
418043f8832SMatthias Ringwald    global database_hash_message
419043f8832SMatthias Ringwald    database_hash_append_uint8(value & 0xff)
420043f8832SMatthias Ringwald    database_hash_append_uint8((value >> 8) & 0xff)
421043f8832SMatthias Ringwald
422043f8832SMatthias Ringwalddef database_hash_append_value(value):
423043f8832SMatthias Ringwald    global database_hash_message
424043f8832SMatthias Ringwald    for byte in value:
425043f8832SMatthias Ringwald        database_hash_append_uint8(byte)
426043f8832SMatthias Ringwald
427b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type):
428b3fcedb9SMatthias Ringwald    global handle
429b3fcedb9SMatthias Ringwald    global total_size
430b3fcedb9SMatthias Ringwald    global current_service_uuid_string
431b3fcedb9SMatthias Ringwald    global current_service_start_handle
432b3fcedb9SMatthias Ringwald
433729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
434b3fcedb9SMatthias Ringwald
435d7ec1d24SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
436b3fcedb9SMatthias Ringwald
437b3fcedb9SMatthias Ringwald    write_indent(fout)
438b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
439b3fcedb9SMatthias Ringwald
440b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
441b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
442b3fcedb9SMatthias Ringwald
443b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size + 2
444b3fcedb9SMatthias Ringwald
445b3fcedb9SMatthias Ringwald    if service_type == 0x2802:
446b3fcedb9SMatthias Ringwald        size += 4
447b3fcedb9SMatthias Ringwald
448b3fcedb9SMatthias Ringwald    write_indent(fout)
449b3fcedb9SMatthias Ringwald    write_16(fout, size)
450d7ec1d24SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
451b3fcedb9SMatthias Ringwald    write_16(fout, handle)
452b3fcedb9SMatthias Ringwald    write_16(fout, service_type)
453285653b2SMatthias Ringwald    write_uuid(fout, uuid)
454b3fcedb9SMatthias Ringwald    fout.write("\n")
455b3fcedb9SMatthias Ringwald
456043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
457043f8832SMatthias Ringwald    database_hash_append_uint16(service_type)
458043f8832SMatthias Ringwald    database_hash_append_value(uuid)
459043f8832SMatthias Ringwald
460729074c4SMatthias Ringwald    current_service_uuid_string = c_string_for_uuid(parts[1])
461b3fcedb9SMatthias Ringwald    current_service_start_handle = handle
462b3fcedb9SMatthias Ringwald    handle = handle + 1
463b3fcedb9SMatthias Ringwald    total_size = total_size + size
464b3fcedb9SMatthias Ringwald
465b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts):
466b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2800)
467b3fcedb9SMatthias Ringwald
468b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts):
469b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2801)
470b3fcedb9SMatthias Ringwald
471b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts):
472b3fcedb9SMatthias Ringwald    global handle
473b3fcedb9SMatthias Ringwald    global total_size
474b3fcedb9SMatthias Ringwald
475e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
476b3fcedb9SMatthias Ringwald
477b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
478b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
479b3fcedb9SMatthias Ringwald    if uuid_size > 2:
480b3fcedb9SMatthias Ringwald        uuid_size = 0
481b3fcedb9SMatthias Ringwald
482b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 4 + uuid_size
483b3fcedb9SMatthias Ringwald
484729074c4SMatthias Ringwald    keyUUID = c_string_for_uuid(parts[1])
4856a72ce79SMilanka Ringwald    keys_to_delete = []
4866a72ce79SMilanka Ringwald
48709e90ba6SMilanka Ringwald    for (serviceUUID, service) in services.items():
48809e90ba6SMilanka Ringwald        if serviceUUID.startswith(keyUUID):
48909e90ba6SMilanka Ringwald            write_indent(fout)
49009e90ba6SMilanka Ringwald            fout.write('// 0x%04x %s - range [0x%04x, 0x%04x]\n' % (handle, '-'.join(parts), services[serviceUUID][0], services[serviceUUID][1]))
491b3fcedb9SMatthias Ringwald
492b3fcedb9SMatthias Ringwald            write_indent(fout)
493b3fcedb9SMatthias Ringwald            write_16(fout, size)
494e22a2612SMatthias Ringwald            write_16(fout, read_only_anybody_flags)
495b3fcedb9SMatthias Ringwald            write_16(fout, handle)
496b3fcedb9SMatthias Ringwald            write_16(fout, 0x2802)
49709e90ba6SMilanka Ringwald            write_16(fout, services[serviceUUID][0])
49809e90ba6SMilanka Ringwald            write_16(fout, services[serviceUUID][1])
499b3fcedb9SMatthias Ringwald            if uuid_size > 0:
500285653b2SMatthias Ringwald                write_uuid(fout, uuid)
501b3fcedb9SMatthias Ringwald            fout.write("\n")
502b3fcedb9SMatthias Ringwald
503043f8832SMatthias Ringwald            database_hash_append_uint16(handle)
504043f8832SMatthias Ringwald            database_hash_append_uint16(0x2802)
50509e90ba6SMilanka Ringwald            database_hash_append_uint16(services[serviceUUID][0])
50609e90ba6SMilanka Ringwald            database_hash_append_uint16(services[serviceUUID][1])
507043f8832SMatthias Ringwald            if uuid_size > 0:
508043f8832SMatthias Ringwald                database_hash_append_value(uuid)
509043f8832SMatthias Ringwald
5106a72ce79SMilanka Ringwald            keys_to_delete.append(serviceUUID)
5116a72ce79SMilanka Ringwald
512b3fcedb9SMatthias Ringwald            handle = handle + 1
513b3fcedb9SMatthias Ringwald            total_size = total_size + size
514b3fcedb9SMatthias Ringwald
5156a72ce79SMilanka Ringwald    for key in keys_to_delete:
5166a72ce79SMilanka Ringwald        services.pop(key)
5176a72ce79SMilanka Ringwald
5186a72ce79SMilanka Ringwald
519b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts):
520b3fcedb9SMatthias Ringwald    global handle
521b3fcedb9SMatthias Ringwald    global total_size
522b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
523b3fcedb9SMatthias Ringwald    global characteristic_indices
524b3fcedb9SMatthias Ringwald
525e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
526b3fcedb9SMatthias Ringwald
527b3fcedb9SMatthias Ringwald    # enumerate characteristics with same UUID, using optional name tag if available
528b3fcedb9SMatthias Ringwald    current_characteristic_uuid_string = c_string_for_uuid(parts[1]);
529b3fcedb9SMatthias Ringwald    index = 1
530b3fcedb9SMatthias Ringwald    if current_characteristic_uuid_string in characteristic_indices:
531b3fcedb9SMatthias Ringwald        index = characteristic_indices[current_characteristic_uuid_string] + 1
532b3fcedb9SMatthias Ringwald    characteristic_indices[current_characteristic_uuid_string] = index
533b3fcedb9SMatthias Ringwald    if len(parts) > 4:
534b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_')
535b3fcedb9SMatthias Ringwald    else:
536b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += ('_%02x' % index)
537b3fcedb9SMatthias Ringwald
538b3fcedb9SMatthias Ringwald    uuid       = parseUUID(parts[1])
539b3fcedb9SMatthias Ringwald    uuid_size  = len(uuid)
540b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[2])
541b3fcedb9SMatthias Ringwald    value = ', '.join([str(x) for x in parts[3:]])
542b3fcedb9SMatthias Ringwald
543b3fcedb9SMatthias Ringwald    # reliable writes is defined in an extended properties
544b3fcedb9SMatthias Ringwald    if (properties & property_flags['RELIABLE_WRITE']):
545b3fcedb9SMatthias Ringwald        properties = properties | property_flags['EXTENDED_PROPERTIES']
546b3fcedb9SMatthias Ringwald
547b3fcedb9SMatthias Ringwald    write_indent(fout)
548e5ce8e0eSMilanka Ringwald    fout.write('// 0x%04x %s - %s\n' % (handle, '-'.join(parts[0:2]), prettyPrintProperties(parts[2])))
549b3fcedb9SMatthias Ringwald
550e22a2612SMatthias Ringwald
551e22a2612SMatthias Ringwald    characteristic_properties = gatt_characteristic_properties(properties)
552b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (1+2+uuid_size)
553b3fcedb9SMatthias Ringwald    write_indent(fout)
554b3fcedb9SMatthias Ringwald    write_16(fout, size)
555e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
556b3fcedb9SMatthias Ringwald    write_16(fout, handle)
557b3fcedb9SMatthias Ringwald    write_16(fout, 0x2803)
558e22a2612SMatthias Ringwald    write_8(fout, characteristic_properties)
559b3fcedb9SMatthias Ringwald    write_16(fout, handle+1)
560285653b2SMatthias Ringwald    write_uuid(fout, uuid)
561b3fcedb9SMatthias Ringwald    fout.write("\n")
562b3fcedb9SMatthias Ringwald    total_size = total_size + size
563b3fcedb9SMatthias Ringwald
564043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
565043f8832SMatthias Ringwald    database_hash_append_uint16(0x2803)
566043f8832SMatthias Ringwald    database_hash_append_uint8(characteristic_properties)
567043f8832SMatthias Ringwald    database_hash_append_uint16(handle+1)
568043f8832SMatthias Ringwald    database_hash_append_value(uuid)
569043f8832SMatthias Ringwald
5703c5d4072SMatthias Ringwald    handle = handle + 1
5713c5d4072SMatthias Ringwald
572043f8832SMatthias Ringwald    uuid_is_database_hash = len(uuid) == 2 and uuid[0] == 0x2a and uuid[1] == 0x2b
573043f8832SMatthias Ringwald
574b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size
575043f8832SMatthias Ringwald    if uuid_is_database_hash:
576043f8832SMatthias Ringwald        size +=  16
577043f8832SMatthias Ringwald    else:
578b3fcedb9SMatthias Ringwald        if is_string(value):
579b3fcedb9SMatthias Ringwald            size = size + len(value)
580b3fcedb9SMatthias Ringwald        else:
581b3fcedb9SMatthias Ringwald            size = size + len(value.split())
582b3fcedb9SMatthias Ringwald
583e22a2612SMatthias Ringwald    value_flags = att_flags(properties)
5848ea3236cSMatthias Ringwald
5858ea3236cSMatthias Ringwald    # add UUID128 flag for value handle
586b3fcedb9SMatthias Ringwald    if uuid_size == 16:
587e22a2612SMatthias Ringwald        value_flags = value_flags | property_flags['LONG_UUID'];
588b3fcedb9SMatthias Ringwald
589b3fcedb9SMatthias Ringwald    write_indent(fout)
590e5ce8e0eSMilanka Ringwald    properties_string = prettyPrintProperties(parts[2])
591e5ce8e0eSMilanka Ringwald    if "DYNAMIC" in properties_string:
592e5ce8e0eSMilanka Ringwald        fout.write('// 0x%04x VALUE %s - %s\n' % (handle, '-'.join(parts[0:2]), prettyPrintProperties(parts[2])))
593e5ce8e0eSMilanka Ringwald    else:
594e5ce8e0eSMilanka Ringwald        fout.write('// 0x%04x VALUE %s - %s -'"'%s'"'\n' % (
595e5ce8e0eSMilanka Ringwald        handle, '-'.join(parts[0:2]), prettyPrintProperties(parts[2]), value))
596d7ec1d24SMatthias Ringwald
597d7ec1d24SMatthias Ringwald    dump_flags(fout, value_flags)
598d7ec1d24SMatthias Ringwald
599b3fcedb9SMatthias Ringwald    write_indent(fout)
600b3fcedb9SMatthias Ringwald    write_16(fout, size)
601e22a2612SMatthias Ringwald    write_16(fout, value_flags)
602b3fcedb9SMatthias Ringwald    write_16(fout, handle)
603285653b2SMatthias Ringwald    write_uuid(fout, uuid)
604043f8832SMatthias Ringwald    if uuid_is_database_hash:
605043f8832SMatthias Ringwald        write_database_hash(fout)
606043f8832SMatthias Ringwald    else:
607b3fcedb9SMatthias Ringwald        if is_string(value):
608b3fcedb9SMatthias Ringwald            write_string(fout, value)
609b3fcedb9SMatthias Ringwald        else:
610b3fcedb9SMatthias Ringwald            write_sequence(fout,value)
611b3fcedb9SMatthias Ringwald
612b3fcedb9SMatthias Ringwald    fout.write("\n")
613729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
614b3fcedb9SMatthias Ringwald    handle = handle + 1
615b3fcedb9SMatthias Ringwald
616b3fcedb9SMatthias Ringwald    if add_client_characteristic_configuration(properties):
617e22a2612SMatthias Ringwald        # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC
618d7ec1d24SMatthias Ringwald        flags  = write_permissions_and_key_size_flags_from_properties(properties)
619e22a2612SMatthias Ringwald        flags |= property_flags['READ']
620e22a2612SMatthias Ringwald        flags |= property_flags['WRITE']
6219be4aecfSMatthias Ringwald        flags |= property_flags['WRITE_WITHOUT_RESPONSE']
622e22a2612SMatthias Ringwald        flags |= property_flags['DYNAMIC']
623b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
624d7ec1d24SMatthias Ringwald
625b3fcedb9SMatthias Ringwald        write_indent(fout)
626b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle))
627d7ec1d24SMatthias Ringwald
628d7ec1d24SMatthias Ringwald        dump_flags(fout, flags)
629d7ec1d24SMatthias Ringwald
630b3fcedb9SMatthias Ringwald        write_indent(fout)
631b3fcedb9SMatthias Ringwald        write_16(fout, size)
632e22a2612SMatthias Ringwald        write_16(fout, flags)
633b3fcedb9SMatthias Ringwald        write_16(fout, handle)
634b3fcedb9SMatthias Ringwald        write_16(fout, 0x2902)
635b3fcedb9SMatthias Ringwald        write_16(fout, 0)
636b3fcedb9SMatthias Ringwald        fout.write("\n")
637043f8832SMatthias Ringwald
638043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
639043f8832SMatthias Ringwald        database_hash_append_uint16(0x2902)
640043f8832SMatthias Ringwald
641729074c4SMatthias Ringwald        defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
642b3fcedb9SMatthias Ringwald        handle = handle + 1
643b3fcedb9SMatthias Ringwald
644043f8832SMatthias Ringwald
645b3fcedb9SMatthias Ringwald    if properties & property_flags['RELIABLE_WRITE']:
646b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
647b3fcedb9SMatthias Ringwald        write_indent(fout)
648b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle))
649b3fcedb9SMatthias Ringwald        write_indent(fout)
650b3fcedb9SMatthias Ringwald        write_16(fout, size)
651e22a2612SMatthias Ringwald        write_16(fout, read_only_anybody_flags)
652b3fcedb9SMatthias Ringwald        write_16(fout, handle)
653b3fcedb9SMatthias Ringwald        write_16(fout, 0x2900)
654b3fcedb9SMatthias Ringwald        write_16(fout, 1)   # Reliable Write
655b3fcedb9SMatthias Ringwald        fout.write("\n")
656043f8832SMatthias Ringwald
657043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
658043f8832SMatthias Ringwald        database_hash_append_uint16(0x2900)
659043f8832SMatthias Ringwald        database_hash_append_uint16(1)
660043f8832SMatthias Ringwald
661b3fcedb9SMatthias Ringwald        handle = handle + 1
662b3fcedb9SMatthias Ringwald
66317215335SMatthias Ringwalddef parseGenericDynamicDescriptor(fout, parts, uuid, name):
664b3fcedb9SMatthias Ringwald    global handle
665b3fcedb9SMatthias Ringwald    global total_size
666b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
667b3fcedb9SMatthias Ringwald
668b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
669b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
670b3fcedb9SMatthias Ringwald
671e22a2612SMatthias Ringwald    # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY
672d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
673e22a2612SMatthias Ringwald    flags |= property_flags['READ']
674e22a2612SMatthias Ringwald    flags |= property_flags['WRITE']
675e22a2612SMatthias Ringwald    flags |= property_flags['DYNAMIC']
676e22a2612SMatthias Ringwald
677b3fcedb9SMatthias Ringwald    write_indent(fout)
67817215335SMatthias Ringwald    fout.write('// 0x%04x %s-%s\n' % (handle, name, '-'.join(parts[1:])))
679d7ec1d24SMatthias Ringwald
680d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
681d7ec1d24SMatthias Ringwald
682b3fcedb9SMatthias Ringwald    write_indent(fout)
683b3fcedb9SMatthias Ringwald    write_16(fout, size)
684e22a2612SMatthias Ringwald    write_16(fout, flags)
685b3fcedb9SMatthias Ringwald    write_16(fout, handle)
686766e9e27SMatthias Ringwald    write_16(fout, uuid)
687b3fcedb9SMatthias Ringwald    fout.write("\n")
688043f8832SMatthias Ringwald
689043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
69017215335SMatthias Ringwald    database_hash_append_uint16(uuid)
691043f8832SMatthias Ringwald
69217215335SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_%s_HANDLE 0x%04x' % (current_characteristic_uuid_string, name, handle))
693b3fcedb9SMatthias Ringwald    handle = handle + 1
694b3fcedb9SMatthias Ringwald
69517215335SMatthias Ringwalddef parseGenericDynamicReadOnlyDescriptor(fout, parts, uuid, name):
69617215335SMatthias Ringwald    global handle
69717215335SMatthias Ringwald    global total_size
69817215335SMatthias Ringwald    global current_characteristic_uuid_string
69917215335SMatthias Ringwald
70017215335SMatthias Ringwald    properties = parseProperties(parts[1])
70117215335SMatthias Ringwald    size = 2 + 2 + 2 + 2
70217215335SMatthias Ringwald
70317215335SMatthias Ringwald    # use write permissions and encryption key size from attribute value and set READ, DYNAMIC, READ_ANYBODY
70417215335SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
70517215335SMatthias Ringwald    flags |= property_flags['READ']
70617215335SMatthias Ringwald    flags |= property_flags['DYNAMIC']
70717215335SMatthias Ringwald
70817215335SMatthias Ringwald    write_indent(fout)
70917215335SMatthias Ringwald    fout.write('// 0x%04x %s-%s\n' % (handle, name, '-'.join(parts[1:])))
71017215335SMatthias Ringwald
71117215335SMatthias Ringwald    dump_flags(fout, flags)
71217215335SMatthias Ringwald
71317215335SMatthias Ringwald    write_indent(fout)
71417215335SMatthias Ringwald    write_16(fout, size)
71517215335SMatthias Ringwald    write_16(fout, flags)
71617215335SMatthias Ringwald    write_16(fout, handle)
71717215335SMatthias Ringwald    write_16(fout, 0x2903)
71817215335SMatthias Ringwald    fout.write("\n")
71917215335SMatthias Ringwald
72017215335SMatthias Ringwald    database_hash_append_uint16(handle)
72117215335SMatthias Ringwald    database_hash_append_uint16(uuid)
72217215335SMatthias Ringwald
72317215335SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_%s_HANDLE 0x%04x' % (current_characteristic_uuid_string, name, handle))
72417215335SMatthias Ringwald    handle = handle + 1
72517215335SMatthias Ringwald
72617215335SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts):
72717215335SMatthias Ringwald    parseGenericDynamicDescriptor(fout, parts, 0x2903, 'SERVER_CONFIGURATION')
72817215335SMatthias Ringwald
729b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts):
730b3fcedb9SMatthias Ringwald    global handle
731b3fcedb9SMatthias Ringwald    global total_size
732b3fcedb9SMatthias Ringwald
733e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
734b3fcedb9SMatthias Ringwald
735b3fcedb9SMatthias Ringwald    identifier = parts[1]
736b3fcedb9SMatthias Ringwald    presentation_formats[identifier] = handle
737b3fcedb9SMatthias Ringwald    # print("format '%s' with handle %d\n" % (identifier, handle))
738b3fcedb9SMatthias Ringwald
739b3fcedb9SMatthias Ringwald    format     = parts[2]
740b3fcedb9SMatthias Ringwald    exponent   = parts[3]
741b3fcedb9SMatthias Ringwald    unit       = parseUUID(parts[4])
742b3fcedb9SMatthias Ringwald    name_space = parts[5]
743b3fcedb9SMatthias Ringwald    description = parseUUID(parts[6])
744b3fcedb9SMatthias Ringwald
745b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 7
746b3fcedb9SMatthias Ringwald
747b3fcedb9SMatthias Ringwald    write_indent(fout)
748b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
749b3fcedb9SMatthias Ringwald    write_indent(fout)
750b3fcedb9SMatthias Ringwald    write_16(fout, size)
751e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
752b3fcedb9SMatthias Ringwald    write_16(fout, handle)
753b3fcedb9SMatthias Ringwald    write_16(fout, 0x2904)
754b3fcedb9SMatthias Ringwald    write_sequence(fout, format)
755b3fcedb9SMatthias Ringwald    write_sequence(fout, exponent)
756285653b2SMatthias Ringwald    write_uuid(fout, unit)
757b3fcedb9SMatthias Ringwald    write_sequence(fout, name_space)
758285653b2SMatthias Ringwald    write_uuid(fout, description)
759b3fcedb9SMatthias Ringwald    fout.write("\n")
760043f8832SMatthias Ringwald
761043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
762043f8832SMatthias Ringwald    database_hash_append_uint16(0x2904)
763043f8832SMatthias Ringwald
764b3fcedb9SMatthias Ringwald    handle = handle + 1
765b3fcedb9SMatthias Ringwald
766b3fcedb9SMatthias Ringwald
767b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts):
768b3fcedb9SMatthias Ringwald    global handle
769b3fcedb9SMatthias Ringwald    global total_size
770b3fcedb9SMatthias Ringwald
771e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
772b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2
773b3fcedb9SMatthias Ringwald
774b3fcedb9SMatthias Ringwald    write_indent(fout)
775b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
776b3fcedb9SMatthias Ringwald    write_indent(fout)
777b3fcedb9SMatthias Ringwald    write_16(fout, size)
778e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
779b3fcedb9SMatthias Ringwald    write_16(fout, handle)
780b3fcedb9SMatthias Ringwald    write_16(fout, 0x2905)
781b3fcedb9SMatthias Ringwald    for identifier in parts[1:]:
7828c70002dSMatthias Ringwald        if not identifier in presentation_formats:
7838c70002dSMatthias Ringwald            print(parts)
784b3fcedb9SMatthias Ringwald            print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier)
785b3fcedb9SMatthias Ringwald            sys.exit(1)
7868c70002dSMatthias Ringwald        format_handle = presentation_formats[identifier]
787b3fcedb9SMatthias Ringwald        write_16(fout, format_handle)
788b3fcedb9SMatthias Ringwald    fout.write("\n")
789043f8832SMatthias Ringwald
790043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
791043f8832SMatthias Ringwald    database_hash_append_uint16(0x2905)
792043f8832SMatthias Ringwald
793b3fcedb9SMatthias Ringwald    handle = handle + 1
794b3fcedb9SMatthias Ringwald
79505146de0SMilanka Ringwalddef parseExternalReportReference(fout, parts):
79605146de0SMilanka Ringwald    global handle
79705146de0SMilanka Ringwald    global total_size
79805146de0SMilanka Ringwald
79905146de0SMilanka Ringwald    read_only_anybody_flags = property_flags['READ'];
80005146de0SMilanka Ringwald    size = 2 + 2 + 2 + 2 + 2
80105146de0SMilanka Ringwald
80205146de0SMilanka Ringwald    report_uuid = int(parts[2], 16)
80305146de0SMilanka Ringwald
80405146de0SMilanka Ringwald    write_indent(fout)
80505146de0SMilanka Ringwald    fout.write('// 0x%04x EXTERNAL_REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
80605146de0SMilanka Ringwald    write_indent(fout)
80705146de0SMilanka Ringwald    write_16(fout, size)
80805146de0SMilanka Ringwald    write_16(fout, read_only_anybody_flags)
80905146de0SMilanka Ringwald    write_16(fout, handle)
81005146de0SMilanka Ringwald    write_16(fout, 0x2907)
81105146de0SMilanka Ringwald    write_16(fout, report_uuid)
81205146de0SMilanka Ringwald    fout.write("\n")
81305146de0SMilanka Ringwald    handle = handle + 1
81405146de0SMilanka Ringwald
815b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts):
816b3fcedb9SMatthias Ringwald    global handle
817b3fcedb9SMatthias Ringwald    global total_size
818b3fcedb9SMatthias Ringwald
819e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
820b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1 + 1
821b3fcedb9SMatthias Ringwald
822231a3c5dSMatthias Ringwald    report_id = parts[2]
823231a3c5dSMatthias Ringwald    report_type = parts[3]
824b3fcedb9SMatthias Ringwald
825b3fcedb9SMatthias Ringwald    write_indent(fout)
826b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
827b3fcedb9SMatthias Ringwald    write_indent(fout)
828b3fcedb9SMatthias Ringwald    write_16(fout, size)
829e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
830b3fcedb9SMatthias Ringwald    write_16(fout, handle)
831b3fcedb9SMatthias Ringwald    write_16(fout, 0x2908)
832b3fcedb9SMatthias Ringwald    write_sequence(fout, report_id)
833b3fcedb9SMatthias Ringwald    write_sequence(fout, report_type)
834b3fcedb9SMatthias Ringwald    fout.write("\n")
835b3fcedb9SMatthias Ringwald    handle = handle + 1
836b3fcedb9SMatthias Ringwald
837b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts):
838b3fcedb9SMatthias Ringwald    global handle
839b3fcedb9SMatthias Ringwald    global total_size
840b3fcedb9SMatthias Ringwald
841e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
842b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1
843b3fcedb9SMatthias Ringwald
844b3fcedb9SMatthias Ringwald    no_of_digitals = parts[1]
845b3fcedb9SMatthias Ringwald
846b3fcedb9SMatthias Ringwald    write_indent(fout)
847b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:])))
848b3fcedb9SMatthias Ringwald    write_indent(fout)
849b3fcedb9SMatthias Ringwald    write_16(fout, size)
850e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
851b3fcedb9SMatthias Ringwald    write_16(fout, handle)
852b3fcedb9SMatthias Ringwald    write_16(fout, 0x2909)
853b3fcedb9SMatthias Ringwald    write_sequence(fout, no_of_digitals)
854b3fcedb9SMatthias Ringwald    fout.write("\n")
855b3fcedb9SMatthias Ringwald    handle = handle + 1
856b3fcedb9SMatthias Ringwald
85760b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout):
858b3fcedb9SMatthias Ringwald    global handle
859b3fcedb9SMatthias Ringwald    global total_size
860b3fcedb9SMatthias Ringwald
861b165f97bSMatthias Ringwald    line_count = 0;
862b3fcedb9SMatthias Ringwald    for line in fin:
863b3fcedb9SMatthias Ringwald        line = line.strip("\n\r ")
864b165f97bSMatthias Ringwald        line_count += 1
865b3fcedb9SMatthias Ringwald
866b165f97bSMatthias Ringwald        if line.startswith("//"):
867b165f97bSMatthias Ringwald            fout.write("    //" + line.lstrip('/') + '\n')
868b165f97bSMatthias Ringwald            continue
869b165f97bSMatthias Ringwald
87060b51a4cSMatthias Ringwald        if line.startswith("#import"):
87160b51a4cSMatthias Ringwald            imported_file = ''
87260b51a4cSMatthias Ringwald            parts = re.match('#import\s+<(.*)>\w*',line)
87360b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
874dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
87560b51a4cSMatthias Ringwald            parts = re.match('#import\s+"(.*)"\w*',line)
87660b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
877dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
87860b51a4cSMatthias Ringwald            if len(imported_file) == 0:
87960b51a4cSMatthias Ringwald                print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count))
88060b51a4cSMatthias Ringwald                continue
88160b51a4cSMatthias Ringwald
882dbb3997aSMilanka Ringwald            imported_file = getFile( imported_file )
88360b51a4cSMatthias Ringwald            print("Importing %s" % imported_file)
88460b51a4cSMatthias Ringwald            try:
88560b51a4cSMatthias Ringwald                imported_fin = codecs.open (imported_file, encoding='utf-8')
886e5ce8e0eSMilanka Ringwald                fout.write('\n\n    // ' + line + ' -- BEGIN\n')
88760b51a4cSMatthias Ringwald                parseLines(imported_file, imported_fin, fout)
88860b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- END\n')
88960b51a4cSMatthias Ringwald            except IOError as e:
89060b51a4cSMatthias Ringwald                print('ERROR: Import failed. Please check path.')
89160b51a4cSMatthias Ringwald
89260b51a4cSMatthias Ringwald            continue
89360b51a4cSMatthias Ringwald
89460b51a4cSMatthias Ringwald        if line.startswith("#TODO"):
89560b51a4cSMatthias Ringwald            print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count))
896b165f97bSMatthias Ringwald            print ("'%s'" % line)
897b165f97bSMatthias Ringwald            fout.write("// " + line + '\n')
898b3fcedb9SMatthias Ringwald            continue
899b3fcedb9SMatthias Ringwald
900b3fcedb9SMatthias Ringwald        if len(line) == 0:
901b3fcedb9SMatthias Ringwald            continue
902b3fcedb9SMatthias Ringwald
903b3fcedb9SMatthias Ringwald        f = io.StringIO(line)
904b3fcedb9SMatthias Ringwald        parts_list = csv.reader(f, delimiter=',', quotechar='"')
905b3fcedb9SMatthias Ringwald
906b3fcedb9SMatthias Ringwald        for parts in parts_list:
907b3fcedb9SMatthias Ringwald            for index, object in enumerate(parts):
908b3fcedb9SMatthias Ringwald                parts[index] = object.strip().lstrip('"').rstrip('"')
909b3fcedb9SMatthias Ringwald
910b3fcedb9SMatthias Ringwald            if parts[0] == 'PRIMARY_SERVICE':
911b3fcedb9SMatthias Ringwald                parsePrimaryService(fout, parts)
912b3fcedb9SMatthias Ringwald                continue
913b3fcedb9SMatthias Ringwald
914b3fcedb9SMatthias Ringwald            if parts[0] == 'SECONDARY_SERVICE':
915b3fcedb9SMatthias Ringwald                parseSecondaryService(fout, parts)
916b3fcedb9SMatthias Ringwald                continue
917b3fcedb9SMatthias Ringwald
918b3fcedb9SMatthias Ringwald            if parts[0] == 'INCLUDE_SERVICE':
919b3fcedb9SMatthias Ringwald                parseIncludeService(fout, parts)
920b3fcedb9SMatthias Ringwald                continue
921b3fcedb9SMatthias Ringwald
922b3fcedb9SMatthias Ringwald            # 2803
923b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC':
924b3fcedb9SMatthias Ringwald                parseCharacteristic(fout, parts)
925b3fcedb9SMatthias Ringwald                continue
926b3fcedb9SMatthias Ringwald
927b3fcedb9SMatthias Ringwald            # 2900 Characteristic Extended Properties
928b3fcedb9SMatthias Ringwald
929b3fcedb9SMatthias Ringwald            # 2901
930b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
931766e9e27SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x2901, 'USER_DESCRIPTION')
932b3fcedb9SMatthias Ringwald                continue
933b3fcedb9SMatthias Ringwald
934b165f97bSMatthias Ringwald
935b165f97bSMatthias Ringwald            # 2902 Client Characteristic Configuration - automatically included in Characteristic if
936b3fcedb9SMatthias Ringwald            # notification / indication is supported
937231a3c5dSMatthias Ringwald            if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION':
938b165f97bSMatthias Ringwald                continue
939b3fcedb9SMatthias Ringwald
940b3fcedb9SMatthias Ringwald            # 2903
941b3fcedb9SMatthias Ringwald            if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
94217215335SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x2903, 'SERVER_CONFIGURATION')
943b3fcedb9SMatthias Ringwald                continue
944b3fcedb9SMatthias Ringwald
945b3fcedb9SMatthias Ringwald            # 2904
946b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_FORMAT':
947b3fcedb9SMatthias Ringwald                parseCharacteristicFormat(fout, parts)
948b3fcedb9SMatthias Ringwald                continue
949b3fcedb9SMatthias Ringwald
950b3fcedb9SMatthias Ringwald            # 2905
951b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
952b3fcedb9SMatthias Ringwald                parseCharacteristicAggregateFormat(fout, parts)
953b3fcedb9SMatthias Ringwald                continue
954b3fcedb9SMatthias Ringwald
955b3fcedb9SMatthias Ringwald            # 2906
956b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
9576f08f159SMatthias Ringwald                parseGenericDynamicReadOnlyDescriptor(fout, parts, 0x2906, 'VALID_RANGE')
958b3fcedb9SMatthias Ringwald                continue
959b3fcedb9SMatthias Ringwald
960b3fcedb9SMatthias Ringwald            # 2907
961b3fcedb9SMatthias Ringwald            if parts[0] == 'EXTERNAL_REPORT_REFERENCE':
96205146de0SMilanka Ringwald                parseExternalReportReference(fout, parts)
963b3fcedb9SMatthias Ringwald                continue
964b3fcedb9SMatthias Ringwald
965b3fcedb9SMatthias Ringwald            # 2908
966b3fcedb9SMatthias Ringwald            if parts[0] == 'REPORT_REFERENCE':
967b3fcedb9SMatthias Ringwald                parseReportReference(fout, parts)
968b3fcedb9SMatthias Ringwald                continue
969b3fcedb9SMatthias Ringwald
970b3fcedb9SMatthias Ringwald            # 2909
971b3fcedb9SMatthias Ringwald            if parts[0] == 'NUMBER_OF_DIGITALS':
972b3fcedb9SMatthias Ringwald                parseNumberOfDigitals(fout, parts)
973b3fcedb9SMatthias Ringwald                continue
974b3fcedb9SMatthias Ringwald
975b3fcedb9SMatthias Ringwald            # 290A
976b3fcedb9SMatthias Ringwald            if parts[0] == 'VALUE_TRIGGER_SETTING':
9776f08f159SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x290A, 'VALUE_TRIGGER_SETTING')
978b3fcedb9SMatthias Ringwald                continue
979b3fcedb9SMatthias Ringwald
980b3fcedb9SMatthias Ringwald            # 290B
981b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION':
9826f08f159SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x290B, 'ENVIRONMENTAL_SENSING_CONFIGURATION')
983b3fcedb9SMatthias Ringwald                continue
984b3fcedb9SMatthias Ringwald
985b3fcedb9SMatthias Ringwald            # 290C
986b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT':
9876f08f159SMatthias Ringwald                parseGenericDynamicReadOnlyDescriptor(fout, parts, 0x290C, 'ENVIRONMENTAL_SENSING_MEASUREMENT')
988b3fcedb9SMatthias Ringwald                continue
989b3fcedb9SMatthias Ringwald
990b3fcedb9SMatthias Ringwald            # 290D
991b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING':
9926f08f159SMatthias Ringwald                parseGenericDynamicDescriptor(fout, parts, 0x290D, 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING')
993b3fcedb9SMatthias Ringwald                continue
994b3fcedb9SMatthias Ringwald
995b3fcedb9SMatthias Ringwald            print("WARNING: unknown token: %s\n" % (parts[0]))
996b3fcedb9SMatthias Ringwald
9977050bf34SMatthias Ringwalddef parse(fname_in, fin, fname_out, tool_path, fout):
99860b51a4cSMatthias Ringwald    global handle
99960b51a4cSMatthias Ringwald    global total_size
100060b51a4cSMatthias Ringwald
10017050bf34SMatthias Ringwald    fout.write(header.format(fname_out, fname_in, tool_path))
100260b51a4cSMatthias Ringwald    fout.write('{\n')
1003fd1be25dSMatthias Ringwald    write_indent(fout)
1004fd1be25dSMatthias Ringwald    fout.write('// ATT DB Version\n')
1005fd1be25dSMatthias Ringwald    write_indent(fout)
1006fd1be25dSMatthias Ringwald    fout.write('1,\n')
1007fd1be25dSMatthias Ringwald    fout.write("\n")
100860b51a4cSMatthias Ringwald
100960b51a4cSMatthias Ringwald    parseLines(fname_in, fin, fout)
101060b51a4cSMatthias Ringwald
1011729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
1012b3fcedb9SMatthias Ringwald    write_indent(fout)
1013b3fcedb9SMatthias Ringwald    fout.write("// END\n");
1014b3fcedb9SMatthias Ringwald    write_indent(fout)
1015b3fcedb9SMatthias Ringwald    write_16(fout,0)
1016b3fcedb9SMatthias Ringwald    fout.write("\n")
1017b3fcedb9SMatthias Ringwald    total_size = total_size + 2
1018b3fcedb9SMatthias Ringwald
1019b3fcedb9SMatthias Ringwald    fout.write("}; // total size %u bytes \n" % total_size);
1020b3fcedb9SMatthias Ringwald
1021b3fcedb9SMatthias Ringwalddef listHandles(fout):
1022b3fcedb9SMatthias Ringwald    fout.write('\n\n')
1023b3fcedb9SMatthias Ringwald    fout.write('//\n')
1024729074c4SMatthias Ringwald    fout.write('// list service handle ranges\n')
1025729074c4SMatthias Ringwald    fout.write('//\n')
1026729074c4SMatthias Ringwald    for define in defines_for_services:
1027729074c4SMatthias Ringwald        fout.write(define)
1028729074c4SMatthias Ringwald        fout.write('\n')
1029729074c4SMatthias Ringwald    fout.write('\n')
1030729074c4SMatthias Ringwald    fout.write('//\n')
1031b3fcedb9SMatthias Ringwald    fout.write('// list mapping between characteristics and handles\n')
1032b3fcedb9SMatthias Ringwald    fout.write('//\n')
1033729074c4SMatthias Ringwald    for define in defines_for_characteristics:
1034b3fcedb9SMatthias Ringwald        fout.write(define)
1035b3fcedb9SMatthias Ringwald        fout.write('\n')
1036b3fcedb9SMatthias Ringwald
1037dbb3997aSMilanka Ringwalddef getFile( fileName ):
103878b65b0aSMatthias Ringwald    for d in include_paths:
103978b65b0aSMatthias Ringwald        fullFile = os.path.normpath(d + os.sep + fileName) # because Windows exists
104078b65b0aSMatthias Ringwald        # print("test %s" % fullFile)
1041dbb3997aSMilanka Ringwald        if os.path.isfile( fullFile ) == True:
1042dbb3997aSMilanka Ringwald            return fullFile
1043dbb3997aSMilanka Ringwald    print ("'{0}' not found".format( fileName ))
104478b65b0aSMatthias Ringwald    print ("Include paths: %s" % ", ".join(include_paths))
1045dbb3997aSMilanka Ringwald    exit(-1)
1046dbb3997aSMilanka Ringwald
1047dbb3997aSMilanka Ringwald
1048dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
1049*d567aeb3SMatthias Ringwalddefault_includes = [os.path.normpath(path) for path in [
1050*d567aeb3SMatthias Ringwald    btstack_root + '/src/',
1051*d567aeb3SMatthias Ringwald    btstack_root + '/src/ble/gatt-service/',
1052*d567aeb3SMatthias Ringwald    btstack_root + '/src/le-audio/gatt-service/',
1053*d567aeb3SMatthias Ringwald    btstack_root + '/src/mesh/gatt-service/'
1054*d567aeb3SMatthias Ringwald]]
1055dbb3997aSMilanka Ringwald
1056dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack')
1057dbb3997aSMilanka Ringwald
1058dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes',
1059dbb3997aSMilanka Ringwald        help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes))
1060dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str,
1061dbb3997aSMilanka Ringwald        help='gatt file to be compiled')
1062dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str,
1063dbb3997aSMilanka Ringwald        help='header file to be generated')
1064dbb3997aSMilanka Ringwald
1065dbb3997aSMilanka Ringwaldargs = parser.parse_args()
1066dbb3997aSMilanka Ringwald
106778b65b0aSMatthias Ringwald# add include path arguments
106878b65b0aSMatthias Ringwaldif args.I != None:
106978b65b0aSMatthias Ringwald    for d in args.I:
107078b65b0aSMatthias Ringwald        include_paths.append(os.path.normpath(d[0]))
107178b65b0aSMatthias Ringwald
1072dbb3997aSMilanka Ringwald# append default include paths
107378b65b0aSMatthias Ringwaldinclude_paths.extend(default_includes)
1074dbb3997aSMilanka Ringwald
1075b3fcedb9SMatthias Ringwaldtry:
1076b165f97bSMatthias Ringwald    # read defines from bluetooth_gatt.h
1077dbb3997aSMilanka Ringwald    gen_path = getFile( 'bluetooth_gatt.h' )
1078b165f97bSMatthias Ringwald    bluetooth_gatt = read_defines(gen_path)
1079b165f97bSMatthias Ringwald
1080dbb3997aSMilanka Ringwald    filename = args.hfile
1081dbb3997aSMilanka Ringwald    fin  = codecs.open (args.gattfile, encoding='utf-8')
1082285653b2SMatthias Ringwald
1083285653b2SMatthias Ringwald    # pass 1: create temp .h file
1084d78181d6SMatthias Ringwald    ftemp = tempfile.TemporaryFile(mode='w+t')
1085285653b2SMatthias Ringwald    parse(args.gattfile, fin, filename, sys.argv[0], ftemp)
1086285653b2SMatthias Ringwald    listHandles(ftemp)
1087285653b2SMatthias Ringwald
1088043f8832SMatthias Ringwald    # calc GATT Database Hash
1089043f8832SMatthias Ringwald    db_hash = aes_cmac(bytearray(16), database_hash_message)
1090043f8832SMatthias Ringwald    if isinstance(db_hash, str):
1091043f8832SMatthias Ringwald        # python2
1092043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % ord(i)) for i in db_hash]
1093043f8832SMatthias Ringwald    elif isinstance(db_hash, bytes):
1094043f8832SMatthias Ringwald        # python3
1095043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % i) for i in db_hash]
1096043f8832SMatthias Ringwald    else:
1097043f8832SMatthias Ringwald        print("AES CMAC returns unexpected type %s, abort" % type(db_hash))
1098043f8832SMatthias Ringwald        sys.exit(1)
1099043f8832SMatthias Ringwald    # reverse hash to get little endian
1100043f8832SMatthias Ringwald    db_hash_sequence.reverse()
1101043f8832SMatthias Ringwald    db_hash_string = ', '.join(db_hash_sequence) + ', '
1102043f8832SMatthias Ringwald
1103285653b2SMatthias Ringwald    # pass 2: insert GATT Database Hash
1104b3fcedb9SMatthias Ringwald    fout = open (filename, 'w')
1105285653b2SMatthias Ringwald    ftemp.seek(0)
1106285653b2SMatthias Ringwald    for line in ftemp:
1107043f8832SMatthias Ringwald        fout.write(line.replace('THE-DATABASE-HASH', db_hash_string))
1108b3fcedb9SMatthias Ringwald    fout.close()
1109285653b2SMatthias Ringwald    ftemp.close()
1110285653b2SMatthias Ringwald
1111b165f97bSMatthias Ringwald    print('Created %s' % filename)
1112b3fcedb9SMatthias Ringwald
1113b3fcedb9SMatthias Ringwaldexcept IOError as e:
1114e22a2612SMatthias Ringwald
1115b3fcedb9SMatthias Ringwald    print(usage)
1116b3fcedb9SMatthias Ringwald    sys.exit(1)
1117b3fcedb9SMatthias Ringwald
1118b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n')
1119