xref: /btstack/example/le_mitm.c (revision 4a9eead824c50b40e12b6f72611a74a3f57a47f6)
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         default:
467             break;
468     }
469 }
470 
471 static void att_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *packet, uint16_t size){
472     att_pdu_t * pdu;
473     switch (packet_type){
474         case ATT_DATA_PACKET:
475             printf("[%10s] ", mitm_name_for_handle(handle));
476             printf_hexdump(packet, size);
477             pdu = btstack_memory_att_pdu_get();
478             if (!pdu) break;
479             // handle att mtu exchange directly
480             if (packet[0] == ATT_EXCHANGE_MTU_REQUEST){
481                 pdu->handle = handle;
482                 pdu->len = 3;
483                 pdu->data[0] = ATT_EXCHANGE_MTU_RESPONSE;
484                 little_endian_store_16(pdu->data, 1, MAX_ATT_MTU);
485             } else {
486                 btstack_assert(size <= MAX_ATT_MTU);
487                 pdu->handle = mitm_opposite_handle(handle);
488                 pdu->len = (uint8_t) size;
489                 memcpy(pdu->data, packet, size);
490             }
491             btstack_linked_list_add_tail(&outgoing_att_pdus, (btstack_linked_item_t *) pdu);
492             mitm_request_to_send();
493             break;
494         case HCI_EVENT_PACKET:
495             if (packet[0] == L2CAP_EVENT_CAN_SEND_NOW) {
496                 // send next packet
497                 pdu = (att_pdu_t *) btstack_linked_list_pop(&outgoing_att_pdus);
498                 if (pdu == NULL) break;
499                 l2cap_send_connectionless(pdu->handle, L2CAP_CID_ATTRIBUTE_PROTOCOL, pdu->data, pdu->len);
500                 btstack_memory_att_pdu_free(pdu);
501                 // request to send again if more packets queued
502                 mitm_request_to_send();
503             }
504             break;
505         default:
506             break;
507     }
508 }
509 
510 static void stdin_process(char cmd) {
511     unsigned int index;
512     switch(state){
513         case TC_OFF:
514             break;
515         case TC_SCANNING:
516             if ((cmd >= '0') && (cmd <= '9')){
517                 index = cmd - '0';
518             } else if ((cmd >= 'a') && (cmd <= 'z')){
519                 index = cmd - 'a' + 10;
520             } else {
521                 break;
522             }
523             if (index >= devices_found) break;
524             mitm_connect(index);
525             break;
526         case TC_CONNECTED:
527             switch (cmd){
528                 case 'p':
529                     printf("[-] Start pairing / encryption with Peripheral\n");
530                     sm_request_pairing(remote_peripheral_handle);
531                     break;
532                 default:
533                     mitm_console_connected_menu();
534                     break;
535             }
536             break;
537         default:
538             break;
539     }
540 }
541 
542 int btstack_main(int argc, const char * argv[]);
543 int btstack_main(int argc, const char * argv[]){
544 
545     (void)argc;
546     (void)argv;
547 
548     l2cap_init();
549 
550     l2cap_register_fixed_channel(att_packet_handler, L2CAP_CID_ATTRIBUTE_PROTOCOL);
551 
552     sm_init();
553 
554     hci_event_callback_registration.callback = &hci_event_handler;
555     hci_add_event_handler(&hci_event_callback_registration);
556 
557     sm_event_callback_registration.callback = &hci_event_handler;
558     sm_add_event_handler(&sm_event_callback_registration);
559 
560 #ifndef HAVE_MALLOC
561     btstack_memory_pool_create(&att_pdu_pool, att_pdu_storage, MAX_NUM_ATT_PDUS, sizeof(att_pdu_t));
562 #endif
563 
564 #ifdef HAVE_BTSTACK_STDIN
565     btstack_stdin_setup(stdin_process);
566 #endif
567 
568     // turn on!
569     hci_power_control(HCI_POWER_ON);
570 
571     return 0;
572 }
573 /* EXAMPLE_END */
574