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