xref: /btstack/tool/compile_gatt.py (revision 7050bf34e55122559e021a9e38e3f78511171543)
1b3fcedb9SMatthias Ringwald#!/usr/bin/env python
2b3fcedb9SMatthias Ringwald#
3dbb3997aSMilanka Ringwald# BLE GATT configuration generator for use with BTstack
4dbb3997aSMilanka Ringwald# Copyright 2018 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
10b3fcedb9SMatthias Ringwaldimport codecs
11b165f97bSMatthias Ringwaldimport csv
12b165f97bSMatthias Ringwaldimport io
13b165f97bSMatthias Ringwaldimport os
14b165f97bSMatthias Ringwaldimport re
15b165f97bSMatthias Ringwaldimport string
1660b51a4cSMatthias Ringwaldimport sys
17dbb3997aSMilanka Ringwaldimport argparse
18b3fcedb9SMatthias Ringwald
19b3fcedb9SMatthias Ringwaldheader = '''
20b3fcedb9SMatthias Ringwald// {0} generated from {1} for BTstack
21*7050bf34SMatthias Ringwald// it needs to be regenerated when the .gatt file is updated.
22*7050bf34SMatthias Ringwald
23*7050bf34SMatthias Ringwald// To generate {0}:
24*7050bf34SMatthias Ringwald// {2} {1} {0}
25*7050bf34SMatthias Ringwald
26fd1be25dSMatthias Ringwald// att db format version 1
27b3fcedb9SMatthias Ringwald
28fd1be25dSMatthias Ringwald// binary attribute representation:
29fd1be25dSMatthias Ringwald// - size in bytes (16), flags(16), handle (16), uuid (16/128), value(...)
30b3fcedb9SMatthias Ringwald
31b3fcedb9SMatthias Ringwald#include <stdint.h>
32b3fcedb9SMatthias Ringwald
33b3fcedb9SMatthias Ringwaldconst uint8_t profile_data[] =
34b3fcedb9SMatthias Ringwald'''
35b3fcedb9SMatthias Ringwald
36b3fcedb9SMatthias Ringwaldprint('''
37dbb3997aSMilanka RingwaldBLE configuration generator for use with BTstack
38dbb3997aSMilanka RingwaldCopyright 2018 BlueKitchen GmbH
39b3fcedb9SMatthias Ringwald''')
40b3fcedb9SMatthias Ringwald
41b3fcedb9SMatthias Ringwaldassigned_uuids = {
42b3fcedb9SMatthias Ringwald    'GAP_SERVICE'          : 0x1800,
43b3fcedb9SMatthias Ringwald    'GATT_SERVICE'         : 0x1801,
44b3fcedb9SMatthias Ringwald    'GAP_DEVICE_NAME'      : 0x2a00,
45b3fcedb9SMatthias Ringwald    'GAP_APPEARANCE'       : 0x2a01,
46b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02,
47b3fcedb9SMatthias Ringwald    'GAP_RECONNECTION_ADDRESS'    : 0x2A03,
48b3fcedb9SMatthias Ringwald    'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04,
49b3fcedb9SMatthias Ringwald    'GATT_SERVICE_CHANGED' : 0x2a05,
50b3fcedb9SMatthias Ringwald}
51b3fcedb9SMatthias Ringwald
52e72176f8SMatthias Ringwaldsecurity_permsission = ['ANYBODY','ENCRYPTED', 'AUTHENTICATED', 'AUTHORIZED', 'AUTHENTICATED_SC']
53d7ec1d24SMatthias Ringwald
54b3fcedb9SMatthias Ringwaldproperty_flags = {
55eb6072adSMatthias Ringwald    # GATT Characteristic Properties
56b3fcedb9SMatthias Ringwald    'BROADCAST' :                   0x01,
57b3fcedb9SMatthias Ringwald    'READ' :                        0x02,
58b3fcedb9SMatthias Ringwald    'WRITE_WITHOUT_RESPONSE' :      0x04,
59b3fcedb9SMatthias Ringwald    'WRITE' :                       0x08,
60b3fcedb9SMatthias Ringwald    'NOTIFY':                       0x10,
61b3fcedb9SMatthias Ringwald    'INDICATE' :                    0x20,
62b3fcedb9SMatthias Ringwald    'AUTHENTICATED_SIGNED_WRITE' :  0x40,
63b3fcedb9SMatthias Ringwald    'EXTENDED_PROPERTIES' :         0x80,
64b3fcedb9SMatthias Ringwald    # custom BTstack extension
65b3fcedb9SMatthias Ringwald    'DYNAMIC':                      0x100,
66b3fcedb9SMatthias Ringwald    'LONG_UUID':                    0x200,
67e22a2612SMatthias Ringwald
68e22a2612SMatthias Ringwald    # read permissions
69e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_0':        0x400,
70e22a2612SMatthias Ringwald    'READ_PERMISSION_BIT_1':        0x800,
71e22a2612SMatthias Ringwald
72e22a2612SMatthias Ringwald    #
73b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_7':       0x6000,
74b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_8':       0x7000,
75b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_9':       0x8000,
76b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_10':      0x9000,
77b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_11':      0xa000,
78b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_12':      0xb000,
79b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_13':      0xc000,
80b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_14':      0xd000,
81b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_15':      0xe000,
82b3fcedb9SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_16':      0xf000,
83e22a2612SMatthias Ringwald    'ENCRYPTION_KEY_SIZE_MASK':    0xf000,
84eb6072adSMatthias Ringwald
85b3fcedb9SMatthias Ringwald    # only used by gatt compiler >= 0xffff
86b3fcedb9SMatthias Ringwald    # Extended Properties
87e72176f8SMatthias Ringwald    'RELIABLE_WRITE':              0x00010000,
88e72176f8SMatthias Ringwald    'AUTHENTICATION_REQUIRED':     0x00020000,
89e72176f8SMatthias Ringwald    'AUTHORIZATION_REQUIRED':      0x00040000,
90e72176f8SMatthias Ringwald    'READ_ANYBODY':                0x00080000,
91e72176f8SMatthias Ringwald    'READ_ENCRYPTED':              0x00100000,
92e72176f8SMatthias Ringwald    'READ_AUTHENTICATED':          0x00200000,
93e72176f8SMatthias Ringwald    'READ_AUTHENTICATED_SC':       0x00400000,
94e72176f8SMatthias Ringwald    'READ_AUTHORIZED':             0x00800000,
95e72176f8SMatthias Ringwald    'WRITE_ANYBODY':               0x01000000,
96e72176f8SMatthias Ringwald    'WRITE_ENCRYPTED':             0x02000000,
97e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED':         0x04000000,
98e72176f8SMatthias Ringwald    'WRITE_AUTHENTICATED_SC':      0x08000000,
99e72176f8SMatthias Ringwald    'WRITE_AUTHORIZED':            0x10000000,
100eb6072adSMatthias Ringwald
101eb6072adSMatthias Ringwald    # Broadcast, Notify, Indicate, Extended Properties are only used to describe a GATT Characteristic, but are free to use with att_db
102e72176f8SMatthias Ringwald    # - write permissions
103e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_0':      0x01,
104e22a2612SMatthias Ringwald    'WRITE_PERMISSION_BIT_1':      0x10,
105e72176f8SMatthias Ringwald    # - SC required
106e72176f8SMatthias Ringwald    'READ_PERMISSION_SC':          0x20,
107e72176f8SMatthias Ringwald    'WRITE_PERMISSION_SC':         0x80,
108b3fcedb9SMatthias Ringwald}
109b3fcedb9SMatthias Ringwald
110b3fcedb9SMatthias Ringwaldservices = dict()
111b3fcedb9SMatthias Ringwaldcharacteristic_indices = dict()
112b3fcedb9SMatthias Ringwaldpresentation_formats = dict()
113b3fcedb9SMatthias Ringwaldcurrent_service_uuid_string = ""
114b3fcedb9SMatthias Ringwaldcurrent_service_start_handle = 0
115b3fcedb9SMatthias Ringwaldcurrent_characteristic_uuid_string = ""
116729074c4SMatthias Ringwalddefines_for_characteristics = []
117729074c4SMatthias Ringwalddefines_for_services = []
118b3fcedb9SMatthias Ringwald
119b3fcedb9SMatthias Ringwaldhandle = 1
120b3fcedb9SMatthias Ringwaldtotal_size = 0
121b3fcedb9SMatthias Ringwald
122b165f97bSMatthias Ringwalddef read_defines(infile):
123b165f97bSMatthias Ringwald    defines = dict()
124b165f97bSMatthias Ringwald    with open (infile, 'rt') as fin:
125b165f97bSMatthias Ringwald        for line in fin:
126b165f97bSMatthias Ringwald            parts = re.match('#define\s+(\w+)\s+(\w+)',line)
127b165f97bSMatthias Ringwald            if parts and len(parts.groups()) == 2:
128b165f97bSMatthias Ringwald                (key, value) = parts.groups()
129b165f97bSMatthias Ringwald                defines[key] = int(value, 16)
130b165f97bSMatthias Ringwald    return defines
131b165f97bSMatthias Ringwald
132b3fcedb9SMatthias Ringwalddef keyForUUID(uuid):
133b3fcedb9SMatthias Ringwald    keyUUID = ""
134b3fcedb9SMatthias Ringwald    for i in uuid:
135b3fcedb9SMatthias Ringwald        keyUUID += "%02x" % i
136b3fcedb9SMatthias Ringwald    return keyUUID
137b3fcedb9SMatthias Ringwald
138b3fcedb9SMatthias Ringwalddef c_string_for_uuid(uuid):
139b3fcedb9SMatthias Ringwald    return uuid.replace('-', '_')
140b3fcedb9SMatthias Ringwald
141b3fcedb9SMatthias Ringwalddef twoByteLEFor(value):
142b3fcedb9SMatthias Ringwald    return [ (value & 0xff), (value >> 8)]
143b3fcedb9SMatthias Ringwald
144b3fcedb9SMatthias Ringwalddef is_128bit_uuid(text):
145b3fcedb9SMatthias 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):
146b3fcedb9SMatthias Ringwald        return True
147b3fcedb9SMatthias Ringwald    return False
148b3fcedb9SMatthias Ringwald
149b3fcedb9SMatthias Ringwalddef parseUUID128(uuid):
150b3fcedb9SMatthias 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)
151b3fcedb9SMatthias Ringwald    uuid_bytes = []
152b3fcedb9SMatthias Ringwald    for i in range(8, 0, -1):
153b3fcedb9SMatthias Ringwald        uuid_bytes = uuid_bytes + twoByteLEFor(int(parts.group(i),16))
154b3fcedb9SMatthias Ringwald    return uuid_bytes
155b3fcedb9SMatthias Ringwald
156b3fcedb9SMatthias Ringwalddef parseUUID(uuid):
157b3fcedb9SMatthias Ringwald    if uuid in assigned_uuids:
158b3fcedb9SMatthias Ringwald        return twoByteLEFor(assigned_uuids[uuid])
159b165f97bSMatthias Ringwald    uuid_upper = uuid.upper().replace('.','_')
160b165f97bSMatthias Ringwald    if uuid_upper in bluetooth_gatt:
161b165f97bSMatthias Ringwald        return twoByteLEFor(bluetooth_gatt[uuid_upper])
162b3fcedb9SMatthias Ringwald    if is_128bit_uuid(uuid):
163b3fcedb9SMatthias Ringwald        return parseUUID128(uuid)
164b3fcedb9SMatthias Ringwald    uuidInt = int(uuid, 16)
165b3fcedb9SMatthias Ringwald    return twoByteLEFor(uuidInt)
166b3fcedb9SMatthias Ringwald
167b3fcedb9SMatthias Ringwalddef parseProperties(properties):
168b3fcedb9SMatthias Ringwald    value = 0
169b3fcedb9SMatthias Ringwald    parts = properties.split("|")
170b3fcedb9SMatthias Ringwald    for property in parts:
171b3fcedb9SMatthias Ringwald        property = property.strip()
172b3fcedb9SMatthias Ringwald        if property in property_flags:
173b3fcedb9SMatthias Ringwald            value |= property_flags[property]
174b3fcedb9SMatthias Ringwald        else:
175b3fcedb9SMatthias Ringwald            print("WARNING: property %s undefined" % (property))
176e22a2612SMatthias Ringwald
177e22a2612SMatthias Ringwald    return value;
178e22a2612SMatthias Ringwald
179e22a2612SMatthias Ringwalddef gatt_characteristic_properties(properties):
180e22a2612SMatthias Ringwald    return properties & 0xff
181e22a2612SMatthias Ringwald
182e22a2612SMatthias Ringwalddef att_flags(properties):
183e72176f8SMatthias Ringwald    # drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Properties (0x80) - not used for flags
184e72176f8SMatthias Ringwald    properties &= 0xffffff4e
185e22a2612SMatthias Ringwald
186e22a2612SMatthias Ringwald    # rw permissions distinct
187e22a2612SMatthias Ringwald    distinct_permissions_used = properties & (
188e22a2612SMatthias Ringwald        property_flags['READ_AUTHORIZED'] |
189e72176f8SMatthias Ringwald        property_flags['READ_AUTHENTICATED_SC'] |
190e22a2612SMatthias Ringwald        property_flags['READ_AUTHENTICATED'] |
191e22a2612SMatthias Ringwald        property_flags['READ_ENCRYPTED'] |
192e22a2612SMatthias Ringwald        property_flags['READ_ANYBODY'] |
193e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHORIZED'] |
194e22a2612SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED'] |
195e72176f8SMatthias Ringwald        property_flags['WRITE_AUTHENTICATED_SC'] |
196e22a2612SMatthias Ringwald        property_flags['WRITE_ENCRYPTED'] |
197e22a2612SMatthias Ringwald        property_flags['WRITE_ANYBODY']
198e22a2612SMatthias Ringwald    ) != 0
199e22a2612SMatthias Ringwald
200e22a2612SMatthias Ringwald    # post process properties
201e22a2612SMatthias Ringwald    encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0
202e22a2612SMatthias Ringwald
203d7ec1d24SMatthias Ringwald    # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted
204e22a2612SMatthias Ringwald    if encryption_key_size_specified and not distinct_permissions_used:
205e22a2612SMatthias Ringwald        properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED']
206e22a2612SMatthias Ringwald
207d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated
208d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used:
209d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED']
210d7ec1d24SMatthias Ringwald
211d7ec1d24SMatthias Ringwald    # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized
212d7ec1d24SMatthias Ringwald    if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used:
213d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED']
214d7ec1d24SMatthias Ringwald
215d7ec1d24SMatthias Ringwald    # determine read/write security requirements
216d7ec1d24SMatthias Ringwald    read_security_level  = 0
217d7ec1d24SMatthias Ringwald    write_security_level = 0
218e72176f8SMatthias Ringwald    read_requires_sc     = False
219e72176f8SMatthias Ringwald    write_requires_sc    = False
220e22a2612SMatthias Ringwald    if properties & property_flags['READ_AUTHORIZED']:
221d7ec1d24SMatthias Ringwald        read_security_level = 3
222e22a2612SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED']:
223d7ec1d24SMatthias Ringwald        read_security_level = 2
224e72176f8SMatthias Ringwald    elif properties & property_flags['READ_AUTHENTICATED_SC']:
225e72176f8SMatthias Ringwald        read_security_level = 2
226e72176f8SMatthias Ringwald        read_requires_sc = True
227e22a2612SMatthias Ringwald    elif properties & property_flags['READ_ENCRYPTED']:
228d7ec1d24SMatthias Ringwald        read_security_level = 1
229e22a2612SMatthias Ringwald    if properties & property_flags['WRITE_AUTHORIZED']:
230d7ec1d24SMatthias Ringwald        write_security_level = 3
231e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED']:
232d7ec1d24SMatthias Ringwald        write_security_level = 2
233e72176f8SMatthias Ringwald    elif properties & property_flags['WRITE_AUTHENTICATED_SC']:
234e72176f8SMatthias Ringwald        write_security_level = 2
235e72176f8SMatthias Ringwald        write_requires_sc = True
236e22a2612SMatthias Ringwald    elif properties & property_flags['WRITE_ENCRYPTED']:
237d7ec1d24SMatthias Ringwald        write_security_level = 1
238d7ec1d24SMatthias Ringwald
239d7ec1d24SMatthias Ringwald    # map security requirements to flags
240d7ec1d24SMatthias Ringwald    if read_security_level & 2:
241d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_1']
242d7ec1d24SMatthias Ringwald    if read_security_level & 1:
243d7ec1d24SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_BIT_0']
244e72176f8SMatthias Ringwald    if read_requires_sc:
245e72176f8SMatthias Ringwald        properties |= property_flags['READ_PERMISSION_SC']
246d7ec1d24SMatthias Ringwald    if write_security_level & 2:
247d7ec1d24SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_1']
248d7ec1d24SMatthias Ringwald    if write_security_level & 1:
249e22a2612SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_BIT_0']
250e72176f8SMatthias Ringwald    if write_requires_sc:
251e72176f8SMatthias Ringwald        properties |= property_flags['WRITE_PERMISSION_SC']
252e22a2612SMatthias Ringwald
253e22a2612SMatthias Ringwald    return properties
254e22a2612SMatthias Ringwald
255d7ec1d24SMatthias Ringwalddef write_permissions_and_key_size_flags_from_properties(properties):
256e22a2612SMatthias Ringwald    return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1'])
257b3fcedb9SMatthias Ringwald
258b3fcedb9SMatthias Ringwalddef write_8(fout, value):
259b3fcedb9SMatthias Ringwald    fout.write( "0x%02x, " % (value & 0xff))
260b3fcedb9SMatthias Ringwald
261b3fcedb9SMatthias Ringwalddef write_16(fout, value):
262b3fcedb9SMatthias Ringwald    fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff))
263b3fcedb9SMatthias Ringwald
264b3fcedb9SMatthias Ringwalddef write_uuid(uuid):
265b3fcedb9SMatthias Ringwald    for byte in uuid:
266b3fcedb9SMatthias Ringwald        fout.write( "0x%02x, " % byte)
267b3fcedb9SMatthias Ringwald
268b3fcedb9SMatthias Ringwalddef write_string(fout, text):
269b3fcedb9SMatthias Ringwald    for l in text.lstrip('"').rstrip('"'):
270b3fcedb9SMatthias Ringwald        write_8(fout, ord(l))
271b3fcedb9SMatthias Ringwald
272b3fcedb9SMatthias Ringwalddef write_sequence(fout, text):
273b3fcedb9SMatthias Ringwald    parts = text.split()
274b3fcedb9SMatthias Ringwald    for part in parts:
275b3fcedb9SMatthias Ringwald        fout.write("0x%s, " % (part.strip()))
276b3fcedb9SMatthias Ringwald
277b3fcedb9SMatthias Ringwalddef write_indent(fout):
278b3fcedb9SMatthias Ringwald    fout.write("    ")
279b3fcedb9SMatthias Ringwald
280d7ec1d24SMatthias Ringwalddef read_permissions_from_flags(flags):
281d7ec1d24SMatthias Ringwald    permissions = 0
282d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_0']:
283d7ec1d24SMatthias Ringwald        permissions |= 1
284d7ec1d24SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_BIT_1']:
285d7ec1d24SMatthias Ringwald        permissions |= 2
286e72176f8SMatthias Ringwald    if flags & property_flags['READ_PERMISSION_SC'] and permissions == 2:
287e72176f8SMatthias Ringwald        permissions = 4
288d7ec1d24SMatthias Ringwald    return permissions
289d7ec1d24SMatthias Ringwald
290d7ec1d24SMatthias Ringwalddef write_permissions_from_flags(flags):
291d7ec1d24SMatthias Ringwald    permissions = 0
292d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_0']:
293d7ec1d24SMatthias Ringwald        permissions |= 1
294d7ec1d24SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_BIT_1']:
295d7ec1d24SMatthias Ringwald        permissions |= 2
296e72176f8SMatthias Ringwald    if flags & property_flags['WRITE_PERMISSION_SC'] and permissions == 2:
297e72176f8SMatthias Ringwald        permissions = 4
298d7ec1d24SMatthias Ringwald    return permissions
299d7ec1d24SMatthias Ringwald
300d7ec1d24SMatthias Ringwalddef encryption_key_size_from_flags(flags):
301d7ec1d24SMatthias Ringwald    encryption_key_size = (flags & 0xf000) >> 12
302d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
303d7ec1d24SMatthias Ringwald        encryption_key_size += 1
304d7ec1d24SMatthias Ringwald    return encryption_key_size
305d7ec1d24SMatthias Ringwald
306b3fcedb9SMatthias Ringwalddef is_string(text):
307b3fcedb9SMatthias Ringwald    for item in text.split(" "):
308b3fcedb9SMatthias Ringwald        if not all(c in string.hexdigits for c in item):
309b3fcedb9SMatthias Ringwald            return True
310b3fcedb9SMatthias Ringwald    return False
311b3fcedb9SMatthias Ringwald
312b3fcedb9SMatthias Ringwalddef add_client_characteristic_configuration(properties):
313b3fcedb9SMatthias Ringwald    return properties & (property_flags['NOTIFY'] | property_flags['INDICATE'])
314b3fcedb9SMatthias Ringwald
315729074c4SMatthias Ringwalddef serviceDefinitionComplete(fout):
316729074c4SMatthias Ringwald    global services
317729074c4SMatthias Ringwald    if current_service_uuid_string:
318729074c4SMatthias Ringwald        fout.write("\n")
319729074c4SMatthias Ringwald        # print("append service %s = [%d, %d]" % (current_characteristic_uuid_string, current_service_start_handle, handle-1))
320729074c4SMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle))
321729074c4SMatthias Ringwald        defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1))
322729074c4SMatthias Ringwald        services[current_service_uuid_string] = [current_service_start_handle, handle-1]
323729074c4SMatthias Ringwald
324d7ec1d24SMatthias Ringwalddef dump_flags(fout, flags):
325d7ec1d24SMatthias Ringwald    global security_permsission
326d7ec1d24SMatthias Ringwald    encryption_key_size = encryption_key_size_from_flags(flags)
327d7ec1d24SMatthias Ringwald    read_permissions    = security_permsission[read_permissions_from_flags(flags)]
328d7ec1d24SMatthias Ringwald    write_permissions   = security_permsission[write_permissions_from_flags(flags)]
329d7ec1d24SMatthias Ringwald    write_indent(fout)
330d7ec1d24SMatthias Ringwald    fout.write('// ')
331d7ec1d24SMatthias Ringwald    first = 1
332d7ec1d24SMatthias Ringwald    if flags & property_flags['READ']:
333d7ec1d24SMatthias Ringwald        fout.write('READ_%s' % read_permissions)
334d7ec1d24SMatthias Ringwald        first = 0
335d7ec1d24SMatthias Ringwald    if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']):
336d7ec1d24SMatthias Ringwald        if not first:
337d7ec1d24SMatthias Ringwald            fout.write(', ')
338d7ec1d24SMatthias Ringwald        first = 0
339d7ec1d24SMatthias Ringwald        fout.write('WRITE_%s' % write_permissions)
340d7ec1d24SMatthias Ringwald    if encryption_key_size > 0:
341d7ec1d24SMatthias Ringwald        if not first:
342d7ec1d24SMatthias Ringwald            fout.write(', ')
343d7ec1d24SMatthias Ringwald        first = 0
344d7ec1d24SMatthias Ringwald        fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size)
345d7ec1d24SMatthias Ringwald    fout.write('\n')
346d7ec1d24SMatthias Ringwald
347b3fcedb9SMatthias Ringwalddef parseService(fout, parts, service_type):
348b3fcedb9SMatthias Ringwald    global handle
349b3fcedb9SMatthias Ringwald    global total_size
350b3fcedb9SMatthias Ringwald    global current_service_uuid_string
351b3fcedb9SMatthias Ringwald    global current_service_start_handle
352b3fcedb9SMatthias Ringwald
353729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
354b3fcedb9SMatthias Ringwald
355d7ec1d24SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
356b3fcedb9SMatthias Ringwald
357b3fcedb9SMatthias Ringwald    write_indent(fout)
358b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
359b3fcedb9SMatthias Ringwald
360b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
361b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
362b3fcedb9SMatthias Ringwald
363b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size + 2
364b3fcedb9SMatthias Ringwald
365b3fcedb9SMatthias Ringwald    if service_type == 0x2802:
366b3fcedb9SMatthias Ringwald        size += 4
367b3fcedb9SMatthias Ringwald
368b3fcedb9SMatthias Ringwald    write_indent(fout)
369b3fcedb9SMatthias Ringwald    write_16(fout, size)
370d7ec1d24SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
371b3fcedb9SMatthias Ringwald    write_16(fout, handle)
372b3fcedb9SMatthias Ringwald    write_16(fout, service_type)
373b3fcedb9SMatthias Ringwald    write_uuid(uuid)
374b3fcedb9SMatthias Ringwald    fout.write("\n")
375b3fcedb9SMatthias Ringwald
376729074c4SMatthias Ringwald    current_service_uuid_string = c_string_for_uuid(parts[1])
377b3fcedb9SMatthias Ringwald    current_service_start_handle = handle
378b3fcedb9SMatthias Ringwald    handle = handle + 1
379b3fcedb9SMatthias Ringwald    total_size = total_size + size
380b3fcedb9SMatthias Ringwald
381b3fcedb9SMatthias Ringwalddef parsePrimaryService(fout, parts):
382b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2800)
383b3fcedb9SMatthias Ringwald
384b3fcedb9SMatthias Ringwalddef parseSecondaryService(fout, parts):
385b3fcedb9SMatthias Ringwald    parseService(fout, parts, 0x2801)
386b3fcedb9SMatthias Ringwald
387b3fcedb9SMatthias Ringwalddef parseIncludeService(fout, parts):
388b3fcedb9SMatthias Ringwald    global handle
389b3fcedb9SMatthias Ringwald    global total_size
390b3fcedb9SMatthias Ringwald
391e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
392b3fcedb9SMatthias Ringwald
393b3fcedb9SMatthias Ringwald    write_indent(fout)
394b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
395b3fcedb9SMatthias Ringwald
396b3fcedb9SMatthias Ringwald    uuid = parseUUID(parts[1])
397b3fcedb9SMatthias Ringwald    uuid_size = len(uuid)
398b3fcedb9SMatthias Ringwald    if uuid_size > 2:
399b3fcedb9SMatthias Ringwald        uuid_size = 0
400729074c4SMatthias Ringwald    # print("Include Service ", c_string_for_uuid(uuid))
401b3fcedb9SMatthias Ringwald
402b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 4 + uuid_size
403b3fcedb9SMatthias Ringwald
404729074c4SMatthias Ringwald    keyUUID = c_string_for_uuid(parts[1])
405b3fcedb9SMatthias Ringwald
406b3fcedb9SMatthias Ringwald    write_indent(fout)
407b3fcedb9SMatthias Ringwald    write_16(fout, size)
408e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
409b3fcedb9SMatthias Ringwald    write_16(fout, handle)
410b3fcedb9SMatthias Ringwald    write_16(fout, 0x2802)
411b3fcedb9SMatthias Ringwald    write_16(fout, services[keyUUID][0])
412b3fcedb9SMatthias Ringwald    write_16(fout, services[keyUUID][1])
413b3fcedb9SMatthias Ringwald    if uuid_size > 0:
414b3fcedb9SMatthias Ringwald        write_uuid(uuid)
415b3fcedb9SMatthias Ringwald    fout.write("\n")
416b3fcedb9SMatthias Ringwald
417b3fcedb9SMatthias Ringwald    handle = handle + 1
418b3fcedb9SMatthias Ringwald    total_size = total_size + size
419b3fcedb9SMatthias Ringwald
420b3fcedb9SMatthias Ringwald
421b3fcedb9SMatthias Ringwalddef parseCharacteristic(fout, parts):
422b3fcedb9SMatthias Ringwald    global handle
423b3fcedb9SMatthias Ringwald    global total_size
424b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
425b3fcedb9SMatthias Ringwald    global characteristic_indices
426b3fcedb9SMatthias Ringwald
427e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
428b3fcedb9SMatthias Ringwald
429b3fcedb9SMatthias Ringwald    # enumerate characteristics with same UUID, using optional name tag if available
430b3fcedb9SMatthias Ringwald    current_characteristic_uuid_string = c_string_for_uuid(parts[1]);
431b3fcedb9SMatthias Ringwald    index = 1
432b3fcedb9SMatthias Ringwald    if current_characteristic_uuid_string in characteristic_indices:
433b3fcedb9SMatthias Ringwald        index = characteristic_indices[current_characteristic_uuid_string] + 1
434b3fcedb9SMatthias Ringwald    characteristic_indices[current_characteristic_uuid_string] = index
435b3fcedb9SMatthias Ringwald    if len(parts) > 4:
436b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += '_' + parts[4].upper().replace(' ','_')
437b3fcedb9SMatthias Ringwald    else:
438b3fcedb9SMatthias Ringwald        current_characteristic_uuid_string += ('_%02x' % index)
439b3fcedb9SMatthias Ringwald
440b3fcedb9SMatthias Ringwald    uuid       = parseUUID(parts[1])
441b3fcedb9SMatthias Ringwald    uuid_size  = len(uuid)
442b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[2])
443b3fcedb9SMatthias Ringwald    value = ', '.join([str(x) for x in parts[3:]])
444b3fcedb9SMatthias Ringwald
445b3fcedb9SMatthias Ringwald    # reliable writes is defined in an extended properties
446b3fcedb9SMatthias Ringwald    if (properties & property_flags['RELIABLE_WRITE']):
447b3fcedb9SMatthias Ringwald        properties = properties | property_flags['EXTENDED_PROPERTIES']
448b3fcedb9SMatthias Ringwald
449b3fcedb9SMatthias Ringwald    write_indent(fout)
450b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts[0:3])))
451b3fcedb9SMatthias Ringwald
452e22a2612SMatthias Ringwald
453e22a2612SMatthias Ringwald    characteristic_properties = gatt_characteristic_properties(properties)
454b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (1+2+uuid_size)
455b3fcedb9SMatthias Ringwald    write_indent(fout)
456b3fcedb9SMatthias Ringwald    write_16(fout, size)
457e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
458b3fcedb9SMatthias Ringwald    write_16(fout, handle)
459b3fcedb9SMatthias Ringwald    write_16(fout, 0x2803)
460e22a2612SMatthias Ringwald    write_8(fout, characteristic_properties)
461b3fcedb9SMatthias Ringwald    write_16(fout, handle+1)
462b3fcedb9SMatthias Ringwald    write_uuid(uuid)
463b3fcedb9SMatthias Ringwald    fout.write("\n")
464b3fcedb9SMatthias Ringwald    handle = handle + 1
465b3fcedb9SMatthias Ringwald    total_size = total_size + size
466b3fcedb9SMatthias Ringwald
467b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + uuid_size
468b3fcedb9SMatthias Ringwald    if is_string(value):
469b3fcedb9SMatthias Ringwald        size = size + len(value)
470b3fcedb9SMatthias Ringwald    else:
471b3fcedb9SMatthias Ringwald        size = size + len(value.split())
472b3fcedb9SMatthias Ringwald
473e22a2612SMatthias Ringwald    value_flags = att_flags(properties)
4748ea3236cSMatthias Ringwald
4758ea3236cSMatthias Ringwald    # add UUID128 flag for value handle
476b3fcedb9SMatthias Ringwald    if uuid_size == 16:
477e22a2612SMatthias Ringwald        value_flags = value_flags | property_flags['LONG_UUID'];
478b3fcedb9SMatthias Ringwald
479b3fcedb9SMatthias Ringwald    write_indent(fout)
480b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value))
481d7ec1d24SMatthias Ringwald
482d7ec1d24SMatthias Ringwald    dump_flags(fout, value_flags)
483d7ec1d24SMatthias Ringwald
484b3fcedb9SMatthias Ringwald    write_indent(fout)
485b3fcedb9SMatthias Ringwald    write_16(fout, size)
486e22a2612SMatthias Ringwald    write_16(fout, value_flags)
487b3fcedb9SMatthias Ringwald    write_16(fout, handle)
488b3fcedb9SMatthias Ringwald    write_uuid(uuid)
489b3fcedb9SMatthias Ringwald    if is_string(value):
490b3fcedb9SMatthias Ringwald        write_string(fout, value)
491b3fcedb9SMatthias Ringwald    else:
492b3fcedb9SMatthias Ringwald        write_sequence(fout,value)
493b3fcedb9SMatthias Ringwald
494b3fcedb9SMatthias Ringwald    fout.write("\n")
495729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
496b3fcedb9SMatthias Ringwald    handle = handle + 1
497b3fcedb9SMatthias Ringwald
498b3fcedb9SMatthias Ringwald    if add_client_characteristic_configuration(properties):
499e22a2612SMatthias Ringwald        # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC
500d7ec1d24SMatthias Ringwald        flags  = write_permissions_and_key_size_flags_from_properties(properties)
501e22a2612SMatthias Ringwald        flags |= property_flags['READ']
502e22a2612SMatthias Ringwald        flags |= property_flags['WRITE']
5039be4aecfSMatthias Ringwald        flags |= property_flags['WRITE_WITHOUT_RESPONSE']
504e22a2612SMatthias Ringwald        flags |= property_flags['DYNAMIC']
505b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
506d7ec1d24SMatthias Ringwald
507b3fcedb9SMatthias Ringwald        write_indent(fout)
508b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle))
509d7ec1d24SMatthias Ringwald
510d7ec1d24SMatthias Ringwald        dump_flags(fout, flags)
511d7ec1d24SMatthias Ringwald
512b3fcedb9SMatthias Ringwald        write_indent(fout)
513b3fcedb9SMatthias Ringwald        write_16(fout, size)
514e22a2612SMatthias Ringwald        write_16(fout, flags)
515b3fcedb9SMatthias Ringwald        write_16(fout, handle)
516b3fcedb9SMatthias Ringwald        write_16(fout, 0x2902)
517b3fcedb9SMatthias Ringwald        write_16(fout, 0)
518b3fcedb9SMatthias Ringwald        fout.write("\n")
519729074c4SMatthias Ringwald        defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
520b3fcedb9SMatthias Ringwald        handle = handle + 1
521b3fcedb9SMatthias Ringwald
522b3fcedb9SMatthias Ringwald    if properties & property_flags['RELIABLE_WRITE']:
523b3fcedb9SMatthias Ringwald        size = 2 + 2 + 2 + 2 + 2
524b3fcedb9SMatthias Ringwald        write_indent(fout)
525b3fcedb9SMatthias Ringwald        fout.write('// 0x%04x CHARACTERISTIC_EXTENDED_PROPERTIES\n' % (handle))
526b3fcedb9SMatthias Ringwald        write_indent(fout)
527b3fcedb9SMatthias Ringwald        write_16(fout, size)
528e22a2612SMatthias Ringwald        write_16(fout, read_only_anybody_flags)
529b3fcedb9SMatthias Ringwald        write_16(fout, handle)
530b3fcedb9SMatthias Ringwald        write_16(fout, 0x2900)
531b3fcedb9SMatthias Ringwald        write_16(fout, 1)   # Reliable Write
532b3fcedb9SMatthias Ringwald        fout.write("\n")
533b3fcedb9SMatthias Ringwald        handle = handle + 1
534b3fcedb9SMatthias Ringwald
535b3fcedb9SMatthias Ringwalddef parseCharacteristicUserDescription(fout, parts):
536b3fcedb9SMatthias Ringwald    global handle
537b3fcedb9SMatthias Ringwald    global total_size
538b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
539b3fcedb9SMatthias Ringwald
540b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
541b3fcedb9SMatthias Ringwald    value      = parts[2]
542b3fcedb9SMatthias Ringwald
543b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
544b3fcedb9SMatthias Ringwald    if is_string(value):
545b7647eb6SMatthias Ringwald        size = size + len(value)
546b3fcedb9SMatthias Ringwald    else:
547b3fcedb9SMatthias Ringwald        size = size + len(value.split())
548b3fcedb9SMatthias Ringwald
549e22a2612SMatthias Ringwald    # use write, write permissions and encryption key size from attribute value and set READ_ANYBODY
550d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
551e22a2612SMatthias Ringwald    flags |= properties & property_flags['WRITE']
552e22a2612SMatthias Ringwald    flags |= property_flags['READ']
553e22a2612SMatthias Ringwald
554b3fcedb9SMatthias Ringwald    write_indent(fout)
555b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:])))
556d7ec1d24SMatthias Ringwald
557d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
558d7ec1d24SMatthias Ringwald
559b3fcedb9SMatthias Ringwald    write_indent(fout)
560b3fcedb9SMatthias Ringwald    write_16(fout, size)
561e22a2612SMatthias Ringwald    write_16(fout, flags)
562b3fcedb9SMatthias Ringwald    write_16(fout, handle)
563b3fcedb9SMatthias Ringwald    write_16(fout, 0x2901)
564b3fcedb9SMatthias Ringwald    if is_string(value):
565b3fcedb9SMatthias Ringwald        write_string(fout, value)
566b3fcedb9SMatthias Ringwald    else:
567b3fcedb9SMatthias Ringwald        write_sequence(fout,value)
568b3fcedb9SMatthias Ringwald    fout.write("\n")
569729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_USER_DESCRIPTION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
570b3fcedb9SMatthias Ringwald    handle = handle + 1
571b3fcedb9SMatthias Ringwald
572b3fcedb9SMatthias Ringwalddef parseServerCharacteristicConfiguration(fout, parts):
573b3fcedb9SMatthias Ringwald    global handle
574b3fcedb9SMatthias Ringwald    global total_size
575b3fcedb9SMatthias Ringwald    global current_characteristic_uuid_string
576b3fcedb9SMatthias Ringwald
577b3fcedb9SMatthias Ringwald    properties = parseProperties(parts[1])
578b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2
579b3fcedb9SMatthias Ringwald
580e22a2612SMatthias Ringwald    # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY
581d7ec1d24SMatthias Ringwald    flags  = write_permissions_and_key_size_flags_from_properties(properties)
582e22a2612SMatthias Ringwald    flags |= property_flags['READ']
583e22a2612SMatthias Ringwald    flags |= property_flags['WRITE']
584e22a2612SMatthias Ringwald    flags |= property_flags['DYNAMIC']
585e22a2612SMatthias Ringwald
586b3fcedb9SMatthias Ringwald    write_indent(fout)
587b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:])))
588d7ec1d24SMatthias Ringwald
589d7ec1d24SMatthias Ringwald    dump_flags(fout, flags)
590d7ec1d24SMatthias Ringwald
591b3fcedb9SMatthias Ringwald    write_indent(fout)
592b3fcedb9SMatthias Ringwald    write_16(fout, size)
593e22a2612SMatthias Ringwald    write_16(fout, flags)
594b3fcedb9SMatthias Ringwald    write_16(fout, handle)
595b3fcedb9SMatthias Ringwald    write_16(fout, 0x2903)
596b3fcedb9SMatthias Ringwald    fout.write("\n")
597729074c4SMatthias Ringwald    defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
598b3fcedb9SMatthias Ringwald    handle = handle + 1
599b3fcedb9SMatthias Ringwald
600b3fcedb9SMatthias Ringwalddef parseCharacteristicFormat(fout, parts):
601b3fcedb9SMatthias Ringwald    global handle
602b3fcedb9SMatthias Ringwald    global total_size
603b3fcedb9SMatthias Ringwald
604e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
605b3fcedb9SMatthias Ringwald
606b3fcedb9SMatthias Ringwald    identifier = parts[1]
607b3fcedb9SMatthias Ringwald    presentation_formats[identifier] = handle
608b3fcedb9SMatthias Ringwald    # print("format '%s' with handle %d\n" % (identifier, handle))
609b3fcedb9SMatthias Ringwald
610b3fcedb9SMatthias Ringwald    format     = parts[2]
611b3fcedb9SMatthias Ringwald    exponent   = parts[3]
612b3fcedb9SMatthias Ringwald    unit       = parseUUID(parts[4])
613b3fcedb9SMatthias Ringwald    name_space = parts[5]
614b3fcedb9SMatthias Ringwald    description = parseUUID(parts[6])
615b3fcedb9SMatthias Ringwald
616b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 7
617b3fcedb9SMatthias Ringwald
618b3fcedb9SMatthias Ringwald    write_indent(fout)
619b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
620b3fcedb9SMatthias Ringwald    write_indent(fout)
621b3fcedb9SMatthias Ringwald    write_16(fout, size)
622e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
623b3fcedb9SMatthias Ringwald    write_16(fout, handle)
624b3fcedb9SMatthias Ringwald    write_16(fout, 0x2904)
625b3fcedb9SMatthias Ringwald    write_sequence(fout, format)
626b3fcedb9SMatthias Ringwald    write_sequence(fout, exponent)
627b3fcedb9SMatthias Ringwald    write_uuid(unit)
628b3fcedb9SMatthias Ringwald    write_sequence(fout, name_space)
629b3fcedb9SMatthias Ringwald    write_uuid(description)
630b3fcedb9SMatthias Ringwald    fout.write("\n")
631b3fcedb9SMatthias Ringwald    handle = handle + 1
632b3fcedb9SMatthias Ringwald
633b3fcedb9SMatthias Ringwald
634b3fcedb9SMatthias Ringwalddef parseCharacteristicAggregateFormat(fout, parts):
635b3fcedb9SMatthias Ringwald    global handle
636b3fcedb9SMatthias Ringwald    global total_size
637b3fcedb9SMatthias Ringwald
638e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
639b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + (len(parts)-1) * 2
640b3fcedb9SMatthias Ringwald
641b3fcedb9SMatthias Ringwald    write_indent(fout)
642b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x CHARACTERISTIC_AGGREGATE_FORMAT-%s\n' % (handle, '-'.join(parts[1:])))
643b3fcedb9SMatthias Ringwald    write_indent(fout)
644b3fcedb9SMatthias Ringwald    write_16(fout, size)
645e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
646b3fcedb9SMatthias Ringwald    write_16(fout, handle)
647b3fcedb9SMatthias Ringwald    write_16(fout, 0x2905)
648b3fcedb9SMatthias Ringwald    for identifier in parts[1:]:
649b3fcedb9SMatthias Ringwald        format_handle = presentation_formats[identifier]
650b3fcedb9SMatthias Ringwald        if format == 0:
651b3fcedb9SMatthias Ringwald            print("ERROR: identifier '%s' in CHARACTERISTIC_AGGREGATE_FORMAT undefined" % identifier)
652b3fcedb9SMatthias Ringwald            sys.exit(1)
653b3fcedb9SMatthias Ringwald        write_16(fout, format_handle)
654b3fcedb9SMatthias Ringwald    fout.write("\n")
655b3fcedb9SMatthias Ringwald    handle = handle + 1
656b3fcedb9SMatthias Ringwald
657b3fcedb9SMatthias Ringwalddef parseReportReference(fout, parts):
658b3fcedb9SMatthias Ringwald    global handle
659b3fcedb9SMatthias Ringwald    global total_size
660b3fcedb9SMatthias Ringwald
661e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
662b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1 + 1
663b3fcedb9SMatthias Ringwald
664231a3c5dSMatthias Ringwald    report_id = parts[2]
665231a3c5dSMatthias Ringwald    report_type = parts[3]
666b3fcedb9SMatthias Ringwald
667b3fcedb9SMatthias Ringwald    write_indent(fout)
668b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x REPORT_REFERENCE-%s\n' % (handle, '-'.join(parts[1:])))
669b3fcedb9SMatthias Ringwald    write_indent(fout)
670b3fcedb9SMatthias Ringwald    write_16(fout, size)
671e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
672b3fcedb9SMatthias Ringwald    write_16(fout, handle)
673b3fcedb9SMatthias Ringwald    write_16(fout, 0x2908)
674b3fcedb9SMatthias Ringwald    write_sequence(fout, report_id)
675b3fcedb9SMatthias Ringwald    write_sequence(fout, report_type)
676b3fcedb9SMatthias Ringwald    fout.write("\n")
677b3fcedb9SMatthias Ringwald    handle = handle + 1
678b3fcedb9SMatthias Ringwald
679b3fcedb9SMatthias Ringwald
680b3fcedb9SMatthias Ringwalddef parseNumberOfDigitals(fout, parts):
681b3fcedb9SMatthias Ringwald    global handle
682b3fcedb9SMatthias Ringwald    global total_size
683b3fcedb9SMatthias Ringwald
684e22a2612SMatthias Ringwald    read_only_anybody_flags = property_flags['READ'];
685b3fcedb9SMatthias Ringwald    size = 2 + 2 + 2 + 2 + 1
686b3fcedb9SMatthias Ringwald
687b3fcedb9SMatthias Ringwald    no_of_digitals = parts[1]
688b3fcedb9SMatthias Ringwald
689b3fcedb9SMatthias Ringwald    write_indent(fout)
690b3fcedb9SMatthias Ringwald    fout.write('// 0x%04x NUMBER_OF_DIGITALS-%s\n' % (handle, '-'.join(parts[1:])))
691b3fcedb9SMatthias Ringwald    write_indent(fout)
692b3fcedb9SMatthias Ringwald    write_16(fout, size)
693e22a2612SMatthias Ringwald    write_16(fout, read_only_anybody_flags)
694b3fcedb9SMatthias Ringwald    write_16(fout, handle)
695b3fcedb9SMatthias Ringwald    write_16(fout, 0x2909)
696b3fcedb9SMatthias Ringwald    write_sequence(fout, no_of_digitals)
697b3fcedb9SMatthias Ringwald    fout.write("\n")
698b3fcedb9SMatthias Ringwald    handle = handle + 1
699b3fcedb9SMatthias Ringwald
70060b51a4cSMatthias Ringwalddef parseLines(fname_in, fin, fout):
701b3fcedb9SMatthias Ringwald    global handle
702b3fcedb9SMatthias Ringwald    global total_size
703b3fcedb9SMatthias Ringwald
704b165f97bSMatthias Ringwald    line_count = 0;
705b3fcedb9SMatthias Ringwald    for line in fin:
706b3fcedb9SMatthias Ringwald        line = line.strip("\n\r ")
707b165f97bSMatthias Ringwald        line_count += 1
708b3fcedb9SMatthias Ringwald
709b165f97bSMatthias Ringwald        if line.startswith("//"):
710b165f97bSMatthias Ringwald            fout.write("    //" + line.lstrip('/') + '\n')
711b165f97bSMatthias Ringwald            continue
712b165f97bSMatthias Ringwald
71360b51a4cSMatthias Ringwald        if line.startswith("#import"):
71460b51a4cSMatthias Ringwald            imported_file = ''
71560b51a4cSMatthias Ringwald            parts = re.match('#import\s+<(.*)>\w*',line)
71660b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
717dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
71860b51a4cSMatthias Ringwald            parts = re.match('#import\s+"(.*)"\w*',line)
71960b51a4cSMatthias Ringwald            if parts and len(parts.groups()) == 1:
720dbb3997aSMilanka Ringwald                imported_file = parts.groups()[0]
72160b51a4cSMatthias Ringwald            if len(imported_file) == 0:
72260b51a4cSMatthias Ringwald                print('ERROR: #import in file %s - line %u neither <name.gatt> nor "name.gatt" form', (fname_in, line_count))
72360b51a4cSMatthias Ringwald                continue
72460b51a4cSMatthias Ringwald
725dbb3997aSMilanka Ringwald            imported_file = getFile( imported_file )
72660b51a4cSMatthias Ringwald            print("Importing %s" % imported_file)
72760b51a4cSMatthias Ringwald            try:
72860b51a4cSMatthias Ringwald                imported_fin = codecs.open (imported_file, encoding='utf-8')
72960b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- BEGIN\n')
73060b51a4cSMatthias Ringwald                parseLines(imported_file, imported_fin, fout)
73160b51a4cSMatthias Ringwald                fout.write('    // ' + line + ' -- END\n')
73260b51a4cSMatthias Ringwald            except IOError as e:
73360b51a4cSMatthias Ringwald                print('ERROR: Import failed. Please check path.')
73460b51a4cSMatthias Ringwald
73560b51a4cSMatthias Ringwald            continue
73660b51a4cSMatthias Ringwald
73760b51a4cSMatthias Ringwald        if line.startswith("#TODO"):
73860b51a4cSMatthias Ringwald            print ("WARNING: #TODO in file %s - line %u not handled, skipping declaration:" % (fname_in, line_count))
739b165f97bSMatthias Ringwald            print ("'%s'" % line)
740b165f97bSMatthias Ringwald            fout.write("// " + line + '\n')
741b3fcedb9SMatthias Ringwald            continue
742b3fcedb9SMatthias Ringwald
743b3fcedb9SMatthias Ringwald        if len(line) == 0:
744b3fcedb9SMatthias Ringwald            continue
745b3fcedb9SMatthias Ringwald
746b3fcedb9SMatthias Ringwald        f = io.StringIO(line)
747b3fcedb9SMatthias Ringwald        parts_list = csv.reader(f, delimiter=',', quotechar='"')
748b3fcedb9SMatthias Ringwald
749b3fcedb9SMatthias Ringwald        for parts in parts_list:
750b3fcedb9SMatthias Ringwald            for index, object in enumerate(parts):
751b3fcedb9SMatthias Ringwald                parts[index] = object.strip().lstrip('"').rstrip('"')
752b3fcedb9SMatthias Ringwald
753b3fcedb9SMatthias Ringwald            if parts[0] == 'PRIMARY_SERVICE':
754b3fcedb9SMatthias Ringwald                parsePrimaryService(fout, parts)
755b3fcedb9SMatthias Ringwald                continue
756b3fcedb9SMatthias Ringwald
757b3fcedb9SMatthias Ringwald            if parts[0] == 'SECONDARY_SERVICE':
758b3fcedb9SMatthias Ringwald                parseSecondaryService(fout, parts)
759b3fcedb9SMatthias Ringwald                continue
760b3fcedb9SMatthias Ringwald
761b3fcedb9SMatthias Ringwald            if parts[0] == 'INCLUDE_SERVICE':
762b3fcedb9SMatthias Ringwald                parseIncludeService(fout, parts)
763b3fcedb9SMatthias Ringwald                continue
764b3fcedb9SMatthias Ringwald
765b3fcedb9SMatthias Ringwald            # 2803
766b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC':
767b3fcedb9SMatthias Ringwald                parseCharacteristic(fout, parts)
768b3fcedb9SMatthias Ringwald                continue
769b3fcedb9SMatthias Ringwald
770b3fcedb9SMatthias Ringwald            # 2900 Characteristic Extended Properties
771b3fcedb9SMatthias Ringwald
772b3fcedb9SMatthias Ringwald            # 2901
773b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_USER_DESCRIPTION':
774b3fcedb9SMatthias Ringwald                parseCharacteristicUserDescription(fout, parts)
775b3fcedb9SMatthias Ringwald                continue
776b3fcedb9SMatthias Ringwald
777b165f97bSMatthias Ringwald
778b165f97bSMatthias Ringwald            # 2902 Client Characteristic Configuration - automatically included in Characteristic if
779b3fcedb9SMatthias Ringwald            # notification / indication is supported
780231a3c5dSMatthias Ringwald            if parts[0] == 'CLIENT_CHARACTERISTIC_CONFIGURATION':
781b165f97bSMatthias Ringwald                continue
782b3fcedb9SMatthias Ringwald
783b3fcedb9SMatthias Ringwald            # 2903
784b3fcedb9SMatthias Ringwald            if parts[0] == 'SERVER_CHARACTERISTIC_CONFIGURATION':
785b3fcedb9SMatthias Ringwald                parseServerCharacteristicConfiguration(fout, parts)
786b3fcedb9SMatthias Ringwald                continue
787b3fcedb9SMatthias Ringwald
788b3fcedb9SMatthias Ringwald            # 2904
789b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_FORMAT':
790b3fcedb9SMatthias Ringwald                parseCharacteristicFormat(fout, parts)
791b3fcedb9SMatthias Ringwald                continue
792b3fcedb9SMatthias Ringwald
793b3fcedb9SMatthias Ringwald            # 2905
794b3fcedb9SMatthias Ringwald            if parts[0] == 'CHARACTERISTIC_AGGREGATE_FORMAT':
795b3fcedb9SMatthias Ringwald                parseCharacteristicAggregateFormat(fout, parts)
796b3fcedb9SMatthias Ringwald                continue
797b3fcedb9SMatthias Ringwald
798b3fcedb9SMatthias Ringwald            # 2906
799b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
800b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
801b3fcedb9SMatthias Ringwald                continue
802b3fcedb9SMatthias Ringwald
803b3fcedb9SMatthias Ringwald            # 2907
804b3fcedb9SMatthias Ringwald            if parts[0] == 'EXTERNAL_REPORT_REFERENCE':
805b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
806b3fcedb9SMatthias Ringwald                continue
807b3fcedb9SMatthias Ringwald
808b3fcedb9SMatthias Ringwald            # 2908
809b3fcedb9SMatthias Ringwald            if parts[0] == 'REPORT_REFERENCE':
810b3fcedb9SMatthias Ringwald                parseReportReference(fout, parts)
811b3fcedb9SMatthias Ringwald                continue
812b3fcedb9SMatthias Ringwald
813b3fcedb9SMatthias Ringwald            # 2909
814b3fcedb9SMatthias Ringwald            if parts[0] == 'NUMBER_OF_DIGITALS':
815b3fcedb9SMatthias Ringwald                parseNumberOfDigitals(fout, parts)
816b3fcedb9SMatthias Ringwald                continue
817b3fcedb9SMatthias Ringwald
818b3fcedb9SMatthias Ringwald            # 290A
819b3fcedb9SMatthias Ringwald            if parts[0] == 'VALUE_TRIGGER_SETTING':
820b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
821b3fcedb9SMatthias Ringwald                continue
822b3fcedb9SMatthias Ringwald
823b3fcedb9SMatthias Ringwald            # 290B
824b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_CONFIGURATION':
825b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
826b3fcedb9SMatthias Ringwald                continue
827b3fcedb9SMatthias Ringwald
828b3fcedb9SMatthias Ringwald            # 290C
829b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_MEASUREMENT':
830b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
831b3fcedb9SMatthias Ringwald                continue
832b3fcedb9SMatthias Ringwald
833b3fcedb9SMatthias Ringwald            # 290D
834b3fcedb9SMatthias Ringwald            if parts[0] == 'ENVIRONMENTAL_SENSING_TRIGGER_SETTING':
835b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
836b3fcedb9SMatthias Ringwald                continue
837b3fcedb9SMatthias Ringwald
838b3fcedb9SMatthias Ringwald            # 2906
839b3fcedb9SMatthias Ringwald            if parts[0] == 'VALID_RANGE':
840b3fcedb9SMatthias Ringwald                print("WARNING: %s not implemented yet\n" % (parts[0]))
841b3fcedb9SMatthias Ringwald                continue
842b3fcedb9SMatthias Ringwald
843b3fcedb9SMatthias Ringwald            print("WARNING: unknown token: %s\n" % (parts[0]))
844b3fcedb9SMatthias Ringwald
845*7050bf34SMatthias Ringwalddef parse(fname_in, fin, fname_out, tool_path, fout):
84660b51a4cSMatthias Ringwald    global handle
84760b51a4cSMatthias Ringwald    global total_size
84860b51a4cSMatthias Ringwald
849*7050bf34SMatthias Ringwald    fout.write(header.format(fname_out, fname_in, tool_path))
85060b51a4cSMatthias Ringwald    fout.write('{\n')
851fd1be25dSMatthias Ringwald    write_indent(fout)
852fd1be25dSMatthias Ringwald    fout.write('// ATT DB Version\n')
853fd1be25dSMatthias Ringwald    write_indent(fout)
854fd1be25dSMatthias Ringwald    fout.write('1,\n')
855fd1be25dSMatthias Ringwald    fout.write("\n")
85660b51a4cSMatthias Ringwald
85760b51a4cSMatthias Ringwald    parseLines(fname_in, fin, fout)
85860b51a4cSMatthias Ringwald
859729074c4SMatthias Ringwald    serviceDefinitionComplete(fout)
860b3fcedb9SMatthias Ringwald    write_indent(fout)
861b3fcedb9SMatthias Ringwald    fout.write("// END\n");
862b3fcedb9SMatthias Ringwald    write_indent(fout)
863b3fcedb9SMatthias Ringwald    write_16(fout,0)
864b3fcedb9SMatthias Ringwald    fout.write("\n")
865b3fcedb9SMatthias Ringwald    total_size = total_size + 2
866b3fcedb9SMatthias Ringwald
867b3fcedb9SMatthias Ringwald    fout.write("}; // total size %u bytes \n" % total_size);
868b3fcedb9SMatthias Ringwald
869b3fcedb9SMatthias Ringwalddef listHandles(fout):
870b3fcedb9SMatthias Ringwald    fout.write('\n\n')
871b3fcedb9SMatthias Ringwald    fout.write('//\n')
872729074c4SMatthias Ringwald    fout.write('// list service handle ranges\n')
873729074c4SMatthias Ringwald    fout.write('//\n')
874729074c4SMatthias Ringwald    for define in defines_for_services:
875729074c4SMatthias Ringwald        fout.write(define)
876729074c4SMatthias Ringwald        fout.write('\n')
877729074c4SMatthias Ringwald    fout.write('\n')
878729074c4SMatthias Ringwald    fout.write('//\n')
879b3fcedb9SMatthias Ringwald    fout.write('// list mapping between characteristics and handles\n')
880b3fcedb9SMatthias Ringwald    fout.write('//\n')
881729074c4SMatthias Ringwald    for define in defines_for_characteristics:
882b3fcedb9SMatthias Ringwald        fout.write(define)
883b3fcedb9SMatthias Ringwald        fout.write('\n')
884b3fcedb9SMatthias Ringwald
885dbb3997aSMilanka Ringwalddef getFile( fileName ):
886dbb3997aSMilanka Ringwald    inc = args.I
887dbb3997aSMilanka Ringwald    for d in inc:
888dbb3997aSMilanka Ringwald        fullFile = d[0] + fileName
889dbb3997aSMilanka Ringwald        print("test %s" % fullFile)
890dbb3997aSMilanka Ringwald        if os.path.isfile( fullFile ) == True:
891dbb3997aSMilanka Ringwald            return fullFile
892dbb3997aSMilanka Ringwald    print ("'{0}' not found".format( fileName ))
893dbb3997aSMilanka Ringwald    print ("Include paths: %s" % ", ".join(inc))
894dbb3997aSMilanka Ringwald    exit(-1)
895dbb3997aSMilanka Ringwald
896dbb3997aSMilanka Ringwald
897dbb3997aSMilanka Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..')
898dbb3997aSMilanka Ringwalddefault_includes = [ btstack_root + '/src/', btstack_root + '/src/ble/gatt-service/']
899dbb3997aSMilanka Ringwald
900dbb3997aSMilanka Ringwaldparser = argparse.ArgumentParser(description='BLE GATT configuration generator for use with BTstack')
901dbb3997aSMilanka Ringwald
902dbb3997aSMilanka Ringwaldparser.add_argument('-I', action='append', nargs=1, metavar='includes',
903dbb3997aSMilanka Ringwald        help='include search path for .gatt service files and bluetooth_gatt.h (default: %s)' % ", ".join(default_includes))
904dbb3997aSMilanka Ringwaldparser.add_argument('gattfile', metavar='gattfile', type=str,
905dbb3997aSMilanka Ringwald        help='gatt file to be compiled')
906dbb3997aSMilanka Ringwaldparser.add_argument('hfile', metavar='hfile', type=str,
907dbb3997aSMilanka Ringwald        help='header file to be generated')
908dbb3997aSMilanka Ringwald
909dbb3997aSMilanka Ringwaldargs = parser.parse_args()
910dbb3997aSMilanka Ringwald
911dbb3997aSMilanka Ringwald# append default include paths
912dbb3997aSMilanka Ringwaldif args.I == None:
913dbb3997aSMilanka Ringwald    args.I = []
914dbb3997aSMilanka Ringwaldfor d in default_includes:
915dbb3997aSMilanka Ringwald    args.I.append([d])
916dbb3997aSMilanka Ringwald
917b3fcedb9SMatthias Ringwaldtry:
918b165f97bSMatthias Ringwald    # read defines from bluetooth_gatt.h
919dbb3997aSMilanka Ringwald    gen_path = getFile( 'bluetooth_gatt.h' )
920b165f97bSMatthias Ringwald    bluetooth_gatt = read_defines(gen_path)
921b165f97bSMatthias Ringwald
922dbb3997aSMilanka Ringwald    filename = args.hfile
923dbb3997aSMilanka Ringwald    fin  = codecs.open (args.gattfile, encoding='utf-8')
924b3fcedb9SMatthias Ringwald    fout = open (filename, 'w')
925*7050bf34SMatthias Ringwald    parse(args.gattfile, fin, filename, sys.argv[0], fout)
926b3fcedb9SMatthias Ringwald    listHandles(fout)
927b3fcedb9SMatthias Ringwald    fout.close()
928b165f97bSMatthias Ringwald    print('Created %s' % filename)
929b3fcedb9SMatthias Ringwald
930b3fcedb9SMatthias Ringwaldexcept IOError as e:
931e22a2612SMatthias Ringwald
932b3fcedb9SMatthias Ringwald    print(usage)
933b3fcedb9SMatthias Ringwald    sys.exit(1)
934b3fcedb9SMatthias Ringwald
935b3fcedb9SMatthias Ringwaldprint('Compilation successful!\n')
936