173a17974SMatthias Ringwald#!/usr/bin/env python 273a17974SMatthias Ringwald# BlueKitchen GmbH (c) 2018 373a17974SMatthias Ringwald 473a17974SMatthias Ringwaldimport glob 573a17974SMatthias Ringwaldimport re 673a17974SMatthias Ringwaldimport sys 773a17974SMatthias Ringwaldimport os 873a17974SMatthias Ringwald 973a17974SMatthias Ringwaldimport btstack_parser as parser 1073a17974SMatthias Ringwald 1173a17974SMatthias Ringwaldprint(''' 1273a17974SMatthias RingwaldPython binding generator for BTstack Server 1373a17974SMatthias RingwaldCopyright 2018, BlueKitchen GmbH 1473a17974SMatthias Ringwald''') 1573a17974SMatthias Ringwald 1673a17974SMatthias Ringwald# com.bluekitchen.btstack.BTstack.java templates 1773a17974SMatthias Ringwaldcommand_builder_header = \ 1873a17974SMatthias Ringwald'''#!/usr/bin/env python3 1973a17974SMatthias Ringwald 2073a17974SMatthias Ringwaldimport struct 2173a17974SMatthias Ringwald 2201aeeea2SMatthias Ringwalddef opcode(ogf, ocf): 2301aeeea2SMatthias Ringwald return ocf | (ogf << 10) 2473a17974SMatthias Ringwald 2573a17974SMatthias Ringwalddef pack24(value): 2673a17974SMatthias Ringwald return struct.pack("B", value & 0xff) + struct.pack("<H", value >> 8) 2773a17974SMatthias Ringwald 2873a17974SMatthias Ringwalddef name248(str): 2973a17974SMatthias Ringwald arg = str.encode('utf-8') 3073a17974SMatthias Ringwald return arg[:248] + bytes(248-len(arg)) 3173a17974SMatthias Ringwald 3201aeeea2SMatthias Ringwald# Command Builder 3301aeeea2SMatthias Ringwald 3401aeeea2SMatthias Ringwaldclass CommandBuilder(object): 35*760c6692SMatthias Ringwald 3601aeeea2SMatthias Ringwald def __init__(self): 3701aeeea2SMatthias Ringwald pass 3801aeeea2SMatthias Ringwald 3973a17974SMatthias Ringwald def send_command(command): 4073a17974SMatthias Ringwald return FALSE 4173a17974SMatthias Ringwald 4273a17974SMatthias Ringwald''' 43*760c6692SMatthias Ringwald 4473a17974SMatthias Ringwaldcommand_builder_command = ''' 4501aeeea2SMatthias Ringwald def {name}(self, {args}): 4673a17974SMatthias Ringwald cmd_args = bytes() 4773a17974SMatthias Ringwald{args_builder} 4801aeeea2SMatthias Ringwald cmd = struct.pack("<HB", opcode(self.{ogf}, self.{ocf}), len(cmd_args)) + cmd_args 4973a17974SMatthias Ringwald return self.send_hci_command(cmd) 5073a17974SMatthias Ringwald''' 5173a17974SMatthias Ringwald 5273a17974SMatthias Ringwald# com.bluekitchen.btstack.EventFactory template 53*760c6692SMatthias Ringwaldevent_factory_template = \ 54dbd33601SMatthias Ringwald''' 5573a17974SMatthias Ringwald 56*760c6692SMatthias Ringwald# dictionary to map hci event types to event classes 57*760c6692SMatthias Ringwaldevent_class_for_type = {{ 58*760c6692SMatthias Ringwald{1}}} 5973a17974SMatthias Ringwald 60*760c6692SMatthias Ringwald# dictionary to map hci le event types to event classes 61*760c6692SMatthias Ringwaldle_event_class_for_type = {{ 62*760c6692SMatthias Ringwald{2}}} 6373a17974SMatthias Ringwald 64*760c6692SMatthias Ringwald# list of all event types - not actually used 65dbd33601SMatthias Ringwald{0} 6673a17974SMatthias Ringwald 67*760c6692SMatthias Ringwalddef event_for_payload(payload): 68*760c6692SMatthias Ringwald event_type = payload[0] 69*760c6692SMatthias Ringwald event_class = btstack.btstack_types.Event 70*760c6692SMatthias Ringwald # LE Subevent 71*760c6692SMatthias Ringwald if event_type == 0x3e: 72*760c6692SMatthias Ringwald subevent_type = payload[2] 73*760c6692SMatthias Ringwald event_class = le_event_class_for_type[subevent_type] 74*760c6692SMatthias Ringwald else: 75*760c6692SMatthias Ringwald event_class = event_class_for_type[event_type] 76*760c6692SMatthias Ringwald return event_class(payload) 7773a17974SMatthias Ringwald''' 78*760c6692SMatthias Ringwaldevent_factory_event = \ 79*760c6692SMatthias Ringwald''' {0} : {1}, 8073a17974SMatthias Ringwald''' 81*760c6692SMatthias Ringwaldevent_factory_subevent = \ 82*760c6692SMatthias Ringwald''' {0} : {1}, 8373a17974SMatthias Ringwald''' 8473a17974SMatthias Ringwald 85*760c6692SMatthias Ringwaldevent_header = ''' 86*760c6692SMatthias Ringwaldimport btstack.btstack_types 87dbd33601SMatthias Ringwald''' 8873a17974SMatthias Ringwald 89*760c6692SMatthias Ringwaldevent_template = \ 90*760c6692SMatthias Ringwald''' 9173a17974SMatthias Ringwald 92*760c6692SMatthias Ringwaldclass {0}(btstack.btstack_types.Event): 93*760c6692SMatthias Ringwald 94*760c6692SMatthias Ringwald def __init__(self, payload): 95*760c6692SMatthias Ringwald super().__init__(payload) 96dbd33601SMatthias Ringwald {1} 9773a17974SMatthias Ringwald {2} 9873a17974SMatthias Ringwald''' 9973a17974SMatthias Ringwald 100*760c6692SMatthias Ringwaldevent_getter = \ 10173a17974SMatthias Ringwald''' 102*760c6692SMatthias Ringwald def get_{0}(self): 103dbd33601SMatthias Ringwald {1} 104*760c6692SMatthias Ringwald''' 105*760c6692SMatthias Ringwald 106*760c6692SMatthias Ringwaldevent_getter_data = \ 107*760c6692SMatthias Ringwald'''# len = self.get_{length_name}() 108*760c6692SMatthias Ringwald return self.payload[{offset},{offset}+len] 109*760c6692SMatthias Ringwald''' 110*760c6692SMatthias Ringwald 111*760c6692SMatthias Ringwaldevent_getter_data_fixed = \ 112*760c6692SMatthias Ringwald'''return self.payload[{offset},{offset}+{size}] 113*760c6692SMatthias Ringwald''' 114*760c6692SMatthias Ringwald 115*760c6692SMatthias Ringwaldevent_to_string = \ 116*760c6692SMatthias Ringwald'''def __repr__(self): 117*760c6692SMatthias Ringwald repr = '{0} < type=0x%02x' % self.get_event_type() 118*760c6692SMatthias Ringwald{1} 119*760c6692SMatthias Ringwald repr += " >" 120*760c6692SMatthias Ringwald return repr 12173a17974SMatthias Ringwald''' 12273a17974SMatthias Ringwald 12373a17974SMatthias Ringwald 12473a17974SMatthias Ringwald# global variables/defines 12573a17974SMatthias Ringwaldpackage ='com.bluekitchen.btstack' 12673a17974SMatthias Ringwaldgen_path = 'gen/' + package.replace('.', '/') 12773a17974SMatthias Ringwald 12873a17974SMatthias Ringwalddefines = dict() 12973a17974SMatthias Ringwalddefines_used = set() 13073a17974SMatthias Ringwald 13173a17974SMatthias Ringwalddef size_for_type(type): 13273a17974SMatthias Ringwald param_sizes = { '1' : 1, '2' : 2, '3' : 3, '4' : 4, 'H' : 2, 'B' : 6, 'D' : 8, 'E' : 240, 'N' : 248, 'P' : 16, 13373a17974SMatthias Ringwald 'A' : 31, 'S' : -1, 'V': -1, 'J' : 1, 'L' : 2, 'Q' : 32, 'U' : 16, 'X' : 20, 'Y' : 24, 'Z' : 18, 'T':-1} 13473a17974SMatthias Ringwald return param_sizes[type] 13573a17974SMatthias Ringwald 13673a17974SMatthias Ringwalddef create_command_python(fout, name, ogf, ocf, format, params): 13773a17974SMatthias Ringwald global command_builder_command 13873a17974SMatthias Ringwald 13973a17974SMatthias Ringwald ind = ' ' 14073a17974SMatthias Ringwald param_store = { 14173a17974SMatthias Ringwald '1' : 'cmd_args += struct.pack("B", %s)', 14273a17974SMatthias Ringwald 'J' : 'cmd_args += struct.pack("B", %s)', 14373a17974SMatthias Ringwald '2' : 'cmd_args += struct.pack("<H", %s)', 14473a17974SMatthias Ringwald 'H' : 'cmd_args += struct.pack("<H", %s)', 14573a17974SMatthias Ringwald 'L' : 'cmd_args += struct.pack("<H", %s)', 14601aeeea2SMatthias Ringwald '3' : 'cmd_args += pack24(%s)', 14773a17974SMatthias Ringwald '4' : 'cmd_args += struct.pack("<H", %s)', 14801aeeea2SMatthias Ringwald 'N' : 'cmd_args += name248(%s)', 14973a17974SMatthias Ringwald 'B' : 'cmd_args += %s.get_bytes()', 15073a17974SMatthias Ringwald 'U' : 'cmd_args += %s.get_bytes()', 15173a17974SMatthias Ringwald 'X' : 'cmd_args += %s.get_bytes()', 15273a17974SMatthias Ringwald 'Y' : 'cmd_args += %s.get_bytes()', 15373a17974SMatthias Ringwald 'Z' : 'cmd_args += %s.get_bytes()', 15473a17974SMatthias Ringwald 'S' : 'cmd_args += %s', 15573a17974SMatthias Ringwald # TODO: support serialization for these 15673a17974SMatthias Ringwald 'D' : '# D / TODO Util.storeBytes(command, offset, %s, 8)', 15773a17974SMatthias Ringwald 'E' : '# E / TODO Util.storeBytes(command, offset, %s, 240)', 15873a17974SMatthias Ringwald 'P' : '# P / TODO Util.storeBytes(command, offset, %s, 16)', 15973a17974SMatthias Ringwald 'Q' : '# Q / TODO Util.storeBytes(command, offset, %s, 32)', 16073a17974SMatthias Ringwald 'A' : '# A / TODO Util.storeBytes(command, offset, %s, 31)', 16173a17974SMatthias Ringwald } 16273a17974SMatthias Ringwald # method arguments 16373a17974SMatthias Ringwald arg_counter = 1 16473a17974SMatthias Ringwald args = [] 16573a17974SMatthias Ringwald for param_type, arg_name in zip(format, params): 16673a17974SMatthias Ringwald arg_size = size_for_type(param_type) 16773a17974SMatthias Ringwald arg = (param_type, arg_size, arg_name) 16873a17974SMatthias Ringwald args.append(arg) 16973a17974SMatthias Ringwald arg_counter += 1 17073a17974SMatthias Ringwald 17173a17974SMatthias Ringwald # method argument declaration 17273a17974SMatthias Ringwald args2 = [] 17373a17974SMatthias Ringwald for arg in args: 17473a17974SMatthias Ringwald args2.append(arg[2]) 17573a17974SMatthias Ringwald args_string = ', '.join(args2) 17673a17974SMatthias Ringwald 17773a17974SMatthias Ringwald # command size (opcode, len) 17873a17974SMatthias Ringwald size_fixed = 3 17973a17974SMatthias Ringwald size_var = '' 18073a17974SMatthias Ringwald for arg in args: 18173a17974SMatthias Ringwald size = arg[1] 18273a17974SMatthias Ringwald if size > 0: 18373a17974SMatthias Ringwald size_fixed += size 18473a17974SMatthias Ringwald else: 18573a17974SMatthias Ringwald size_var += ' + %s.length' % arg[2] 18673a17974SMatthias Ringwald size_string = '%u%s' % (size_fixed, size_var) 18773a17974SMatthias Ringwald 18873a17974SMatthias Ringwald store_params = '' 18973a17974SMatthias Ringwald 19073a17974SMatthias Ringwald length_name = '' 19173a17974SMatthias Ringwald for (param_type, arg_size, arg_name) in args: 19273a17974SMatthias Ringwald if param_type in ['L', 'J']: 19373a17974SMatthias Ringwald length_name = arg_name 19473a17974SMatthias Ringwald if param_type == 'V': 19573a17974SMatthias Ringwald store_params += ind + 'Util.storeBytes(command, offset, %s, %s);' % (arg_name, length_name) + '\n'; 19673a17974SMatthias Ringwald length_name = '' 19773a17974SMatthias Ringwald else: 19873a17974SMatthias Ringwald store_params += ind + (param_store[param_type] % arg_name) + '\n'; 19973a17974SMatthias Ringwald size = arg_size 20073a17974SMatthias Ringwald 20173a17974SMatthias Ringwald fout.write( command_builder_command.format(name=name, args=args_string, ogf=ogf, ocf=ocf, args_builder=store_params)) 20273a17974SMatthias Ringwald 20373a17974SMatthias Ringwalddef mark_define_as_used(term): 20473a17974SMatthias Ringwald if term.startswith('0'): 20573a17974SMatthias Ringwald return 20673a17974SMatthias Ringwald defines_used.add(term) 20773a17974SMatthias Ringwald 20801aeeea2SMatthias Ringwalddef python_define_string(key): 20973a17974SMatthias Ringwald global defines 21073a17974SMatthias Ringwald if key in defines: 21101aeeea2SMatthias Ringwald return ' %s = %s\n' % (key, defines[key]) 21273a17974SMatthias Ringwald else: 21301aeeea2SMatthias Ringwald return ' # defines[%s] not set\n' % key 21473a17974SMatthias Ringwald 215b2929115SMatthias Ringwalddef python_defines_string(keys): 21601aeeea2SMatthias Ringwald return '\n'.join( map(python_define_string, sorted(keys))) 21773a17974SMatthias Ringwald 21873a17974SMatthias Ringwalddef create_command_builder(commands): 21973a17974SMatthias Ringwald global gen_path 22073a17974SMatthias Ringwald parser.assert_dir(gen_path) 22173a17974SMatthias Ringwald 22273a17974SMatthias Ringwald outfile = '%s/command_builder.py' % gen_path 22373a17974SMatthias Ringwald 22473a17974SMatthias Ringwald with open(outfile, 'wt') as fout: 22573a17974SMatthias Ringwald 22673a17974SMatthias Ringwald fout.write(command_builder_header) 22773a17974SMatthias Ringwald 22873a17974SMatthias Ringwald for command in commands: 22973a17974SMatthias Ringwald (command_name, ogf, ocf, format, params) = command 23073a17974SMatthias Ringwald create_command_python(fout, command_name, ogf, ocf, format, params); 23173a17974SMatthias Ringwald mark_define_as_used(ogf) 23273a17974SMatthias Ringwald mark_define_as_used(ocf) 23373a17974SMatthias Ringwald 23401aeeea2SMatthias Ringwald fout.write('\n # defines used\n\n') 23501aeeea2SMatthias Ringwald for key in sorted(defines_used): 23601aeeea2SMatthias Ringwald fout.write(python_define_string(key)) 23773a17974SMatthias Ringwald 238b2929115SMatthias Ringwalddef create_event(fout, event_name, format, args): 23973a17974SMatthias Ringwald global gen_path 240*760c6692SMatthias Ringwald global event_template 24173a17974SMatthias Ringwald 24273a17974SMatthias Ringwald param_read = { 243dbd33601SMatthias Ringwald '1' : 'return self.payload[{offset}]', 244dbd33601SMatthias Ringwald 'J' : 'return self.payload[{offset}]', 245dbd33601SMatthias Ringwald '2' : 'return struct.unpack("<H", self.payload[{offset}, {offset}+2])', 246dbd33601SMatthias Ringwald 'H' : 'return struct.unpack("<H", self.payload[{offset}, {offset}+2])', 247dbd33601SMatthias Ringwald 'L' : 'return struct.unpack("<H", self.payload[{offset}, {offset}+2])', 248dbd33601SMatthias Ringwald '3' : 'return btstack.btstack_types.unpack24(self.payload[{offset}:3])', 249dbd33601SMatthias Ringwald '4' : 'return struct.unpack("<I", self.payload[{offset}, {offset}+4])', 250dbd33601SMatthias Ringwald 'B' : 'return btstack.btstack_types.BD_ADDR(self.payload[{offset}:6])', 251dbd33601SMatthias Ringwald 'X' : 'return btstack.btstack_types.GATTService(self.payload[{offset}:20])', 252dbd33601SMatthias Ringwald 'Y' : 'return btstack.btstack_types.GATTCharacteristic(self.payload[{offset}:24])', 253dbd33601SMatthias Ringwald 'Z' : 'return btstack.btstack_types.GATTCharacteristicDescriptor(self.payload[{offset}:18])', 254*760c6692SMatthias Ringwald 'T' : 'return self.payload[{offset}:].decode("utf-8")', 255*760c6692SMatthias Ringwald 'N' : 'return self.payload[{offset}:{offset}+248].decode("utf-8")', 256dbd33601SMatthias Ringwald # 'D' : 'Util.storeBytes(self.payload, %u, 8);', 257dbd33601SMatthias Ringwald # 'Q' : 'Util.storeBytes(self.payload, %u, 32);', 25873a17974SMatthias Ringwald # 'E' : 'Util.storeBytes(data, %u, 240);', 25973a17974SMatthias Ringwald # 'P' : 'Util.storeBytes(data, %u, 16);', 26073a17974SMatthias Ringwald # 'A' : 'Util.storeBytes(data, %u, 31);', 26173a17974SMatthias Ringwald # 'S' : 'Util.storeBytes(data, %u);' 262*760c6692SMatthias Ringwald 'R' : 'return self.payload[{offset}:]', 26373a17974SMatthias Ringwald } 26473a17974SMatthias Ringwald 26573a17974SMatthias Ringwald offset = 2 26673a17974SMatthias Ringwald getters = '' 26773a17974SMatthias Ringwald length_name = '' 26873a17974SMatthias Ringwald for f, arg in zip(format, args): 26973a17974SMatthias Ringwald # just remember name 27073a17974SMatthias Ringwald if f in ['L','J']: 271dbd33601SMatthias Ringwald length_name = arg.lower() 27273a17974SMatthias Ringwald if f == 'R': 27373a17974SMatthias Ringwald # remaining data 274*760c6692SMatthias Ringwald access = param_read[f].format(offset=offset) 27573a17974SMatthias Ringwald size = 0 27673a17974SMatthias Ringwald elif f == 'V': 277*760c6692SMatthias Ringwald access = event_getter_data.format(length_name=length_name, offset=offset) 27873a17974SMatthias Ringwald size = 0 27973a17974SMatthias Ringwald elif f in ['D', 'Q']: 28073a17974SMatthias Ringwald size = size_for_type(f) 281*760c6692SMatthias Ringwald access = event_getter_data_fixed.format(size=size, offset=offset) 28273a17974SMatthias Ringwald else: 283dbd33601SMatthias Ringwald access = param_read[f].format(offset=offset) 28473a17974SMatthias Ringwald size = size_for_type(f) 285*760c6692SMatthias Ringwald getters += event_getter.format(arg.lower(), access) 28673a17974SMatthias Ringwald offset += size 28773a17974SMatthias Ringwald to_string_args = '' 28873a17974SMatthias Ringwald for arg in args: 289*760c6692SMatthias Ringwald to_string_args += ' repr += ", %s = "\n' % arg 290*760c6692SMatthias Ringwald to_string_args += ' repr += str(self.get_%s())\n' % arg.lower() 291*760c6692SMatthias Ringwald to_string_method = event_to_string.format(event_name, to_string_args) 292*760c6692SMatthias Ringwald fout.write(event_template.format(event_name, getters, to_string_method)) 29373a17974SMatthias Ringwald 29473a17974SMatthias Ringwalddef event_supported(event_name): 29573a17974SMatthias Ringwald parts = event_name.split('_') 29673a17974SMatthias Ringwald return parts[0] in ['ATT', 'BTSTACK', 'DAEMON', 'L2CAP', 'RFCOMM', 'SDP', 'GATT', 'GAP', 'HCI', 'SM', 'BNEP'] 29773a17974SMatthias Ringwald 29873a17974SMatthias Ringwalddef class_name_for_event(event_name): 29973a17974SMatthias Ringwald return parser.camel_case(event_name.replace('SUBEVENT','EVENT')) 30073a17974SMatthias Ringwald 301b2929115SMatthias Ringwalddef create_events(fout, events): 30273a17974SMatthias Ringwald global gen_path 30373a17974SMatthias Ringwald gen_path_events = gen_path + '/event' 30473a17974SMatthias Ringwald parser.assert_dir(gen_path_events) 30573a17974SMatthias Ringwald 30673a17974SMatthias Ringwald for event_type, event_name, format, args in events: 30773a17974SMatthias Ringwald if not event_supported(event_name): 30873a17974SMatthias Ringwald continue 30973a17974SMatthias Ringwald class_name = class_name_for_event(event_name) 310b2929115SMatthias Ringwald create_event(fout, class_name, format, args) 31173a17974SMatthias Ringwald 31273a17974SMatthias Ringwald 31373a17974SMatthias Ringwalddef create_event_factory(events, subevents, defines): 31473a17974SMatthias Ringwald global gen_path 315*760c6692SMatthias Ringwald global event_factory_event 316*760c6692SMatthias Ringwald global event_factory_template 31773a17974SMatthias Ringwald 318b2929115SMatthias Ringwald outfile = '%s/event_factory.py' % gen_path 31973a17974SMatthias Ringwald 320*760c6692SMatthias Ringwald 32173a17974SMatthias Ringwald cases = '' 32273a17974SMatthias Ringwald for event_type, event_name, format, args in events: 32373a17974SMatthias Ringwald event_name = parser.camel_case(event_name) 324*760c6692SMatthias Ringwald cases += event_factory_event.format(event_type, event_name) 32573a17974SMatthias Ringwald subcases = '' 32673a17974SMatthias Ringwald for event_type, event_name, format, args in subevents: 32773a17974SMatthias Ringwald if not event_supported(event_name): 32873a17974SMatthias Ringwald continue 32973a17974SMatthias Ringwald class_name = class_name_for_event(event_name) 330*760c6692SMatthias Ringwald subcases += event_factory_subevent.format(event_type, class_name) 33173a17974SMatthias Ringwald 33273a17974SMatthias Ringwald with open(outfile, 'wt') as fout: 333*760c6692SMatthias Ringwald # header 334*760c6692SMatthias Ringwald fout.write(event_header) 335b2929115SMatthias Ringwald # event classes 336b2929115SMatthias Ringwald create_events(fout, events) 337b2929115SMatthias Ringwald create_events(fout, subevents) 338b2929115SMatthias Ringwald # 339*760c6692SMatthias Ringwald defines_text = '' 340*760c6692SMatthias Ringwald # python_defines_string(,defines) 341*760c6692SMatthias Ringwald fout.write(event_factory_template.format(defines_text, cases, subcases)) 34273a17974SMatthias Ringwald 34373a17974SMatthias Ringwald# find root 34473a17974SMatthias Ringwaldbtstack_root = os.path.abspath(os.path.dirname(sys.argv[0]) + '/..') 34573a17974SMatthias Ringwaldgen_path = btstack_root + '/platform/daemon/binding/python/btstack/' 34673a17974SMatthias Ringwald 34773a17974SMatthias Ringwald 34873a17974SMatthias Ringwald# read defines from hci_cmds.h and hci.h 34973a17974SMatthias Ringwalddefines = parser.parse_defines() 35073a17974SMatthias Ringwald 35173a17974SMatthias Ringwald# parse commands 3529fb9416bSMatthias Ringwaldcommands = parser.parse_commands(camel_case=False) 35373a17974SMatthias Ringwald 35473a17974SMatthias Ringwald# parse bluetooth.h to get used events 355b2929115SMatthias Ringwald(events, le_events, event_types) = parser.parse_events() 35673a17974SMatthias Ringwald 35773a17974SMatthias Ringwald# create events, le meta events, event factory, and 358b2929115SMatthias Ringwaldcreate_event_factory(events, le_events, event_types) 35973a17974SMatthias Ringwaldcreate_command_builder(commands) 36073a17974SMatthias Ringwald 36173a17974SMatthias Ringwald# done 36273a17974SMatthias Ringwaldprint('Done!') 363