xref: /btstack/tool/compile_gatt.py (revision 4783d25609a5032739e1b6e67d2236f2d80f2100)
1*4783d256SMatheus 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
12043f8832SMatthias Ringwald
13b3fcedb9SMatthias Ringwaldimport codecs
14b165f97bSMatthias Ringwaldimport csv
15b165f97bSMatthias Ringwaldimport io
16b165f97bSMatthias Ringwaldimport os
17b165f97bSMatthias Ringwaldimport re
18b165f97bSMatthias Ringwaldimport string
1960b51a4cSMatthias Ringwaldimport sys
20dbb3997aSMilanka Ringwaldimport argparse
21285653b2SMatthias Ringwaldimport tempfile
22b3fcedb9SMatthias Ringwald
23043f8832SMatthias Ringwald# try to import Cryptodome
24043f8832SMatthias Ringwaldtry:
25379d3aceSMatthias Ringwald    from Cryptodome.Cipher import AES
26043f8832SMatthias Ringwald    from Cryptodome.Hash import CMAC
27043f8832SMatthias Ringwald    have_crypto = True
28043f8832SMatthias Ringwaldexcept ImportError:
29043f8832SMatthias Ringwald    have_crypto = False
307490175eSMatthias Ringwald    print("\n[!] PyCryptodome required to calculate GATT Database Hash but not installed (using random value instead)")
31043f8832SMatthias Ringwald    print("[!] Please install PyCryptodome, e.g. 'pip install pycryptodomex'\n")
32043f8832SMatthias Ringwald
33b3fcedb9SMatthias Ringwaldheader = '''
34b3fcedb9SMatthias Ringwald// {0} generated from {1} for BTstack
357050bf34SMatthias Ringwald// it needs to be regenerated when the .gatt file is updated.
367050bf34SMatthias Ringwald
377050bf34SMatthias Ringwald// To generate {0}:
387050bf34SMatthias Ringwald// {2} {1} {0}
397050bf34SMatthias Ringwald
40fd1be25dSMatthias Ringwald// att db format version 1
41b3fcedb9SMatthias Ringwald
42fd1be25dSMatthias Ringwald// binary attribute representation:
43fd1be25dSMatthias Ringwald// - size in bytes (16), flags(16), handle (16), uuid (16/128), value(...)
44b3fcedb9SMatthias Ringwald
45b3fcedb9SMatthias Ringwald#include <stdint.h>
46b3fcedb9SMatthias Ringwald
47b3fcedb9SMatthias Ringwaldconst uint8_t profile_data[] =
48b3fcedb9SMatthias Ringwald'''
49b3fcedb9SMatthias Ringwald
50b3fcedb9SMatthias Ringwaldprint('''
51dbb3997aSMilanka RingwaldBLE configuration generator for use with BTstack
52dbb3997aSMilanka RingwaldCopyright 2018 BlueKitchen GmbH
53b3fcedb9SMatthias Ringwald''')
54b3fcedb9SMatthias Ringwald
55b3fcedb9SMatthias Ringwaldassigned_uuids = {
56b3fcedb9SMatthias Ringwald    'GAP_SERVICE'          : 0x1800,
57b3fcedb9SMatthias Ringwald    'GATT_SERVICE'         : 0x1801,
58b3fcedb9SMatthias Ringwald    'GAP_DEVICE_NAME'      : 0x2a00,
59b3fcedb9SMatthias Ringwald    'GAP_APPEARANCE'       : 0x2a01,
60b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02,
61b3fcedb9SMatthias Ringwald    'GAP_RECONNECTION_ADDRESS'    : 0x2A03,
62b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04,
63b3fcedb9SMatthias Ringwald    'GATT_SERVICE_CHANGED' : 0x2a05,
64043f8832SMatthias Ringwald    'GATT_DATABASE_HASH' : 0x2b2a
65b3fcedb9SMatthias Ringwald}
66b3fcedb9SMatthias Ringwald
67e72176f8SMatthias Ringwaldsecurity_permsission = ['ANYBODY','ENCRYPTED', 'AUTHENTICATED', 'AUTHORIZED', 'AUTHENTICATED_SC']
68d7ec1d24SMatthias Ringwald
69b3fcedb9SMatthias Ringwaldproperty_flags = {
70eb6072adSMatthias Ringwald    # GATT Characteristic Properties
71b3fcedb9SMatthias Ringwald    'BROADCAST' :                   0x01,
72b3fcedb9SMatthias Ringwald    'READ' :                        0x02,
73b3fcedb9SMatthias Ringwald    'WRITE_WITHOUT_RESPONSE' :      0x04,
74b3fcedb9SMatthias Ringwald    'WRITE' :                       0x08,
75b3fcedb9SMatthias Ringwald    'NOTIFY':                       0x10,
76b3fcedb9SMatthias Ringwald    'INDICATE' :                    0x20,
77b3fcedb9SMatthias Ringwald    'AUTHENTICATED_SIGNED_WRITE' :  0x40,
78b3fcedb9SMatthias Ringwald    'EXTENDED_PROPERTIES' :         0x80,
79b3fcedb9SMatthias Ringwald    # custom BTstack extension
80b3fcedb9SMatthias Ringwald    'DYNAMIC':                      0x100,
81b3fcedb9SMatthias Ringwald    'LONG_UUID':                    0x200,
82e22a2612SMatthias Ringwald
83e22a2612SMatthias Ringwald    # read permissions
84e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_0':        0x400,
85e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_1':        0x800,
86e22a2612SMatthias Ringwald
87e22a2612SMatthias Ringwald    #
88b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_7':       0x6000,
89b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_8':       0x7000,
90b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_9':       0x8000,
91b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_10':      0x9000,
92b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_11':      0xa000,
93b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_12':      0xb000,
94b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_13':      0xc000,
95b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_14':      0xd000,
96b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_15':      0xe000,
97b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_16':      0xf000,
98e22a2612SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_MASK':    0xf000,
99eb6072adSMatthias Ringwald
100b3fcedb9SMatthias Ringwald    # only used by gatt compiler >= 0xffff
101b3fcedb9SMatthias Ringwald    # Extended Properties
102e72176f8SMatthias Ringwald    'RELIABLE_WRITE':              0x00010000,
103e72176f8SMatthias Ringwald    'AUTHENTICATION_REQUIRED':     0x00020000,
104e72176f8SMatthias Ringwald    'AUTHORIZATION_REQUIRED':      0x00040000,
105e72176f8SMatthias Ringwald    'READ_ANYBODY':                0x00080000,
106e72176f8SMatthias Ringwald    'READ_ENCRYPTED':              0x00100000,
107e72176f8SMatthias Ringwald    'READ_AUTHENTICATED':          0x00200000,
108e72176f8SMatthias Ringwald    'READ_AUTHENTICATED_SC':       0x00400000,
109e72176f8SMatthias Ringwald    'READ_AUTHORIZED':             0x00800000,
110e72176f8SMatthias Ringwald    'WRITE_ANYBODY':               0x01000000,
111e72176f8SMatthias Ringwald    'WRITE_ENCRYPTED':             0x02000000,
112e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED':         0x04000000,
113e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED_SC':      0x08000000,
114e72176f8SMatthias Ringwald    'WRITE_AUTHORIZED':            0x10000000,
115eb6072adSMatthias Ringwald
116eb6072adSMatthias Ringwald    # Broadcast, Notify, Indicate, Extended Properties are only used to describe a GATT Characteristic, but are free to use with att_db
117e72176f8SMatthias Ringwald    # - write permissions
118e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_0':      0x01,
119e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_1':      0x10,
120e72176f8SMatthias Ringwald    # - SC required
121e72176f8SMatthias Ringwald    'READ_PERMISSION_SC':          0x20,
122e72176f8SMatthias Ringwald    'WRITE_PERMISSION_SC':         0x80,
123b3fcedb9SMatthias Ringwald}
124b3fcedb9SMatthias Ringwald
125b3fcedb9SMatthias Ringwaldservices = dict()
126b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict()
127b3fcedb9SMatthias Ringwaldpresentation_formats = dict()
128b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = ""
129b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0
130b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = ""
131729074c4SMatthias Ringwalddefines_for_characteristics = []
132729074c4SMatthias Ringwalddefines_for_services = []
13378b65b0aSMatthias Ringwaldinclude_paths = []
134043f8832SMatthias Ringwalddatabase_hash_message = bytearray()
135b3fcedb9SMatthias Ringwald
136b3fcedb9SMatthias Ringwaldhandle = 1
137b3fcedb9SMatthias Ringwaldtotal_size = 0
138b3fcedb9SMatthias Ringwald
139043f8832SMatthias Ringwalddef aes_cmac(key, n):
140043f8832SMatthias Ringwald    if have_crypto:
141043f8832SMatthias Ringwald        cobj = CMAC.new(key, ciphermod=AES)
142043f8832SMatthias Ringwald        cobj.update(n)
143043f8832SMatthias Ringwald        return cobj.digest()
144043f8832SMatthias Ringwald    else:
1457490175eSMatthias Ringwald        # return random value
1467490175eSMatthias Ringwald        return os.urandom(16)
147043f8832SMatthias Ringwald
148b165f97bSMatthias Ringwalddef read_defines(infile):
149b165f97bSMatthias Ringwald    defines = dict()
150b165f97bSMatthias Ringwald    with open (infile, 'rt') as fin:
151b165f97bSMatthias Ringwald        for line in fin:
152b165f97bSMatthias Ringwald            parts = re.match('#define\s+(\w+)\s+(\w+)',line)
153b165f97bSMatthias Ringwald            if parts and len(parts.groups()) == 2:
154b165f97bSMatthias Ringwald                (key, value) = parts.groups()
155b165f97bSMatthias Ringwald                defines[key] = int(value, 16)
156b165f97bSMatthias Ringwald    return defines
157b165f97bSMatthias Ringwald
158b3fcedb9SMatthias Ringwalddef keyForUUID(uuid):
159b3fcedb9SMatthias Ringwald    keyUUID = ""
160b3fcedb9SMatthias Ringwald    for i in uuid:
161b3fcedb9SMatthias Ringwald        keyUUID += "%02x" % i
162b3fcedb9SMatthias Ringwald    return keyUUID
163b3fcedb9SMatthias Ringwald
164b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid):
165b3fcedb9SMatthias Ringwald    return uuid.replace('-', '_')
166b3fcedb9SMatthias Ringwald
167b3fcedb9SMatthias Ringwalddef twoByteLEFor(value):
168b3fcedb9SMatthias Ringwald    return [ (value & 0xff), (value >> 8)]
169b3fcedb9SMatthias Ringwald
170b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text):
171b3fcedb9SMatthias 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):
172b3fcedb9SMatthias Ringwald        return True
173b3fcedb9SMatthias Ringwald    return False
174b3fcedb9SMatthias Ringwald
175b3fcedb9SMatthias Ringwalddef parseUUID128(uuid):
176b3fcedb9SMatthias 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)
177b3fcedb9SMatthias Ringwald    uuid_bytes = []
178b3fcedb9SMatthias Ringwald    for i in range(8, 0, -1):
179b3fcedb9SMatthias Ringwald        uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16))
180b3fcedb9SMatthias Ringwald    return uuid_bytes
181b3fcedb9SMatthias Ringwald
182b3fcedb9SMatthias Ringwalddef parseUUID(uuid):
183b3fcedb9SMatthias Ringwald    if uuid in assigned_uuids:
184b3fcedb9SMatthias Ringwald        return twoByteLEFor(assigned_uuids[uuid])
185b165f97bSMatthias Ringwald    uuid_upper = uuid.upper().replace('.','_')
186b165f97bSMatthias Ringwald    if uuid_upper in bluetooth_gatt:
187b165f97bSMatthias Ringwald        return twoByteLEFor(bluetooth_gatt[uuid_upper])
188b3fcedb9SMatthias Ringwald    if is_128bit_uuid(uuid):
189b3fcedb9SMatthias Ringwald        return parseUUID128(uuid)
190b3fcedb9SMatthias Ringwald    uuidInt = int(uuid, 16)
191b3fcedb9SMatthias Ringwald    return twoByteLEFor(uuidInt)
192b3fcedb9SMatthias Ringwald
193b3fcedb9SMatthias Ringwalddef parseProperties(properties):
194b3fcedb9SMatthias Ringwald    value = 0
195b3fcedb9SMatthias Ringwald    parts = properties.split("|")
196b3fcedb9SMatthias Ringwald    for property in parts:
197b3fcedb9SMatthias Ringwald        property = property.strip()
198b3fcedb9SMatthias Ringwald        if property in property_flags:
199b3fcedb9SMatthias Ringwald            value |= property_flags[property]
200b3fcedb9SMatthias Ringwald        else:
201b3fcedb9SMatthias Ringwald            print("WARNING: property %s undefined" % (property))
202e22a2612SMatthias Ringwald
203e22a2612SMatthias Ringwald    return value;
204e22a2612SMatthias Ringwald
205e22a2612SMatthias Ringwalddef gatt_characteristic_properties(properties):
206e22a2612SMatthias Ringwald    return properties & 0xff
207e22a2612SMatthias Ringwald
208e22a2612SMatthias Ringwalddef att_flags(properties):
209e72176f8SMatthias Ringwald    # drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Properties (0x80) - not used for flags
210e72176f8SMatthias Ringwald    properties &= 0xffffff4e
211e22a2612SMatthias Ringwald
212e22a2612SMatthias Ringwald    # rw permissions distinct
213e22a2612SMatthias Ringwald    distinct_permissions_used = properties & (
214e22a2612SMatthias Ringwald        property_flags['READ_AUTHORIZED'] |
215e72176f8SMatthias Ringwald        property_flags['READ_AUTHENTICATED_SC'] |
216e22a2612SMatthias Ringwald        property_flags['READ_AUTHENTICATED'] |
217e22a2612SMatthias Ringwald        property_flags['READ_ENCRYPTED'] |
218e22a2612SMatthias Ringwald        property_flags['READ_ANYBODY'] |
219e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHORIZED'] |
220e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED'] |
221e72176f8SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED_SC'] |
222e22a2612SMatthias Ringwald        property_flags['WRITE_ENCRYPTED'] |
223e22a2612SMatthias Ringwald        property_flags['WRITE_ANYBODY']
224e22a2612SMatthias Ringwald    ) != 0
225e22a2612SMatthias Ringwald
226e22a2612SMatthias Ringwald    # post process properties
227e22a2612SMatthias Ringwald    encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0
228e22a2612SMatthias Ringwald
229d7ec1d24SMatthias Ringwald    # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted
230e22a2612SMatthias Ringwald    if encryption_key_size_specified and not distinct_permissions_used:
231e22a2612SMatthias Ringwald        properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED']
232e22a2612SMatthias Ringwald
233d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated
234d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used:
235d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED']
236d7ec1d24SMatthias Ringwald
237d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized
238d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used:
239d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED']
240d7ec1d24SMatthias Ringwald
241d7ec1d24SMatthias Ringwald    # determine read/write security requirements
242d7ec1d24SMatthias Ringwald    read_security_level  = 0
243d7ec1d24SMatthias Ringwald    write_security_level = 0
244e72176f8SMatthias Ringwald    read_requires_sc     = False
245e72176f8SMatthias Ringwald    write_requires_sc    = False
246e22a2612SMatthias Ringwald    if properties & property_flags['READ_AUTHORIZED']:
247d7ec1d24SMatthias Ringwald        read_security_level = 3
248e22a2612SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED']:
249d7ec1d24SMatthias Ringwald        read_security_level = 2
250e72176f8SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED_SC']:
251e72176f8SMatthias Ringwald        read_security_level = 2
252e72176f8SMatthias Ringwald        read_requires_sc = True
253e22a2612SMatthias Ringwald    elif properties & property_flags['READ_ENCRYPTED']:
254d7ec1d24SMatthias Ringwald        read_security_level = 1
255e22a2612SMatthias Ringwald    if properties & property_flags['WRITE_AUTHORIZED']:
256d7ec1d24SMatthias Ringwald        write_security_level = 3
257e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED']:
258d7ec1d24SMatthias Ringwald        write_security_level = 2
259e72176f8SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED_SC']:
260e72176f8SMatthias Ringwald        write_security_level = 2
261e72176f8SMatthias Ringwald        write_requires_sc = True
262e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_ENCRYPTED']:
263d7ec1d24SMatthias Ringwald        write_security_level = 1
264d7ec1d24SMatthias Ringwald
265d7ec1d24SMatthias Ringwald    # map security requirements to flags
266d7ec1d24SMatthias Ringwald    if read_security_level & 2:
267d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_1']
268d7ec1d24SMatthias Ringwald    if read_security_level & 1:
269d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_0']
270e72176f8SMatthias Ringwald    if read_requires_sc:
271e72176f8SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_SC']
272d7ec1d24SMatthias Ringwald    if write_security_level & 2:
273d7ec1d24SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_1']
274d7ec1d24SMatthias Ringwald    if write_security_level & 1:
275e22a2612SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_0']
276e72176f8SMatthias Ringwald    if write_requires_sc:
277e72176f8SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_SC']
278e22a2612SMatthias Ringwald
279e22a2612SMatthias Ringwald    return properties
280e22a2612SMatthias Ringwald
281d7ec1d24SMatthias Ringwalddef write_permissions_and_key_size_flags_from_properties(properties):
282e22a2612SMatthias Ringwald    return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1'])
283b3fcedb9SMatthias Ringwald
284b3fcedb9SMatthias Ringwalddef write_8(fout, value):
285b3fcedb9SMatthias Ringwald    fout.write( "0x%02x, " % (value & 0xff))
286b3fcedb9SMatthias Ringwald
287b3fcedb9SMatthias Ringwalddef write_16(fout, value):
288b3fcedb9SMatthias Ringwald    fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff))
289b3fcedb9SMatthias Ringwald
290285653b2SMatthias Ringwalddef write_uuid(fout, uuid):
291b3fcedb9SMatthias Ringwald    for byte in uuid:
292b3fcedb9SMatthias Ringwald        fout.write( "0x%02x, " % byte)
293b3fcedb9SMatthias Ringwald
294b3fcedb9SMatthias Ringwalddef write_string(fout, text):
295b3fcedb9SMatthias Ringwald    for l in text.lstrip('"').rstrip('"'):
296b3fcedb9SMatthias Ringwald        write_8(fout, ord(l))
297b3fcedb9SMatthias Ringwald
298b3fcedb9SMatthias Ringwalddef write_sequence(fout, text):
299b3fcedb9SMatthias Ringwald    parts = text.split()
300b3fcedb9SMatthias Ringwald    for part in parts:
301b3fcedb9SMatthias Ringwald        fout.write("0x%s, " % (part.strip()))
302b3fcedb9SMatthias Ringwald
303043f8832SMatthias Ringwalddef write_database_hash(fout):
304043f8832SMatthias Ringwald    fout.write("THE-DATABASE-HASH")
305043f8832SMatthias Ringwald
306b3fcedb9SMatthias Ringwalddef write_indent(fout):
307b3fcedb9SMatthias Ringwald    fout.write("    ")
308b3fcedb9SMatthias Ringwald
309d7ec1d24SMatthias Ringwalddef read_permissions_from_flags(flags):
310d7ec1d24SMatthias Ringwald    permissions = 0
311d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_0']:
312d7ec1d24SMatthias Ringwald        permissions |= 1
313d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_1']:
314d7ec1d24SMatthias Ringwald        permissions |= 2
315e72176f8SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_SC'] and permissions == 2:
316e72176f8SMatthias Ringwald        permissions = 4
317d7ec1d24SMatthias Ringwald    return permissions
318d7ec1d24SMatthias Ringwald
319d7ec1d24SMatthias Ringwalddef write_permissions_from_flags(flags):
320d7ec1d24SMatthias Ringwald    permissions = 0
321d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_0']:
322d7ec1d24SMatthias Ringwald        permissions |= 1
323d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_1']:
324d7ec1d24SMatthias Ringwald        permissions |= 2
325e72176f8SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_SC'] and permissions == 2:
326e72176f8SMatthias Ringwald        permissions = 4
327d7ec1d24SMatthias Ringwald    return permissions
328d7ec1d24SMatthias Ringwald
329d7ec1d24SMatthias Ringwalddef encryption_key_size_from_flags(flags):
330d7ec1d24SMatthias Ringwald    encryption_key_size = (flags & 0xf000) >> 12
331d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
332d7ec1d24SMatthias Ringwald        encryption_key_size += 1
333d7ec1d24SMatthias Ringwald    return encryption_key_size
334d7ec1d24SMatthias Ringwald
335b3fcedb9SMatthias Ringwalddef is_string(text):
336b3fcedb9SMatthias Ringwald    for item in text.split(" "):
337b3fcedb9SMatthias Ringwald        if not all(c in string.hexdigits for c in item):
338b3fcedb9SMatthias Ringwald            return True
339b3fcedb9SMatthias Ringwald    return False
340b3fcedb9SMatthias Ringwald
341b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties):
342b3fcedb9SMatthias Ringwald    return properties & (property_flags['NOTIFY'] | property_flags['INDICATE'])
343b3fcedb9SMatthias Ringwald
344729074c4SMatthias Ringwalddef serviceDefinitionComplete(fout):
345729074c4SMatthias Ringwald    global services
346729074c4SMatthias Ringwald    if current_service_uuid_string:
347729074c4SMatthias Ringwald        fout.write("\n")
348729074c4SMatthias Ringwald        # print("append service %s = [%d, %d]" % (current_characteristic_uuid_string, current_service_start_handle, handle-1))
349729074c4SMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle))
350729074c4SMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1))
351729074c4SMatthias Ringwald        services[current_service_uuid_string] = [current_service_start_handle, handle-1]
352729074c4SMatthias Ringwald
353d7ec1d24SMatthias Ringwalddef dump_flags(fout, flags):
354d7ec1d24SMatthias Ringwald    global security_permsission
355d7ec1d24SMatthias Ringwald    encryption_key_size = encryption_key_size_from_flags(flags)
356d7ec1d24SMatthias Ringwald    read_permissions    = security_permsission[read_permissions_from_flags(flags)]
357d7ec1d24SMatthias Ringwald    write_permissions   = security_permsission[write_permissions_from_flags(flags)]
358d7ec1d24SMatthias Ringwald    write_indent(fout)
359d7ec1d24SMatthias Ringwald    fout.write('// ')
360d7ec1d24SMatthias Ringwald    first = 1
361d7ec1d24SMatthias Ringwald    if flags & property_flags['READ']:
362d7ec1d24SMatthias Ringwald        fout.write('READ_%s' % read_permissions)
363d7ec1d24SMatthias Ringwald        first = 0
364d7ec1d24SMatthias Ringwald    if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']):
365d7ec1d24SMatthias Ringwald        if not first:
366d7ec1d24SMatthias Ringwald            fout.write(', ')
367d7ec1d24SMatthias Ringwald        first = 0
368d7ec1d24SMatthias Ringwald        fout.write('WRITE_%s' % write_permissions)
369d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
370d7ec1d24SMatthias Ringwald        if not first:
371d7ec1d24SMatthias Ringwald            fout.write(', ')
372d7ec1d24SMatthias Ringwald        first = 0
373d7ec1d24SMatthias Ringwald        fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size)
374d7ec1d24SMatthias Ringwald    fout.write('\n')
375d7ec1d24SMatthias Ringwald
376043f8832SMatthias Ringwalddef database_hash_append_uint8(value):
377043f8832SMatthias Ringwald    global database_hash_message
378043f8832SMatthias Ringwald    database_hash_message.append(value)
379043f8832SMatthias Ringwald
380043f8832SMatthias Ringwalddef database_hash_append_uint16(value):
381043f8832SMatthias Ringwald    global database_hash_message
382043f8832SMatthias Ringwald    database_hash_append_uint8(value & 0xff)
383043f8832SMatthias Ringwald    database_hash_append_uint8((value >> 8) & 0xff)
384043f8832SMatthias Ringwald
385043f8832SMatthias Ringwalddef database_hash_append_value(value):
386043f8832SMatthias Ringwald    global database_hash_message
387043f8832SMatthias Ringwald    for byte in value:
388043f8832SMatthias Ringwald        database_hash_append_uint8(byte)
389043f8832SMatthias Ringwald
390b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type):
391b3fcedb9SMatthias Ringwald    global handle
392b3fcedb9SMatthias Ringwald    global total_size
393b3fcedb9SMatthias Ringwald    global current_service_uuid_string
394b3fcedb9SMatthias Ringwald    global current_service_start_handle
395b3fcedb9SMatthias Ringwald
396729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
397b3fcedb9SMatthias Ringwald
398d7ec1d24SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
399b3fcedb9SMatthias Ringwald
400b3fcedb9SMatthias Ringwald    write_indent(fout)
401b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
402b3fcedb9SMatthias Ringwald
403b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
404b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
405b3fcedb9SMatthias Ringwald
406b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size + 2
407b3fcedb9SMatthias Ringwald
408b3fcedb9SMatthias Ringwald    if service_type == 0x2802:
409b3fcedb9SMatthias Ringwald        size += 4
410b3fcedb9SMatthias Ringwald
411b3fcedb9SMatthias Ringwald    write_indent(fout)
412b3fcedb9SMatthias Ringwald    write_16(fout, size)
413d7ec1d24SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
414b3fcedb9SMatthias Ringwald    write_16(fout, handle)
415b3fcedb9SMatthias Ringwald    write_16(fout, service_type)
416285653b2SMatthias Ringwald    write_uuid(fout, uuid)
417b3fcedb9SMatthias Ringwald    fout.write("\n")
418b3fcedb9SMatthias Ringwald
419043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
420043f8832SMatthias Ringwald    database_hash_append_uint16(service_type)
421043f8832SMatthias Ringwald    database_hash_append_value(uuid)
422043f8832SMatthias Ringwald
423729074c4SMatthias Ringwald    current_service_uuid_string = c_string_for_uuid(parts[1])
424b3fcedb9SMatthias Ringwald    current_service_start_handle = handle
425b3fcedb9SMatthias Ringwald    handle = handle + 1
426b3fcedb9SMatthias Ringwald    total_size = total_size + size
427b3fcedb9SMatthias Ringwald
428b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts):
429b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2800)
430b3fcedb9SMatthias Ringwald
431b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts):
432b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2801)
433b3fcedb9SMatthias Ringwald
434b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts):
435b3fcedb9SMatthias Ringwald    global handle
436b3fcedb9SMatthias Ringwald    global total_size
437b3fcedb9SMatthias Ringwald
438e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
439b3fcedb9SMatthias Ringwald
440b3fcedb9SMatthias Ringwald    write_indent(fout)
441b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
442b3fcedb9SMatthias Ringwald
443b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
444b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
445b3fcedb9SMatthias Ringwald    if uuid_size > 2:
446b3fcedb9SMatthias Ringwald        uuid_size = 0
447729074c4SMatthias Ringwald    # print("Include Service ", c_string_for_uuid(uuid))
448b3fcedb9SMatthias Ringwald
449b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 4 + uuid_size
450b3fcedb9SMatthias Ringwald
451729074c4SMatthias Ringwald    keyUUID = c_string_for_uuid(parts[1])
452b3fcedb9SMatthias Ringwald
453b3fcedb9SMatthias Ringwald    write_indent(fout)
454b3fcedb9SMatthias Ringwald    write_16(fout, size)
455e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
456b3fcedb9SMatthias Ringwald    write_16(fout, handle)
457b3fcedb9SMatthias Ringwald    write_16(fout, 0x2802)
458b3fcedb9SMatthias Ringwald    write_16(fout, services[keyUUID][0])
459b3fcedb9SMatthias Ringwald    write_16(fout, services[keyUUID][1])
460b3fcedb9SMatthias Ringwald    if uuid_size > 0:
461285653b2SMatthias Ringwald        write_uuid(fout, uuid)
462b3fcedb9SMatthias Ringwald    fout.write("\n")
463b3fcedb9SMatthias Ringwald
464043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
465043f8832SMatthias Ringwald    database_hash_append_uint16(0x2802)
466043f8832SMatthias Ringwald    database_hash_append_uint16(services[keyUUID][0])
467043f8832SMatthias Ringwald    database_hash_append_uint16(services[keyUUID][1])
468043f8832SMatthias Ringwald    if uuid_size > 0:
469043f8832SMatthias Ringwald        database_hash_append_value(uuid)
470043f8832SMatthias Ringwald
471b3fcedb9SMatthias Ringwald    handle = handle + 1
472b3fcedb9SMatthias Ringwald    total_size = total_size + size
473b3fcedb9SMatthias Ringwald
474b3fcedb9SMatthias Ringwald
475b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts):
476b3fcedb9SMatthias Ringwald    global handle
477b3fcedb9SMatthias Ringwald    global total_size
478b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
479b3fcedb9SMatthias Ringwald    global characteristic_indices
480b3fcedb9SMatthias Ringwald
481e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
482b3fcedb9SMatthias Ringwald
483b3fcedb9SMatthias Ringwald    # enumerate characteristics with same UUID, using optional name tag if available
484b3fcedb9SMatthias Ringwald    current_characteristic_uuid_string = c_string_for_uuid(parts[1]);
485b3fcedb9SMatthias Ringwald    index = 1
486b3fcedb9SMatthias Ringwald    if current_characteristic_uuid_string in characteristic_indices:
487b3fcedb9SMatthias Ringwald        index = characteristic_indices[current_characteristic_uuid_string] + 1
488b3fcedb9SMatthias Ringwald    characteristic_indices[current_characteristic_uuid_string] = index
489b3fcedb9SMatthias Ringwald    if len(parts) > 4:
490b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_')
491b3fcedb9SMatthias Ringwald    else:
492b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += ('_%02x' % index)
493b3fcedb9SMatthias Ringwald
494b3fcedb9SMatthias Ringwald    uuid       = parseUUID(parts[1])
495b3fcedb9SMatthias Ringwald    uuid_size  = len(uuid)
496b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[2])
497b3fcedb9SMatthias Ringwald    value = ', '.join([str(x) for x in parts[3:]])
498b3fcedb9SMatthias Ringwald
499b3fcedb9SMatthias Ringwald    # reliable writes is defined in an extended properties
500b3fcedb9SMatthias Ringwald    if (properties & property_flags['RELIABLE_WRITE']):
501b3fcedb9SMatthias Ringwald        properties = properties | property_flags['EXTENDED_PROPERTIES']
502b3fcedb9SMatthias Ringwald
503b3fcedb9SMatthias Ringwald    write_indent(fout)
504b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3])))
505b3fcedb9SMatthias Ringwald
506e22a2612SMatthias Ringwald
507e22a2612SMatthias Ringwald    characteristic_properties = gatt_characteristic_properties(properties)
508b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (1+2+uuid_size)
509b3fcedb9SMatthias Ringwald    write_indent(fout)
510b3fcedb9SMatthias Ringwald    write_16(fout, size)
511e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
512b3fcedb9SMatthias Ringwald    write_16(fout, handle)
513b3fcedb9SMatthias Ringwald    write_16(fout, 0x2803)
514e22a2612SMatthias Ringwald    write_8(fout, characteristic_properties)
515b3fcedb9SMatthias Ringwald    write_16(fout, handle+1)
516285653b2SMatthias Ringwald    write_uuid(fout, uuid)
517b3fcedb9SMatthias Ringwald    fout.write("\n")
518b3fcedb9SMatthias Ringwald    handle = handle + 1
519b3fcedb9SMatthias Ringwald    total_size = total_size + size
520b3fcedb9SMatthias Ringwald
521043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
522043f8832SMatthias Ringwald    database_hash_append_uint16(0x2803)
523043f8832SMatthias Ringwald    database_hash_append_uint8(characteristic_properties)
524043f8832SMatthias Ringwald    database_hash_append_uint16(handle+1)
525043f8832SMatthias Ringwald    database_hash_append_value(uuid)
526043f8832SMatthias Ringwald
527043f8832SMatthias Ringwald    uuid_is_database_hash = len(uuid) == 2 and uuid[0] == 0x2a and uuid[1] == 0x2b
528043f8832SMatthias Ringwald
529b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size
530043f8832SMatthias Ringwald    if uuid_is_database_hash:
531043f8832SMatthias Ringwald        size +=  16
532043f8832SMatthias Ringwald    else:
533b3fcedb9SMatthias Ringwald        if is_string(value):
534b3fcedb9SMatthias Ringwald            size = size + len(value)
535b3fcedb9SMatthias Ringwald        else:
536b3fcedb9SMatthias Ringwald            size = size + len(value.split())
537b3fcedb9SMatthias Ringwald
538e22a2612SMatthias Ringwald    value_flags = att_flags(properties)
5398ea3236cSMatthias Ringwald
5408ea3236cSMatthias Ringwald    # add UUID128 flag for value handle
541b3fcedb9SMatthias Ringwald    if uuid_size == 16:
542e22a2612SMatthias Ringwald        value_flags = value_flags | property_flags['LONG_UUID'];
543b3fcedb9SMatthias Ringwald
544b3fcedb9SMatthias Ringwald    write_indent(fout)
545b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value))
546d7ec1d24SMatthias Ringwald
547d7ec1d24SMatthias Ringwald    dump_flags(fout, value_flags)
548d7ec1d24SMatthias Ringwald
549b3fcedb9SMatthias Ringwald    write_indent(fout)
550b3fcedb9SMatthias Ringwald    write_16(fout, size)
551e22a2612SMatthias Ringwald    write_16(fout, value_flags)
552b3fcedb9SMatthias Ringwald    write_16(fout, handle)
553285653b2SMatthias Ringwald    write_uuid(fout, uuid)
554043f8832SMatthias Ringwald    if uuid_is_database_hash:
555043f8832SMatthias Ringwald        write_database_hash(fout)
556043f8832SMatthias Ringwald    else:
557b3fcedb9SMatthias Ringwald        if is_string(value):
558b3fcedb9SMatthias Ringwald            write_string(fout, value)
559b3fcedb9SMatthias Ringwald        else:
560b3fcedb9SMatthias Ringwald            write_sequence(fout,value)
561b3fcedb9SMatthias Ringwald
562b3fcedb9SMatthias Ringwald    fout.write("\n")
563729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
564b3fcedb9SMatthias Ringwald    handle = handle + 1
565b3fcedb9SMatthias Ringwald
566b3fcedb9SMatthias Ringwald    if add_client_characteristic_configuration(properties):
567e22a2612SMatthias Ringwald        # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC
568d7ec1d24SMatthias Ringwald        flags  = write_permissions_and_key_size_flags_from_properties(properties)
569e22a2612SMatthias Ringwald        flags |= property_flags['READ']
570e22a2612SMatthias Ringwald        flags |= property_flags['WRITE']
5719be4aecfSMatthias Ringwald        flags |= property_flags['WRITE_WITHOUT_RESPONSE']
572e22a2612SMatthias Ringwald        flags |= property_flags['DYNAMIC']
573b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
574d7ec1d24SMatthias Ringwald
575b3fcedb9SMatthias Ringwald        write_indent(fout)
576b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle))
577d7ec1d24SMatthias Ringwald
578d7ec1d24SMatthias Ringwald        dump_flags(fout, flags)
579d7ec1d24SMatthias Ringwald
580b3fcedb9SMatthias Ringwald        write_indent(fout)
581b3fcedb9SMatthias Ringwald        write_16(fout, size)
582e22a2612SMatthias Ringwald        write_16(fout, flags)
583b3fcedb9SMatthias Ringwald        write_16(fout, handle)
584b3fcedb9SMatthias Ringwald        write_16(fout, 0x2902)
585b3fcedb9SMatthias Ringwald        write_16(fout, 0)
586b3fcedb9SMatthias Ringwald        fout.write("\n")
587043f8832SMatthias Ringwald
588043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
589043f8832SMatthias Ringwald        database_hash_append_uint16(0x2902)
590043f8832SMatthias Ringwald
591729074c4SMatthias Ringwald        defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
592b3fcedb9SMatthias Ringwald        handle = handle + 1
593b3fcedb9SMatthias Ringwald
594043f8832SMatthias Ringwald
595b3fcedb9SMatthias Ringwald    if properties & property_flags['RELIABLE_WRITE']:
596b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
597b3fcedb9SMatthias Ringwald        write_indent(fout)
598b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle))
599b3fcedb9SMatthias Ringwald        write_indent(fout)
600b3fcedb9SMatthias Ringwald        write_16(fout, size)
601e22a2612SMatthias Ringwald        write_16(fout, read_only_anybody_flags)
602b3fcedb9SMatthias Ringwald        write_16(fout, handle)
603b3fcedb9SMatthias Ringwald        write_16(fout, 0x2900)
604b3fcedb9SMatthias Ringwald        write_16(fout, 1)   # Reliable Write
605b3fcedb9SMatthias Ringwald        fout.write("\n")
606043f8832SMatthias Ringwald
607043f8832SMatthias Ringwald        database_hash_append_uint16(handle)
608043f8832SMatthias Ringwald        database_hash_append_uint16(0x2900)
609043f8832SMatthias Ringwald        database_hash_append_uint16(1)
610043f8832SMatthias Ringwald
611b3fcedb9SMatthias Ringwald        handle = handle + 1
612b3fcedb9SMatthias Ringwald
613b3fcedb9SMatthias Ringwalddef parseCharacteristicUserDescription(fout, parts):
614b3fcedb9SMatthias Ringwald    global handle
615b3fcedb9SMatthias Ringwald    global total_size
616b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
617b3fcedb9SMatthias Ringwald
618b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
619b3fcedb9SMatthias Ringwald    value      = parts[2]
620b3fcedb9SMatthias Ringwald
621b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
622b3fcedb9SMatthias Ringwald    if is_string(value):
623b7647eb6SMatthias Ringwald        size = size + len(value)
624b3fcedb9SMatthias Ringwald    else:
625b3fcedb9SMatthias Ringwald        size = size + len(value.split())
626b3fcedb9SMatthias Ringwald
627e22a2612SMatthias Ringwald    # use write, write permissions and encryption key size from attribute value and set READ_ANYBODY
628d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
629e22a2612SMatthias Ringwald    flags |= properties & property_flags['WRITE']
630e22a2612SMatthias Ringwald    flags |= property_flags['READ']
631e22a2612SMatthias Ringwald
632b3fcedb9SMatthias Ringwald    write_indent(fout)
633b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:])))
634d7ec1d24SMatthias Ringwald
635d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
636d7ec1d24SMatthias Ringwald
637b3fcedb9SMatthias Ringwald    write_indent(fout)
638b3fcedb9SMatthias Ringwald    write_16(fout, size)
639e22a2612SMatthias Ringwald    write_16(fout, flags)
640b3fcedb9SMatthias Ringwald    write_16(fout, handle)
641b3fcedb9SMatthias Ringwald    write_16(fout, 0x2901)
642b3fcedb9SMatthias Ringwald    if is_string(value):
643b3fcedb9SMatthias Ringwald        write_string(fout, value)
644b3fcedb9SMatthias Ringwald    else:
645b3fcedb9SMatthias Ringwald        write_sequence(fout,value)
646b3fcedb9SMatthias Ringwald    fout.write("\n")
647043f8832SMatthias Ringwald
648043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
649043f8832SMatthias Ringwald    database_hash_append_uint16(0x2901)
650043f8832SMatthias Ringwald
651729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
652b3fcedb9SMatthias Ringwald    handle = handle + 1
653b3fcedb9SMatthias Ringwald
654b3fcedb9SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts):
655b3fcedb9SMatthias Ringwald    global handle
656b3fcedb9SMatthias Ringwald    global total_size
657b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
658b3fcedb9SMatthias Ringwald
659b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
660b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
661b3fcedb9SMatthias Ringwald
662e22a2612SMatthias Ringwald    # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY
663d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
664e22a2612SMatthias Ringwald    flags |= property_flags['READ']
665e22a2612SMatthias Ringwald    flags |= property_flags['WRITE']
666e22a2612SMatthias Ringwald    flags |= property_flags['DYNAMIC']
667e22a2612SMatthias Ringwald
668b3fcedb9SMatthias Ringwald    write_indent(fout)
669b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:])))
670d7ec1d24SMatthias Ringwald
671d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
672d7ec1d24SMatthias Ringwald
673b3fcedb9SMatthias Ringwald    write_indent(fout)
674b3fcedb9SMatthias Ringwald    write_16(fout, size)
675e22a2612SMatthias Ringwald    write_16(fout, flags)
676b3fcedb9SMatthias Ringwald    write_16(fout, handle)
677b3fcedb9SMatthias Ringwald    write_16(fout, 0x2903)
678b3fcedb9SMatthias Ringwald    fout.write("\n")
679043f8832SMatthias Ringwald
680043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
681043f8832SMatthias Ringwald    database_hash_append_uint16(0x2903)
682043f8832SMatthias Ringwald
683729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
684b3fcedb9SMatthias Ringwald    handle = handle + 1
685b3fcedb9SMatthias Ringwald
686b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts):
687b3fcedb9SMatthias Ringwald    global handle
688b3fcedb9SMatthias Ringwald    global total_size
689b3fcedb9SMatthias Ringwald
690e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
691b3fcedb9SMatthias Ringwald
692b3fcedb9SMatthias Ringwald    identifier = parts[1]
693b3fcedb9SMatthias Ringwald    presentation_formats[identifier] = handle
694b3fcedb9SMatthias Ringwald    # print("format '%s' with handle %d\n" % (identifier, handle))
695b3fcedb9SMatthias Ringwald
696b3fcedb9SMatthias Ringwald    format     = parts[2]
697b3fcedb9SMatthias Ringwald    exponent   = parts[3]
698b3fcedb9SMatthias Ringwald    unit       = parseUUID(parts[4])
699b3fcedb9SMatthias Ringwald    name_space = parts[5]
700b3fcedb9SMatthias Ringwald    description = parseUUID(parts[6])
701b3fcedb9SMatthias Ringwald
702b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 7
703b3fcedb9SMatthias Ringwald
704b3fcedb9SMatthias Ringwald    write_indent(fout)
705b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
706b3fcedb9SMatthias Ringwald    write_indent(fout)
707b3fcedb9SMatthias Ringwald    write_16(fout, size)
708e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
709b3fcedb9SMatthias Ringwald    write_16(fout, handle)
710b3fcedb9SMatthias Ringwald    write_16(fout, 0x2904)
711b3fcedb9SMatthias Ringwald    write_sequence(fout, format)
712b3fcedb9SMatthias Ringwald    write_sequence(fout, exponent)
713285653b2SMatthias Ringwald    write_uuid(fout, unit)
714b3fcedb9SMatthias Ringwald    write_sequence(fout, name_space)
715285653b2SMatthias Ringwald    write_uuid(fout, description)
716b3fcedb9SMatthias Ringwald    fout.write("\n")
717043f8832SMatthias Ringwald
718043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
719043f8832SMatthias Ringwald    database_hash_append_uint16(0x2904)
720043f8832SMatthias Ringwald
721b3fcedb9SMatthias Ringwald    handle = handle + 1
722b3fcedb9SMatthias Ringwald
723b3fcedb9SMatthias Ringwald
724b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts):
725b3fcedb9SMatthias Ringwald    global handle
726b3fcedb9SMatthias Ringwald    global total_size
727b3fcedb9SMatthias Ringwald
728e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
729b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2
730b3fcedb9SMatthias Ringwald
731b3fcedb9SMatthias Ringwald    write_indent(fout)
732b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
733b3fcedb9SMatthias Ringwald    write_indent(fout)
734b3fcedb9SMatthias Ringwald    write_16(fout, size)
735e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
736b3fcedb9SMatthias Ringwald    write_16(fout, handle)
737b3fcedb9SMatthias Ringwald    write_16(fout, 0x2905)
738b3fcedb9SMatthias Ringwald    for identifier in parts[1:]:
739b3fcedb9SMatthias Ringwald        format_handle = presentation_formats[identifier]
740b3fcedb9SMatthias Ringwald        if format == 0:
741b3fcedb9SMatthias Ringwald            print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier)
742b3fcedb9SMatthias Ringwald            sys.exit(1)
743b3fcedb9SMatthias Ringwald        write_16(fout, format_handle)
744b3fcedb9SMatthias Ringwald    fout.write("\n")
745043f8832SMatthias Ringwald
746043f8832SMatthias Ringwald    database_hash_append_uint16(handle)
747043f8832SMatthias Ringwald    database_hash_append_uint16(0x2905)
748043f8832SMatthias Ringwald
749b3fcedb9SMatthias Ringwald    handle = handle + 1
750b3fcedb9SMatthias Ringwald
751b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts):
752b3fcedb9SMatthias Ringwald    global handle
753b3fcedb9SMatthias Ringwald    global total_size
754b3fcedb9SMatthias Ringwald
755e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
756b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1 + 1
757b3fcedb9SMatthias Ringwald
758231a3c5dSMatthias Ringwald    report_id = parts[2]
759231a3c5dSMatthias Ringwald    report_type = parts[3]
760b3fcedb9SMatthias Ringwald
761b3fcedb9SMatthias Ringwald    write_indent(fout)
762b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
763b3fcedb9SMatthias Ringwald    write_indent(fout)
764b3fcedb9SMatthias Ringwald    write_16(fout, size)
765e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
766b3fcedb9SMatthias Ringwald    write_16(fout, handle)
767b3fcedb9SMatthias Ringwald    write_16(fout, 0x2908)
768b3fcedb9SMatthias Ringwald    write_sequence(fout, report_id)
769b3fcedb9SMatthias Ringwald    write_sequence(fout, report_type)
770b3fcedb9SMatthias Ringwald    fout.write("\n")
771b3fcedb9SMatthias Ringwald    handle = handle + 1
772b3fcedb9SMatthias Ringwald
773b3fcedb9SMatthias Ringwald
774b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts):
775b3fcedb9SMatthias Ringwald    global handle
776b3fcedb9SMatthias Ringwald    global total_size
777b3fcedb9SMatthias Ringwald
778e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
779b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1
780b3fcedb9SMatthias Ringwald
781b3fcedb9SMatthias Ringwald    no_of_digitals = parts[1]
782b3fcedb9SMatthias Ringwald
783b3fcedb9SMatthias Ringwald    write_indent(fout)
784b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:])))
785b3fcedb9SMatthias Ringwald    write_indent(fout)
786b3fcedb9SMatthias Ringwald    write_16(fout, size)
787e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
788b3fcedb9SMatthias Ringwald    write_16(fout, handle)
789b3fcedb9SMatthias Ringwald    write_16(fout, 0x2909)
790b3fcedb9SMatthias Ringwald    write_sequence(fout, no_of_digitals)
791b3fcedb9SMatthias Ringwald    fout.write("\n")
792b3fcedb9SMatthias Ringwald    handle = handle + 1
793b3fcedb9SMatthias Ringwald
79460b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout):
795b3fcedb9SMatthias Ringwald    global handle
796b3fcedb9SMatthias Ringwald    global total_size
797b3fcedb9SMatthias Ringwald
798b165f97bSMatthias Ringwald    line_count = 0;
799b3fcedb9SMatthias Ringwald    for line in fin:
800b3fcedb9SMatthias Ringwald        line = line.strip("\n\r ")
801b165f97bSMatthias Ringwald        line_count += 1
802b3fcedb9SMatthias Ringwald
803b165f97bSMatthias Ringwald        if line.startswith("//"):
804b165f97bSMatthias Ringwald            fout.write("    //" + line.lstrip('/') + '\n')
805b165f97bSMatthias Ringwald            continue
806b165f97bSMatthias Ringwald
80760b51a4cSMatthias Ringwald        if line.startswith("#import"):
80860b51a4cSMatthias Ringwald            imported_file = ''
80960b51a4cSMatthias Ringwald            parts = re.match('#import\s+<(.*)>\w*',line)
81060b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
811dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
81260b51a4cSMatthias Ringwald            parts = re.match('#import\s+"(.*)"\w*',line)
81360b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
814dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
81560b51a4cSMatthias Ringwald            if len(imported_file) == 0:
81660b51a4cSMatthias Ringwald                print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count))
81760b51a4cSMatthias Ringwald                continue
81860b51a4cSMatthias Ringwald
819dbb3997aSMilanka Ringwald            imported_file = getFile( imported_file )
82060b51a4cSMatthias Ringwald            print("Importing %s" % imported_file)
82160b51a4cSMatthias Ringwald            try:
82260b51a4cSMatthias Ringwald                imported_fin = codecs.open (imported_file, encoding='utf-8')
82360b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- BEGIN\n')
82460b51a4cSMatthias Ringwald                parseLines(imported_file, imported_fin, fout)
82560b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- END\n')
82660b51a4cSMatthias Ringwald            except IOError as e:
82760b51a4cSMatthias Ringwald                print('ERROR: Import failed. Please check path.')
82860b51a4cSMatthias Ringwald
82960b51a4cSMatthias Ringwald            continue
83060b51a4cSMatthias Ringwald
83160b51a4cSMatthias Ringwald        if line.startswith("#TODO"):
83260b51a4cSMatthias Ringwald            print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count))
833b165f97bSMatthias Ringwald            print ("'%s'" % line)
834b165f97bSMatthias Ringwald            fout.write("// " + line + '\n')
835b3fcedb9SMatthias Ringwald            continue
836b3fcedb9SMatthias Ringwald
837b3fcedb9SMatthias Ringwald        if len(line) == 0:
838b3fcedb9SMatthias Ringwald            continue
839b3fcedb9SMatthias Ringwald
840b3fcedb9SMatthias Ringwald        f = io.StringIO(line)
841b3fcedb9SMatthias Ringwald        parts_list = csv.reader(f, delimiter=',', quotechar='"')
842b3fcedb9SMatthias Ringwald
843b3fcedb9SMatthias Ringwald        for parts in parts_list:
844b3fcedb9SMatthias Ringwald            for index, object in enumerate(parts):
845b3fcedb9SMatthias Ringwald                parts[index] = object.strip().lstrip('"').rstrip('"')
846b3fcedb9SMatthias Ringwald
847b3fcedb9SMatthias Ringwald            if parts[0] == 'PRIMARY_SERVICE':
848b3fcedb9SMatthias Ringwald                parsePrimaryService(fout, parts)
849b3fcedb9SMatthias Ringwald                continue
850b3fcedb9SMatthias Ringwald
851b3fcedb9SMatthias Ringwald            if parts[0] == 'SECONDARY_SERVICE':
852b3fcedb9SMatthias Ringwald                parseSecondaryService(fout, parts)
853b3fcedb9SMatthias Ringwald                continue
854b3fcedb9SMatthias Ringwald
855b3fcedb9SMatthias Ringwald            if parts[0] == 'INCLUDE_SERVICE':
856b3fcedb9SMatthias Ringwald                parseIncludeService(fout, parts)
857b3fcedb9SMatthias Ringwald                continue
858b3fcedb9SMatthias Ringwald
859b3fcedb9SMatthias Ringwald            # 2803
860b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC':
861b3fcedb9SMatthias Ringwald                parseCharacteristic(fout, parts)
862b3fcedb9SMatthias Ringwald                continue
863b3fcedb9SMatthias Ringwald
864b3fcedb9SMatthias Ringwald            # 2900 Characteristic Extended Properties
865b3fcedb9SMatthias Ringwald
866b3fcedb9SMatthias Ringwald            # 2901
867b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
868b3fcedb9SMatthias Ringwald                parseCharacteristicUserDescription(fout, parts)
869b3fcedb9SMatthias Ringwald                continue
870b3fcedb9SMatthias Ringwald
871b165f97bSMatthias Ringwald
872b165f97bSMatthias Ringwald            # 2902 Client Characteristic Configuration - automatically included in Characteristic if
873b3fcedb9SMatthias Ringwald            # notification / indication is supported
874231a3c5dSMatthias Ringwald            if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION':
875b165f97bSMatthias Ringwald                continue
876b3fcedb9SMatthias Ringwald
877b3fcedb9SMatthias Ringwald            # 2903
878b3fcedb9SMatthias Ringwald            if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
879b3fcedb9SMatthias Ringwald                parseServerCharacteristicConfiguration(fout, parts)
880b3fcedb9SMatthias Ringwald                continue
881b3fcedb9SMatthias Ringwald
882b3fcedb9SMatthias Ringwald            # 2904
883b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_FORMAT':
884b3fcedb9SMatthias Ringwald                parseCharacteristicFormat(fout, parts)
885b3fcedb9SMatthias Ringwald                continue
886b3fcedb9SMatthias Ringwald
887b3fcedb9SMatthias Ringwald            # 2905
888b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
889b3fcedb9SMatthias Ringwald                parseCharacteristicAggregateFormat(fout, parts)
890b3fcedb9SMatthias Ringwald                continue
891b3fcedb9SMatthias Ringwald
892b3fcedb9SMatthias Ringwald            # 2906
893b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
894b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
895b3fcedb9SMatthias Ringwald                continue
896b3fcedb9SMatthias Ringwald
897b3fcedb9SMatthias Ringwald            # 2907
898b3fcedb9SMatthias Ringwald            if parts[0] == 'EXTERNAL_REPORT_REFERENCE':
899b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
900b3fcedb9SMatthias Ringwald                continue
901b3fcedb9SMatthias Ringwald
902b3fcedb9SMatthias Ringwald            # 2908
903b3fcedb9SMatthias Ringwald            if parts[0] == 'REPORT_REFERENCE':
904b3fcedb9SMatthias Ringwald                parseReportReference(fout, parts)
905b3fcedb9SMatthias Ringwald                continue
906b3fcedb9SMatthias Ringwald
907b3fcedb9SMatthias Ringwald            # 2909
908b3fcedb9SMatthias Ringwald            if parts[0] == 'NUMBER_OF_DIGITALS':
909b3fcedb9SMatthias Ringwald                parseNumberOfDigitals(fout, parts)
910b3fcedb9SMatthias Ringwald                continue
911b3fcedb9SMatthias Ringwald
912b3fcedb9SMatthias Ringwald            # 290A
913b3fcedb9SMatthias Ringwald            if parts[0] == 'VALUE_TRIGGER_SETTING':
914b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
915b3fcedb9SMatthias Ringwald                continue
916b3fcedb9SMatthias Ringwald
917b3fcedb9SMatthias Ringwald            # 290B
918b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION':
919b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
920b3fcedb9SMatthias Ringwald                continue
921b3fcedb9SMatthias Ringwald
922b3fcedb9SMatthias Ringwald            # 290C
923b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT':
924b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
925b3fcedb9SMatthias Ringwald                continue
926b3fcedb9SMatthias Ringwald
927b3fcedb9SMatthias Ringwald            # 290D
928b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING':
929b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
930b3fcedb9SMatthias Ringwald                continue
931b3fcedb9SMatthias Ringwald
932b3fcedb9SMatthias Ringwald            # 2906
933b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
934b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
935b3fcedb9SMatthias Ringwald                continue
936b3fcedb9SMatthias Ringwald
937b3fcedb9SMatthias Ringwald            print("WARNING: unknown token: %s\n" % (parts[0]))
938b3fcedb9SMatthias Ringwald
9397050bf34SMatthias Ringwalddef parse(fname_in, fin, fname_out, tool_path, fout):
94060b51a4cSMatthias Ringwald    global handle
94160b51a4cSMatthias Ringwald    global total_size
94260b51a4cSMatthias Ringwald
9437050bf34SMatthias Ringwald    fout.write(header.format(fname_out, fname_in, tool_path))
94460b51a4cSMatthias Ringwald    fout.write('{\n')
945fd1be25dSMatthias Ringwald    write_indent(fout)
946fd1be25dSMatthias Ringwald    fout.write('// ATT DB Version\n')
947fd1be25dSMatthias Ringwald    write_indent(fout)
948fd1be25dSMatthias Ringwald    fout.write('1,\n')
949fd1be25dSMatthias Ringwald    fout.write("\n")
95060b51a4cSMatthias Ringwald
95160b51a4cSMatthias Ringwald    parseLines(fname_in, fin, fout)
95260b51a4cSMatthias Ringwald
953729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
954b3fcedb9SMatthias Ringwald    write_indent(fout)
955b3fcedb9SMatthias Ringwald    fout.write("// END\n");
956b3fcedb9SMatthias Ringwald    write_indent(fout)
957b3fcedb9SMatthias Ringwald    write_16(fout,0)
958b3fcedb9SMatthias Ringwald    fout.write("\n")
959b3fcedb9SMatthias Ringwald    total_size = total_size + 2
960b3fcedb9SMatthias Ringwald
961b3fcedb9SMatthias Ringwald    fout.write("}; // total size %u bytes \n" % total_size);
962b3fcedb9SMatthias Ringwald
963b3fcedb9SMatthias Ringwalddef listHandles(fout):
964b3fcedb9SMatthias Ringwald    fout.write('\n\n')
965b3fcedb9SMatthias Ringwald    fout.write('//\n')
966729074c4SMatthias Ringwald    fout.write('// list service handle ranges\n')
967729074c4SMatthias Ringwald    fout.write('//\n')
968729074c4SMatthias Ringwald    for define in defines_for_services:
969729074c4SMatthias Ringwald        fout.write(define)
970729074c4SMatthias Ringwald        fout.write('\n')
971729074c4SMatthias Ringwald    fout.write('\n')
972729074c4SMatthias Ringwald    fout.write('//\n')
973b3fcedb9SMatthias Ringwald    fout.write('// list mapping between characteristics and handles\n')
974b3fcedb9SMatthias Ringwald    fout.write('//\n')
975729074c4SMatthias Ringwald    for define in defines_for_characteristics:
976b3fcedb9SMatthias Ringwald        fout.write(define)
977b3fcedb9SMatthias Ringwald        fout.write('\n')
978b3fcedb9SMatthias Ringwald
979dbb3997aSMilanka Ringwalddef getFile( fileName ):
98078b65b0aSMatthias Ringwald    for d in include_paths:
98178b65b0aSMatthias Ringwald        fullFile = os.path.normpath(d + os.sep + fileName) # because Windows exists
98278b65b0aSMatthias Ringwald        # print("test %s" % fullFile)
983dbb3997aSMilanka Ringwald        if os.path.isfile( fullFile ) == True:
984dbb3997aSMilanka Ringwald            return fullFile
985dbb3997aSMilanka Ringwald    print ("'{0}' not found".format( fileName ))
98678b65b0aSMatthias Ringwald    print ("Include paths: %s" % ", ".join(include_paths))
987dbb3997aSMilanka Ringwald    exit(-1)
988dbb3997aSMilanka Ringwald
989dbb3997aSMilanka Ringwald
990dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
99178b65b0aSMatthias Ringwalddefault_includes = [os.path.normpath(path) for path in [ btstack_root + '/src/', btstack_root + '/src/ble/gatt-service/']]
992dbb3997aSMilanka Ringwald
993dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack')
994dbb3997aSMilanka Ringwald
995dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes',
996dbb3997aSMilanka Ringwald        help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes))
997dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str,
998dbb3997aSMilanka Ringwald        help='gatt file to be compiled')
999dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str,
1000dbb3997aSMilanka Ringwald        help='header file to be generated')
1001dbb3997aSMilanka Ringwald
1002dbb3997aSMilanka Ringwaldargs = parser.parse_args()
1003dbb3997aSMilanka Ringwald
100478b65b0aSMatthias Ringwald# add include path arguments
100578b65b0aSMatthias Ringwaldif args.I != None:
100678b65b0aSMatthias Ringwald    for d in args.I:
100778b65b0aSMatthias Ringwald        include_paths.append(os.path.normpath(d[0]))
100878b65b0aSMatthias Ringwald
1009dbb3997aSMilanka Ringwald# append default include paths
101078b65b0aSMatthias Ringwaldinclude_paths.extend(default_includes)
1011dbb3997aSMilanka Ringwald
1012b3fcedb9SMatthias Ringwaldtry:
1013b165f97bSMatthias Ringwald    # read defines from bluetooth_gatt.h
1014dbb3997aSMilanka Ringwald    gen_path = getFile( 'bluetooth_gatt.h' )
1015b165f97bSMatthias Ringwald    bluetooth_gatt = read_defines(gen_path)
1016b165f97bSMatthias Ringwald
1017dbb3997aSMilanka Ringwald    filename = args.hfile
1018dbb3997aSMilanka Ringwald    fin  = codecs.open (args.gattfile, encoding='utf-8')
1019285653b2SMatthias Ringwald
1020285653b2SMatthias Ringwald    # pass 1: create temp .h file
1021d78181d6SMatthias Ringwald    ftemp = tempfile.TemporaryFile(mode='w+t')
1022285653b2SMatthias Ringwald    parse(args.gattfile, fin, filename, sys.argv[0], ftemp)
1023285653b2SMatthias Ringwald    listHandles(ftemp)
1024285653b2SMatthias Ringwald
1025043f8832SMatthias Ringwald    # calc GATT Database Hash
1026043f8832SMatthias Ringwald    db_hash = aes_cmac(bytearray(16), database_hash_message)
1027043f8832SMatthias Ringwald    if isinstance(db_hash, str):
1028043f8832SMatthias Ringwald        # python2
1029043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % ord(i)) for i in db_hash]
1030043f8832SMatthias Ringwald    elif isinstance(db_hash, bytes):
1031043f8832SMatthias Ringwald        # python3
1032043f8832SMatthias Ringwald        db_hash_sequence = [('0x%02x' % i) for i in db_hash]
1033043f8832SMatthias Ringwald    else:
1034043f8832SMatthias Ringwald        print("AES CMAC returns unexpected type %s, abort" % type(db_hash))
1035043f8832SMatthias Ringwald        sys.exit(1)
1036043f8832SMatthias Ringwald    # reverse hash to get little endian
1037043f8832SMatthias Ringwald    db_hash_sequence.reverse()
1038043f8832SMatthias Ringwald    db_hash_string = ', '.join(db_hash_sequence) + ', '
1039043f8832SMatthias Ringwald
1040285653b2SMatthias Ringwald    # pass 2: insert GATT Database Hash
1041b3fcedb9SMatthias Ringwald    fout = open (filename, 'w')
1042285653b2SMatthias Ringwald    ftemp.seek(0)
1043285653b2SMatthias Ringwald    for line in ftemp:
1044043f8832SMatthias Ringwald        fout.write(line.replace('THE-DATABASE-HASH', db_hash_string))
1045b3fcedb9SMatthias Ringwald    fout.close()
1046285653b2SMatthias Ringwald    ftemp.close()
1047285653b2SMatthias Ringwald
1048b165f97bSMatthias Ringwald    print('Created %s' % filename)
1049b3fcedb9SMatthias Ringwald
1050b3fcedb9SMatthias Ringwaldexcept IOError as e:
1051e22a2612SMatthias Ringwald
1052b3fcedb9SMatthias Ringwald    print(usage)
1053b3fcedb9SMatthias Ringwald    sys.exit(1)
1054b3fcedb9SMatthias Ringwald
1055b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n')
1056