1#!/usr/bin/env python3 2# BlueKitchen GmbH (c) 2014 3 4import btstack_parser as parser 5 6print(''' 7Java binding generator for BTstack 8Copyright 2014, BlueKitchen GmbH 9''') 10 11# com.bluekitchen.btstack.BTstack.java templates 12java_btstack_header = \ 13'''/** 14 * BTstack Client Library 15 */ 16 17package %s; 18 19public class BTstack extends BTstackClient { 20 21''' 22java_btstack_command = ''' 23 public boolean %s(%s){ 24 // %s 25 int command_len = %s; 26 byte[] command = new byte[command_len]; 27 Util.storeBt16(command, 0, Util.opcode(%s, %s)); 28 int offset = 2; 29 Util.storeByte(command, offset, command_len - 3); 30 offset++; 31%s 32 Packet packet = new Packet(Packet.HCI_COMMAND_PACKET, 0, command, command.length); 33 return sendPacket(packet); 34 } 35''' 36java_btstack_footer = ''' 37} 38''' 39 40# com.bluekitchen.btstack.EventFactory template 41java_event_factory_template = \ 42'''package {0}; 43 44import {0}.event.*; 45 46public class EventFactory {{ 47 48 /** @brief event codes */ 49 50{1} 51 public static Event eventForPacket(Packet packet){{ 52 int eventType = Util.readByte(packet.getBuffer(), 0); 53 switch (eventType){{ 54{2} 55 case 0x3e: // LE_META_EVENT 56 int subEventType = Util.readByte(packet.getBuffer(), 2); 57 switch (subEventType){{ 58{3} 59 default: 60 return new Event(packet); 61 }} 62 63 default: 64 return new Event(packet); 65 }} 66 }} 67}} 68''' 69java_event_factory_event = ''' 70 case {0}: 71 return new {1}(packet); 72''' 73java_event_factory_subevent = ''' 74 case {0}: 75 return new {1}(packet); 76''' 77 78# com.bluekitchen.btstack.events.* template 79java_event_template = \ 80'''package {0}.event; 81 82import {0}.*; 83 84public class {1} extends Event {{ 85 86 public {1}(Packet packet) {{ 87 super(packet); 88 }} 89 {2} 90 {3} 91}} 92''' 93 94java_event_getter = \ 95''' 96 /** 97 * @return {1} as {0} 98 */ 99 public {0} get{1}(){{ 100 {2} 101 }} 102''' 103 104java_event_getter_data = \ 105'''int len = get{0}(); 106 byte[] result = new byte[len]; 107 System.arraycopy(data, {1}, result, 0, len); 108 return result;''' 109 110java_event_getter_data_fixed = \ 111'''int len = {0}; 112 byte[] result = new byte[len]; 113 System.arraycopy(data, {1}, result, 0, len); 114 return result;''' 115 116java_event_getter_remaining_data = \ 117'''int len = getPayloadLen() - {0}; 118 byte[] result = new byte[len]; 119 System.arraycopy(data, {0}, result, 0, len); 120 return result;''' 121 122java_event_to_string = \ 123''' 124 public String toString(){{ 125 StringBuffer t = new StringBuffer(); 126 t.append("{0} < type = "); 127 t.append(String.format("0x%02x, ", getEventType())); 128 t.append(getEventType()); 129{1} t.append(" >"); 130 return t.toString(); 131 }} 132''' 133 134 135# global variables/defines 136package ='com.bluekitchen.btstack' 137gen_path = 'gen/' + package.replace('.', '/') 138 139defines = dict() 140defines_used = set() 141 142def java_type_for_btstack_type(type): 143 param_types = { '1' : 'int', '2' : 'int', '3' : 'int', '4' : 'long', 'H' : 'int', 'B' : 'BD_ADDR', 144 'D' : 'byte []', 'E' : 'byte [] ', 'N' : 'String' , 'P' : 'byte []', 'A' : 'byte []', 145 'R' : 'byte []', 'S' : 'byte []', 'Q' : 'byte []', 146 'J' : 'int', 'L' : 'int', 'V' : 'byte []', 'U' : 'BT_UUID', 147 'X' : 'GATTService', 'Y' : 'GATTCharacteristic', 'Z' : 'GATTCharacteristicDescriptor', 148 'T' : 'String'} 149 return param_types[type] 150 151def size_for_type(type): 152 param_sizes = { '1' : 1, '2' : 2, '3' : 3, '4' : 4, 'H' : 2, 'B' : 6, 'D' : 8, 'E' : 240, 'N' : 248, 'P' : 16, 153 'A' : 31, 'S' : -1, 'V': -1, 'J' : 1, 'L' : 2, 'Q' : 32, 'U' : 16, 'X' : 20, 'Y' : 24, 'Z' : 18, 'T':-1} 154 return param_sizes[type] 155 156def create_command_java(fout, name, ogf, ocf, format, params): 157 global java_btstack_command 158 159 ind = ' ' 160 param_store = { 161 '1' : 'Util.storeByte(command, offset, %s);', 162 'J' : 'Util.storeByte(command, offset, %s);', 163 '2' : 'Util.storeBt16(command, offset, %s);', 164 'H' : 'Util.storeBt16(command, offset, %s);', 165 'L' : 'Util.storeBt16(command, offset, %s);', 166 '3' : 'Util.storeBt24(command, offset, %s);', 167 '4' : 'Util.storeBt32(command, offset, %s);', 168 'D' : 'Util.storeBytes(command, offset, %s, 8);', 169 'E' : 'Util.storeBytes(command, offset, %s, 240);', 170 'P' : 'Util.storeBytes(command, offset, %s, 16);', 171 'Q' : 'Util.storeBytes(command, offset, %s, 32);', 172 'A' : 'Util.storeBytes(command, offset, %s, 31);', 173 'S' : 'Util.storeBytes(command, offset, %s);', 174 'B' : 'Util.storeBytes(command, offset, %s.getBytes());', 175 'U' : 'Util.storeBytes(command, offset, %s.getBytes());', 176 'X' : 'Util.storeBytes(command, offset, %s.getBytes());', 177 'Y' : 'Util.storeBytes(command, offset, %s.getBytes());', 178 'Z' : 'Util.storeBytes(command, offset, %s.getBytes());', 179 'N' : 'Util.storeString(command, offset, %s, 248);', 180 } 181 # method arguments 182 arg_counter = 1 183 args = [] 184 for param_type, arg_name in zip(format, params): 185 arg_type = java_type_for_btstack_type(param_type) 186 arg_size = size_for_type(param_type) 187 arg = (param_type, arg_type, arg_size, arg_name) 188 args.append(arg) 189 arg_counter += 1 190 191 # method argument declaration 192 args2 = [] 193 for arg in args: 194 args2.append('%s %s' % (arg[1], arg[3])) 195 args_string = ', '.join(args2) 196 197 # command size (opcode, len) 198 size_fixed = 3 199 size_var = '' 200 for arg in args: 201 size = arg[2] 202 if size > 0: 203 size_fixed += size 204 else: 205 size_var += ' + %s.length' % arg[3] 206 size_string = '%u%s' % (size_fixed, size_var) 207 208 store_params = '' 209 210 length_name = '' 211 for (param_type, arg_type, arg_size, arg_name) in args: 212 if param_type in ['L', 'J']: 213 length_name = arg_name 214 if param_type == 'V': 215 store_params += ind + 'Util.storeBytes(command, offset, %s, %s);' % (arg_name, length_name) + '\n'; 216 store_params += ind + 'offset += %s;\n' % length_name; 217 length_name = '' 218 else: 219 store_params += ind + (param_store[param_type] % arg_name) + '\n'; 220 size = arg_size 221 if size > 0: 222 store_params += ind + 'offset += %u;\n' % arg_size; 223 else: 224 store_params += ind + 'offset += %s.length;\n' % arg_name 225 226 fout.write( java_btstack_command % (name, args_string, format, size_string, ogf, ocf, store_params)) 227 228def mark_define_as_used(term): 229 if term.startswith('0'): 230 return 231 defines_used.add(term) 232 233def java_define_string(key): 234 global defines 235 if key in defines: 236 return ' public static final int %s = %s;\n' % (key, defines[key]) 237 else: 238 return ' // defines[%s] not set\n' % key 239 240def java_defines_string(keys): 241 return '\n'.join( map(java_define_string, sorted(keys))) 242 243def create_btstack_java(commands): 244 global gen_path 245 parser.assert_dir(gen_path) 246 247 outfile = '%s/BTstack.java' % gen_path 248 249 with open(outfile, 'wt') as fout: 250 251 fout.write(java_btstack_header % package) 252 253 for command in commands: 254 (command_name, ogf, ocf, format, params) = command 255 create_command_java(fout, command_name, ogf, ocf, format, params); 256 mark_define_as_used(ogf) 257 mark_define_as_used(ocf) 258 259 fout.write('\n /** defines used */\n\n') 260 for key in sorted(defines_used): 261 fout.write(java_define_string(key)) 262 263 fout.write(java_btstack_footer) 264 265def create_event(event_name, format, args): 266 global gen_path 267 global package 268 global java_event_template 269 270 param_read = { 271 '1' : 'return Util.readByte(data, %u);', 272 'J' : 'return Util.readByte(data, %u);', 273 '2' : 'return Util.readBt16(data, %u);', 274 'H' : 'return Util.readBt16(data, %u);', 275 'L' : 'return Util.readBt16(data, %u);', 276 '3' : 'return Util.readBt24(data, %u);', 277 '4' : 'return Util.readBt32(data, %u);', 278 'B' : 'return Util.readBdAddr(data, %u);', 279 'X' : 'return Util.readGattService(data, %u);', 280 'Y' : 'return Util.readGattCharacteristic(data, %u);', 281 'Z' : 'return Util.readGattCharacteristicDescriptor(data, %u);', 282 'T' : 'int offset = %u; \n return Util.getText(data, offset, getPayloadLen()-offset);', 283 'N' : 'return Util.getText(data, %u, 248);', 284 'D' : 'Util.storeBytes(data, %u, 8);', 285 'Q' : 'Util.storeBytes(data, %u, 32);', 286 # 'E' : 'Util.storeBytes(data, %u, 240);', 287 # 'P' : 'Util.storeBytes(data, %u, 16);', 288 # 'A' : 'Util.storeBytes(data, %u, 31);', 289 # 'S' : 'Util.storeBytes(data, %u);' 290 } 291 292 gen_event_path = '%s/event' % gen_path 293 outfile = '%s/%s.java' % (gen_event_path, event_name) 294 with open(outfile, 'wt') as fout: 295 offset = 2 296 getters = '' 297 length_name = '' 298 for f, arg in zip(format, args): 299 # just remember name 300 if f in ['L','J']: 301 length_name = parser.camel_case(arg) 302 if f == 'R': 303 # remaining data 304 access = java_event_getter_remaining_data.format(offset) 305 size = 0 306 elif f == 'V': 307 access = java_event_getter_data.format(length_name, offset) 308 size = 0 309 elif f in ['D', 'Q']: 310 size = size_for_type(f) 311 access = java_event_getter_data_fixed.format(size, offset) 312 else: 313 access = param_read[f] % offset 314 size = size_for_type(f) 315 getters += java_event_getter.format(java_type_for_btstack_type(f), parser.camel_case(arg), access) 316 offset += size 317 to_string_args = '' 318 for arg in args: 319 to_string_args += ' t.append(", %s = ");\n' % arg 320 to_string_args += ' t.append(get%s());\n' % parser.camel_case(arg) 321 to_string_method = java_event_to_string.format(event_name, to_string_args) 322 fout.write(java_event_template.format(package, event_name, getters, to_string_method)) 323 324def event_supported(event_name): 325 parts = event_name.split('_') 326 return parts[0] in ['ATT', 'BTSTACK', 'DAEMON', 'L2CAP', 'RFCOMM', 'SDP', 'GATT', 'GAP', 'HCI', 'SM', 'BNEP'] 327 328def class_name_for_event(event_name): 329 return parser.camel_case(event_name.replace('SUBEVENT','EVENT')) 330 331def create_events(events): 332 global gen_path 333 gen_path_events = gen_path + '/event' 334 parser.assert_dir(gen_path_events) 335 336 for event_type, event_name, format, args in events: 337 if not event_supported(event_name): 338 continue 339 class_name = class_name_for_event(event_name) 340 create_event(class_name, format, args) 341 342 343def create_event_factory(events, subevents, defines): 344 global gen_path 345 global package 346 global java_event_factory_event 347 global java_event_factory_template 348 349 outfile = '%s/EventFactory.java' % gen_path 350 351 cases = '' 352 for event_type, event_name, format, args in events: 353 event_name = parser.camel_case(event_name) 354 cases += java_event_factory_event.format(event_type, event_name) 355 subcases = '' 356 for event_type, event_name, format, args in subevents: 357 if not event_supported(event_name): 358 continue 359 class_name = class_name_for_event(event_name) 360 print(class_name) 361 subcases += java_event_factory_subevent.format(event_type, class_name) 362 363 with open(outfile, 'wt') as fout: 364 defines_text = java_defines_string(defines) 365 fout.write(java_event_factory_template.format(package, defines_text, cases, subcases)) 366 367 368# read defines from hci_cmds.h and hci.h 369defines = parser.parse_defines() 370 371# # parse commands 372commands = parser.parse_commands() 373 374# parse bluetooth.h to get used events 375(events, le_events, event_types) = parser.parse_events() 376 377# create events, le meta events, event factory, and 378create_events(events) 379create_events(le_events) 380create_event_factory(events, le_events, event_types) 381create_btstack_java(commands) 382 383# done 384print('Done!') 385