xref: /btstack/example/hog_boot_host_demo.c (revision cd5f23a3250874824c01a2b3326a9522fea3f99f)
1 /*
2  * Copyright (C) 2020 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 MATTHIAS
24  * RINGWALD 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__ "hog_boot_host_demo.c"
39 
40 /*
41  * hog_boot_host_demo.c
42  */
43 
44 /* EXAMPLE_START(hog_boot_host_demo): HID Boot Host LE
45  *
46  * @text This example implements a minimal HID-over-GATT Boot Host. It scans for LE HID devices, connects to it,
47  * discovers the Characteristics relevant for the HID Service and enables Notifications on them.
48  * It then dumps all Boot Keyboard and Mouse Input Reports
49  */
50 
51 #include <inttypes.h>
52 #include <stdio.h>
53 #include <btstack_tlv.h>
54 
55 #include "btstack_config.h"
56 #include "btstack.h"
57 
58 // TAG to store remote device address and type in TLV
59 #define TLV_TAG_HOGD ((((uint32_t) 'H') << 24 ) | (((uint32_t) 'O') << 16) | (((uint32_t) 'G') << 8) | 'D')
60 
61 typedef struct {
62     bd_addr_t addr;
63     bd_addr_type_t addr_type;
64 } le_device_addr_t;
65 
66 static enum {
67     W4_WORKING,
68     W4_HID_DEVICE_FOUND,
69     W4_CONNECTED,
70     W4_ENCRYPTED,
71     W4_HID_SERVICE_FOUND,
72     W4_HID_CHARACTERISTICS_FOUND,
73     W4_BOOT_KEYBOARD_ENABLED,
74     W4_BOOT_MOUSE_ENABLED,
75     READY,
76     W4_TIMEOUT_THEN_SCAN,
77     W4_TIMEOUT_THEN_RECONNECT,
78 } app_state;
79 
80 static le_device_addr_t remote_device;
81 static hci_con_handle_t connection_handle;
82 
83 // used for GATT queries
84 static gatt_client_service_t       hid_service;
85 static gatt_client_characteristic_t protocol_mode_characteristic;
86 static gatt_client_characteristic_t boot_keyboard_input_characteristic;
87 static gatt_client_characteristic_t boot_mouse_input_characteristic;
88 static gatt_client_notification_t   keyboard_notifications;
89 static gatt_client_notification_t   mouse_notifications;
90 
91 // used to implement connection timeout and reconnect timer
92 static btstack_timer_source_t connection_timer;
93 
94 // register for events from HCI/GAP and SM
95 static btstack_packet_callback_registration_t hci_event_callback_registration;
96 static btstack_packet_callback_registration_t sm_event_callback_registration;
97 
98 // used to store remote device in TLV
99 static const btstack_tlv_t * btstack_tlv_singleton_impl;
100 static void *                btstack_tlv_singleton_context;
101 
102 // Simplified US Keyboard with Shift modifier
103 
104 #define CHAR_ILLEGAL     0xff
105 #define CHAR_RETURN     '\n'
106 #define CHAR_ESCAPE      27
107 #define CHAR_TAB         '\t'
108 #define CHAR_BACKSPACE   0x7f
109 
110 /**
111  * English (US)
112  */
113 static const uint8_t keytable_us_none [] = {
114         CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,             /*   0-3 */
115         'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',                   /*  4-13 */
116         'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',                   /* 14-23 */
117         'u', 'v', 'w', 'x', 'y', 'z',                                       /* 24-29 */
118         '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',                   /* 30-39 */
119         CHAR_RETURN, CHAR_ESCAPE, CHAR_BACKSPACE, CHAR_TAB, ' ',            /* 40-44 */
120         '-', '=', '[', ']', '\\', CHAR_ILLEGAL, ';', '\'', 0x60, ',',       /* 45-54 */
121         '.', '/', CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,   /* 55-60 */
122         CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,             /* 61-64 */
123         CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,             /* 65-68 */
124         CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,             /* 69-72 */
125         CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,             /* 73-76 */
126         CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,             /* 77-80 */
127         CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,             /* 81-84 */
128         '*', '-', '+', '\n', '1', '2', '3', '4', '5',                       /* 85-97 */
129         '6', '7', '8', '9', '0', '.', 0xa7,                                 /* 97-100 */
130 };
131 
132 static const uint8_t keytable_us_shift[] = {
133         CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,             /*  0-3  */
134         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',                   /*  4-13 */
135         'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',                   /* 14-23 */
136         'U', 'V', 'W', 'X', 'Y', 'Z',                                       /* 24-29 */
137         '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',                   /* 30-39 */
138         CHAR_RETURN, CHAR_ESCAPE, CHAR_BACKSPACE, CHAR_TAB, ' ',            /* 40-44 */
139         '_', '+', '{', '}', '|', CHAR_ILLEGAL, ':', '"', 0x7E, '<',         /* 45-54 */
140         '>', '?', CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,   /* 55-60 */
141         CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,             /* 61-64 */
142         CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,             /* 65-68 */
143         CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,             /* 69-72 */
144         CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,             /* 73-76 */
145         CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,             /* 77-80 */
146         CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL,             /* 81-84 */
147         '*', '-', '+', '\n', '1', '2', '3', '4', '5',                       /* 85-97 */
148         '6', '7', '8', '9', '0', '.', 0xb1,                                 /* 97-100 */
149 };
150 
151 /**
152  * @section HOG Boot Keyboard Handler
153  * @text Boot Keyboard Input Report contains a report of format
154  * [ modifier, reserved, 6 x usage for key 1..6 from keyboard usage]
155  * Track new usages, map key usage to actual character and simulate terminal
156  */
157 
158 /* last_keys stores keyboard report to detect new key down events */
159 #define NUM_KEYS 6
160 static uint8_t last_keys[NUM_KEYS];
161 
162 static void handle_boot_keyboard_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
163     UNUSED(packet_type);
164     UNUSED(channel);
165     UNUSED(size);
166 
167     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
168     const uint8_t * data = gatt_event_notification_get_value(packet);
169 
170     uint8_t new_keys[NUM_KEYS];
171     memset(new_keys, 0, sizeof(new_keys));
172     int new_keys_count = 0;
173 
174     bool shift = (data[0] & 0x22) != 0;
175 
176     uint8_t key_index;
177     for (key_index = 0; key_index < NUM_KEYS; key_index++){
178 
179         uint16_t usage = data[2 + key_index];
180         if (usage == 0) continue;
181         if (usage >= sizeof(keytable_us_none)) continue;
182 
183         // store new keys
184         new_keys[new_keys_count++] = usage;
185 
186         // check if usage was used last time (and ignore in that case)
187         int i;
188         for (i=0;i<NUM_KEYS;i++){
189             if (usage == last_keys[i]){
190                 usage = 0;
191             }
192         }
193         if (usage == 0) continue;
194 
195         // lookup character based on usage + shift modifier
196         uint8_t key;
197         if (shift){
198             key = keytable_us_shift[usage];
199         } else {
200             key = keytable_us_none[usage];
201         }
202         if (key == CHAR_ILLEGAL) continue;
203         if (key == CHAR_BACKSPACE){
204             printf("\b \b");    // go back one char, print space, go back one char again
205             continue;
206         }
207         printf("%c", key);
208     }
209 
210     // store current as last report
211     memcpy(last_keys, new_keys, NUM_KEYS);
212 }
213 
214 /**
215  * @section HOG Boot Mouse Handler
216  * @text Boot Mouse Input Report contains a report of format
217  * [ buttons, dx, dy, dz = scroll wheel]
218  * Decode packet and print on stdout
219  *
220  * @param packet_type
221  * @param channel
222  * @param packet
223  * @param size
224  */
225 static void handle_boot_mouse_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
226     UNUSED(packet_type);
227     UNUSED(channel);
228     UNUSED(size);
229 
230     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
231     const uint8_t * data = gatt_event_notification_get_value(packet);
232     uint8_t buttons =          data[0];
233     int8_t dx       = (int8_t) data[1];
234     int8_t dy       = (int8_t) data[2];
235     int8_t dwheel   = (int8_t) data[3];
236     printf("Mouse: %i, %i - wheel %i - buttons 0x%02x\n", dx, dy, dwheel, buttons);
237 }
238 
239 /**
240  * @section Test if advertisement contains HID UUID
241  * @param packet
242  * @param size
243  * @returns true if it does
244  */
245 static bool adv_event_contains_hid_service(const uint8_t * packet){
246     const uint8_t * ad_data = gap_event_advertising_report_get_data(packet);
247     uint16_t ad_len = gap_event_advertising_report_get_data_length(packet);
248     return ad_data_contains_uuid16(ad_len, ad_data, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
249 }
250 
251 /**
252  * Start scanning
253  */
254 static void hog_start_scan(void){
255     printf("Scanning for LE HID devices...\n");
256     app_state = W4_HID_DEVICE_FOUND;
257     // Passive scanning, 100% (scan interval = scan window)
258     gap_set_scan_parameters(0,48,48);
259     gap_start_scan();
260 }
261 
262 /**
263  * Handle timeout for outgoing connection
264  * @param ts
265  */
266 static void hog_connection_timeout(btstack_timer_source_t * ts){
267     UNUSED(ts);
268     printf("Timeout - abort connection\n");
269     gap_connect_cancel();
270     hog_start_scan();
271 }
272 
273 
274 /**
275  * Connect to remote device but set timer for timeout
276  */
277 static void hog_connect(void) {
278     // set timer
279     btstack_run_loop_set_timer(&connection_timer, 10000);
280     btstack_run_loop_set_timer_handler(&connection_timer, &hog_connection_timeout);
281     btstack_run_loop_add_timer(&connection_timer);
282     app_state = W4_CONNECTED;
283     gap_connect(remote_device.addr, remote_device.addr_type);
284 }
285 
286 /**
287  * Handle timer event to trigger reconnect
288  * @param ts
289  */
290 static void hog_reconnect_timeout(btstack_timer_source_t * ts){
291     UNUSED(ts);
292     switch (app_state){
293         case W4_TIMEOUT_THEN_RECONNECT:
294             hog_connect();
295             break;
296         case W4_TIMEOUT_THEN_SCAN:
297             hog_start_scan();
298             break;
299         default:
300             break;
301     }
302 }
303 
304 /**
305  * Start connecting after boot up: connect to last used device if possible, start scan otherwise
306  */
307 static void hog_start_connect(void){
308     // check if we have a bonded device
309     btstack_tlv_get_instance(&btstack_tlv_singleton_impl, &btstack_tlv_singleton_context);
310     if (btstack_tlv_singleton_impl){
311         int len = btstack_tlv_singleton_impl->get_tag(btstack_tlv_singleton_context, TLV_TAG_HOGD, (uint8_t *) &remote_device, sizeof(remote_device));
312         if (len == sizeof(remote_device)){
313             printf("Bonded, connect to device with %s address %s ...\n", remote_device.addr_type == 0 ? "public" : "random" , bd_addr_to_str(remote_device.addr));
314             hog_connect();
315             return;
316         }
317     }
318     // otherwise, scan for HID devices
319     hog_start_scan();
320 }
321 
322 /**
323  * In case of error, disconnect and start scanning again
324  */
325 static void handle_outgoing_connection_error(void){
326     printf("Error occurred, disconnect and start over\n");
327     gap_disconnect(connection_handle);
328     hog_start_scan();
329 }
330 
331 /**
332  * Handle GATT Client Events dependent on current state
333  *
334  * @param packet_type
335  * @param channel
336  * @param packet
337  * @param size
338  */
339 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
340     UNUSED(packet_type);
341     UNUSED(channel);
342     UNUSED(size);
343     gatt_client_characteristic_t characteristic;
344     static uint8_t boot_protocol_mode = 0;
345 
346     switch (app_state) {
347         case W4_HID_SERVICE_FOUND:
348             switch (hci_event_packet_get_type(packet)) {
349                 case GATT_EVENT_SERVICE_QUERY_RESULT:
350                     // store service (we expect only one)
351                     gatt_event_service_query_result_get_service(packet, &hid_service);
352                     break;
353                 case GATT_EVENT_QUERY_COMPLETE:
354                     if (gatt_event_query_complete_get_att_status(packet) != ATT_ERROR_SUCCESS) {
355                         printf("ATT Error status %x.\n", gatt_event_query_complete_get_att_status(packet));
356                         handle_outgoing_connection_error();
357                         break;
358                     }
359                     printf("Find required HID Service Characteristics...\n");
360                     app_state = W4_HID_CHARACTERISTICS_FOUND;
361                     gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, connection_handle, &hid_service);
362                     break;
363                 default:
364                     break;
365             }
366             break;
367         case W4_HID_CHARACTERISTICS_FOUND:
368             switch (hci_event_packet_get_type(packet)){
369                 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
370                     // get characteristic
371                     gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
372                     switch (characteristic.uuid16){
373                         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
374                             printf("Found CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT, value handle 0x%04x\n", characteristic.value_handle);
375                             memcpy(&boot_keyboard_input_characteristic, &characteristic, sizeof(gatt_client_characteristic_t));
376                             break;
377                         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
378                             printf("Found CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT, value handle 0x%04x\n", characteristic.value_handle);
379                             memcpy(&boot_mouse_input_characteristic, &characteristic, sizeof(gatt_client_characteristic_t));
380                             break;
381                         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
382                             printf("Found CHARACTERISTIC_PROTOCOL_MODE\n");
383                             memcpy(&protocol_mode_characteristic, &characteristic, sizeof(gatt_client_characteristic_t));
384                             break;
385                         default:
386                             break;
387                     }
388                     break;
389                 case GATT_EVENT_QUERY_COMPLETE:
390                     if (gatt_event_query_complete_get_att_status(packet) != ATT_ERROR_SUCCESS) {
391                         printf("ATT Error status %x.\n", gatt_event_query_complete_get_att_status(packet));
392                         handle_outgoing_connection_error();
393                         break;
394                     }
395                     printf("Enable Notifications for Boot Keyboard Input Report..\n");
396                     app_state = W4_BOOT_KEYBOARD_ENABLED;
397                     gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, connection_handle, &boot_keyboard_input_characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
398                     break;
399                 default:
400                     break;
401             }
402             break;
403         case W4_BOOT_KEYBOARD_ENABLED:
404             switch (hci_event_packet_get_type(packet)){
405                 case GATT_EVENT_QUERY_COMPLETE:
406                     if (gatt_event_query_complete_get_att_status(packet) != ATT_ERROR_SUCCESS) {
407                         printf("ATT Error status %x.\n", gatt_event_query_complete_get_att_status(packet));
408                         handle_outgoing_connection_error();
409                         break;
410                     }
411                     // setup listener
412                     gatt_client_listen_for_characteristic_value_updates(&keyboard_notifications, &handle_boot_keyboard_event, connection_handle, &boot_keyboard_input_characteristic);
413                     //
414                     printf("Enable Notifications for Boot Mouse Input Report..\n");
415                     app_state = W4_BOOT_MOUSE_ENABLED;
416                     gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, connection_handle, &boot_mouse_input_characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
417                     break;
418                 default:
419                     break;
420             }
421             break;
422         case W4_BOOT_MOUSE_ENABLED:
423             switch (hci_event_packet_get_type(packet)) {
424                 case GATT_EVENT_QUERY_COMPLETE:
425                     if (gatt_event_query_complete_get_att_status(packet) != ATT_ERROR_SUCCESS) {
426                         printf("ATT Error status %x.\n", gatt_event_query_complete_get_att_status(packet));
427                         handle_outgoing_connection_error();
428                         break;
429                     }
430                     // setup listener
431                     gatt_client_listen_for_characteristic_value_updates(&mouse_notifications, &handle_boot_mouse_event, connection_handle, &boot_mouse_input_characteristic);
432 
433                     // switch to boot mode
434                     printf("Set Protocol Mode to Boot Mode..\n");
435                     gatt_client_write_value_of_characteristic_without_response(connection_handle, protocol_mode_characteristic.value_handle, 1, &boot_protocol_mode);
436 
437                     // store device as bonded
438                     if (btstack_tlv_singleton_impl){
439                         btstack_tlv_singleton_impl->store_tag(btstack_tlv_singleton_context, TLV_TAG_HOGD, (const uint8_t *) &remote_device, sizeof(remote_device));
440                     }
441                     // done
442                     printf("Ready - please start typing or mousing..\n");
443                     app_state = READY;
444                     break;
445                 default:
446                     break;
447             }
448             break;
449         default:
450             break;
451     }
452 }
453 
454 /* LISTING_START(packetHandler): Packet Handler */
455 static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
456     /* LISTING_PAUSE */
457     UNUSED(channel);
458     UNUSED(size);
459     uint8_t   event;
460     /* LISTING_RESUME */
461     switch (packet_type) {
462         case HCI_EVENT_PACKET:
463             event = hci_event_packet_get_type(packet);
464             switch (event) {
465                 case BTSTACK_EVENT_STATE:
466                     if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) break;
467                     btstack_assert(app_state == W4_WORKING);
468                     hog_start_connect();
469                     break;
470                 case GAP_EVENT_ADVERTISING_REPORT:
471                     if (app_state != W4_HID_DEVICE_FOUND) break;
472                     if (adv_event_contains_hid_service(packet) == false) break;
473                     // stop scan
474                     gap_stop_scan();
475                     // store remote device address and type
476                     gap_event_advertising_report_get_address(packet, remote_device.addr);
477                     remote_device.addr_type = gap_event_advertising_report_get_address_type(packet);
478                     // connect
479                     printf("Found, connect to device with %s address %s ...\n", remote_device.addr_type == 0 ? "public" : "random" , bd_addr_to_str(remote_device.addr));
480                     hog_connect();
481                     break;
482                 case HCI_EVENT_DISCONNECTION_COMPLETE:
483                     connection_handle = HCI_CON_HANDLE_INVALID;
484                     switch (app_state){
485                         case READY:
486                             printf("\nDisconnected, try to reconnect...\n");
487                             app_state = W4_TIMEOUT_THEN_RECONNECT;
488                             break;
489                         default:
490                             printf("\nDisconnected, start over...\n");
491                             app_state = W4_TIMEOUT_THEN_SCAN;
492                             break;
493                     }
494                     // set timer
495                     btstack_run_loop_set_timer(&connection_timer, 100);
496                     btstack_run_loop_set_timer_handler(&connection_timer, &hog_reconnect_timeout);
497                     btstack_run_loop_add_timer(&connection_timer);
498                     break;
499                 case HCI_EVENT_LE_META:
500                     // wait for connection complete
501                     if (hci_event_le_meta_get_subevent_code(packet) != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break;
502                     if (app_state != W4_CONNECTED) return;
503                     btstack_run_loop_remove_timer(&connection_timer);
504                     connection_handle = hci_subevent_le_connection_complete_get_connection_handle(packet);
505                     // request security
506                     app_state = W4_ENCRYPTED;
507                     sm_request_pairing(connection_handle);
508                     break;
509                 case HCI_EVENT_ENCRYPTION_CHANGE:
510                     if (connection_handle != hci_event_encryption_change_get_connection_handle(packet)) break;
511                     printf("Connection encrypted: %u\n", hci_event_encryption_change_get_encryption_enabled(packet));
512                     if (hci_event_encryption_change_get_encryption_enabled(packet) == 0){
513                         printf("Encryption failed -> abort\n");
514                         handle_outgoing_connection_error();
515                         break;
516                     }
517                     // continue - query primary services
518                     printf("Search for HID service.\n");
519                     app_state = W4_HID_SERVICE_FOUND;
520                     gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, connection_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
521                     break;
522                 default:
523                     break;
524             }
525             break;
526         default:
527             break;
528     }
529 }
530 /* LISTING_END */
531 
532 /* @section HCI packet handler
533  *
534  * @text The SM packet handler receives Security Manager Events required for pairing.
535  * It also receives events generated during Identity Resolving
536  * see Listing SMPacketHandler.
537  */
538 
539 /* LISTING_START(SMPacketHandler): Scanning and receiving advertisements */
540 
541 static void sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
542     UNUSED(channel);
543     UNUSED(size);
544 
545     if (packet_type != HCI_EVENT_PACKET) return;
546 
547     switch (hci_event_packet_get_type(packet)) {
548         case SM_EVENT_JUST_WORKS_REQUEST:
549             printf("Just works requested\n");
550             sm_just_works_confirm(sm_event_just_works_request_get_handle(packet));
551             break;
552         case SM_EVENT_NUMERIC_COMPARISON_REQUEST:
553             printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet));
554             sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet));
555             break;
556         case SM_EVENT_PASSKEY_DISPLAY_NUMBER:
557             printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet));
558             break;
559         case SM_EVENT_PAIRING_COMPLETE:
560             switch (sm_event_pairing_complete_get_status(packet)){
561                 case ERROR_CODE_SUCCESS:
562                     printf("Pairing complete, success\n");
563                     break;
564                 case ERROR_CODE_CONNECTION_TIMEOUT:
565                     printf("Pairing failed, timeout\n");
566                     break;
567                 case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION:
568                     printf("Pairing faileed, disconnected\n");
569                     break;
570                 case ERROR_CODE_AUTHENTICATION_FAILURE:
571                     printf("Pairing failed, reason = %u\n", sm_event_pairing_complete_get_reason(packet));
572                     break;
573                 default:
574                     break;
575             }
576             break;
577         default:
578             break;
579     }
580 }
581 /* LISTING_END */
582 
583 int btstack_main(int argc, const char * argv[]);
584 int btstack_main(int argc, const char * argv[]){
585 
586     (void)argc;
587     (void)argv;
588 
589     /* LISTING_START(HogBootHostSetup): HID-over-GATT Boot Host Setup */
590 
591     // register for events from HCI
592     hci_event_callback_registration.callback = &packet_handler;
593     hci_add_event_handler(&hci_event_callback_registration);
594 
595     // register for events from Security Manager
596     sm_event_callback_registration.callback = &sm_packet_handler;
597     sm_add_event_handler(&sm_event_callback_registration);
598 
599     // setup le device db
600     le_device_db_init();
601 
602     //
603     l2cap_init();
604     sm_init();
605     gatt_client_init();
606 
607     /* LISTING_END */
608 
609     // Disable stdout buffering
610     setbuf(stdout, NULL);
611 
612     app_state = W4_WORKING;
613 
614     // Turn on the device
615     hci_power_control(HCI_POWER_ON);
616     return 0;
617 }
618 
619 /* EXAMPLE_END */
620