12d05904dSMatthias Ringwald /* 22d05904dSMatthias Ringwald * Copyright (C) 2017 BlueKitchen GmbH 32d05904dSMatthias Ringwald * 42d05904dSMatthias Ringwald * Redistribution and use in source and binary forms, with or without 52d05904dSMatthias Ringwald * modification, are permitted provided that the following conditions 62d05904dSMatthias Ringwald * are met: 72d05904dSMatthias Ringwald * 82d05904dSMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 92d05904dSMatthias Ringwald * notice, this list of conditions and the following disclaimer. 102d05904dSMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 112d05904dSMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 122d05904dSMatthias Ringwald * documentation and/or other materials provided with the distribution. 132d05904dSMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 142d05904dSMatthias Ringwald * contributors may be used to endorse or promote products derived 152d05904dSMatthias Ringwald * from this software without specific prior written permission. 162d05904dSMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 172d05904dSMatthias Ringwald * personal benefit and not for any commercial purpose or for 182d05904dSMatthias Ringwald * monetary gain. 192d05904dSMatthias Ringwald * 202d05904dSMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 212d05904dSMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 222d05904dSMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 232d05904dSMatthias Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 242d05904dSMatthias Ringwald * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 252d05904dSMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 262d05904dSMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 272d05904dSMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 282d05904dSMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 292d05904dSMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 302d05904dSMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 312d05904dSMatthias Ringwald * SUCH DAMAGE. 322d05904dSMatthias Ringwald * 332d05904dSMatthias Ringwald * Please inquire about commercial licensing options at 342d05904dSMatthias Ringwald * [email protected] 352d05904dSMatthias Ringwald * 362d05904dSMatthias Ringwald */ 372d05904dSMatthias Ringwald 382d05904dSMatthias Ringwald #define __BTSTACK_FILE__ "hid_host_demo.c" 392d05904dSMatthias Ringwald 402d05904dSMatthias Ringwald /* 412d05904dSMatthias Ringwald * hid_host_demo.c 422d05904dSMatthias Ringwald */ 432d05904dSMatthias Ringwald 442d05904dSMatthias Ringwald /* EXAMPLE_START(hid_host_demo): HID Host Demo 452d05904dSMatthias Ringwald * 46e85416c1SMatthias Ringwald * @text This example implements an HID Host. For now, it connnects to a fixed device, queries the HID SDP 47e85416c1SMatthias Ringwald * record and opens the HID Control + Interrupt channels 482d05904dSMatthias Ringwald */ 492d05904dSMatthias Ringwald 502d05904dSMatthias Ringwald #include <stdio.h> 512d05904dSMatthias Ringwald 522d05904dSMatthias Ringwald #include "btstack_config.h" 532d05904dSMatthias Ringwald #include "btstack.h" 542d05904dSMatthias Ringwald 5567c74d26SMatthias Ringwald #define MAX_ATTRIBUTE_VALUE_SIZE 300 562d05904dSMatthias Ringwald 57e85416c1SMatthias Ringwald // SDP 582d05904dSMatthias Ringwald static uint8_t hid_descriptor[MAX_ATTRIBUTE_VALUE_SIZE]; 592d05904dSMatthias Ringwald static uint16_t hid_descriptor_len; 602d05904dSMatthias Ringwald 612d05904dSMatthias Ringwald static uint16_t hid_control_psm; 622d05904dSMatthias Ringwald static uint16_t hid_interrupt_psm; 632d05904dSMatthias Ringwald 642d05904dSMatthias Ringwald static uint8_t attribute_value[MAX_ATTRIBUTE_VALUE_SIZE]; 652d05904dSMatthias Ringwald static const unsigned int attribute_value_buffer_size = MAX_ATTRIBUTE_VALUE_SIZE; 662d05904dSMatthias Ringwald 67e85416c1SMatthias Ringwald // L2CAP 68e85416c1SMatthias Ringwald static uint16_t l2cap_hid_control_cid; 69e85416c1SMatthias Ringwald static uint16_t l2cap_hid_interrupt_cid; 70e85416c1SMatthias Ringwald 712d05904dSMatthias Ringwald // MBP 2016 722d05904dSMatthias Ringwald static const char * remote_addr_string = "F4-0F-24-3B-1B-E1"; 732d05904dSMatthias Ringwald // iMpulse static const char * remote_addr_string = "64:6E:6C:C1:AA:B5"; 742d05904dSMatthias Ringwald 752d05904dSMatthias Ringwald static bd_addr_t remote_addr; 762d05904dSMatthias Ringwald 772d05904dSMatthias Ringwald static btstack_packet_callback_registration_t hci_event_callback_registration; 782d05904dSMatthias Ringwald 792d05904dSMatthias Ringwald 802d05904dSMatthias Ringwald /* @section Main application configuration 812d05904dSMatthias Ringwald * 822d05904dSMatthias Ringwald * @text In the application configuration, L2CAP is initialized 832d05904dSMatthias Ringwald */ 842d05904dSMatthias Ringwald 852d05904dSMatthias Ringwald /* LISTING_START(PanuSetup): Panu setup */ 862d05904dSMatthias Ringwald static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 872d05904dSMatthias Ringwald static void handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 882d05904dSMatthias Ringwald 892d05904dSMatthias Ringwald static void hid_host_setup(void){ 902d05904dSMatthias Ringwald 912d05904dSMatthias Ringwald // register for HCI events 922d05904dSMatthias Ringwald hci_event_callback_registration.callback = &packet_handler; 932d05904dSMatthias Ringwald hci_add_event_handler(&hci_event_callback_registration); 942d05904dSMatthias Ringwald 952d05904dSMatthias Ringwald // Initialize L2CAP 962d05904dSMatthias Ringwald l2cap_init(); 972d05904dSMatthias Ringwald } 982d05904dSMatthias Ringwald /* LISTING_END */ 992d05904dSMatthias Ringwald 1002d05904dSMatthias Ringwald /* @section SDP parser callback 1012d05904dSMatthias Ringwald * 1022d05904dSMatthias Ringwald * @text The SDP parsers retrieves the BNEP PAN UUID as explained in 1032d05904dSMatthias Ringwald * Section [on SDP BNEP Query example](#sec:sdpbnepqueryExample}. 1042d05904dSMatthias Ringwald */ 1052d05904dSMatthias Ringwald 1062d05904dSMatthias Ringwald static void handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 1072d05904dSMatthias Ringwald 1082d05904dSMatthias Ringwald UNUSED(packet_type); 1092d05904dSMatthias Ringwald UNUSED(channel); 1102d05904dSMatthias Ringwald UNUSED(size); 1112d05904dSMatthias Ringwald 1122d05904dSMatthias Ringwald des_iterator_t attribute_list_it; 1132d05904dSMatthias Ringwald des_iterator_t additional_des_it; 1142d05904dSMatthias Ringwald des_iterator_t prot_it; 1152d05904dSMatthias Ringwald uint8_t *des_element; 1162d05904dSMatthias Ringwald uint8_t *element; 1172d05904dSMatthias Ringwald uint32_t uuid; 118e85416c1SMatthias Ringwald uint8_t status; 1192d05904dSMatthias Ringwald 1202d05904dSMatthias Ringwald switch (hci_event_packet_get_type(packet)){ 1212d05904dSMatthias Ringwald case SDP_EVENT_QUERY_ATTRIBUTE_VALUE: 1222d05904dSMatthias Ringwald if (sdp_event_query_attribute_byte_get_attribute_length(packet) <= attribute_value_buffer_size) { 1232d05904dSMatthias Ringwald attribute_value[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet); 1242d05904dSMatthias Ringwald if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) == sdp_event_query_attribute_byte_get_attribute_length(packet)) { 1252d05904dSMatthias Ringwald switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) { 1262d05904dSMatthias Ringwald case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST: 1272d05904dSMatthias Ringwald for (des_iterator_init(&attribute_list_it, attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) { 1282d05904dSMatthias Ringwald if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue; 1292d05904dSMatthias Ringwald des_element = des_iterator_get_element(&attribute_list_it); 1302d05904dSMatthias Ringwald des_iterator_init(&prot_it, des_element); 1312d05904dSMatthias Ringwald element = des_iterator_get_element(&prot_it); 1322d05904dSMatthias Ringwald if (de_get_element_type(element) != DE_UUID) continue; 1332d05904dSMatthias Ringwald uuid = de_get_uuid32(element); 1342d05904dSMatthias Ringwald switch (uuid){ 1352d05904dSMatthias Ringwald case BLUETOOTH_PROTOCOL_L2CAP: 1362d05904dSMatthias Ringwald if (!des_iterator_has_more(&prot_it)) continue; 1372d05904dSMatthias Ringwald des_iterator_next(&prot_it); 1382d05904dSMatthias Ringwald de_element_get_uint16(des_iterator_get_element(&prot_it), &hid_control_psm); 139*8d995dffSMatthias Ringwald printf("HID Control PSM: 0x%04x\n", (int) hid_control_psm); 1402d05904dSMatthias Ringwald break; 1412d05904dSMatthias Ringwald default: 1422d05904dSMatthias Ringwald break; 1432d05904dSMatthias Ringwald } 1442d05904dSMatthias Ringwald } 1452d05904dSMatthias Ringwald break; 1462d05904dSMatthias Ringwald case BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS: 1472d05904dSMatthias Ringwald for (des_iterator_init(&attribute_list_it, attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) { 1482d05904dSMatthias Ringwald if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue; 1492d05904dSMatthias Ringwald des_element = des_iterator_get_element(&attribute_list_it); 1502d05904dSMatthias Ringwald for (des_iterator_init(&additional_des_it, des_element); des_iterator_has_more(&additional_des_it); des_iterator_next(&additional_des_it)) { 1512d05904dSMatthias Ringwald if (des_iterator_get_type(&additional_des_it) != DE_DES) continue; 1522d05904dSMatthias Ringwald des_element = des_iterator_get_element(&additional_des_it); 1532d05904dSMatthias Ringwald des_iterator_init(&prot_it, des_element); 1542d05904dSMatthias Ringwald element = des_iterator_get_element(&prot_it); 1552d05904dSMatthias Ringwald if (de_get_element_type(element) != DE_UUID) continue; 1562d05904dSMatthias Ringwald uuid = de_get_uuid32(element); 1572d05904dSMatthias Ringwald switch (uuid){ 1582d05904dSMatthias Ringwald case BLUETOOTH_PROTOCOL_L2CAP: 1592d05904dSMatthias Ringwald if (!des_iterator_has_more(&prot_it)) continue; 1602d05904dSMatthias Ringwald des_iterator_next(&prot_it); 1612d05904dSMatthias Ringwald de_element_get_uint16(des_iterator_get_element(&prot_it), &hid_interrupt_psm); 162*8d995dffSMatthias Ringwald printf("HID Interrupt PSM: 0x%04x\n", (int) hid_interrupt_psm); 1632d05904dSMatthias Ringwald break; 1642d05904dSMatthias Ringwald default: 1652d05904dSMatthias Ringwald break; 1662d05904dSMatthias Ringwald } 1672d05904dSMatthias Ringwald } 1682d05904dSMatthias Ringwald } 1692d05904dSMatthias Ringwald break; 1702d05904dSMatthias Ringwald case BLUETOOTH_ATTRIBUTE_HID_DESCRIPTOR_LIST: 1712d05904dSMatthias Ringwald hid_descriptor_len = sdp_event_query_attribute_byte_get_attribute_length(packet); 1722d05904dSMatthias Ringwald memcpy(hid_descriptor, attribute_value, hid_descriptor_len); 1732d05904dSMatthias Ringwald printf("HID Descriptor:\n"); 1742d05904dSMatthias Ringwald printf_hexdump(hid_descriptor, hid_descriptor_len); 1752d05904dSMatthias Ringwald break; 1762d05904dSMatthias Ringwald default: 1772d05904dSMatthias Ringwald break; 1782d05904dSMatthias Ringwald } 1792d05904dSMatthias Ringwald } 1802d05904dSMatthias Ringwald } else { 1812d05904dSMatthias Ringwald fprintf(stderr, "SDP attribute value buffer size exceeded: available %d, required %d\n", attribute_value_buffer_size, sdp_event_query_attribute_byte_get_attribute_length(packet)); 1822d05904dSMatthias Ringwald } 1832d05904dSMatthias Ringwald break; 1842d05904dSMatthias Ringwald 1852d05904dSMatthias Ringwald case SDP_EVENT_QUERY_COMPLETE: 1862d05904dSMatthias Ringwald if (!hid_control_psm) { 1872d05904dSMatthias Ringwald printf("HID Control PSM missing\n"); 1882d05904dSMatthias Ringwald break; 1892d05904dSMatthias Ringwald } 1902d05904dSMatthias Ringwald if (!hid_interrupt_psm) { 1912d05904dSMatthias Ringwald printf("HID Interrupt PSM missing\n"); 1922d05904dSMatthias Ringwald break; 1932d05904dSMatthias Ringwald } 1942d05904dSMatthias Ringwald printf("Setup HID\n"); 195e85416c1SMatthias Ringwald status = l2cap_create_channel(packet_handler, remote_addr, hid_control_psm, 48, &l2cap_hid_control_cid); 196e85416c1SMatthias Ringwald if (status){ 197e85416c1SMatthias Ringwald printf("Connecting to HID Control failed: 0x%02x\n", status); 198e85416c1SMatthias Ringwald } 1992d05904dSMatthias Ringwald break; 2002d05904dSMatthias Ringwald } 2012d05904dSMatthias Ringwald } 2022d05904dSMatthias Ringwald 2032d05904dSMatthias Ringwald /* 2042d05904dSMatthias Ringwald * @section Packet Handler 2052d05904dSMatthias Ringwald * 2062d05904dSMatthias Ringwald * @text The packet handler responds to various HCI Events. 2072d05904dSMatthias Ringwald */ 2082d05904dSMatthias Ringwald 2092d05904dSMatthias Ringwald /* LISTING_START(packetHandler): Packet Handler */ 2102d05904dSMatthias Ringwald static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) 2112d05904dSMatthias Ringwald { 2122d05904dSMatthias Ringwald /* LISTING_PAUSE */ 2132d05904dSMatthias Ringwald uint8_t event; 2142d05904dSMatthias Ringwald bd_addr_t event_addr; 215e85416c1SMatthias Ringwald uint8_t status; 216e85416c1SMatthias Ringwald uint16_t l2cap_cid; 2172d05904dSMatthias Ringwald 2182d05904dSMatthias Ringwald /* LISTING_RESUME */ 2192d05904dSMatthias Ringwald switch (packet_type) { 2202d05904dSMatthias Ringwald case HCI_EVENT_PACKET: 2212d05904dSMatthias Ringwald event = hci_event_packet_get_type(packet); 2222d05904dSMatthias Ringwald switch (event) { 2232d05904dSMatthias Ringwald /* @text When BTSTACK_EVENT_STATE with state HCI_STATE_WORKING 2242d05904dSMatthias Ringwald * is received and the example is started in client mode, the remote SDP HID query is started. 2252d05904dSMatthias Ringwald */ 2262d05904dSMatthias Ringwald case BTSTACK_EVENT_STATE: 2272d05904dSMatthias Ringwald if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){ 2282d05904dSMatthias Ringwald printf("Start SDP HID query for remote HID Device.\n"); 2292d05904dSMatthias Ringwald sdp_client_query_uuid16(&handle_sdp_client_query_result, remote_addr, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE); 2302d05904dSMatthias Ringwald } 2312d05904dSMatthias Ringwald break; 2322d05904dSMatthias Ringwald 2332d05904dSMatthias Ringwald /* LISTING_PAUSE */ 2342d05904dSMatthias Ringwald case HCI_EVENT_PIN_CODE_REQUEST: 2352d05904dSMatthias Ringwald // inform about pin code request 2362d05904dSMatthias Ringwald printf("Pin code request - using '0000'\n"); 2372d05904dSMatthias Ringwald hci_event_pin_code_request_get_bd_addr(packet, event_addr); 2382d05904dSMatthias Ringwald gap_pin_code_response(event_addr, "0000"); 2392d05904dSMatthias Ringwald break; 2402d05904dSMatthias Ringwald 2412d05904dSMatthias Ringwald case HCI_EVENT_USER_CONFIRMATION_REQUEST: 2422d05904dSMatthias Ringwald // inform about user confirmation request 2432d05904dSMatthias Ringwald printf("SSP User Confirmation Request with numeric value '%06u'\n", little_endian_read_32(packet, 8)); 2442d05904dSMatthias Ringwald printf("SSP User Confirmation Auto accept\n"); 2452d05904dSMatthias Ringwald break; 2462d05904dSMatthias Ringwald 2472d05904dSMatthias Ringwald /* LISTING_RESUME */ 248e85416c1SMatthias Ringwald 249e85416c1SMatthias Ringwald case L2CAP_EVENT_CHANNEL_OPENED: 250e85416c1SMatthias Ringwald status = packet[2]; 251e85416c1SMatthias Ringwald if (status){ 252e85416c1SMatthias Ringwald printf("L2CAP Connection failed: 0x%02x\n", status); 253e85416c1SMatthias Ringwald break; 254e85416c1SMatthias Ringwald } 255e85416c1SMatthias Ringwald l2cap_cid = little_endian_read_16(packet, 13); 256e85416c1SMatthias Ringwald if (!l2cap_cid) break; 257e85416c1SMatthias Ringwald if (l2cap_cid == l2cap_hid_control_cid){ 258e85416c1SMatthias Ringwald status = l2cap_create_channel(packet_handler, remote_addr, hid_interrupt_psm, 48, &l2cap_hid_interrupt_cid); 259e85416c1SMatthias Ringwald if (status){ 260e85416c1SMatthias Ringwald printf("Connecting to HID Control failed: 0x%02x\n", status); 261e85416c1SMatthias Ringwald break; 262e85416c1SMatthias Ringwald } 263e85416c1SMatthias Ringwald } 264e85416c1SMatthias Ringwald if (l2cap_cid == l2cap_hid_interrupt_cid){ 265e85416c1SMatthias Ringwald printf("HID Connection established\n"); 266e85416c1SMatthias Ringwald } 267e85416c1SMatthias Ringwald break; 2682d05904dSMatthias Ringwald default: 2692d05904dSMatthias Ringwald break; 2702d05904dSMatthias Ringwald } 271e85416c1SMatthias Ringwald break; 272e85416c1SMatthias Ringwald case L2CAP_DATA_PACKET: 273e85416c1SMatthias Ringwald // for now, just dump incoming data 274e85416c1SMatthias Ringwald if (channel == l2cap_hid_interrupt_cid){ 275e85416c1SMatthias Ringwald printf("HID Interrupt: "); 276e85416c1SMatthias Ringwald } else if (channel == l2cap_hid_control_cid){ 277e85416c1SMatthias Ringwald printf("HID Control: "); 278e85416c1SMatthias Ringwald } else { 279e85416c1SMatthias Ringwald break; 280e85416c1SMatthias Ringwald } 281e85416c1SMatthias Ringwald printf_hexdump(packet, size); 2822d05904dSMatthias Ringwald default: 2832d05904dSMatthias Ringwald break; 2842d05904dSMatthias Ringwald } 2852d05904dSMatthias Ringwald } 2862d05904dSMatthias Ringwald /* LISTING_END */ 2872d05904dSMatthias Ringwald 2882d05904dSMatthias Ringwald int btstack_main(int argc, const char * argv[]); 2892d05904dSMatthias Ringwald int btstack_main(int argc, const char * argv[]){ 2902d05904dSMatthias Ringwald 2912d05904dSMatthias Ringwald (void)argc; 2922d05904dSMatthias Ringwald (void)argv; 2932d05904dSMatthias Ringwald 2942d05904dSMatthias Ringwald hid_host_setup(); 2952d05904dSMatthias Ringwald 2962d05904dSMatthias Ringwald // parse human readable Bluetooth address 2972d05904dSMatthias Ringwald sscanf_bd_addr(remote_addr_string, remote_addr); 2982d05904dSMatthias Ringwald 2992d05904dSMatthias Ringwald // Turn on the device 3002d05904dSMatthias Ringwald hci_power_control(HCI_POWER_ON); 3012d05904dSMatthias Ringwald return 0; 3022d05904dSMatthias Ringwald } 3032d05904dSMatthias Ringwald 3042d05904dSMatthias Ringwald /* EXAMPLE_END */ 305