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 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__ "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 uint8_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 uint8_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 uint8_t att_status; 345 static uint8_t boot_protocol_mode = 0; 346 347 switch (app_state) { 348 case W4_HID_SERVICE_FOUND: 349 switch (hci_event_packet_get_type(packet)) { 350 case GATT_EVENT_SERVICE_QUERY_RESULT: 351 // store service (we expect only one) 352 gatt_event_service_query_result_get_service(packet, &hid_service); 353 break; 354 case GATT_EVENT_QUERY_COMPLETE: 355 att_status = gatt_event_query_complete_get_att_status(packet); 356 if (att_status != ATT_ERROR_SUCCESS) { 357 printf("Query failed, ATT Error 0x%02x.\n", att_status); 358 handle_outgoing_connection_error(); 359 break; 360 } 361 printf("Find required HID Service Characteristics...\n"); 362 app_state = W4_HID_CHARACTERISTICS_FOUND; 363 gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, connection_handle, &hid_service); 364 break; 365 default: 366 break; 367 } 368 break; 369 case W4_HID_CHARACTERISTICS_FOUND: 370 switch (hci_event_packet_get_type(packet)){ 371 case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT: 372 // get characteristic 373 gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); 374 switch (characteristic.uuid16){ 375 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT: 376 printf("Found CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT, value handle 0x%04x\n", characteristic.value_handle); 377 memcpy(&boot_keyboard_input_characteristic, &characteristic, sizeof(gatt_client_characteristic_t)); 378 break; 379 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT: 380 printf("Found CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT, value handle 0x%04x\n", characteristic.value_handle); 381 memcpy(&boot_mouse_input_characteristic, &characteristic, sizeof(gatt_client_characteristic_t)); 382 break; 383 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE: 384 printf("Found CHARACTERISTIC_PROTOCOL_MODE\n"); 385 memcpy(&protocol_mode_characteristic, &characteristic, sizeof(gatt_client_characteristic_t)); 386 break; 387 default: 388 break; 389 } 390 break; 391 case GATT_EVENT_QUERY_COMPLETE: 392 att_status = gatt_event_query_complete_get_att_status(packet); 393 if (att_status != ATT_ERROR_SUCCESS) { 394 printf("Query failed, ATT Error 0x%02x.\n", att_status); 395 handle_outgoing_connection_error(); 396 break; 397 } 398 printf("Enable Notifications for Boot Keyboard Input Report..\n"); 399 app_state = W4_BOOT_KEYBOARD_ENABLED; 400 gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, connection_handle, &boot_keyboard_input_characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 401 break; 402 default: 403 break; 404 } 405 break; 406 case W4_BOOT_KEYBOARD_ENABLED: 407 switch (hci_event_packet_get_type(packet)){ 408 case GATT_EVENT_QUERY_COMPLETE: 409 att_status = gatt_event_query_complete_get_att_status(packet); 410 if (att_status != ATT_ERROR_SUCCESS) { 411 printf("Query failed, ATT Error 0x%02x.\n", att_status); 412 handle_outgoing_connection_error(); 413 break; 414 } 415 // setup listener 416 gatt_client_listen_for_characteristic_value_updates(&keyboard_notifications, &handle_boot_keyboard_event, connection_handle, &boot_keyboard_input_characteristic); 417 // 418 printf("Enable Notifications for Boot Mouse Input Report..\n"); 419 app_state = W4_BOOT_MOUSE_ENABLED; 420 gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, connection_handle, &boot_mouse_input_characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); 421 break; 422 default: 423 break; 424 } 425 break; 426 case W4_BOOT_MOUSE_ENABLED: 427 switch (hci_event_packet_get_type(packet)) { 428 case GATT_EVENT_QUERY_COMPLETE: 429 att_status = gatt_event_query_complete_get_att_status(packet); 430 if (att_status != ATT_ERROR_SUCCESS) { 431 printf("Query failed, ATT Error 0x%02x.\n", att_status); 432 handle_outgoing_connection_error(); 433 break; 434 } 435 // setup listener 436 gatt_client_listen_for_characteristic_value_updates(&mouse_notifications, &handle_boot_mouse_event, connection_handle, &boot_mouse_input_characteristic); 437 438 // switch to boot mode 439 printf("Set Protocol Mode to Boot Mode..\n"); 440 gatt_client_write_value_of_characteristic_without_response(connection_handle, protocol_mode_characteristic.value_handle, 1, &boot_protocol_mode); 441 442 // store device as bonded 443 if (btstack_tlv_singleton_impl){ 444 btstack_tlv_singleton_impl->store_tag(btstack_tlv_singleton_context, TLV_TAG_HOGD, (const uint8_t *) &remote_device, sizeof(remote_device)); 445 } 446 // done 447 printf("Ready - please start typing or mousing..\n"); 448 app_state = READY; 449 break; 450 default: 451 break; 452 } 453 break; 454 default: 455 break; 456 } 457 } 458 459 /* LISTING_START(packetHandler): Packet Handler */ 460 static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 461 /* LISTING_PAUSE */ 462 UNUSED(channel); 463 UNUSED(size); 464 uint8_t event; 465 /* LISTING_RESUME */ 466 switch (packet_type) { 467 case HCI_EVENT_PACKET: 468 event = hci_event_packet_get_type(packet); 469 switch (event) { 470 case BTSTACK_EVENT_STATE: 471 if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) break; 472 btstack_assert(app_state == W4_WORKING); 473 hog_start_connect(); 474 break; 475 case GAP_EVENT_ADVERTISING_REPORT: 476 if (app_state != W4_HID_DEVICE_FOUND) break; 477 if (adv_event_contains_hid_service(packet) == false) break; 478 // stop scan 479 gap_stop_scan(); 480 // store remote device address and type 481 gap_event_advertising_report_get_address(packet, remote_device.addr); 482 remote_device.addr_type = gap_event_advertising_report_get_address_type(packet); 483 // connect 484 printf("Found, connect to device with %s address %s ...\n", remote_device.addr_type == 0 ? "public" : "random" , bd_addr_to_str(remote_device.addr)); 485 hog_connect(); 486 break; 487 case HCI_EVENT_DISCONNECTION_COMPLETE: 488 if (app_state != READY) break; 489 490 connection_handle = HCI_CON_HANDLE_INVALID; 491 switch (app_state){ 492 case READY: 493 printf("\nDisconnected, try to reconnect...\n"); 494 app_state = W4_TIMEOUT_THEN_RECONNECT; 495 break; 496 default: 497 printf("\nDisconnected, start over...\n"); 498 app_state = W4_TIMEOUT_THEN_SCAN; 499 break; 500 } 501 // set timer 502 btstack_run_loop_set_timer(&connection_timer, 100); 503 btstack_run_loop_set_timer_handler(&connection_timer, &hog_reconnect_timeout); 504 btstack_run_loop_add_timer(&connection_timer); 505 break; 506 case HCI_EVENT_LE_META: 507 // wait for connection complete 508 if (hci_event_le_meta_get_subevent_code(packet) != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break; 509 if (app_state != W4_CONNECTED) return; 510 btstack_run_loop_remove_timer(&connection_timer); 511 connection_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); 512 // request security 513 app_state = W4_ENCRYPTED; 514 sm_request_pairing(connection_handle); 515 break; 516 default: 517 break; 518 } 519 break; 520 default: 521 break; 522 } 523 } 524 /* LISTING_END */ 525 526 /* @section HCI packet handler 527 * 528 * @text The SM packet handler receives Security Manager Events required for pairing. 529 * It also receives events generated during Identity Resolving 530 * see Listing SMPacketHandler. 531 */ 532 533 /* LISTING_START(SMPacketHandler): Scanning and receiving advertisements */ 534 535 static void sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 536 UNUSED(channel); 537 UNUSED(size); 538 539 if (packet_type != HCI_EVENT_PACKET) return; 540 541 switch (hci_event_packet_get_type(packet)) { 542 case SM_EVENT_JUST_WORKS_REQUEST: 543 printf("Just works requested\n"); 544 sm_just_works_confirm(sm_event_just_works_request_get_handle(packet)); 545 break; 546 case SM_EVENT_NUMERIC_COMPARISON_REQUEST: 547 printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet)); 548 sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet)); 549 break; 550 case SM_EVENT_PASSKEY_DISPLAY_NUMBER: 551 printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet)); 552 break; 553 case SM_EVENT_PAIRING_COMPLETE: 554 switch (sm_event_pairing_complete_get_status(packet)){ 555 case ERROR_CODE_SUCCESS: 556 printf("Pairing complete, success\n"); 557 // continue - query primary services 558 printf("Search for HID service.\n"); 559 app_state = W4_HID_SERVICE_FOUND; 560 gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, connection_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE); 561 break; 562 case ERROR_CODE_CONNECTION_TIMEOUT: 563 printf("Pairing failed, timeout\n"); 564 break; 565 case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION: 566 printf("Pairing failed, disconnected\n"); 567 break; 568 case ERROR_CODE_AUTHENTICATION_FAILURE: 569 printf("Pairing failed, reason = %u\n", sm_event_pairing_complete_get_reason(packet)); 570 break; 571 default: 572 break; 573 } 574 break; 575 default: 576 break; 577 } 578 } 579 /* LISTING_END */ 580 581 int btstack_main(int argc, const char * argv[]); 582 int btstack_main(int argc, const char * argv[]){ 583 584 (void)argc; 585 (void)argv; 586 587 /* LISTING_START(HogBootHostSetup): HID-over-GATT Boot Host Setup */ 588 589 // register for events from HCI 590 hci_event_callback_registration.callback = &packet_handler; 591 hci_add_event_handler(&hci_event_callback_registration); 592 593 // register for events from Security Manager 594 sm_event_callback_registration.callback = &sm_packet_handler; 595 sm_add_event_handler(&sm_event_callback_registration); 596 597 // 598 l2cap_init(); 599 sm_init(); 600 gatt_client_init(); 601 602 /* LISTING_END */ 603 604 // Disable stdout buffering 605 setvbuf(stdin, NULL, _IONBF, 0); 606 607 app_state = W4_WORKING; 608 609 // Turn on the device 610 hci_power_control(HCI_POWER_ON); 611 return 0; 612 } 613 614 /* EXAMPLE_END */ 615