1 /* 2 * Copyright (C) 2019 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the copyright holders nor the names of 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 4. Any redistribution, use, or modification is done solely for 17 * personal benefit and not for any commercial purpose or for 18 * monetary gain. 19 * 20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 24 * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * Please inquire about commercial licensing options at 34 * [email protected] 35 * 36 */ 37 38 #define BTSTACK_FILE__ "le_mitm.c" 39 40 // ***************************************************************************** 41 /* EXAMPLE_START(le_mitm): LE Man-in-the-Middle Tool 42 * 43 * @text The example first does an LE scan and allows the user to select a Peripheral 44 * device. Then, it connects to the Peripheral and starts advertising with the same 45 * data as the Peripheral device. 46 * ATT Requests and responses are forwarded between the peripheral and the central 47 * Security requests are handled locally. 48 * 49 * @note A Bluetooth Controller that supports Central and Peripheral Role 50 * at the same time is required for this example. See chipset/README.md 51 * 52 */ 53 // ***************************************************************************** 54 55 56 #include <inttypes.h> 57 #include <stdint.h> 58 #include <stdio.h> 59 #include <string.h> 60 61 #include "btstack.h" 62 63 // Number of devices shown during scanning 64 #define MAX_NUM_DEVICES 36 65 66 // Max number of ATT PTUs to queue (if malloc is not used) 67 #define MAX_NUM_ATT_PDUS 20 68 69 // Max ATT MTU - can be increased if needed 70 #define MAX_ATT_MTU ATT_DEFAULT_MTU 71 72 typedef struct { 73 bd_addr_t addr; 74 bd_addr_type_t addr_type; 75 int8_t rssi; 76 uint8_t ad_len; 77 uint8_t ad_data[31]; 78 uint8_t scan_len; 79 uint8_t scan_data[31]; 80 } device_info_t; 81 82 typedef struct { 83 btstack_linked_item_t item; 84 hci_con_handle_t handle; 85 uint8_t len; 86 uint8_t data[MAX_ATT_MTU]; 87 } att_pdu_t; 88 89 typedef enum { 90 TC_OFF, 91 TC_SCANNING, 92 TC_W4_CONNECT, 93 TC_CONNECTED, 94 } app_state_t; 95 96 static uint16_t devices_found; 97 static device_info_t devices[MAX_NUM_DEVICES]; 98 99 static uint16_t remote_peripheral_index; 100 static bd_addr_t remote_peripheral_addr; 101 static bd_addr_type_t remote_peripheral_addr_type; 102 static hci_con_handle_t remote_peripheral_handle; 103 104 static hci_con_handle_t remote_central_handle; 105 106 static btstack_linked_list_t outgoing_att_pdus; 107 108 static app_state_t state = TC_OFF; 109 static btstack_packet_callback_registration_t hci_event_callback_registration; 110 static btstack_packet_callback_registration_t sm_event_callback_registration; 111 112 static const char * ad_types[] = { 113 "", 114 "Flags", 115 "Incomplete 16-bit UUIDs", 116 "Complete 16-bit UUIDs", 117 "Incomplete 32-bit UUIDs", 118 "Complete 32-bit UUIDs", 119 "Incomplete 128-bit UUIDs", 120 "Complete 128-bit UUIDs", 121 "Short Name", 122 "Complete Name", 123 "Tx Power Level", 124 "", 125 "", 126 "Class of Device", 127 "Simple Pairing Hash C", 128 "Simple Pairing Randomizer R", 129 "Device ID", 130 "Security Manager TK Value", 131 "Slave Connection Interval Range", 132 "", 133 "16-bit Solicitation UUIDs", 134 "128-bit Solicitation UUIDs", 135 "Service Data", 136 "Public Target Address", 137 "Random Target Address", 138 "Appearance", 139 "Advertising Interval" 140 }; 141 142 static const char * adv_failed_warning = "\n" 143 "[!] Start advertising failed!\n" 144 "[!] Make sure your Bluetooth Controller supports Central and Peripheral Roles at the same time.\n\n"; 145 146 // att pdu pool implementation 147 #ifndef HAVE_MALLOC 148 static att_pdu_t att_pdu_storage[MAX_NUM_ATT_PDUS]; 149 static btstack_memory_pool_t att_pdu_pool; 150 static att_pdu_t * btstack_memory_att_pdu_get(void){ 151 void * buffer = btstack_memory_pool_get(&att_pdu_pool); 152 if (buffer){ 153 memset(buffer, 0, sizeof(att_pdu_t)); 154 } 155 return (att_pdu_t *) buffer; 156 } 157 static void btstack_memory_att_pdu_free(att_pdu_t *att_pdu){ 158 btstack_memory_pool_free(&att_pdu_pool, att_pdu); 159 } 160 #else 161 static att_pdu_t * btstack_memory_att_pdu_get(void){ 162 void * buffer = malloc(sizeof(att_pdu_t)); 163 if (buffer){ 164 memset(buffer, 0, sizeof(att_pdu_t)); 165 } 166 return (att_pdu_t *) buffer; 167 } 168 static void btstack_memory_att_pdu_free(att_pdu_t * att_pdu){ 169 free(att_pdu); 170 } 171 #endif 172 173 static void mitm_start_scan(btstack_timer_source_t * ts){ 174 UNUSED(ts); 175 printf("[-] Start scanning\n"); 176 printf("To select device, enter advertisement number:\n"); 177 state = TC_SCANNING; 178 gap_set_scan_parameters(0,0x0030, 0x0030); 179 gap_start_scan(); 180 } 181 182 static void mitm_connect(uint16_t index){ 183 // stop scanning, and connect to the device 184 gap_stop_scan(); 185 state = TC_W4_CONNECT; 186 remote_peripheral_index = index; 187 memcpy(remote_peripheral_addr, devices[index].addr, 6); 188 remote_peripheral_addr_type = devices[index].addr_type; 189 printf("\n"); 190 printf("[-] Connecting to Peripheral %s\n", bd_addr_to_str(remote_peripheral_addr)); 191 gap_auto_connection_start(remote_peripheral_addr_type, remote_peripheral_addr); 192 } 193 194 static void mitm_start_advertising(void){ 195 // set adv + scan data if available 196 if (devices[remote_peripheral_index].ad_len > 0){ 197 gap_advertisements_set_data(devices[remote_peripheral_index].ad_len, devices[remote_peripheral_index].ad_data); 198 printf("[-] Setup adv data (len %02u): ", devices[remote_peripheral_index].ad_len); 199 printf_hexdump(devices[remote_peripheral_index].ad_data, devices[remote_peripheral_index].ad_len); 200 } 201 if (devices[remote_peripheral_index].scan_len > 0){ 202 gap_scan_response_set_data(devices[remote_peripheral_index].scan_len, devices[remote_peripheral_index].scan_data); 203 printf("[-] Setup scan data (len %02u): ", devices[remote_peripheral_index].scan_len); 204 printf_hexdump(devices[remote_peripheral_index].ad_data, devices[remote_peripheral_index].ad_len); 205 } 206 uint16_t adv_int_min = 0x0030; 207 uint16_t adv_int_max = 0x0030; 208 uint8_t adv_type = 0; 209 bd_addr_t null_addr; 210 memset(null_addr, 0, 6); 211 gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00); 212 gap_advertisements_enable(1); 213 } 214 215 static void mitm_print_advertisement(uint16_t index) { 216 // get character for index 217 char c; 218 if (index < 10) { 219 c = '0' + index; 220 } else { 221 c = 'a' + (index - 10); 222 } 223 224 printf("%c. %s (%-3d dBm)", c, bd_addr_to_str(devices[index].addr), devices[index].rssi); 225 226 ad_context_t context; 227 bd_addr_t address; 228 uint8_t uuid_128[16]; 229 for (ad_iterator_init(&context, devices[index].ad_len, devices[index].ad_data); ad_iterator_has_more( 230 &context); ad_iterator_next(&context)) { 231 uint8_t data_type = ad_iterator_get_data_type(&context); 232 uint8_t size = ad_iterator_get_data_len(&context); 233 const uint8_t *data = ad_iterator_get_data(&context); 234 235 if (data_type > 0 && data_type < 0x1B) { 236 printf(" - %s: ", ad_types[data_type]); 237 } 238 uint8_t i; 239 switch (data_type) { 240 case BLUETOOTH_DATA_TYPE_FLAGS: 241 printf("0x%02x", data[0]); 242 break; 243 case BLUETOOTH_DATA_TYPE_INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS: 244 case BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS: 245 case BLUETOOTH_DATA_TYPE_LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS: 246 for (i = 0; i < size; i += 2) { 247 printf("%02X ", little_endian_read_16(data, i)); 248 } 249 break; 250 case BLUETOOTH_DATA_TYPE_INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS: 251 case BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS: 252 case BLUETOOTH_DATA_TYPE_LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS: 253 for (i = 0; i < size; i += 4) { 254 printf("%04"PRIX32, little_endian_read_32(data, i)); 255 } 256 break; 257 case BLUETOOTH_DATA_TYPE_INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS: 258 case BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS: 259 case BLUETOOTH_DATA_TYPE_LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS: 260 reverse_128(data, uuid_128); 261 printf("%s", uuid128_to_str(uuid_128)); 262 break; 263 case BLUETOOTH_DATA_TYPE_SHORTENED_LOCAL_NAME: 264 case BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME: 265 for (i = 0; i < size; i++) { 266 printf("%c", (char) (data[i])); 267 } 268 break; 269 case BLUETOOTH_DATA_TYPE_TX_POWER_LEVEL: 270 printf("%d dBm", *(int8_t *) data); 271 break; 272 case BLUETOOTH_DATA_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE: 273 printf("Connection Interval Min = %u ms, Max = %u ms", little_endian_read_16(data, 0) * 5 / 4, 274 little_endian_read_16(data, 2) * 5 / 4); 275 break; 276 case BLUETOOTH_DATA_TYPE_SERVICE_DATA: 277 printf_hexdump(data, size); 278 break; 279 case BLUETOOTH_DATA_TYPE_PUBLIC_TARGET_ADDRESS: 280 case BLUETOOTH_DATA_TYPE_RANDOM_TARGET_ADDRESS: 281 reverse_bd_addr(data, address); 282 printf("%s", bd_addr_to_str(address)); 283 break; 284 case BLUETOOTH_DATA_TYPE_APPEARANCE: 285 // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml 286 printf("%02X", little_endian_read_16(data, 0)); 287 break; 288 case BLUETOOTH_DATA_TYPE_ADVERTISING_INTERVAL: 289 printf("%u ms", little_endian_read_16(data, 0) * 5 / 8); 290 break; 291 case BLUETOOTH_DATA_TYPE_3D_INFORMATION_DATA: 292 printf_hexdump(data, size); 293 break; 294 case BLUETOOTH_DATA_TYPE_MANUFACTURER_SPECIFIC_DATA: 295 case BLUETOOTH_DATA_TYPE_CLASS_OF_DEVICE: 296 case BLUETOOTH_DATA_TYPE_SIMPLE_PAIRING_HASH_C: 297 case BLUETOOTH_DATA_TYPE_SIMPLE_PAIRING_RANDOMIZER_R: 298 case BLUETOOTH_DATA_TYPE_DEVICE_ID: 299 case BLUETOOTH_DATA_TYPE_SECURITY_MANAGER_OUT_OF_BAND_FLAGS: 300 default: 301 break; 302 } 303 } 304 printf("\n"); 305 } 306 307 static void mitm_handle_adv(uint8_t * packet){ 308 // get addr and type 309 bd_addr_t remote_addr; 310 gap_event_advertising_report_get_address(packet, remote_addr); 311 bd_addr_type_t remote_addr_type = gap_event_advertising_report_get_address_type(packet); 312 uint8_t adv_event_type = gap_event_advertising_report_get_advertising_event_type(packet); 313 bool is_scan_response = adv_event_type == 2 || adv_event_type == 4; 314 315 // find remote in list 316 uint16_t i; 317 for (i=0;i<devices_found;i++) { 318 if (memcmp(remote_addr, devices[i].addr, 6) != 0) continue; 319 if (remote_addr_type != devices[i].addr_type) continue; 320 break; 321 } 322 323 if (i == MAX_NUM_DEVICES) return; 324 325 if (devices_found == i){ 326 // skip first event with scan response data (should not happen) 327 if (is_scan_response) return; 328 memset(&devices[i], 0, sizeof(device_info_t)); 329 devices[i].rssi = (int8_t) gap_event_advertising_report_get_rssi(packet); 330 devices[i].addr_type = remote_addr_type; 331 memcpy(devices[i].addr, remote_addr, 6); 332 devices[i].ad_len = gap_event_advertising_report_get_data_length(packet); 333 memcpy(devices[i].ad_data, gap_event_advertising_report_get_data(packet), devices[i].ad_len); 334 mitm_print_advertisement(i); 335 devices_found++; 336 return; 337 } 338 339 // store scan data 340 if (!is_scan_response) return; 341 devices[i].scan_len = gap_event_advertising_report_get_data_length(packet); 342 memcpy(devices[i].scan_data, gap_event_advertising_report_get_data(packet), devices[i].scan_len); 343 } 344 345 static void mitm_console_connected_menu(void){ 346 printf("=== Connected menu ===\n"); 347 printf("p - Pair Peripheral\n"); 348 } 349 350 static hci_con_handle_t mitm_opposite_handle(hci_con_handle_t handle){ 351 if (handle == remote_peripheral_handle) { 352 return remote_central_handle; 353 } else { 354 return remote_peripheral_handle; 355 } 356 } 357 358 static void mitm_request_to_send(void){ 359 // request to send again if more packets queued 360 if (btstack_linked_list_empty(&outgoing_att_pdus)) return; 361 att_pdu_t * pdu = (att_pdu_t *) btstack_linked_list_get_first_item((&outgoing_att_pdus)); 362 l2cap_request_can_send_fix_channel_now_event(pdu->handle, L2CAP_CID_ATTRIBUTE_PROTOCOL); 363 } 364 365 static const char * mitm_name_for_handle(hci_con_handle_t handle){ 366 if (handle == remote_peripheral_handle) return "Peripheral"; 367 if (handle == remote_central_handle) return "Central"; 368 return "(unknown handle)'"; 369 } 370 371 static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 372 UNUSED(channel); 373 UNUSED(size); 374 375 if (packet_type != HCI_EVENT_PACKET) return; 376 377 uint8_t event = hci_event_packet_get_type(packet); 378 hci_con_handle_t connection_handle; 379 uint32_t passkey; 380 381 switch (event) { 382 case BTSTACK_EVENT_STATE: 383 // BTstack activated, get started 384 if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) { 385 mitm_start_scan(NULL); 386 state = TC_SCANNING; 387 } else { 388 state = TC_OFF; 389 } 390 break; 391 case GAP_EVENT_ADVERTISING_REPORT: 392 if (state != TC_SCANNING) return; 393 mitm_handle_adv(packet); 394 break; 395 case HCI_EVENT_COMMAND_COMPLETE: 396 // warn if adv enable fails 397 if (hci_event_command_complete_get_command_opcode(packet) != hci_le_set_advertise_enable.opcode) break; 398 if (hci_event_command_complete_get_return_parameters(packet)[0] == ERROR_CODE_SUCCESS) break; 399 printf("%s", adv_failed_warning); 400 break; 401 case HCI_EVENT_LE_META: 402 // wait for connection complete 403 if (hci_event_le_meta_get_subevent_code(packet) != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break; 404 switch (state){ 405 case TC_W4_CONNECT: 406 state = TC_CONNECTED; 407 remote_peripheral_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); 408 printf("[-] Peripheral connected\n"); 409 mitm_start_advertising(); 410 printf ("You can connect now!\n"); 411 printf("\n"); 412 mitm_console_connected_menu(); 413 break; 414 case TC_CONNECTED: 415 remote_central_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); 416 printf("[-] Central connected!\n"); 417 break; 418 default: 419 break; 420 } 421 break; 422 case HCI_EVENT_DISCONNECTION_COMPLETE: 423 // unregister listener 424 connection_handle = HCI_CON_HANDLE_INVALID; 425 printf("[-] %s disconnected", mitm_name_for_handle(connection_handle)); 426 if (connection_handle == remote_peripheral_handle){ 427 mitm_start_scan(NULL); 428 state = TC_SCANNING; 429 } 430 break; 431 case SM_EVENT_JUST_WORKS_REQUEST: 432 connection_handle = sm_event_just_works_request_get_handle(packet); 433 printf("[-] %s request 'Just Works' pairing\n", mitm_name_for_handle(connection_handle)); 434 sm_just_works_confirm(connection_handle); 435 break; 436 case SM_EVENT_NUMERIC_COMPARISON_REQUEST: 437 passkey = sm_event_numeric_comparison_request_get_passkey(packet); 438 connection_handle = sm_event_numeric_comparison_request_get_handle(packet); 439 printf("[-] %s accepting numeric comparison: %"PRIu32"\n", mitm_name_for_handle(connection_handle), passkey); 440 sm_numeric_comparison_confirm(connection_handle); 441 break; 442 case SM_EVENT_PASSKEY_DISPLAY_NUMBER: 443 passkey = sm_event_passkey_display_number_get_passkey(packet); 444 connection_handle = sm_event_passkey_display_number_get_handle(packet); 445 printf("[-] %s display passkey: %"PRIu32"\n", mitm_name_for_handle(connection_handle), passkey); 446 break; 447 case SM_EVENT_PAIRING_COMPLETE: 448 connection_handle = sm_event_pairing_complete_get_handle(packet); 449 switch (sm_event_pairing_complete_get_status(packet)){ 450 case ERROR_CODE_SUCCESS: 451 printf("[-] %s pairing complete, success\n", mitm_name_for_handle(connection_handle)); 452 break; 453 case ERROR_CODE_CONNECTION_TIMEOUT: 454 printf("[-] %s pairing failed, timeout\n", mitm_name_for_handle(connection_handle)); 455 break; 456 case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION: 457 printf("[-] %s pairing failed, disconnected\n", mitm_name_for_handle(connection_handle)); 458 break; 459 case ERROR_CODE_AUTHENTICATION_FAILURE: 460 printf("[-] %s pairing failed, reason = %u\n", mitm_name_for_handle(connection_handle), sm_event_pairing_complete_get_reason(packet)); 461 break; 462 default: 463 break; 464 } 465 break; 466 case SM_EVENT_REENCRYPTION_COMPLETE: 467 connection_handle = sm_event_reencryption_complete_get_handle(packet); 468 printf("[-] %s Re-encryption complete, success\n", mitm_name_for_handle(connection_handle)); 469 break; 470 default: 471 break; 472 } 473 } 474 475 static void att_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *packet, uint16_t size){ 476 att_pdu_t * pdu; 477 switch (packet_type){ 478 case ATT_DATA_PACKET: 479 printf("[%10s] ", mitm_name_for_handle(handle)); 480 printf_hexdump(packet, size); 481 pdu = btstack_memory_att_pdu_get(); 482 if (!pdu) break; 483 // handle att mtu exchange directly 484 if (packet[0] == ATT_EXCHANGE_MTU_REQUEST){ 485 pdu->handle = handle; 486 pdu->len = 3; 487 pdu->data[0] = ATT_EXCHANGE_MTU_RESPONSE; 488 little_endian_store_16(pdu->data, 1, MAX_ATT_MTU); 489 } else { 490 btstack_assert(size <= MAX_ATT_MTU); 491 pdu->handle = mitm_opposite_handle(handle); 492 pdu->len = (uint8_t) size; 493 memcpy(pdu->data, packet, size); 494 } 495 btstack_linked_list_add_tail(&outgoing_att_pdus, (btstack_linked_item_t *) pdu); 496 mitm_request_to_send(); 497 break; 498 case HCI_EVENT_PACKET: 499 if (packet[0] == L2CAP_EVENT_CAN_SEND_NOW) { 500 // send next packet 501 pdu = (att_pdu_t *) btstack_linked_list_pop(&outgoing_att_pdus); 502 if (pdu == NULL) break; 503 l2cap_send_connectionless(pdu->handle, L2CAP_CID_ATTRIBUTE_PROTOCOL, pdu->data, pdu->len); 504 btstack_memory_att_pdu_free(pdu); 505 // request to send again if more packets queued 506 mitm_request_to_send(); 507 } 508 break; 509 default: 510 break; 511 } 512 } 513 514 static void stdin_process(char cmd) { 515 unsigned int index; 516 switch(state){ 517 case TC_OFF: 518 break; 519 case TC_SCANNING: 520 if ((cmd >= '0') && (cmd <= '9')){ 521 index = cmd - '0'; 522 } else if ((cmd >= 'a') && (cmd <= 'z')){ 523 index = cmd - 'a' + 10; 524 } else { 525 break; 526 } 527 if (index >= devices_found) break; 528 mitm_connect(index); 529 break; 530 case TC_CONNECTED: 531 switch (cmd){ 532 case 'p': 533 printf("[-] Start pairing / encryption with Peripheral\n"); 534 sm_request_pairing(remote_peripheral_handle); 535 break; 536 default: 537 mitm_console_connected_menu(); 538 break; 539 } 540 break; 541 default: 542 break; 543 } 544 } 545 546 int btstack_main(int argc, const char * argv[]); 547 int btstack_main(int argc, const char * argv[]){ 548 549 (void)argc; 550 (void)argv; 551 552 l2cap_init(); 553 554 l2cap_register_fixed_channel(att_packet_handler, L2CAP_CID_ATTRIBUTE_PROTOCOL); 555 556 sm_init(); 557 558 hci_event_callback_registration.callback = &hci_event_handler; 559 hci_add_event_handler(&hci_event_callback_registration); 560 561 sm_event_callback_registration.callback = &hci_event_handler; 562 sm_add_event_handler(&sm_event_callback_registration); 563 564 #ifndef HAVE_MALLOC 565 btstack_memory_pool_create(&att_pdu_pool, att_pdu_storage, MAX_NUM_ATT_PDUS, sizeof(att_pdu_t)); 566 #endif 567 568 #ifdef HAVE_BTSTACK_STDIN 569 btstack_stdin_setup(stdin_process); 570 #endif 571 572 // turn on! 573 hci_power_control(HCI_POWER_ON); 574 575 return 0; 576 } 577 /* EXAMPLE_END */ 578