172a8858fSMilanka Ringwald /* 272a8858fSMilanka Ringwald * Copyright (C) 2020 BlueKitchen GmbH 372a8858fSMilanka Ringwald * 472a8858fSMilanka Ringwald * Redistribution and use in source and binary forms, with or without 572a8858fSMilanka Ringwald * modification, are permitted provided that the following conditions 672a8858fSMilanka Ringwald * are met: 772a8858fSMilanka Ringwald * 872a8858fSMilanka Ringwald * 1. Redistributions of source code must retain the above copyright 972a8858fSMilanka Ringwald * notice, this list of conditions and the following disclaimer. 1072a8858fSMilanka Ringwald * 2. Redistributions in binary form must reproduce the above copyright 1172a8858fSMilanka Ringwald * notice, this list of conditions and the following disclaimer in the 1272a8858fSMilanka Ringwald * documentation and/or other materials provided with the distribution. 1372a8858fSMilanka Ringwald * 3. Neither the name of the copyright holders nor the names of 1472a8858fSMilanka Ringwald * contributors may be used to endorse or promote products derived 1572a8858fSMilanka Ringwald * from this software without specific prior written permission. 1672a8858fSMilanka Ringwald * 4. Any redistribution, use, or modification is done solely for 1772a8858fSMilanka Ringwald * personal benefit and not for any commercial purpose or for 1872a8858fSMilanka Ringwald * monetary gain. 1972a8858fSMilanka Ringwald * 2072a8858fSMilanka Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 2172a8858fSMilanka Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2272a8858fSMilanka Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 232fca4dadSMilanka Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 242fca4dadSMilanka Ringwald * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2572a8858fSMilanka Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2672a8858fSMilanka Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 2772a8858fSMilanka Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2872a8858fSMilanka Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2972a8858fSMilanka Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 3072a8858fSMilanka Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3172a8858fSMilanka Ringwald * SUCH DAMAGE. 3272a8858fSMilanka Ringwald * 3372a8858fSMilanka Ringwald * Please inquire about commercial licensing options at 3472a8858fSMilanka Ringwald * [email protected] 3572a8858fSMilanka Ringwald * 3672a8858fSMilanka Ringwald */ 3772a8858fSMilanka Ringwald 38021192e1SMilanka Ringwald #define BTSTACK_FILE__ "hog_host_demo.c" 3972a8858fSMilanka Ringwald 4072a8858fSMilanka Ringwald /* 41021192e1SMilanka Ringwald * hog_host_demo.c 4272a8858fSMilanka Ringwald */ 4372a8858fSMilanka Ringwald 44021192e1SMilanka Ringwald /* EXAMPLE_START(hog_host_demo): HID Host LE 4572a8858fSMilanka Ringwald * 46021192e1SMilanka Ringwald * @text This example implements a minimal HID-over-GATT Host. It scans for LE HID devices, connects to it, 4772a8858fSMilanka Ringwald * discovers the Characteristics relevant for the HID Service and enables Notifications on them. 4872a8858fSMilanka Ringwald * It then dumps all Boot Keyboard and Mouse Input Reports 4972a8858fSMilanka Ringwald */ 5072a8858fSMilanka Ringwald 5172a8858fSMilanka Ringwald #include <inttypes.h> 5272a8858fSMilanka Ringwald #include <stdio.h> 5372a8858fSMilanka Ringwald #include <btstack_tlv.h> 5472a8858fSMilanka Ringwald 5572a8858fSMilanka Ringwald #include "btstack_config.h" 5672a8858fSMilanka Ringwald #include "btstack.h" 5772a8858fSMilanka Ringwald 5872a8858fSMilanka Ringwald // TAG to store remote device address and type in TLV 5972a8858fSMilanka Ringwald #define TLV_TAG_HOGD ((((uint32_t) 'H') << 24 ) | (((uint32_t) 'O') << 16) | (((uint32_t) 'G') << 8) | 'D') 6072a8858fSMilanka Ringwald 6172a8858fSMilanka Ringwald typedef struct { 6272a8858fSMilanka Ringwald bd_addr_t addr; 6372a8858fSMilanka Ringwald bd_addr_type_t addr_type; 6472a8858fSMilanka Ringwald } le_device_addr_t; 6572a8858fSMilanka Ringwald 6672a8858fSMilanka Ringwald static enum { 6772a8858fSMilanka Ringwald W4_WORKING, 6872a8858fSMilanka Ringwald W4_HID_DEVICE_FOUND, 6972a8858fSMilanka Ringwald W4_CONNECTED, 7072a8858fSMilanka Ringwald W4_ENCRYPTED, 712901a9b7SMilanka Ringwald W4_HID_CLIENT_CONNECTED, 7272a8858fSMilanka Ringwald READY, 7372a8858fSMilanka Ringwald W4_TIMEOUT_THEN_SCAN, 7472a8858fSMilanka Ringwald W4_TIMEOUT_THEN_RECONNECT, 7572a8858fSMilanka Ringwald } app_state; 7672a8858fSMilanka Ringwald 7772a8858fSMilanka Ringwald static le_device_addr_t remote_device; 7872a8858fSMilanka Ringwald static hci_con_handle_t connection_handle; 7972a8858fSMilanka Ringwald static uint16_t hids_cid; 80021192e1SMilanka Ringwald static hid_protocol_mode_t protocol_mode = HID_PROTOCOL_MODE_REPORT; 81021192e1SMilanka Ringwald 82021192e1SMilanka Ringwald // SDP 83021192e1SMilanka Ringwald static uint8_t hid_descriptor_storage[500]; 8472a8858fSMilanka Ringwald 8572a8858fSMilanka Ringwald // used to implement connection timeout and reconnect timer 8672a8858fSMilanka Ringwald static btstack_timer_source_t connection_timer; 8772a8858fSMilanka Ringwald 8872a8858fSMilanka Ringwald // register for events from HCI/GAP and SM 8972a8858fSMilanka Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration; 9072a8858fSMilanka Ringwald static btstack_packet_callback_registration_t sm_event_callback_registration; 9172a8858fSMilanka Ringwald 9272a8858fSMilanka Ringwald // used to store remote device in TLV 9372a8858fSMilanka Ringwald static const btstack_tlv_t * btstack_tlv_singleton_impl; 9472a8858fSMilanka Ringwald static void * btstack_tlv_singleton_context; 9572a8858fSMilanka Ringwald 9672a8858fSMilanka Ringwald // Simplified US Keyboard with Shift modifier 9772a8858fSMilanka Ringwald 9872a8858fSMilanka Ringwald #define CHAR_ILLEGAL 0xff 9972a8858fSMilanka Ringwald #define CHAR_RETURN '\n' 10072a8858fSMilanka Ringwald #define CHAR_ESCAPE 27 10172a8858fSMilanka Ringwald #define CHAR_TAB '\t' 10272a8858fSMilanka Ringwald #define CHAR_BACKSPACE 0x7f 10372a8858fSMilanka Ringwald 10472a8858fSMilanka Ringwald /** 10572a8858fSMilanka Ringwald * English (US) 10672a8858fSMilanka Ringwald */ 10772a8858fSMilanka Ringwald static const uint8_t keytable_us_none [] = { 10872a8858fSMilanka Ringwald CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 0-3 */ 10972a8858fSMilanka Ringwald 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', /* 4-13 */ 11072a8858fSMilanka Ringwald 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', /* 14-23 */ 11172a8858fSMilanka Ringwald 'u', 'v', 'w', 'x', 'y', 'z', /* 24-29 */ 11272a8858fSMilanka Ringwald '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', /* 30-39 */ 11372a8858fSMilanka Ringwald CHAR_RETURN, CHAR_ESCAPE, CHAR_BACKSPACE, CHAR_TAB, ' ', /* 40-44 */ 11472a8858fSMilanka Ringwald '-', '=', '[', ']', '\\', CHAR_ILLEGAL, ';', '\'', 0x60, ',', /* 45-54 */ 11572a8858fSMilanka Ringwald '.', '/', CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 55-60 */ 11672a8858fSMilanka Ringwald CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 61-64 */ 11772a8858fSMilanka Ringwald CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 65-68 */ 11872a8858fSMilanka Ringwald CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 69-72 */ 11972a8858fSMilanka Ringwald CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 73-76 */ 12072a8858fSMilanka Ringwald CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 77-80 */ 12172a8858fSMilanka Ringwald CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 81-84 */ 12272a8858fSMilanka Ringwald '*', '-', '+', '\n', '1', '2', '3', '4', '5', /* 85-97 */ 12372a8858fSMilanka Ringwald '6', '7', '8', '9', '0', '.', 0xa7, /* 97-100 */ 12472a8858fSMilanka Ringwald }; 12572a8858fSMilanka Ringwald 12672a8858fSMilanka Ringwald static const uint8_t keytable_us_shift[] = { 12772a8858fSMilanka Ringwald CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 0-3 */ 12872a8858fSMilanka Ringwald 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', /* 4-13 */ 12972a8858fSMilanka Ringwald 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', /* 14-23 */ 13072a8858fSMilanka Ringwald 'U', 'V', 'W', 'X', 'Y', 'Z', /* 24-29 */ 13172a8858fSMilanka Ringwald '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', /* 30-39 */ 13272a8858fSMilanka Ringwald CHAR_RETURN, CHAR_ESCAPE, CHAR_BACKSPACE, CHAR_TAB, ' ', /* 40-44 */ 13372a8858fSMilanka Ringwald '_', '+', '{', '}', '|', CHAR_ILLEGAL, ':', '"', 0x7E, '<', /* 45-54 */ 13472a8858fSMilanka Ringwald '>', '?', CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 55-60 */ 13572a8858fSMilanka Ringwald CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 61-64 */ 13672a8858fSMilanka Ringwald CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 65-68 */ 13772a8858fSMilanka Ringwald CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 69-72 */ 13872a8858fSMilanka Ringwald CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 73-76 */ 13972a8858fSMilanka Ringwald CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 77-80 */ 14072a8858fSMilanka Ringwald CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, CHAR_ILLEGAL, /* 81-84 */ 14172a8858fSMilanka Ringwald '*', '-', '+', '\n', '1', '2', '3', '4', '5', /* 85-97 */ 14272a8858fSMilanka Ringwald '6', '7', '8', '9', '0', '.', 0xb1, /* 97-100 */ 14372a8858fSMilanka Ringwald }; 14472a8858fSMilanka Ringwald 14572a8858fSMilanka Ringwald 146021192e1SMilanka Ringwald 14772a8858fSMilanka Ringwald #define NUM_KEYS 6 14872a8858fSMilanka Ringwald static uint8_t last_keys[NUM_KEYS]; 149021192e1SMilanka Ringwald static void hid_handle_input_report(uint8_t service_index, const uint8_t * report, uint16_t report_len){ 150021192e1SMilanka Ringwald // check if HID Input Report 15172a8858fSMilanka Ringwald 152021192e1SMilanka Ringwald if (report_len < 1) return; 15372a8858fSMilanka Ringwald 154021192e1SMilanka Ringwald btstack_hid_parser_t parser; 155021192e1SMilanka Ringwald 156021192e1SMilanka Ringwald switch (protocol_mode){ 157021192e1SMilanka Ringwald case HID_PROTOCOL_MODE_BOOT: 158021192e1SMilanka Ringwald btstack_hid_parser_init(&parser, 159e9933dfeSMatthias Ringwald btstack_hid_get_boot_descriptor_data(), 160e9933dfeSMatthias Ringwald btstack_hid_get_boot_descriptor_len(), 161021192e1SMilanka Ringwald HID_REPORT_TYPE_INPUT, report, report_len); 162021192e1SMilanka Ringwald break; 163021192e1SMilanka Ringwald 164021192e1SMilanka Ringwald default: 165021192e1SMilanka Ringwald btstack_hid_parser_init(&parser, 166021192e1SMilanka Ringwald hids_client_descriptor_storage_get_descriptor_data(hids_cid, service_index), 167021192e1SMilanka Ringwald hids_client_descriptor_storage_get_descriptor_len(hids_cid, service_index), 168021192e1SMilanka Ringwald HID_REPORT_TYPE_INPUT, report, report_len); 169021192e1SMilanka Ringwald break; 170021192e1SMilanka Ringwald 171021192e1SMilanka Ringwald } 172021192e1SMilanka Ringwald 173021192e1SMilanka Ringwald int shift = 0; 17472a8858fSMilanka Ringwald uint8_t new_keys[NUM_KEYS]; 17572a8858fSMilanka Ringwald memset(new_keys, 0, sizeof(new_keys)); 17672a8858fSMilanka Ringwald int new_keys_count = 0; 177021192e1SMilanka Ringwald while (btstack_hid_parser_has_more(&parser)){ 178021192e1SMilanka Ringwald uint16_t usage_page; 179021192e1SMilanka Ringwald uint16_t usage; 180021192e1SMilanka Ringwald int32_t value; 181021192e1SMilanka Ringwald btstack_hid_parser_get_field(&parser, &usage_page, &usage, &value); 182021192e1SMilanka Ringwald if (usage_page != 0x07) continue; 183021192e1SMilanka Ringwald switch (usage){ 184021192e1SMilanka Ringwald case 0xe1: 185021192e1SMilanka Ringwald case 0xe6: 186021192e1SMilanka Ringwald if (value){ 187021192e1SMilanka Ringwald shift = 1; 188021192e1SMilanka Ringwald } 189021192e1SMilanka Ringwald continue; 190021192e1SMilanka Ringwald case 0x00: 191021192e1SMilanka Ringwald continue; 192021192e1SMilanka Ringwald default: 193021192e1SMilanka Ringwald break; 194021192e1SMilanka Ringwald } 19572a8858fSMilanka Ringwald if (usage >= sizeof(keytable_us_none)) continue; 19672a8858fSMilanka Ringwald 19772a8858fSMilanka Ringwald // store new keys 1980801ef92SMatthias Ringwald new_keys[new_keys_count++] = (uint8_t) usage; 19972a8858fSMilanka Ringwald 20072a8858fSMilanka Ringwald // check if usage was used last time (and ignore in that case) 20172a8858fSMilanka Ringwald int i; 20272a8858fSMilanka Ringwald for (i=0;i<NUM_KEYS;i++){ 20372a8858fSMilanka Ringwald if (usage == last_keys[i]){ 20472a8858fSMilanka Ringwald usage = 0; 20572a8858fSMilanka Ringwald } 20672a8858fSMilanka Ringwald } 20772a8858fSMilanka Ringwald if (usage == 0) continue; 20872a8858fSMilanka Ringwald 20972a8858fSMilanka Ringwald uint8_t key; 21072a8858fSMilanka Ringwald if (shift){ 21172a8858fSMilanka Ringwald key = keytable_us_shift[usage]; 21272a8858fSMilanka Ringwald } else { 21372a8858fSMilanka Ringwald key = keytable_us_none[usage]; 21472a8858fSMilanka Ringwald } 21572a8858fSMilanka Ringwald if (key == CHAR_ILLEGAL) continue; 21672a8858fSMilanka Ringwald if (key == CHAR_BACKSPACE){ 21772a8858fSMilanka Ringwald printf("\b \b"); // go back one char, print space, go back one char again 21872a8858fSMilanka Ringwald continue; 21972a8858fSMilanka Ringwald } 22072a8858fSMilanka Ringwald printf("%c", key); 22172a8858fSMilanka Ringwald } 22272a8858fSMilanka Ringwald memcpy(last_keys, new_keys, NUM_KEYS); 22372a8858fSMilanka Ringwald } 22472a8858fSMilanka Ringwald 22572a8858fSMilanka Ringwald /** 22672a8858fSMilanka Ringwald * @section Test if advertisement contains HID UUID 22772a8858fSMilanka Ringwald * @param packet 22872a8858fSMilanka Ringwald * @param size 22972a8858fSMilanka Ringwald * @returns true if it does 23072a8858fSMilanka Ringwald */ 23172a8858fSMilanka Ringwald static bool adv_event_contains_hid_service(const uint8_t * packet){ 23272a8858fSMilanka Ringwald const uint8_t * ad_data = gap_event_advertising_report_get_data(packet); 2330801ef92SMatthias Ringwald uint8_t ad_len = gap_event_advertising_report_get_data_length(packet); 23472a8858fSMilanka Ringwald return ad_data_contains_uuid16(ad_len, ad_data, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE); 23572a8858fSMilanka Ringwald } 23672a8858fSMilanka Ringwald 23772a8858fSMilanka Ringwald /** 23872a8858fSMilanka Ringwald * Start scanning 23972a8858fSMilanka Ringwald */ 24072a8858fSMilanka Ringwald static void hog_start_scan(void){ 24172a8858fSMilanka Ringwald printf("Scanning for LE HID devices...\n"); 24272a8858fSMilanka Ringwald app_state = W4_HID_DEVICE_FOUND; 24372a8858fSMilanka Ringwald // Passive scanning, 100% (scan interval = scan window) 24472a8858fSMilanka Ringwald gap_set_scan_parameters(0,48,48); 24572a8858fSMilanka Ringwald gap_start_scan(); 24672a8858fSMilanka Ringwald } 24772a8858fSMilanka Ringwald 24872a8858fSMilanka Ringwald /** 24972a8858fSMilanka Ringwald * Handle timeout for outgoing connection 25072a8858fSMilanka Ringwald * @param ts 25172a8858fSMilanka Ringwald */ 25272a8858fSMilanka Ringwald static void hog_connection_timeout(btstack_timer_source_t * ts){ 25372a8858fSMilanka Ringwald UNUSED(ts); 25472a8858fSMilanka Ringwald printf("Timeout - abort connection\n"); 25572a8858fSMilanka Ringwald gap_connect_cancel(); 25672a8858fSMilanka Ringwald hog_start_scan(); 25772a8858fSMilanka Ringwald } 25872a8858fSMilanka Ringwald 25972a8858fSMilanka Ringwald 26072a8858fSMilanka Ringwald /** 26172a8858fSMilanka Ringwald * Connect to remote device but set timer for timeout 26272a8858fSMilanka Ringwald */ 26372a8858fSMilanka Ringwald static void hog_connect(void) { 26472a8858fSMilanka Ringwald // set timer 26572a8858fSMilanka Ringwald btstack_run_loop_set_timer(&connection_timer, 10000); 26672a8858fSMilanka Ringwald btstack_run_loop_set_timer_handler(&connection_timer, &hog_connection_timeout); 26772a8858fSMilanka Ringwald btstack_run_loop_add_timer(&connection_timer); 26872a8858fSMilanka Ringwald app_state = W4_CONNECTED; 26972a8858fSMilanka Ringwald gap_connect(remote_device.addr, remote_device.addr_type); 27072a8858fSMilanka Ringwald } 27172a8858fSMilanka Ringwald 27272a8858fSMilanka Ringwald /** 27372a8858fSMilanka Ringwald * Handle timer event to trigger reconnect 27472a8858fSMilanka Ringwald * @param ts 27572a8858fSMilanka Ringwald */ 27672a8858fSMilanka Ringwald static void hog_reconnect_timeout(btstack_timer_source_t * ts){ 27772a8858fSMilanka Ringwald UNUSED(ts); 27872a8858fSMilanka Ringwald switch (app_state){ 27972a8858fSMilanka Ringwald case W4_TIMEOUT_THEN_RECONNECT: 28072a8858fSMilanka Ringwald hog_connect(); 28172a8858fSMilanka Ringwald break; 28272a8858fSMilanka Ringwald case W4_TIMEOUT_THEN_SCAN: 28372a8858fSMilanka Ringwald hog_start_scan(); 28472a8858fSMilanka Ringwald break; 28572a8858fSMilanka Ringwald default: 28672a8858fSMilanka Ringwald break; 28772a8858fSMilanka Ringwald } 28872a8858fSMilanka Ringwald } 28972a8858fSMilanka Ringwald 29072a8858fSMilanka Ringwald /** 29172a8858fSMilanka Ringwald * Start connecting after boot up: connect to last used device if possible, start scan otherwise 29272a8858fSMilanka Ringwald */ 29372a8858fSMilanka Ringwald static void hog_start_connect(void){ 29472a8858fSMilanka Ringwald // check if we have a bonded device 29572a8858fSMilanka Ringwald btstack_tlv_get_instance(&btstack_tlv_singleton_impl, &btstack_tlv_singleton_context); 29672a8858fSMilanka Ringwald if (btstack_tlv_singleton_impl){ 29772a8858fSMilanka Ringwald int len = btstack_tlv_singleton_impl->get_tag(btstack_tlv_singleton_context, TLV_TAG_HOGD, (uint8_t *) &remote_device, sizeof(remote_device)); 29872a8858fSMilanka Ringwald if (len == sizeof(remote_device)){ 29972a8858fSMilanka Ringwald printf("Bonded, connect to device with %s address %s ...\n", remote_device.addr_type == 0 ? "public" : "random" , bd_addr_to_str(remote_device.addr)); 30072a8858fSMilanka Ringwald hog_connect(); 30172a8858fSMilanka Ringwald return; 30272a8858fSMilanka Ringwald } 30372a8858fSMilanka Ringwald } 30472a8858fSMilanka Ringwald // otherwise, scan for HID devices 30572a8858fSMilanka Ringwald hog_start_scan(); 30672a8858fSMilanka Ringwald } 30772a8858fSMilanka Ringwald 30872a8858fSMilanka Ringwald /** 30972a8858fSMilanka Ringwald * In case of error, disconnect and start scanning again 31072a8858fSMilanka Ringwald */ 31172a8858fSMilanka Ringwald static void handle_outgoing_connection_error(void){ 31272a8858fSMilanka Ringwald printf("Error occurred, disconnect and start over\n"); 31372a8858fSMilanka Ringwald gap_disconnect(connection_handle); 31472a8858fSMilanka Ringwald hog_start_scan(); 31572a8858fSMilanka Ringwald } 31672a8858fSMilanka Ringwald 31772a8858fSMilanka Ringwald /** 31872a8858fSMilanka Ringwald * Handle GATT Client Events dependent on current state 31972a8858fSMilanka Ringwald * 32072a8858fSMilanka Ringwald * @param packet_type 32172a8858fSMilanka Ringwald * @param channel 32272a8858fSMilanka Ringwald * @param packet 32372a8858fSMilanka Ringwald * @param size 32472a8858fSMilanka Ringwald */ 32572a8858fSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 32672a8858fSMilanka Ringwald UNUSED(packet_type); 32772a8858fSMilanka Ringwald UNUSED(channel); 32872a8858fSMilanka Ringwald UNUSED(size); 32972a8858fSMilanka Ringwald 33072a8858fSMilanka Ringwald uint8_t status; 33172a8858fSMilanka Ringwald 33272a8858fSMilanka Ringwald if (hci_event_packet_get_type(packet) != HCI_EVENT_GATTSERVICE_META){ 33372a8858fSMilanka Ringwald return; 33472a8858fSMilanka Ringwald } 33572a8858fSMilanka Ringwald 33672a8858fSMilanka Ringwald switch (hci_event_gattservice_meta_get_subevent_code(packet)){ 33772a8858fSMilanka Ringwald case GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED: 33872a8858fSMilanka Ringwald status = gattservice_subevent_hid_service_connected_get_status(packet); 33972a8858fSMilanka Ringwald switch (status){ 34072a8858fSMilanka Ringwald case ERROR_CODE_SUCCESS: 34172a8858fSMilanka Ringwald printf("HID service client connected, found %d services\n", 34272a8858fSMilanka Ringwald gattservice_subevent_hid_service_connected_get_num_instances(packet)); 34372a8858fSMilanka Ringwald 34472a8858fSMilanka Ringwald // store device as bonded 34572a8858fSMilanka Ringwald if (btstack_tlv_singleton_impl){ 34672a8858fSMilanka Ringwald btstack_tlv_singleton_impl->store_tag(btstack_tlv_singleton_context, TLV_TAG_HOGD, (const uint8_t *) &remote_device, sizeof(remote_device)); 34772a8858fSMilanka Ringwald } 34872a8858fSMilanka Ringwald // done 34972a8858fSMilanka Ringwald printf("Ready - please start typing or mousing..\n"); 35072a8858fSMilanka Ringwald app_state = READY; 35172a8858fSMilanka Ringwald break; 35272a8858fSMilanka Ringwald default: 353*fcd55a0bSMilanka Ringwald printf("HID service client connection failed, status 0x%02x.\n", status); 35472a8858fSMilanka Ringwald handle_outgoing_connection_error(); 35572a8858fSMilanka Ringwald break; 35672a8858fSMilanka Ringwald } 35772a8858fSMilanka Ringwald break; 358021192e1SMilanka Ringwald 35972a8858fSMilanka Ringwald case GATTSERVICE_SUBEVENT_HID_REPORT: 360021192e1SMilanka Ringwald hid_handle_input_report( 361021192e1SMilanka Ringwald gattservice_subevent_hid_report_get_service_index(packet), 36272a8858fSMilanka Ringwald gattservice_subevent_hid_report_get_report(packet), 36372a8858fSMilanka Ringwald gattservice_subevent_hid_report_get_report_len(packet)); 36472a8858fSMilanka Ringwald break; 36572a8858fSMilanka Ringwald 36672a8858fSMilanka Ringwald default: 36772a8858fSMilanka Ringwald break; 36872a8858fSMilanka Ringwald } 36972a8858fSMilanka Ringwald } 37072a8858fSMilanka Ringwald 37172a8858fSMilanka Ringwald /* LISTING_START(packetHandler): Packet Handler */ 37272a8858fSMilanka Ringwald static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 37372a8858fSMilanka Ringwald /* LISTING_PAUSE */ 37472a8858fSMilanka Ringwald UNUSED(channel); 37572a8858fSMilanka Ringwald UNUSED(size); 37672a8858fSMilanka Ringwald uint8_t event; 37772a8858fSMilanka Ringwald /* LISTING_RESUME */ 37872a8858fSMilanka Ringwald switch (packet_type) { 37972a8858fSMilanka Ringwald case HCI_EVENT_PACKET: 38072a8858fSMilanka Ringwald event = hci_event_packet_get_type(packet); 38172a8858fSMilanka Ringwald switch (event) { 38272a8858fSMilanka Ringwald case BTSTACK_EVENT_STATE: 38372a8858fSMilanka Ringwald if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) break; 38472a8858fSMilanka Ringwald btstack_assert(app_state == W4_WORKING); 3852901a9b7SMilanka Ringwald 38672a8858fSMilanka Ringwald hog_start_connect(); 38772a8858fSMilanka Ringwald break; 38872a8858fSMilanka Ringwald case GAP_EVENT_ADVERTISING_REPORT: 38972a8858fSMilanka Ringwald if (app_state != W4_HID_DEVICE_FOUND) break; 39072a8858fSMilanka Ringwald if (adv_event_contains_hid_service(packet) == false) break; 39172a8858fSMilanka Ringwald // stop scan 39272a8858fSMilanka Ringwald gap_stop_scan(); 39372a8858fSMilanka Ringwald // store remote device address and type 39472a8858fSMilanka Ringwald gap_event_advertising_report_get_address(packet, remote_device.addr); 39572a8858fSMilanka Ringwald remote_device.addr_type = gap_event_advertising_report_get_address_type(packet); 39672a8858fSMilanka Ringwald // connect 39772a8858fSMilanka Ringwald printf("Found, connect to device with %s address %s ...\n", remote_device.addr_type == 0 ? "public" : "random" , bd_addr_to_str(remote_device.addr)); 39872a8858fSMilanka Ringwald hog_connect(); 39972a8858fSMilanka Ringwald break; 40072a8858fSMilanka Ringwald case HCI_EVENT_DISCONNECTION_COMPLETE: 4012901a9b7SMilanka Ringwald if (app_state != READY) break; 40272a8858fSMilanka Ringwald connection_handle = HCI_CON_HANDLE_INVALID; 40372a8858fSMilanka Ringwald switch (app_state){ 40472a8858fSMilanka Ringwald case READY: 40572a8858fSMilanka Ringwald printf("\nDisconnected, try to reconnect...\n"); 40672a8858fSMilanka Ringwald app_state = W4_TIMEOUT_THEN_RECONNECT; 40772a8858fSMilanka Ringwald break; 40872a8858fSMilanka Ringwald default: 40972a8858fSMilanka Ringwald printf("\nDisconnected, start over...\n"); 41072a8858fSMilanka Ringwald app_state = W4_TIMEOUT_THEN_SCAN; 41172a8858fSMilanka Ringwald break; 41272a8858fSMilanka Ringwald } 41372a8858fSMilanka Ringwald // set timer 41472a8858fSMilanka Ringwald btstack_run_loop_set_timer(&connection_timer, 100); 41572a8858fSMilanka Ringwald btstack_run_loop_set_timer_handler(&connection_timer, &hog_reconnect_timeout); 41672a8858fSMilanka Ringwald btstack_run_loop_add_timer(&connection_timer); 41772a8858fSMilanka Ringwald break; 41872a8858fSMilanka Ringwald case HCI_EVENT_LE_META: 41972a8858fSMilanka Ringwald // wait for connection complete 42072a8858fSMilanka Ringwald if (hci_event_le_meta_get_subevent_code(packet) != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break; 42172a8858fSMilanka Ringwald if (app_state != W4_CONNECTED) return; 42272a8858fSMilanka Ringwald btstack_run_loop_remove_timer(&connection_timer); 42372a8858fSMilanka Ringwald connection_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); 42472a8858fSMilanka Ringwald // request security 42572a8858fSMilanka Ringwald app_state = W4_ENCRYPTED; 42672a8858fSMilanka Ringwald sm_request_pairing(connection_handle); 42772a8858fSMilanka Ringwald break; 42872a8858fSMilanka Ringwald default: 42972a8858fSMilanka Ringwald break; 43072a8858fSMilanka Ringwald } 43172a8858fSMilanka Ringwald break; 43272a8858fSMilanka Ringwald default: 43372a8858fSMilanka Ringwald break; 43472a8858fSMilanka Ringwald } 43572a8858fSMilanka Ringwald } 43672a8858fSMilanka Ringwald /* LISTING_END */ 43772a8858fSMilanka Ringwald 43872a8858fSMilanka Ringwald /* @section HCI packet handler 43972a8858fSMilanka Ringwald * 44072a8858fSMilanka Ringwald * @text The SM packet handler receives Security Manager Events required for pairing. 44172a8858fSMilanka Ringwald * It also receives events generated during Identity Resolving 44272a8858fSMilanka Ringwald * see Listing SMPacketHandler. 44372a8858fSMilanka Ringwald */ 44472a8858fSMilanka Ringwald 44572a8858fSMilanka Ringwald /* LISTING_START(SMPacketHandler): Scanning and receiving advertisements */ 44672a8858fSMilanka Ringwald 44772a8858fSMilanka Ringwald static void sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 44872a8858fSMilanka Ringwald UNUSED(channel); 44972a8858fSMilanka Ringwald UNUSED(size); 45072a8858fSMilanka Ringwald 45172a8858fSMilanka Ringwald if (packet_type != HCI_EVENT_PACKET) return; 45272a8858fSMilanka Ringwald 45372a8858fSMilanka Ringwald switch (hci_event_packet_get_type(packet)) { 45472a8858fSMilanka Ringwald case SM_EVENT_JUST_WORKS_REQUEST: 45572a8858fSMilanka Ringwald printf("Just works requested\n"); 45672a8858fSMilanka Ringwald sm_just_works_confirm(sm_event_just_works_request_get_handle(packet)); 45772a8858fSMilanka Ringwald break; 45872a8858fSMilanka Ringwald case SM_EVENT_NUMERIC_COMPARISON_REQUEST: 45972a8858fSMilanka Ringwald printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet)); 46072a8858fSMilanka Ringwald sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet)); 46172a8858fSMilanka Ringwald break; 46272a8858fSMilanka Ringwald case SM_EVENT_PASSKEY_DISPLAY_NUMBER: 46372a8858fSMilanka Ringwald printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet)); 46472a8858fSMilanka Ringwald break; 46572a8858fSMilanka Ringwald case SM_EVENT_PAIRING_COMPLETE: 46672a8858fSMilanka Ringwald switch (sm_event_pairing_complete_get_status(packet)){ 46772a8858fSMilanka Ringwald case ERROR_CODE_SUCCESS: 46872a8858fSMilanka Ringwald printf("Pairing complete, success\n"); 46978404023SMatthias Ringwald 47078404023SMatthias Ringwald // continue - query primary services 47178404023SMatthias Ringwald printf("Search for HID service.\n"); 47278404023SMatthias Ringwald app_state = W4_HID_CLIENT_CONNECTED; 47378404023SMatthias Ringwald hids_client_connect(connection_handle, handle_gatt_client_event, protocol_mode, &hids_cid); 47472a8858fSMilanka Ringwald break; 47572a8858fSMilanka Ringwald case ERROR_CODE_CONNECTION_TIMEOUT: 47672a8858fSMilanka Ringwald printf("Pairing failed, timeout\n"); 47772a8858fSMilanka Ringwald break; 47872a8858fSMilanka Ringwald case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION: 47978404023SMatthias Ringwald printf("Pairing failed, disconnected\n"); 48072a8858fSMilanka Ringwald break; 48172a8858fSMilanka Ringwald case ERROR_CODE_AUTHENTICATION_FAILURE: 48272a8858fSMilanka Ringwald printf("Pairing failed, reason = %u\n", sm_event_pairing_complete_get_reason(packet)); 48372a8858fSMilanka Ringwald break; 48472a8858fSMilanka Ringwald default: 48572a8858fSMilanka Ringwald break; 48672a8858fSMilanka Ringwald } 48772a8858fSMilanka Ringwald break; 48872a8858fSMilanka Ringwald default: 48972a8858fSMilanka Ringwald break; 49072a8858fSMilanka Ringwald } 49172a8858fSMilanka Ringwald } 49272a8858fSMilanka Ringwald /* LISTING_END */ 49372a8858fSMilanka Ringwald 49472a8858fSMilanka Ringwald int btstack_main(int argc, const char * argv[]); 49572a8858fSMilanka Ringwald int btstack_main(int argc, const char * argv[]){ 49672a8858fSMilanka Ringwald 49772a8858fSMilanka Ringwald (void)argc; 49872a8858fSMilanka Ringwald (void)argv; 49972a8858fSMilanka Ringwald 500021192e1SMilanka Ringwald /* LISTING_START(HogBootHostSetup): HID-over-GATT Host Setup */ 50172a8858fSMilanka Ringwald 50272a8858fSMilanka Ringwald // register for events from HCI 50372a8858fSMilanka Ringwald hci_event_callback_registration.callback = &packet_handler; 50472a8858fSMilanka Ringwald hci_add_event_handler(&hci_event_callback_registration); 50572a8858fSMilanka Ringwald 50672a8858fSMilanka Ringwald // register for events from Security Manager 50772a8858fSMilanka Ringwald sm_event_callback_registration.callback = &sm_packet_handler; 50872a8858fSMilanka Ringwald sm_add_event_handler(&sm_event_callback_registration); 50972a8858fSMilanka Ringwald 51072a8858fSMilanka Ringwald // 51172a8858fSMilanka Ringwald l2cap_init(); 51272a8858fSMilanka Ringwald sm_init(); 51372a8858fSMilanka Ringwald gatt_client_init(); 514021192e1SMilanka Ringwald 515021192e1SMilanka Ringwald hids_client_init(hid_descriptor_storage, sizeof(hid_descriptor_storage)); 51672a8858fSMilanka Ringwald 51772a8858fSMilanka Ringwald /* LISTING_END */ 51872a8858fSMilanka Ringwald 51972a8858fSMilanka Ringwald // Disable stdout buffering 5200801ef92SMatthias Ringwald setvbuf(stdin, NULL, _IONBF, 0); 52172a8858fSMilanka Ringwald 52272a8858fSMilanka Ringwald app_state = W4_WORKING; 52372a8858fSMilanka Ringwald 52472a8858fSMilanka Ringwald // Turn on the device 52572a8858fSMilanka Ringwald hci_power_control(HCI_POWER_ON); 52672a8858fSMilanka Ringwald return 0; 52772a8858fSMilanka Ringwald } 52872a8858fSMilanka Ringwald 52972a8858fSMilanka Ringwald /* EXAMPLE_END */ 530