compile_gatt.py (e22a261227a2522f26982870e4eee0b42606d652) compile_gatt.py (d7ec1d24bc1e3be5bf8c482d95765d3ecaaa7bda)
1#!/usr/bin/env python
2#
3# BLE GATT configuration generator for use with BTstack, v0.1
4# Copyright 2011 Matthias Ringwald
5#
6# Format of input file:
7# PRIMARY_SERVICE, SERVICE_UUID
8# CHARACTERISTIC, ATTRIBUTE_TYPE_UUID, [READ | WRITE | DYNAMIC], VALUE

--- 33 unchanged lines hidden (view full) ---

42 'GAP_DEVICE_NAME' : 0x2a00,
43 'GAP_APPEARANCE' : 0x2a01,
44 'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02,
45 'GAP_RECONNECTION_ADDRESS' : 0x2A03,
46 'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04,
47 'GATT_SERVICE_CHANGED' : 0x2a05,
48}
49
1#!/usr/bin/env python
2#
3# BLE GATT configuration generator for use with BTstack, v0.1
4# Copyright 2011 Matthias Ringwald
5#
6# Format of input file:
7# PRIMARY_SERVICE, SERVICE_UUID
8# CHARACTERISTIC, ATTRIBUTE_TYPE_UUID, [READ | WRITE | DYNAMIC], VALUE

--- 33 unchanged lines hidden (view full) ---

42 'GAP_DEVICE_NAME' : 0x2a00,
43 'GAP_APPEARANCE' : 0x2a01,
44 'GAP_PERIPHERAL_PRIVACY_FLAG' : 0x2A02,
45 'GAP_RECONNECTION_ADDRESS' : 0x2A03,
46 'GAP_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS' : 0x2A04,
47 'GATT_SERVICE_CHANGED' : 0x2a05,
48}
49
50security_permsission = ['ANYBODY','ENCRYPTED', 'AUTHENTICATED', 'AUTHORIZED']
51
50property_flags = {
51 # GATT Characteristic Properties
52 'BROADCAST' : 0x01,
53 'READ' : 0x02,
54 'WRITE_WITHOUT_RESPONSE' : 0x04,
55 'WRITE' : 0x08,
56 'NOTIFY': 0x10,
57 'INDICATE' : 0x20,

--- 112 unchanged lines hidden (view full) ---

170 print("WARNING: property %s undefined" % (property))
171
172 return value;
173
174def gatt_characteristic_properties(properties):
175 return properties & 0xff
176
177def att_flags(properties):
52property_flags = {
53 # GATT Characteristic Properties
54 'BROADCAST' : 0x01,
55 'READ' : 0x02,
56 'WRITE_WITHOUT_RESPONSE' : 0x04,
57 'WRITE' : 0x08,
58 'NOTIFY': 0x10,
59 'INDICATE' : 0x20,

--- 112 unchanged lines hidden (view full) ---

172 print("WARNING: property %s undefined" % (property))
173
174 return value;
175
176def gatt_characteristic_properties(properties):
177 return properties & 0xff
178
179def att_flags(properties):
178 print("in %x" % properties)
179 # drop Broadcast (0x01), Notify (0x10), Indicate (0x20)- not used for flags
180 properties &= 0x1ffce
180 # drop Broadcast (0x01), Notify (0x10), Indicate (0x20) - not used for flags
181 properties &= 0xffffffce
181
182 # rw permissions distinct
183 distinct_permissions_used = properties & (
184 property_flags['READ_AUTHORIZED'] |
185 property_flags['READ_AUTHENTICATED'] |
186 property_flags['READ_ENCRYPTED'] |
187 property_flags['READ_ANYBODY'] |
188 property_flags['WRITE_AUTHORIZED'] |
189 property_flags['WRITE_AUTHENTICATED'] |
190 property_flags['WRITE_ENCRYPTED'] |
191 property_flags['WRITE_ANYBODY']
192 ) != 0
193
194 # post process properties
195 encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0
196
182
183 # rw permissions distinct
184 distinct_permissions_used = properties & (
185 property_flags['READ_AUTHORIZED'] |
186 property_flags['READ_AUTHENTICATED'] |
187 property_flags['READ_ENCRYPTED'] |
188 property_flags['READ_ANYBODY'] |
189 property_flags['WRITE_AUTHORIZED'] |
190 property_flags['WRITE_AUTHENTICATED'] |
191 property_flags['WRITE_ENCRYPTED'] |
192 property_flags['WRITE_ANYBODY']
193 ) != 0
194
195 # post process properties
196 encryption_key_size_specified = (properties & property_flags['ENCRYPTION_KEY_SIZE_MASK']) != 0
197
197 # set encrypted for both, if distinct permissions not used
198 # if distinct permissions not used and encyrption key size specified -> set READ/WRITE Encrypted
198 if encryption_key_size_specified and not distinct_permissions_used:
199 properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED']
200
199 if encryption_key_size_specified and not distinct_permissions_used:
200 properties |= property_flags['READ_ENCRYPTED'] | property_flags['WRITE_ENCRYPTED']
201
201 # convert user permissions to att db flags
202 # if distinct permissions not used and authentication is requires -> set READ/WRITE Authenticated
203 if properties & property_flags['AUTHENTICATION_REQUIRED'] and not distinct_permissions_used:
204 properties |= property_flags['READ_AUTHENTICATED'] | property_flags['WRITE_AUTHENTICATED']
205
206 # if distinct permissions not used and authorized is requires -> set READ/WRITE Authorized
207 if properties & property_flags['AUTHORIZATION_REQUIRED'] and not distinct_permissions_used:
208 properties |= property_flags['READ_AUTHORIZED'] | property_flags['WRITE_AUTHORIZED']
209
210 # determine read/write security requirements
211 read_security_level = 0
212 write_security_level = 0
202 if properties & property_flags['READ_AUTHORIZED']:
213 if properties & property_flags['READ_AUTHORIZED']:
203 properties |= property_flags['READ_PERMISSION_BIT_0'] | property_flags['READ_PERMISSION_BIT_1']
214 read_security_level = 3
204 elif properties & property_flags['READ_AUTHENTICATED']:
215 elif properties & property_flags['READ_AUTHENTICATED']:
205 properties |= property_flags['READ_PERMISSION_BIT_1']
216 read_security_level = 2
206 elif properties & property_flags['READ_ENCRYPTED']:
217 elif properties & property_flags['READ_ENCRYPTED']:
207 properties |= property_flags['READ_PERMISSION_BIT_0']
218 read_security_level = 1
208 if properties & property_flags['WRITE_AUTHORIZED']:
219 if properties & property_flags['WRITE_AUTHORIZED']:
209 properties |= property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1']
220 write_security_level = 3
210 elif properties & property_flags['WRITE_AUTHENTICATED']:
221 elif properties & property_flags['WRITE_AUTHENTICATED']:
211 properties |= property_flags['WRITE_PERMISSION_BIT_1']
222 write_security_level = 2
212 elif properties & property_flags['WRITE_ENCRYPTED']:
223 elif properties & property_flags['WRITE_ENCRYPTED']:
224 write_security_level = 1
225
226 # map security requirements to flags
227 if read_security_level & 2:
228 properties |= property_flags['READ_PERMISSION_BIT_1']
229 if read_security_level & 1:
230 properties |= property_flags['READ_PERMISSION_BIT_0']
231 if write_security_level & 2:
232 properties |= property_flags['WRITE_PERMISSION_BIT_1']
233 if write_security_level & 1:
213 properties |= property_flags['WRITE_PERMISSION_BIT_0']
234 properties |= property_flags['WRITE_PERMISSION_BIT_0']
214 print("out %x" % properties)
215
216 return properties
217
235
236 return properties
237
218def write_permissions_and_key_size(properties):
238def write_permissions_and_key_size_flags_from_properties(properties):
219 return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1'])
220
221def write_8(fout, value):
222 fout.write( "0x%02x, " % (value & 0xff))
223
224def write_16(fout, value):
225 fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff))
226

--- 8 unchanged lines hidden (view full) ---

235def write_sequence(fout, text):
236 parts = text.split()
237 for part in parts:
238 fout.write("0x%s, " % (part.strip()))
239
240def write_indent(fout):
241 fout.write(" ")
242
239 return att_flags(properties) & (property_flags['ENCRYPTION_KEY_SIZE_MASK'] | property_flags['WRITE_PERMISSION_BIT_0'] | property_flags['WRITE_PERMISSION_BIT_1'])
240
241def write_8(fout, value):
242 fout.write( "0x%02x, " % (value & 0xff))
243
244def write_16(fout, value):
245 fout.write('0x%02x, 0x%02x, ' % (value & 0xff, (value >> 8) & 0xff))
246

--- 8 unchanged lines hidden (view full) ---

255def write_sequence(fout, text):
256 parts = text.split()
257 for part in parts:
258 fout.write("0x%s, " % (part.strip()))
259
260def write_indent(fout):
261 fout.write(" ")
262
263def read_permissions_from_flags(flags):
264 permissions = 0
265 if flags & property_flags['READ_PERMISSION_BIT_0']:
266 permissions |= 1
267 if flags & property_flags['READ_PERMISSION_BIT_1']:
268 permissions |= 2
269 return permissions
270
271def write_permissions_from_flags(flags):
272 permissions = 0
273 if flags & property_flags['WRITE_PERMISSION_BIT_0']:
274 permissions |= 1
275 if flags & property_flags['WRITE_PERMISSION_BIT_1']:
276 permissions |= 2
277 return permissions
278
279def encryption_key_size_from_flags(flags):
280 encryption_key_size = (flags & 0xf000) >> 12
281 if encryption_key_size > 0:
282 encryption_key_size += 1
283 return encryption_key_size
284
243def is_string(text):
244 for item in text.split(" "):
245 if not all(c in string.hexdigits for c in item):
246 return True
247 return False
248
249def add_client_characteristic_configuration(properties):
250 return properties & (property_flags['NOTIFY'] | property_flags['INDICATE'])
251
252def serviceDefinitionComplete(fout):
253 global services
254 if current_service_uuid_string:
255 fout.write("\n")
256 # print("append service %s = [%d, %d]" % (current_characteristic_uuid_string, current_service_start_handle, handle-1))
257 defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle))
258 defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1))
259 services[current_service_uuid_string] = [current_service_start_handle, handle-1]
260
285def is_string(text):
286 for item in text.split(" "):
287 if not all(c in string.hexdigits for c in item):
288 return True
289 return False
290
291def add_client_characteristic_configuration(properties):
292 return properties & (property_flags['NOTIFY'] | property_flags['INDICATE'])
293
294def serviceDefinitionComplete(fout):
295 global services
296 if current_service_uuid_string:
297 fout.write("\n")
298 # print("append service %s = [%d, %d]" % (current_characteristic_uuid_string, current_service_start_handle, handle-1))
299 defines_for_services.append('#define ATT_SERVICE_%s_START_HANDLE 0x%04x' % (current_service_uuid_string, current_service_start_handle))
300 defines_for_services.append('#define ATT_SERVICE_%s_END_HANDLE 0x%04x' % (current_service_uuid_string, handle-1))
301 services[current_service_uuid_string] = [current_service_start_handle, handle-1]
302
303def dump_flags(fout, flags):
304 global security_permsission
305 encryption_key_size = encryption_key_size_from_flags(flags)
306 read_permissions = security_permsission[read_permissions_from_flags(flags)]
307 write_permissions = security_permsission[write_permissions_from_flags(flags)]
308 write_indent(fout)
309 fout.write('// ')
310 first = 1
311 if flags & property_flags['READ']:
312 fout.write('READ_%s' % read_permissions)
313 first = 0
314 if flags & (property_flags['WRITE'] | property_flags['WRITE_WITHOUT_RESPONSE']):
315 if not first:
316 fout.write(', ')
317 first = 0
318 fout.write('WRITE_%s' % write_permissions)
319 if encryption_key_size > 0:
320 if not first:
321 fout.write(', ')
322 first = 0
323 fout.write('ENCRYPTION_KEY_SIZE=%u' % encryption_key_size)
324 fout.write('\n')
325
261def parseService(fout, parts, service_type):
262 global handle
263 global total_size
264 global current_service_uuid_string
265 global current_service_start_handle
266
267 serviceDefinitionComplete(fout)
268
326def parseService(fout, parts, service_type):
327 global handle
328 global total_size
329 global current_service_uuid_string
330 global current_service_start_handle
331
332 serviceDefinitionComplete(fout)
333
269 property = property_flags['READ'];
334 read_only_anybody_flags = property_flags['READ'];
270
271 write_indent(fout)
272 fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
273
274 uuid = parseUUID(parts[1])
275 uuid_size = len(uuid)
276
277 size = 2 + 2 + 2 + uuid_size + 2
278
279 if service_type == 0x2802:
280 size += 4
281
282 write_indent(fout)
283 write_16(fout, size)
335
336 write_indent(fout)
337 fout.write('// 0x%04x %s\n' % (handle, '-'.join(parts)))
338
339 uuid = parseUUID(parts[1])
340 uuid_size = len(uuid)
341
342 size = 2 + 2 + 2 + uuid_size + 2
343
344 if service_type == 0x2802:
345 size += 4
346
347 write_indent(fout)
348 write_16(fout, size)
284 write_16(fout, property)
349 write_16(fout, read_only_anybody_flags)
285 write_16(fout, handle)
286 write_16(fout, service_type)
287 write_uuid(uuid)
288 fout.write("\n")
289
290 current_service_uuid_string = c_string_for_uuid(parts[1])
291 current_service_start_handle = handle
292 handle = handle + 1

--- 94 unchanged lines hidden (view full) ---

387 value_flags = att_flags(properties)
388
389 # add UUID128 flag for value handle
390 if uuid_size == 16:
391 value_flags = value_flags | property_flags['LONG_UUID'];
392
393 write_indent(fout)
394 fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value))
350 write_16(fout, handle)
351 write_16(fout, service_type)
352 write_uuid(uuid)
353 fout.write("\n")
354
355 current_service_uuid_string = c_string_for_uuid(parts[1])
356 current_service_start_handle = handle
357 handle = handle + 1

--- 94 unchanged lines hidden (view full) ---

452 value_flags = att_flags(properties)
453
454 # add UUID128 flag for value handle
455 if uuid_size == 16:
456 value_flags = value_flags | property_flags['LONG_UUID'];
457
458 write_indent(fout)
459 fout.write('// 0x%04x VALUE-%s-'"'%s'"'\n' % (handle, '-'.join(parts[1:3]),value))
460
461 dump_flags(fout, value_flags)
462
395 write_indent(fout)
396 write_16(fout, size)
397 write_16(fout, value_flags)
398 write_16(fout, handle)
399 write_uuid(uuid)
400 if is_string(value):
401 write_string(fout, value)
402 else:
403 write_sequence(fout,value)
404
405 fout.write("\n")
406 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
407 handle = handle + 1
408
409 if add_client_characteristic_configuration(properties):
410 # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC
463 write_indent(fout)
464 write_16(fout, size)
465 write_16(fout, value_flags)
466 write_16(fout, handle)
467 write_uuid(uuid)
468 if is_string(value):
469 write_string(fout, value)
470 else:
471 write_sequence(fout,value)
472
473 fout.write("\n")
474 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_VALUE_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
475 handle = handle + 1
476
477 if add_client_characteristic_configuration(properties):
478 # use write permissions and encryption key size from attribute value and set READ_ANYBODY | READ | WRITE | DYNAMIC
411 flags = write_permissions_and_key_size(properties)
479 flags = write_permissions_and_key_size_flags_from_properties(properties)
412 flags |= property_flags['READ']
413 flags |= property_flags['WRITE']
414 flags |= property_flags['DYNAMIC']
415 size = 2 + 2 + 2 + 2 + 2
480 flags |= property_flags['READ']
481 flags |= property_flags['WRITE']
482 flags |= property_flags['DYNAMIC']
483 size = 2 + 2 + 2 + 2 + 2
484
416 write_indent(fout)
417 fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle))
485 write_indent(fout)
486 fout.write('// 0x%04x CLIENT_CHARACTERISTIC_CONFIGURATION\n' % (handle))
487
488 dump_flags(fout, flags)
489
418 write_indent(fout)
419 write_16(fout, size)
420 write_16(fout, flags)
421 write_16(fout, handle)
422 write_16(fout, 0x2902)
423 write_16(fout, 0)
424 fout.write("\n")
425 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))

--- 22 unchanged lines hidden (view full) ---

448
449 size = 2 + 2 + 2 + 2
450 if is_string(value):
451 size = size + len(value) - 2
452 else:
453 size = size + len(value.split())
454
455 # use write, write permissions and encryption key size from attribute value and set READ_ANYBODY
490 write_indent(fout)
491 write_16(fout, size)
492 write_16(fout, flags)
493 write_16(fout, handle)
494 write_16(fout, 0x2902)
495 write_16(fout, 0)
496 fout.write("\n")
497 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_CLIENT_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))

--- 22 unchanged lines hidden (view full) ---

520
521 size = 2 + 2 + 2 + 2
522 if is_string(value):
523 size = size + len(value) - 2
524 else:
525 size = size + len(value.split())
526
527 # use write, write permissions and encryption key size from attribute value and set READ_ANYBODY
456 flags = write_permissions_and_key_size(properties)
528 flags = write_permissions_and_key_size_flags_from_properties(properties)
457 flags |= properties & property_flags['WRITE']
458 flags |= property_flags['READ']
459
460 write_indent(fout)
461 fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:])))
529 flags |= properties & property_flags['WRITE']
530 flags |= property_flags['READ']
531
532 write_indent(fout)
533 fout.write('// 0x%04x CHARACTERISTIC_USER_DESCRIPTION-%s\n' % (handle, '-'.join(parts[1:])))
534
535 dump_flags(fout, flags)
536
462 write_indent(fout)
463 write_16(fout, size)
464 write_16(fout, flags)
465 write_16(fout, handle)
466 write_16(fout, 0x2901)
467 if is_string(value):
468 write_string(fout, value)
469 else:

--- 6 unchanged lines hidden (view full) ---

476 global handle
477 global total_size
478 global current_characteristic_uuid_string
479
480 properties = parseProperties(parts[1])
481 size = 2 + 2 + 2 + 2
482
483 # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY
537 write_indent(fout)
538 write_16(fout, size)
539 write_16(fout, flags)
540 write_16(fout, handle)
541 write_16(fout, 0x2901)
542 if is_string(value):
543 write_string(fout, value)
544 else:

--- 6 unchanged lines hidden (view full) ---

551 global handle
552 global total_size
553 global current_characteristic_uuid_string
554
555 properties = parseProperties(parts[1])
556 size = 2 + 2 + 2 + 2
557
558 # use write permissions and encryption key size from attribute value and set READ, WRITE, DYNAMIC, READ_ANYBODY
484 flags = write_permissions_and_key_size(properties)
559 flags = write_permissions_and_key_size_flags_from_properties(properties)
485 flags |= property_flags['READ']
486 flags |= property_flags['WRITE']
487 flags |= property_flags['DYNAMIC']
488
489 write_indent(fout)
490 fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:])))
560 flags |= property_flags['READ']
561 flags |= property_flags['WRITE']
562 flags |= property_flags['DYNAMIC']
563
564 write_indent(fout)
565 fout.write('// 0x%04x SERVER_CHARACTERISTIC_CONFIGURATION-%s\n' % (handle, '-'.join(parts[1:])))
566
567 dump_flags(fout, flags)
568
491 write_indent(fout)
492 write_16(fout, size)
493 write_16(fout, flags)
494 write_16(fout, handle)
495 write_16(fout, 0x2903)
496 fout.write("\n")
497 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
498 handle = handle + 1

--- 303 unchanged lines hidden ---
569 write_indent(fout)
570 write_16(fout, size)
571 write_16(fout, flags)
572 write_16(fout, handle)
573 write_16(fout, 0x2903)
574 fout.write("\n")
575 defines_for_characteristics.append('#define ATT_CHARACTERISTIC_%s_SERVER_CONFIGURATION_HANDLE 0x%04x' % (current_characteristic_uuid_string, handle))
576 handle = handle + 1

--- 303 unchanged lines hidden ---