163bf37cdSMilanka Ringwald /* 263bf37cdSMilanka Ringwald * Copyright (C) 2014 BlueKitchen GmbH 363bf37cdSMilanka Ringwald * 463bf37cdSMilanka Ringwald * Redistribution and use in source and binary forms, with or without 563bf37cdSMilanka Ringwald * modification, are permitted provided that the following conditions 663bf37cdSMilanka Ringwald * are met: 763bf37cdSMilanka Ringwald * 863bf37cdSMilanka Ringwald * 1. Redistributions of source code must retain the above copyright 963bf37cdSMilanka Ringwald * notice, this list of conditions and the following disclaimer. 1063bf37cdSMilanka Ringwald * 2. Redistributions in binary form must reproduce the above copyright 1163bf37cdSMilanka Ringwald * notice, this list of conditions and the following disclaimer in the 1263bf37cdSMilanka Ringwald * documentation and/or other materials provided with the distribution. 1363bf37cdSMilanka Ringwald * 3. Neither the name of the copyright holders nor the names of 1463bf37cdSMilanka Ringwald * contributors may be used to endorse or promote products derived 1563bf37cdSMilanka Ringwald * from this software without specific prior written permission. 1663bf37cdSMilanka Ringwald * 4. Any redistribution, use, or modification is done solely for 1763bf37cdSMilanka Ringwald * personal benefit and not for any commercial purpose or for 1863bf37cdSMilanka Ringwald * monetary gain. 1963bf37cdSMilanka Ringwald * 2063bf37cdSMilanka Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 2163bf37cdSMilanka Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2263bf37cdSMilanka Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 2363bf37cdSMilanka Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 2463bf37cdSMilanka Ringwald * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2563bf37cdSMilanka Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2663bf37cdSMilanka Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 2763bf37cdSMilanka Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2863bf37cdSMilanka Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2963bf37cdSMilanka Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 3063bf37cdSMilanka Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3163bf37cdSMilanka Ringwald * SUCH DAMAGE. 3263bf37cdSMilanka Ringwald * 3363bf37cdSMilanka Ringwald * Please inquire about commercial licensing options at 3463bf37cdSMilanka Ringwald * [email protected] 3563bf37cdSMilanka Ringwald * 3663bf37cdSMilanka Ringwald */ 3763bf37cdSMilanka Ringwald 3863bf37cdSMilanka Ringwald #define BTSTACK_FILE__ "hid_host.c" 3963bf37cdSMilanka Ringwald 4063bf37cdSMilanka Ringwald #include <string.h> 4163bf37cdSMilanka Ringwald 4263bf37cdSMilanka Ringwald #include "bluetooth.h" 4363bf37cdSMilanka Ringwald #include "bluetooth_psm.h" 4463bf37cdSMilanka Ringwald #include "bluetooth_sdp.h" 4563bf37cdSMilanka Ringwald #include "btstack_debug.h" 4663bf37cdSMilanka Ringwald #include "btstack_event.h" 4763bf37cdSMilanka Ringwald #include "btstack_hid_parser.h" 48fd7ba7a6SMilanka Ringwald #include "btstack_memory.h" 495961e155SMilanka Ringwald #include "l2cap.h" 505961e155SMilanka Ringwald 5163bf37cdSMilanka Ringwald #include "classic/hid.h" 5263bf37cdSMilanka Ringwald #include "classic/hid_host.h" 5363bf37cdSMilanka Ringwald #include "classic/sdp_util.h" 54fd7ba7a6SMilanka Ringwald #include "classic/sdp_client.h" 5563bf37cdSMilanka Ringwald 56fd7ba7a6SMilanka Ringwald #define MAX_ATTRIBUTE_VALUE_SIZE 300 5763bf37cdSMilanka Ringwald 5859a2ea74SMilanka Ringwald #define CONTROL_MESSAGE_BITMASK_SUSPEND 1 5959a2ea74SMilanka Ringwald #define CONTROL_MESSAGE_BITMASK_EXIT_SUSPEND 2 6059a2ea74SMilanka Ringwald #define CONTROL_MESSAGE_BITMASK_VIRTUAL_CABLE_UNPLUG 4 6159a2ea74SMilanka Ringwald 6259a2ea74SMilanka Ringwald 63fd7ba7a6SMilanka Ringwald static uint8_t * hid_host_descriptor_storage; 6463bf37cdSMilanka Ringwald static uint16_t hid_host_descriptor_storage_len; 6563bf37cdSMilanka Ringwald 66fd7ba7a6SMilanka Ringwald // SDP 67fd7ba7a6SMilanka Ringwald static uint8_t attribute_value[MAX_ATTRIBUTE_VALUE_SIZE]; 68fd7ba7a6SMilanka Ringwald static const unsigned int attribute_value_buffer_size = MAX_ATTRIBUTE_VALUE_SIZE; 69fd7ba7a6SMilanka Ringwald static uint16_t sdp_query_context_hid_host_control_cid = 0; 70fd7ba7a6SMilanka Ringwald 7159a2ea74SMilanka Ringwald static btstack_linked_list_t connections; 7259a2ea74SMilanka Ringwald static uint16_t hid_host_cid_counter = 0; 7359a2ea74SMilanka Ringwald 7459a2ea74SMilanka Ringwald static btstack_packet_handler_t hid_callback; 75fd7ba7a6SMilanka Ringwald static btstack_context_callback_registration_t hid_host_handle_sdp_client_query_request; 76fd7ba7a6SMilanka Ringwald static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); 775961e155SMilanka Ringwald static void hid_host_handle_start_sdp_client_query(void * context); 78fd7ba7a6SMilanka Ringwald 79fd7ba7a6SMilanka Ringwald static uint16_t hid_descriptor_storage_get_available_space(void){ 80fd7ba7a6SMilanka Ringwald // assumes all descriptors are back to back 81fd7ba7a6SMilanka Ringwald uint16_t free_space = hid_host_descriptor_storage_len; 82fd7ba7a6SMilanka Ringwald 83fd7ba7a6SMilanka Ringwald btstack_linked_list_iterator_t it; 84fd7ba7a6SMilanka Ringwald btstack_linked_list_iterator_init(&it, &connections); 85fd7ba7a6SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 86fd7ba7a6SMilanka Ringwald hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it); 87fd7ba7a6SMilanka Ringwald free_space -= connection->hid_descriptor_len; 88fd7ba7a6SMilanka Ringwald } 89fd7ba7a6SMilanka Ringwald return free_space; 90fd7ba7a6SMilanka Ringwald } 91fd7ba7a6SMilanka Ringwald 92a93a968fSMilanka Ringwald static hid_host_connection_t * hid_host_get_connection_for_hid_cid(uint16_t hid_cid){ 93a93a968fSMilanka Ringwald btstack_linked_list_iterator_t it; 94a93a968fSMilanka Ringwald btstack_linked_list_iterator_init(&it, &connections); 95a93a968fSMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 96a93a968fSMilanka Ringwald hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it); 97a93a968fSMilanka Ringwald if (connection->hid_cid != hid_cid) continue; 98a93a968fSMilanka Ringwald return connection; 99a93a968fSMilanka Ringwald } 100a93a968fSMilanka Ringwald return NULL; 101a93a968fSMilanka Ringwald } 102a93a968fSMilanka Ringwald 103a93a968fSMilanka Ringwald static hid_host_connection_t * hid_host_get_connection_for_l2cap_cid(uint16_t l2cap_cid){ 104a93a968fSMilanka Ringwald btstack_linked_list_iterator_t it; 105a93a968fSMilanka Ringwald btstack_linked_list_iterator_init(&it, &connections); 106a93a968fSMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 107a93a968fSMilanka Ringwald hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it); 108a93a968fSMilanka Ringwald if ((connection->interrupt_cid != l2cap_cid) && (connection->control_cid != l2cap_cid)) continue; 109a93a968fSMilanka Ringwald return connection; 110a93a968fSMilanka Ringwald } 111a93a968fSMilanka Ringwald return NULL; 112a93a968fSMilanka Ringwald } 113a93a968fSMilanka Ringwald 114fd7ba7a6SMilanka Ringwald static void hid_descriptor_storage_init(hid_host_connection_t * connection){ 115fd7ba7a6SMilanka Ringwald connection->hid_descriptor_len = 0; 116fd7ba7a6SMilanka Ringwald connection->hid_descriptor_max_len = hid_descriptor_storage_get_available_space(); 117fd7ba7a6SMilanka Ringwald connection->hid_descriptor_offset = hid_host_descriptor_storage_len - connection->hid_descriptor_max_len; 118fd7ba7a6SMilanka Ringwald } 119fd7ba7a6SMilanka Ringwald 120fd7ba7a6SMilanka Ringwald static bool hid_descriptor_storage_store(hid_host_connection_t * connection, uint8_t byte){ 121fd7ba7a6SMilanka Ringwald if (connection->hid_descriptor_len >= connection->hid_descriptor_max_len) return false; 122fd7ba7a6SMilanka Ringwald 123fd7ba7a6SMilanka Ringwald hid_host_descriptor_storage[connection->hid_descriptor_offset + connection->hid_descriptor_len] = byte; 124fd7ba7a6SMilanka Ringwald connection->hid_descriptor_len++; 125fd7ba7a6SMilanka Ringwald return true; 126fd7ba7a6SMilanka Ringwald } 127fd7ba7a6SMilanka Ringwald 128fd7ba7a6SMilanka Ringwald static void hid_descriptor_storage_delete(hid_host_connection_t * connection){ 129fd7ba7a6SMilanka Ringwald uint16_t next_offset = connection->hid_descriptor_offset + connection->hid_descriptor_len; 130fd7ba7a6SMilanka Ringwald 131fd7ba7a6SMilanka Ringwald memmove(&hid_host_descriptor_storage[connection->hid_descriptor_offset], 132fd7ba7a6SMilanka Ringwald &hid_host_descriptor_storage[next_offset], 133fd7ba7a6SMilanka Ringwald hid_host_descriptor_storage_len - next_offset); 134fd7ba7a6SMilanka Ringwald 135fd7ba7a6SMilanka Ringwald connection->hid_descriptor_len = 0; 136fd7ba7a6SMilanka Ringwald connection->hid_descriptor_offset = 0; 137fd7ba7a6SMilanka Ringwald 138fd7ba7a6SMilanka Ringwald btstack_linked_list_iterator_t it; 139fd7ba7a6SMilanka Ringwald btstack_linked_list_iterator_init(&it, &connections); 140fd7ba7a6SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 141fd7ba7a6SMilanka Ringwald hid_host_connection_t * conn = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it); 142fd7ba7a6SMilanka Ringwald if (conn->hid_descriptor_offset >= next_offset){ 143fd7ba7a6SMilanka Ringwald conn->hid_descriptor_offset = next_offset; 144fd7ba7a6SMilanka Ringwald next_offset += conn->hid_descriptor_len; 145fd7ba7a6SMilanka Ringwald } 146fd7ba7a6SMilanka Ringwald } 147fd7ba7a6SMilanka Ringwald } 148fd7ba7a6SMilanka Ringwald 149a93a968fSMilanka Ringwald const uint8_t * hid_descriptor_storage_get_descriptor_data(uint16_t hid_cid){ 150a93a968fSMilanka Ringwald hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid); 151a93a968fSMilanka Ringwald if (!connection){ 152a93a968fSMilanka Ringwald return NULL; 153a93a968fSMilanka Ringwald } 15459a2ea74SMilanka Ringwald return &hid_host_descriptor_storage[connection->hid_descriptor_offset]; 15559a2ea74SMilanka Ringwald } 15659a2ea74SMilanka Ringwald 157a93a968fSMilanka Ringwald const uint16_t hid_descriptor_storage_get_descriptor_len(uint16_t hid_cid){ 158a93a968fSMilanka Ringwald hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid); 159a93a968fSMilanka Ringwald if (!connection){ 160a93a968fSMilanka Ringwald return 0; 161a93a968fSMilanka Ringwald } 16259a2ea74SMilanka Ringwald return connection->hid_descriptor_len; 16359a2ea74SMilanka Ringwald } 16459a2ea74SMilanka Ringwald 16559a2ea74SMilanka Ringwald 166fd7ba7a6SMilanka Ringwald // HID Util 16731a4e45bSMilanka Ringwald static void hid_emit_connected_event(hid_host_connection_t * connection, uint8_t status){ 168fd7ba7a6SMilanka Ringwald uint8_t event[15]; 169dcec10e7SMilanka Ringwald uint16_t pos = 0; 170fd7ba7a6SMilanka Ringwald event[pos++] = HCI_EVENT_HID_META; 171fd7ba7a6SMilanka Ringwald pos++; // skip len 172fd7ba7a6SMilanka Ringwald event[pos++] = HID_SUBEVENT_CONNECTION_OPENED; 17331a4e45bSMilanka Ringwald little_endian_store_16(event, pos, connection->hid_cid); 174fd7ba7a6SMilanka Ringwald pos+=2; 175fd7ba7a6SMilanka Ringwald event[pos++] = status; 17631a4e45bSMilanka Ringwald reverse_bd_addr(connection->remote_addr, &event[pos]); 177fd7ba7a6SMilanka Ringwald pos += 6; 17831a4e45bSMilanka Ringwald little_endian_store_16(event,pos,connection->con_handle); 179fd7ba7a6SMilanka Ringwald pos += 2; 18031a4e45bSMilanka Ringwald event[pos++] = connection->incoming; 181fd7ba7a6SMilanka Ringwald event[1] = pos - 2; 18231a4e45bSMilanka Ringwald hid_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos); 183fd7ba7a6SMilanka Ringwald } 184fd7ba7a6SMilanka Ringwald 18505439aa6SMilanka Ringwald static void hid_emit_descriptor_available_event(hid_host_connection_t * connection){ 18605439aa6SMilanka Ringwald uint8_t event[6]; 18705439aa6SMilanka Ringwald uint16_t pos = 0; 18805439aa6SMilanka Ringwald event[pos++] = HCI_EVENT_HID_META; 18905439aa6SMilanka Ringwald pos++; // skip len 19005439aa6SMilanka Ringwald event[pos++] = HID_SUBEVENT_DESCRIPTOR_AVAILABLE; 19105439aa6SMilanka Ringwald little_endian_store_16(event,pos,connection->hid_cid); 19205439aa6SMilanka Ringwald pos += 2; 19305439aa6SMilanka Ringwald event[pos++] = connection->hid_descriptor_status; 19405439aa6SMilanka Ringwald event[1] = pos - 2; 19505439aa6SMilanka Ringwald hid_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos); 19605439aa6SMilanka Ringwald } 19731a4e45bSMilanka Ringwald 19831a4e45bSMilanka Ringwald static void hid_emit_event(hid_host_connection_t * connection, uint8_t subevent_type){ 199ab30106eSMilanka Ringwald uint8_t event[5]; 200dcec10e7SMilanka Ringwald uint16_t pos = 0; 201ab30106eSMilanka Ringwald event[pos++] = HCI_EVENT_HID_META; 202ab30106eSMilanka Ringwald pos++; // skip len 203ab30106eSMilanka Ringwald event[pos++] = subevent_type; 20431a4e45bSMilanka Ringwald little_endian_store_16(event,pos,connection->hid_cid); 205ab30106eSMilanka Ringwald pos += 2; 206ab30106eSMilanka Ringwald event[1] = pos - 2; 20731a4e45bSMilanka Ringwald hid_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos); 208ab30106eSMilanka Ringwald } 209ab30106eSMilanka Ringwald 21031a4e45bSMilanka Ringwald static void hid_emit_event_with_status(hid_host_connection_t * connection, uint8_t subevent_type, hid_handshake_param_type_t status){ 2114a4b5586SMilanka Ringwald uint8_t event[6]; 2124a4b5586SMilanka Ringwald uint16_t pos = 0; 2134a4b5586SMilanka Ringwald event[pos++] = HCI_EVENT_HID_META; 2144a4b5586SMilanka Ringwald pos++; // skip len 2154a4b5586SMilanka Ringwald event[pos++] = subevent_type; 21631a4e45bSMilanka Ringwald little_endian_store_16(event,pos,connection->hid_cid); 2174a4b5586SMilanka Ringwald pos += 2; 2184a4b5586SMilanka Ringwald event[pos++] = status; 2194a4b5586SMilanka Ringwald event[1] = pos - 2; 22031a4e45bSMilanka Ringwald hid_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos); 2214a4b5586SMilanka Ringwald } 2224a4b5586SMilanka Ringwald 223de89a6fcSMilanka Ringwald static void hid_emit_set_protocol_response_event(hid_host_connection_t * connection, hid_handshake_param_type_t status){ 224de89a6fcSMilanka Ringwald uint8_t event[7]; 225de89a6fcSMilanka Ringwald uint16_t pos = 0; 226de89a6fcSMilanka Ringwald event[pos++] = HCI_EVENT_HID_META; 227de89a6fcSMilanka Ringwald pos++; // skip len 228de89a6fcSMilanka Ringwald event[pos++] = HID_SUBEVENT_SET_PROTOCOL_RESPONSE; 229de89a6fcSMilanka Ringwald little_endian_store_16(event,pos,connection->hid_cid); 230de89a6fcSMilanka Ringwald pos += 2; 231de89a6fcSMilanka Ringwald event[pos++] = status; 232de89a6fcSMilanka Ringwald event[pos++] = connection->protocol_mode; 233de89a6fcSMilanka Ringwald event[1] = pos - 2; 234de89a6fcSMilanka Ringwald hid_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos); 235de89a6fcSMilanka Ringwald } 236de89a6fcSMilanka Ringwald 23731a4e45bSMilanka Ringwald static void hid_emit_incoming_connection_event(hid_host_connection_t * connection){ 23859a2ea74SMilanka Ringwald uint8_t event[13]; 239dcec10e7SMilanka Ringwald uint16_t pos = 0; 24059a2ea74SMilanka Ringwald event[pos++] = HCI_EVENT_HID_META; 24159a2ea74SMilanka Ringwald pos++; // skip len 24259a2ea74SMilanka Ringwald event[pos++] = HID_SUBEVENT_INCOMING_CONNECTION; 24331a4e45bSMilanka Ringwald little_endian_store_16(event, pos, connection->hid_cid); 24459a2ea74SMilanka Ringwald pos += 2; 24531a4e45bSMilanka Ringwald reverse_bd_addr(connection->remote_addr, &event[pos]); 24659a2ea74SMilanka Ringwald pos += 6; 24731a4e45bSMilanka Ringwald little_endian_store_16(event,pos,connection->con_handle); 24859a2ea74SMilanka Ringwald pos += 2; 24959a2ea74SMilanka Ringwald event[1] = pos - 2; 25031a4e45bSMilanka Ringwald hid_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos); 25159a2ea74SMilanka Ringwald } 25259a2ea74SMilanka Ringwald 253dcec10e7SMilanka Ringwald // setup get report response event - potentially in-place of original l2cap packet 25431a4e45bSMilanka Ringwald static void hid_setup_get_report_event(hid_host_connection_t * connection, hid_handshake_param_type_t status, uint8_t *buffer, uint16_t report_len){ 255dcec10e7SMilanka Ringwald uint16_t pos = 0; 256dcec10e7SMilanka Ringwald buffer[pos++] = HCI_EVENT_HID_META; 257dcec10e7SMilanka Ringwald pos++; // skip len 258dcec10e7SMilanka Ringwald buffer[pos++] = HID_SUBEVENT_GET_REPORT_RESPONSE; 25931a4e45bSMilanka Ringwald little_endian_store_16(buffer, pos, connection->hid_cid); 260dcec10e7SMilanka Ringwald pos += 2; 261dcec10e7SMilanka Ringwald buffer[pos++] = (uint8_t) status; 262dcec10e7SMilanka Ringwald little_endian_store_16(buffer, pos, report_len); 263dcec10e7SMilanka Ringwald pos += 2; 264dcec10e7SMilanka Ringwald buffer[1] = pos + report_len - 2; 265dcec10e7SMilanka Ringwald } 266dcec10e7SMilanka Ringwald 267a93a968fSMilanka Ringwald // setup report event - potentially in-place of original l2cap packet 26831a4e45bSMilanka Ringwald static void hid_setup_report_event(hid_host_connection_t * connection, uint8_t *buffer, uint16_t report_len){ 269a93a968fSMilanka Ringwald uint16_t pos = 0; 270a93a968fSMilanka Ringwald buffer[pos++] = HCI_EVENT_HID_META; 271a93a968fSMilanka Ringwald pos++; // skip len 272a93a968fSMilanka Ringwald buffer[pos++] = HID_SUBEVENT_REPORT; 27331a4e45bSMilanka Ringwald little_endian_store_16(buffer, pos, connection->hid_cid); 274a93a968fSMilanka Ringwald pos += 2; 275a93a968fSMilanka Ringwald little_endian_store_16(buffer, pos, report_len); 276a93a968fSMilanka Ringwald pos += 2; 277a93a968fSMilanka Ringwald buffer[1] = pos + report_len - 2; 278a93a968fSMilanka Ringwald } 279a93a968fSMilanka Ringwald 280a93a968fSMilanka Ringwald 28131a4e45bSMilanka Ringwald static void hid_emit_get_protocol_event(hid_host_connection_t * connection, hid_handshake_param_type_t status, hid_protocol_mode_t protocol_mode){ 2824a4b5586SMilanka Ringwald uint8_t event[7]; 2834a4b5586SMilanka Ringwald uint16_t pos = 0; 2844a4b5586SMilanka Ringwald event[pos++] = HCI_EVENT_HID_META; 2854a4b5586SMilanka Ringwald pos++; // skip len 2864a4b5586SMilanka Ringwald event[pos++] = HID_SUBEVENT_GET_PROTOCOL_RESPONSE; 28731a4e45bSMilanka Ringwald little_endian_store_16(event,pos,connection->hid_cid); 2884a4b5586SMilanka Ringwald pos += 2; 2894a4b5586SMilanka Ringwald event[pos++] = status; 2904a4b5586SMilanka Ringwald event[pos++] = protocol_mode; 2914a4b5586SMilanka Ringwald event[1] = pos - 2; 29231a4e45bSMilanka Ringwald hid_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos); 2934a4b5586SMilanka Ringwald } 2944a4b5586SMilanka Ringwald 295fd7ba7a6SMilanka Ringwald // HID Host 296fd7ba7a6SMilanka Ringwald 297fd7ba7a6SMilanka Ringwald static uint16_t hid_host_get_next_cid(void){ 298fd7ba7a6SMilanka Ringwald if (hid_host_cid_counter == 0xffff) { 299fd7ba7a6SMilanka Ringwald hid_host_cid_counter = 1; 300fd7ba7a6SMilanka Ringwald } else { 301fd7ba7a6SMilanka Ringwald hid_host_cid_counter++; 302fd7ba7a6SMilanka Ringwald } 303fd7ba7a6SMilanka Ringwald return hid_host_cid_counter; 304fd7ba7a6SMilanka Ringwald } 305fd7ba7a6SMilanka Ringwald 306fd7ba7a6SMilanka Ringwald static hid_host_connection_t * hid_host_create_connection(bd_addr_t remote_addr){ 307fd7ba7a6SMilanka Ringwald hid_host_connection_t * connection = btstack_memory_hid_host_connection_get(); 308fd7ba7a6SMilanka Ringwald if (!connection){ 309fd7ba7a6SMilanka Ringwald log_error("Not enough memory to create connection"); 310fd7ba7a6SMilanka Ringwald return NULL; 311fd7ba7a6SMilanka Ringwald } 312fd7ba7a6SMilanka Ringwald connection->state = HID_HOST_IDLE; 313fd7ba7a6SMilanka Ringwald connection->hid_cid = hid_host_get_next_cid(); 31459a2ea74SMilanka Ringwald connection->control_cid = 0; 31559a2ea74SMilanka Ringwald connection->control_psm = 0; 31659a2ea74SMilanka Ringwald connection->interrupt_cid = 0; 31759a2ea74SMilanka Ringwald connection->interrupt_psm = 0; 31859a2ea74SMilanka Ringwald connection->con_handle = HCI_CON_HANDLE_INVALID; 319fd7ba7a6SMilanka Ringwald 32059a2ea74SMilanka Ringwald (void)memcpy(connection->remote_addr, remote_addr, 6); 321fd7ba7a6SMilanka Ringwald btstack_linked_list_add(&connections, (btstack_linked_item_t *) connection); 322fd7ba7a6SMilanka Ringwald return connection; 323fd7ba7a6SMilanka Ringwald } 324fd7ba7a6SMilanka Ringwald 325fd7ba7a6SMilanka Ringwald static hid_host_connection_t * hid_host_get_connection_for_bd_addr(bd_addr_t addr){ 326fd7ba7a6SMilanka Ringwald btstack_linked_list_iterator_t it; 327fd7ba7a6SMilanka Ringwald btstack_linked_list_iterator_init(&it, &connections); 328fd7ba7a6SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 329fd7ba7a6SMilanka Ringwald hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it); 330fd7ba7a6SMilanka Ringwald if (memcmp(addr, connection->remote_addr, 6) != 0) continue; 331fd7ba7a6SMilanka Ringwald return connection; 332fd7ba7a6SMilanka Ringwald } 333fd7ba7a6SMilanka Ringwald return NULL; 334fd7ba7a6SMilanka Ringwald } 335fd7ba7a6SMilanka Ringwald 336ab30106eSMilanka Ringwald 337fd7ba7a6SMilanka Ringwald static void hid_host_finalize_connection(hid_host_connection_t * connection){ 338*7379ca3dSMilanka Ringwald uint16_t interrupt_cid = connection->interrupt_cid; 339*7379ca3dSMilanka Ringwald uint16_t control_cid = connection->control_cid; 340*7379ca3dSMilanka Ringwald 341*7379ca3dSMilanka Ringwald connection->interrupt_cid = 0; 342*7379ca3dSMilanka Ringwald connection->control_cid = 0; 343*7379ca3dSMilanka Ringwald 344*7379ca3dSMilanka Ringwald if (interrupt_cid != 0){ 345*7379ca3dSMilanka Ringwald l2cap_disconnect(interrupt_cid, 0); 346*7379ca3dSMilanka Ringwald } 347*7379ca3dSMilanka Ringwald if (control_cid != 0){ 348*7379ca3dSMilanka Ringwald l2cap_disconnect(control_cid, 0); 349*7379ca3dSMilanka Ringwald } 350fd7ba7a6SMilanka Ringwald btstack_linked_list_remove(&connections, (btstack_linked_item_t*) connection); 351fd7ba7a6SMilanka Ringwald btstack_memory_hid_host_connection_free(connection); 352fd7ba7a6SMilanka Ringwald } 353fd7ba7a6SMilanka Ringwald 354fd7ba7a6SMilanka Ringwald static void hid_host_handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { 355fd7ba7a6SMilanka Ringwald UNUSED(packet_type); 356fd7ba7a6SMilanka Ringwald UNUSED(channel); 357fd7ba7a6SMilanka Ringwald UNUSED(size); 358fd7ba7a6SMilanka Ringwald 359fd7ba7a6SMilanka Ringwald des_iterator_t attribute_list_it; 360fd7ba7a6SMilanka Ringwald des_iterator_t additional_des_it; 361fd7ba7a6SMilanka Ringwald des_iterator_t prot_it; 362fd7ba7a6SMilanka Ringwald uint8_t *des_element; 363fd7ba7a6SMilanka Ringwald uint8_t *element; 364fd7ba7a6SMilanka Ringwald uint32_t uuid; 365fd7ba7a6SMilanka Ringwald uint8_t status = ERROR_CODE_SUCCESS; 36601977ed1SMilanka Ringwald bool try_fallback_to_boot; 36705439aa6SMilanka Ringwald bool finalize_connection; 368fd7ba7a6SMilanka Ringwald 3695961e155SMilanka Ringwald 370ab30106eSMilanka Ringwald hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(sdp_query_context_hid_host_control_cid); 371fd7ba7a6SMilanka Ringwald if (!connection) { 372fd7ba7a6SMilanka Ringwald log_error("SDP query, connection with 0x%02x cid not found", sdp_query_context_hid_host_control_cid); 373fd7ba7a6SMilanka Ringwald return; 374fd7ba7a6SMilanka Ringwald } 3755961e155SMilanka Ringwald 37605439aa6SMilanka Ringwald btstack_assert(connection->state == HID_HOST_W4_SDP_QUERY_RESULT); 3775961e155SMilanka Ringwald 378fd7ba7a6SMilanka Ringwald switch (hci_event_packet_get_type(packet)){ 379fd7ba7a6SMilanka Ringwald case SDP_EVENT_QUERY_ATTRIBUTE_VALUE: 380fd7ba7a6SMilanka Ringwald 381fd7ba7a6SMilanka Ringwald if (sdp_event_query_attribute_byte_get_attribute_length(packet) <= attribute_value_buffer_size) { 382fd7ba7a6SMilanka Ringwald 383fd7ba7a6SMilanka Ringwald attribute_value[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet); 384fd7ba7a6SMilanka Ringwald 385fd7ba7a6SMilanka Ringwald if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) == sdp_event_query_attribute_byte_get_attribute_length(packet)) { 386fd7ba7a6SMilanka Ringwald switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) { 387fd7ba7a6SMilanka Ringwald 388fd7ba7a6SMilanka Ringwald case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST: 389fd7ba7a6SMilanka Ringwald for (des_iterator_init(&attribute_list_it, attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) { 390fd7ba7a6SMilanka Ringwald if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue; 391fd7ba7a6SMilanka Ringwald des_element = des_iterator_get_element(&attribute_list_it); 392fd7ba7a6SMilanka Ringwald des_iterator_init(&prot_it, des_element); 393fd7ba7a6SMilanka Ringwald element = des_iterator_get_element(&prot_it); 394fd7ba7a6SMilanka Ringwald if (de_get_element_type(element) != DE_UUID) continue; 395fd7ba7a6SMilanka Ringwald uuid = de_get_uuid32(element); 396fd7ba7a6SMilanka Ringwald switch (uuid){ 397fd7ba7a6SMilanka Ringwald case BLUETOOTH_PROTOCOL_L2CAP: 398fd7ba7a6SMilanka Ringwald if (!des_iterator_has_more(&prot_it)) continue; 399fd7ba7a6SMilanka Ringwald des_iterator_next(&prot_it); 400fd7ba7a6SMilanka Ringwald de_element_get_uint16(des_iterator_get_element(&prot_it), &connection->control_psm); 401fd7ba7a6SMilanka Ringwald log_info("HID Control PSM: 0x%04x", (int) &connection->control_psm); 402fd7ba7a6SMilanka Ringwald break; 403fd7ba7a6SMilanka Ringwald default: 404fd7ba7a6SMilanka Ringwald break; 405fd7ba7a6SMilanka Ringwald } 406fd7ba7a6SMilanka Ringwald } 407fd7ba7a6SMilanka Ringwald break; 408fd7ba7a6SMilanka Ringwald case BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS: 409fd7ba7a6SMilanka Ringwald for (des_iterator_init(&attribute_list_it, attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) { 410fd7ba7a6SMilanka Ringwald if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue; 411fd7ba7a6SMilanka Ringwald des_element = des_iterator_get_element(&attribute_list_it); 412fd7ba7a6SMilanka Ringwald for (des_iterator_init(&additional_des_it, des_element); des_iterator_has_more(&additional_des_it); des_iterator_next(&additional_des_it)) { 413fd7ba7a6SMilanka Ringwald if (des_iterator_get_type(&additional_des_it) != DE_DES) continue; 414fd7ba7a6SMilanka Ringwald des_element = des_iterator_get_element(&additional_des_it); 415fd7ba7a6SMilanka Ringwald des_iterator_init(&prot_it, des_element); 416fd7ba7a6SMilanka Ringwald element = des_iterator_get_element(&prot_it); 417fd7ba7a6SMilanka Ringwald if (de_get_element_type(element) != DE_UUID) continue; 418fd7ba7a6SMilanka Ringwald uuid = de_get_uuid32(element); 419fd7ba7a6SMilanka Ringwald switch (uuid){ 420fd7ba7a6SMilanka Ringwald case BLUETOOTH_PROTOCOL_L2CAP: 421fd7ba7a6SMilanka Ringwald if (!des_iterator_has_more(&prot_it)) continue; 422fd7ba7a6SMilanka Ringwald des_iterator_next(&prot_it); 423fd7ba7a6SMilanka Ringwald de_element_get_uint16(des_iterator_get_element(&prot_it), &connection->interrupt_psm); 424fd7ba7a6SMilanka Ringwald log_info("HID Interrupt PSM: 0x%04x", (int) &connection->interrupt_psm); 425fd7ba7a6SMilanka Ringwald break; 426fd7ba7a6SMilanka Ringwald default: 427fd7ba7a6SMilanka Ringwald break; 428fd7ba7a6SMilanka Ringwald } 429fd7ba7a6SMilanka Ringwald } 430fd7ba7a6SMilanka Ringwald } 431fd7ba7a6SMilanka Ringwald break; 432fd7ba7a6SMilanka Ringwald 433fd7ba7a6SMilanka Ringwald case BLUETOOTH_ATTRIBUTE_HID_DESCRIPTOR_LIST: 434fd7ba7a6SMilanka Ringwald for (des_iterator_init(&attribute_list_it, attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) { 435fd7ba7a6SMilanka Ringwald if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue; 436fd7ba7a6SMilanka Ringwald des_element = des_iterator_get_element(&attribute_list_it); 437fd7ba7a6SMilanka Ringwald for (des_iterator_init(&additional_des_it, des_element); des_iterator_has_more(&additional_des_it); des_iterator_next(&additional_des_it)) { 438fd7ba7a6SMilanka Ringwald if (des_iterator_get_type(&additional_des_it) != DE_STRING) continue; 439fd7ba7a6SMilanka Ringwald element = des_iterator_get_element(&additional_des_it); 440fd7ba7a6SMilanka Ringwald 441fd7ba7a6SMilanka Ringwald const uint8_t * descriptor = de_get_string(element); 442fd7ba7a6SMilanka Ringwald uint16_t descriptor_len = de_get_data_size(element); 443fd7ba7a6SMilanka Ringwald 444fd7ba7a6SMilanka Ringwald uint16_t i; 44505439aa6SMilanka Ringwald bool stored = false; 44605439aa6SMilanka Ringwald 44705439aa6SMilanka Ringwald connection->hid_descriptor_status = ERROR_CODE_SUCCESS; 448fd7ba7a6SMilanka Ringwald for (i = 0; i < descriptor_len; i++){ 44905439aa6SMilanka Ringwald stored = hid_descriptor_storage_store(connection, descriptor[i]); 45005439aa6SMilanka Ringwald if (!stored){ 45105439aa6SMilanka Ringwald connection->hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; 45205439aa6SMilanka Ringwald break; 45305439aa6SMilanka Ringwald } 454fd7ba7a6SMilanka Ringwald } 455fd7ba7a6SMilanka Ringwald } 456fd7ba7a6SMilanka Ringwald } 457fd7ba7a6SMilanka Ringwald break; 458fd7ba7a6SMilanka Ringwald default: 459fd7ba7a6SMilanka Ringwald break; 460fd7ba7a6SMilanka Ringwald } 461fd7ba7a6SMilanka Ringwald } 462fd7ba7a6SMilanka Ringwald } else { 4635961e155SMilanka Ringwald log_error("SDP attribute value buffer size exceeded: available %d, required %d", attribute_value_buffer_size, sdp_event_query_attribute_byte_get_attribute_length(packet)); 464fd7ba7a6SMilanka Ringwald } 465fd7ba7a6SMilanka Ringwald break; 466fd7ba7a6SMilanka Ringwald 467fd7ba7a6SMilanka Ringwald case SDP_EVENT_QUERY_COMPLETE: 468fe493a7cSMilanka Ringwald status = sdp_event_query_complete_get_status(packet); 46901977ed1SMilanka Ringwald try_fallback_to_boot = false; 47005439aa6SMilanka Ringwald finalize_connection = false; 471fe493a7cSMilanka Ringwald 47201977ed1SMilanka Ringwald switch (status){ 47301977ed1SMilanka Ringwald // remote has SDP server 47401977ed1SMilanka Ringwald case ERROR_CODE_SUCCESS: 47501977ed1SMilanka Ringwald // but no HID record 47601977ed1SMilanka Ringwald if (!connection->control_psm || !connection->interrupt_psm) { 477fd7ba7a6SMilanka Ringwald status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 47805439aa6SMilanka Ringwald if (connection->requested_protocol_mode == HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT){ 47901977ed1SMilanka Ringwald try_fallback_to_boot = true; 48005439aa6SMilanka Ringwald } else { 48105439aa6SMilanka Ringwald finalize_connection = true; 48205439aa6SMilanka Ringwald } 483fd7ba7a6SMilanka Ringwald break; 484fd7ba7a6SMilanka Ringwald } 48505439aa6SMilanka Ringwald // report mode possible 486fd7ba7a6SMilanka Ringwald break; 487fd7ba7a6SMilanka Ringwald 48805439aa6SMilanka Ringwald // SDP connection failed or remote does not have SDP server 489fd7ba7a6SMilanka Ringwald default: 49005439aa6SMilanka Ringwald if (connection->requested_protocol_mode == HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT){ 49105439aa6SMilanka Ringwald try_fallback_to_boot = true; 49205439aa6SMilanka Ringwald } else { 49305439aa6SMilanka Ringwald finalize_connection = true; 49405439aa6SMilanka Ringwald } 49505439aa6SMilanka Ringwald break; 49605439aa6SMilanka Ringwald } 49705439aa6SMilanka Ringwald 49805439aa6SMilanka Ringwald if (finalize_connection){ 49905439aa6SMilanka Ringwald sdp_query_context_hid_host_control_cid = 0; 500fd7ba7a6SMilanka Ringwald hid_emit_connected_event(connection, status); 501fd7ba7a6SMilanka Ringwald hid_host_finalize_connection(connection); 5025961e155SMilanka Ringwald break; 5035961e155SMilanka Ringwald } 5045961e155SMilanka Ringwald 50501977ed1SMilanka Ringwald if (try_fallback_to_boot){ 50605439aa6SMilanka Ringwald if (connection->incoming){ 50705439aa6SMilanka Ringwald connection->set_protocol = true; 508de89a6fcSMilanka Ringwald connection->state = HID_HOST_CONNECTION_ESTABLISHED; 50905439aa6SMilanka Ringwald connection->requested_protocol_mode = HID_PROTOCOL_MODE_BOOT; 510de89a6fcSMilanka Ringwald hid_emit_descriptor_available_event(connection); 51105439aa6SMilanka Ringwald l2cap_request_can_send_now_event(connection->control_cid); 51205439aa6SMilanka Ringwald } else { 51301977ed1SMilanka Ringwald connection->state = HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED; 51405439aa6SMilanka Ringwald status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, BLUETOOTH_PSM_HID_CONTROL, 0xffff, &connection->control_cid); 51501977ed1SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){ 51605439aa6SMilanka Ringwald sdp_query_context_hid_host_control_cid = 0; 51701977ed1SMilanka Ringwald hid_emit_connected_event(connection, status); 51801977ed1SMilanka Ringwald hid_host_finalize_connection(connection); 51905439aa6SMilanka Ringwald } 52001977ed1SMilanka Ringwald } 5215961e155SMilanka Ringwald break; 52201977ed1SMilanka Ringwald } 52305439aa6SMilanka Ringwald 52405439aa6SMilanka Ringwald // report mode possible 52505439aa6SMilanka Ringwald if (connection->incoming) { 52605439aa6SMilanka Ringwald connection->set_protocol = true; 527de89a6fcSMilanka Ringwald connection->state = HID_HOST_CONNECTION_ESTABLISHED; 52805439aa6SMilanka Ringwald connection->requested_protocol_mode = HID_PROTOCOL_MODE_REPORT; 529de89a6fcSMilanka Ringwald hid_emit_descriptor_available_event(connection); 53005439aa6SMilanka Ringwald l2cap_request_can_send_now_event(connection->control_cid); 53105439aa6SMilanka Ringwald } else { 53205439aa6SMilanka Ringwald connection->state = HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED; 53305439aa6SMilanka Ringwald status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, connection->control_psm, 0xffff, &connection->control_cid); 53405439aa6SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){ 53505439aa6SMilanka Ringwald hid_emit_connected_event(connection, status); 53605439aa6SMilanka Ringwald hid_host_finalize_connection(connection); 53705439aa6SMilanka Ringwald } 53805439aa6SMilanka Ringwald } 53901977ed1SMilanka Ringwald break; 54001977ed1SMilanka Ringwald 54101977ed1SMilanka Ringwald default: 54201977ed1SMilanka Ringwald break; 54301977ed1SMilanka Ringwald } 54401977ed1SMilanka Ringwald 545fd7ba7a6SMilanka Ringwald } 546fd7ba7a6SMilanka Ringwald 547ab30106eSMilanka Ringwald 548ab30106eSMilanka Ringwald static void hid_host_handle_control_packet(hid_host_connection_t * connection, uint8_t *packet, uint16_t size){ 549ab30106eSMilanka Ringwald UNUSED(size); 550ab30106eSMilanka Ringwald uint8_t param; 551ab30106eSMilanka Ringwald hid_message_type_t message_type; 552ab30106eSMilanka Ringwald hid_handshake_param_type_t message_status; 5534a4b5586SMilanka Ringwald hid_protocol_mode_t protocol_mode; 5544a4b5586SMilanka Ringwald 555dcec10e7SMilanka Ringwald uint8_t * in_place_event; 556ab30106eSMilanka Ringwald uint8_t status; 557ab30106eSMilanka Ringwald 558ab30106eSMilanka Ringwald message_type = (hid_message_type_t)(packet[0] >> 4); 559dcec10e7SMilanka Ringwald if (message_type == HID_MESSAGE_TYPE_HID_CONTROL){ 560dcec10e7SMilanka Ringwald param = packet[0] & 0x0F; 561dcec10e7SMilanka Ringwald switch ((hid_control_param_t)param){ 562dcec10e7SMilanka Ringwald case HID_CONTROL_PARAM_VIRTUAL_CABLE_UNPLUG: 563dcec10e7SMilanka Ringwald hid_emit_event(connection, HID_SUBEVENT_VIRTUAL_CABLE_UNPLUG); 564dcec10e7SMilanka Ringwald hid_host_disconnect(connection->hid_cid); 565dcec10e7SMilanka Ringwald return; 566dcec10e7SMilanka Ringwald default: 567dcec10e7SMilanka Ringwald break; 568dcec10e7SMilanka Ringwald } 569dcec10e7SMilanka Ringwald } 570ab30106eSMilanka Ringwald 5714a4b5586SMilanka Ringwald message_status = (hid_handshake_param_type_t)(packet[0] & 0x0F); 5724a4b5586SMilanka Ringwald 573ab30106eSMilanka Ringwald switch (connection->state){ 57401977ed1SMilanka Ringwald case HID_HOST_CONNECTION_ESTABLISHED: 57501977ed1SMilanka Ringwald if (!connection->w4_set_protocol_response) break; 57601977ed1SMilanka Ringwald connection->w4_set_protocol_response = false; 577de89a6fcSMilanka Ringwald 57801977ed1SMilanka Ringwald switch (message_status){ 57901977ed1SMilanka Ringwald case HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL: 58001977ed1SMilanka Ringwald connection->protocol_mode = connection->requested_protocol_mode; 58101977ed1SMilanka Ringwald break; 58201977ed1SMilanka Ringwald default: 58301977ed1SMilanka Ringwald break; 58401977ed1SMilanka Ringwald } 585de89a6fcSMilanka Ringwald hid_emit_set_protocol_response_event(connection, message_status); 58601977ed1SMilanka Ringwald break; 58701977ed1SMilanka Ringwald 588de89a6fcSMilanka Ringwald case HID_HOST_CONTROL_CONNECTION_ESTABLISHED: // outgoing 589de89a6fcSMilanka Ringwald case HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED: // outgoing 59001977ed1SMilanka Ringwald if (!connection->w4_set_protocol_response) break; 59101977ed1SMilanka Ringwald connection->w4_set_protocol_response = false; 59205439aa6SMilanka Ringwald 59301977ed1SMilanka Ringwald switch (message_status){ 59401977ed1SMilanka Ringwald case HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL: 59501977ed1SMilanka Ringwald // we are already connected, here it is only confirmed that we are in required protocol 596de89a6fcSMilanka Ringwald btstack_assert(connection->incoming == false); 59705439aa6SMilanka Ringwald status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, connection->interrupt_psm, 0xffff, &connection->interrupt_cid); 598de89a6fcSMilanka Ringwald if (status != ERROR_CODE_SUCCESS){ 5995961e155SMilanka Ringwald log_info("HID Interrupt Connection failed: 0x%02x\n", status); 600de89a6fcSMilanka Ringwald hid_emit_connected_event(connection, status); 601de89a6fcSMilanka Ringwald hid_host_finalize_connection(connection); 60201977ed1SMilanka Ringwald break; 60301977ed1SMilanka Ringwald } 60401977ed1SMilanka Ringwald connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED; 60505439aa6SMilanka Ringwald break; 60601977ed1SMilanka Ringwald default: 60701977ed1SMilanka Ringwald hid_emit_connected_event(connection, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE); 60805439aa6SMilanka Ringwald hid_host_finalize_connection(connection); 60901977ed1SMilanka Ringwald break; 61001977ed1SMilanka Ringwald } 61101977ed1SMilanka Ringwald break; 61201977ed1SMilanka Ringwald 613ab30106eSMilanka Ringwald case HID_HOST_W4_GET_REPORT_RESPONSE: 614dcec10e7SMilanka Ringwald switch (message_type){ 615dcec10e7SMilanka Ringwald case HID_MESSAGE_TYPE_HANDSHAKE:{ 616dcec10e7SMilanka Ringwald uint8_t event[8]; 617dcec10e7SMilanka Ringwald hid_setup_get_report_event(connection, message_status, event, 0); 618dcec10e7SMilanka Ringwald hid_callback(HCI_EVENT_PACKET, connection->hid_cid, event, sizeof(event)); 619dcec10e7SMilanka Ringwald break; 620dcec10e7SMilanka Ringwald } 621dcec10e7SMilanka Ringwald case HID_MESSAGE_TYPE_DATA: 622dcec10e7SMilanka Ringwald // reuse hci+l2cap header - max 8 byte (7 bytes + 1 bytes overwriting hid header) 623dcec10e7SMilanka Ringwald in_place_event = packet - 7; 624dcec10e7SMilanka Ringwald hid_setup_get_report_event(connection, HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL, in_place_event, size-1); 625dcec10e7SMilanka Ringwald hid_callback(HCI_EVENT_PACKET, connection->hid_cid, in_place_event, size + 7); 626dcec10e7SMilanka Ringwald break; 627dcec10e7SMilanka Ringwald default: 628dcec10e7SMilanka Ringwald break; 629dcec10e7SMilanka Ringwald } 6302ba92e70SMilanka Ringwald connection->state = HID_HOST_CONNECTION_ESTABLISHED; 631ab30106eSMilanka Ringwald break; 6324a4b5586SMilanka Ringwald 633ab30106eSMilanka Ringwald case HID_HOST_W4_SET_REPORT_RESPONSE: 6344a4b5586SMilanka Ringwald hid_emit_event_with_status(connection, HID_SUBEVENT_SET_REPORT_RESPONSE, message_status); 6352ba92e70SMilanka Ringwald connection->state = HID_HOST_CONNECTION_ESTABLISHED; 636ab30106eSMilanka Ringwald break; 6374a4b5586SMilanka Ringwald 638ab30106eSMilanka Ringwald case HID_HOST_W4_GET_PROTOCOL_RESPONSE: 6394a4b5586SMilanka Ringwald protocol_mode = connection->protocol_mode; 6404a4b5586SMilanka Ringwald 6414a4b5586SMilanka Ringwald switch (message_type){ 6424a4b5586SMilanka Ringwald case HID_MESSAGE_TYPE_DATA: 6434a4b5586SMilanka Ringwald protocol_mode = (hid_protocol_mode_t)packet[1]; 6444a4b5586SMilanka Ringwald switch (protocol_mode){ 6454a4b5586SMilanka Ringwald case HID_PROTOCOL_MODE_BOOT: 6464a4b5586SMilanka Ringwald case HID_PROTOCOL_MODE_REPORT: 6474a4b5586SMilanka Ringwald message_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL; 648ab30106eSMilanka Ringwald break; 6494a4b5586SMilanka Ringwald default: 6504a4b5586SMilanka Ringwald message_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER; 651ab30106eSMilanka Ringwald break; 6524a4b5586SMilanka Ringwald } 6534a4b5586SMilanka Ringwald break; 6544a4b5586SMilanka Ringwald default: 6554a4b5586SMilanka Ringwald break; 6564a4b5586SMilanka Ringwald } 6574a4b5586SMilanka Ringwald hid_emit_get_protocol_event(connection, message_status, protocol_mode); 6582ba92e70SMilanka Ringwald connection->state = HID_HOST_CONNECTION_ESTABLISHED; 6594a4b5586SMilanka Ringwald break; 6604a4b5586SMilanka Ringwald 661ab30106eSMilanka Ringwald default: 662a93a968fSMilanka Ringwald log_info("ignore invalid HID Control message"); 6632ba92e70SMilanka Ringwald connection->state = HID_HOST_CONNECTION_ESTABLISHED; 664ab30106eSMilanka Ringwald break; 665ab30106eSMilanka Ringwald } 6662ba92e70SMilanka Ringwald 667ab30106eSMilanka Ringwald } 668ab30106eSMilanka Ringwald 669fd7ba7a6SMilanka Ringwald static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ 670fd7ba7a6SMilanka Ringwald UNUSED(channel); 671fd7ba7a6SMilanka Ringwald UNUSED(size); 672fd7ba7a6SMilanka Ringwald 67363bf37cdSMilanka Ringwald uint8_t event; 674fd7ba7a6SMilanka Ringwald bd_addr_t address; 675fd7ba7a6SMilanka Ringwald uint8_t status; 676ab30106eSMilanka Ringwald uint16_t l2cap_cid; 677fd7ba7a6SMilanka Ringwald hid_host_connection_t * connection; 678fd7ba7a6SMilanka Ringwald 67963bf37cdSMilanka Ringwald switch (packet_type) { 680ab30106eSMilanka Ringwald 681ab30106eSMilanka Ringwald case L2CAP_DATA_PACKET: 682ab30106eSMilanka Ringwald connection = hid_host_get_connection_for_l2cap_cid(channel); 683ab30106eSMilanka Ringwald if (!connection) break; 68459a2ea74SMilanka Ringwald 685ab30106eSMilanka Ringwald if (channel == connection->interrupt_cid){ 686fe493a7cSMilanka Ringwald uint8_t * in_place_event = packet - 7; 687a93a968fSMilanka Ringwald hid_setup_report_event(connection, in_place_event, size-1); 688fe493a7cSMilanka Ringwald hid_callback(HCI_EVENT_PACKET, connection->hid_cid, in_place_event, size + 7); 689ab30106eSMilanka Ringwald break; 690ab30106eSMilanka Ringwald } 691a93a968fSMilanka Ringwald 692ab30106eSMilanka Ringwald if (channel == connection->control_cid){ 693ab30106eSMilanka Ringwald hid_host_handle_control_packet(connection, packet, size); 694ab30106eSMilanka Ringwald break; 695ab30106eSMilanka Ringwald } 696ab30106eSMilanka Ringwald break; 697ab30106eSMilanka Ringwald 69863bf37cdSMilanka Ringwald case HCI_EVENT_PACKET: 69963bf37cdSMilanka Ringwald event = hci_event_packet_get_type(packet); 70063bf37cdSMilanka Ringwald switch (event) { 701fd7ba7a6SMilanka Ringwald case L2CAP_EVENT_INCOMING_CONNECTION: 702fd7ba7a6SMilanka Ringwald l2cap_event_incoming_connection_get_address(packet, address); 7035961e155SMilanka Ringwald // connection should exist if psm == PSM_HID_INTERRUPT 704fd7ba7a6SMilanka Ringwald connection = hid_host_get_connection_for_bd_addr(address); 705fd7ba7a6SMilanka Ringwald 706fd7ba7a6SMilanka Ringwald switch (l2cap_event_incoming_connection_get_psm(packet)){ 707fd7ba7a6SMilanka Ringwald case PSM_HID_CONTROL: 708fd7ba7a6SMilanka Ringwald if (connection){ 709fd7ba7a6SMilanka Ringwald l2cap_decline_connection(channel); 710fd7ba7a6SMilanka Ringwald break; 711fd7ba7a6SMilanka Ringwald } 712fd7ba7a6SMilanka Ringwald 713fd7ba7a6SMilanka Ringwald connection = hid_host_create_connection(address); 714fd7ba7a6SMilanka Ringwald if (!connection){ 715fd7ba7a6SMilanka Ringwald log_error("Cannot create connection for %s", bd_addr_to_str(address)); 716fd7ba7a6SMilanka Ringwald l2cap_decline_connection(channel); 717fd7ba7a6SMilanka Ringwald break; 718fd7ba7a6SMilanka Ringwald } 719fd7ba7a6SMilanka Ringwald 720fd7ba7a6SMilanka Ringwald connection->state = HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED; 721e615b0ecSMilanka Ringwald connection->hid_descriptor_status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 722fd7ba7a6SMilanka Ringwald connection->con_handle = l2cap_event_incoming_connection_get_handle(packet); 723fd7ba7a6SMilanka Ringwald connection->control_cid = l2cap_event_incoming_connection_get_local_cid(packet); 724fd7ba7a6SMilanka Ringwald connection->incoming = true; 72559a2ea74SMilanka Ringwald 7265961e155SMilanka Ringwald // emit connection request 72757e682feSMilanka Ringwald // user calls either hid_host_accept_connection or hid_host_decline_connection 72859a2ea74SMilanka Ringwald hid_emit_incoming_connection_event(connection); 729fd7ba7a6SMilanka Ringwald break; 730fd7ba7a6SMilanka Ringwald 731fd7ba7a6SMilanka Ringwald case PSM_HID_INTERRUPT: 7325961e155SMilanka Ringwald if (!connection || (connection->state != HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED)){ 733fd7ba7a6SMilanka Ringwald log_error("Decline connection for %s", bd_addr_to_str(address)); 734fd7ba7a6SMilanka Ringwald l2cap_decline_connection(channel); 735fd7ba7a6SMilanka Ringwald break; 736fd7ba7a6SMilanka Ringwald } 737fd7ba7a6SMilanka Ringwald 738fd7ba7a6SMilanka Ringwald connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED; 739fd7ba7a6SMilanka Ringwald connection->interrupt_cid = l2cap_event_incoming_connection_get_local_cid(packet); 740fd7ba7a6SMilanka Ringwald log_info("Accept connection on Interrupt channel %s", bd_addr_to_str(address)); 741fd7ba7a6SMilanka Ringwald l2cap_accept_connection(channel); 742fd7ba7a6SMilanka Ringwald break; 743fd7ba7a6SMilanka Ringwald 744fd7ba7a6SMilanka Ringwald default: 745fd7ba7a6SMilanka Ringwald log_info("Decline connection for %s", bd_addr_to_str(address)); 746fd7ba7a6SMilanka Ringwald l2cap_decline_connection(channel); 747fd7ba7a6SMilanka Ringwald break; 74863bf37cdSMilanka Ringwald } 74963bf37cdSMilanka Ringwald break; 750fd7ba7a6SMilanka Ringwald 751fd7ba7a6SMilanka Ringwald case L2CAP_EVENT_CHANNEL_OPENED: 752fd7ba7a6SMilanka Ringwald l2cap_event_channel_opened_get_address(packet, address); 753fd7ba7a6SMilanka Ringwald 754fd7ba7a6SMilanka Ringwald connection = hid_host_get_connection_for_bd_addr(address); 755fd7ba7a6SMilanka Ringwald if (!connection){ 756fd7ba7a6SMilanka Ringwald log_error("Connection does not exist %s", bd_addr_to_str(address)); 757fd7ba7a6SMilanka Ringwald break; 758fd7ba7a6SMilanka Ringwald } 759fd7ba7a6SMilanka Ringwald 760c71a99a4SMilanka Ringwald status = l2cap_event_channel_opened_get_status(packet); 76105439aa6SMilanka Ringwald if (status != ERROR_CODE_SUCCESS){ 762c71a99a4SMilanka Ringwald log_info("L2CAP connection %s failed: 0x%02xn", bd_addr_to_str(address), status); 763c71a99a4SMilanka Ringwald hid_emit_connected_event(connection, status); 76405439aa6SMilanka Ringwald hid_host_finalize_connection(connection); 765c71a99a4SMilanka Ringwald break; 766c71a99a4SMilanka Ringwald } 767c71a99a4SMilanka Ringwald 76801977ed1SMilanka Ringwald // handle incoming connection: 76901977ed1SMilanka Ringwald if (connection->incoming){ 7705961e155SMilanka Ringwald switch (connection->state){ 7715961e155SMilanka Ringwald case HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED: 77201977ed1SMilanka Ringwald // A Bluetooth HID Host or Bluetooth HID device shall always open both the control and Interrupt channels. (HID_v1.1.1, Chapter 5.2.2) 77301977ed1SMilanka Ringwald // We expect incomming interrupt connection from remote HID device 77401977ed1SMilanka Ringwald connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED; 77505439aa6SMilanka Ringwald log_info("Incoming control connection opened: w4 interrupt"); 77601977ed1SMilanka Ringwald break; 77701977ed1SMilanka Ringwald 7785961e155SMilanka Ringwald case HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED: 779de89a6fcSMilanka Ringwald hid_emit_connected_event(connection, ERROR_CODE_SUCCESS); 780de89a6fcSMilanka Ringwald connection->state = HID_HOST_CONNECTION_ESTABLISHED; 781fd7ba7a6SMilanka Ringwald 78205439aa6SMilanka Ringwald switch (connection->requested_protocol_mode){ 783fe493a7cSMilanka Ringwald case HID_PROTOCOL_MODE_BOOT: 784e615b0ecSMilanka Ringwald hid_emit_descriptor_available_event(connection); 78501977ed1SMilanka Ringwald connection->set_protocol = true; 786fe493a7cSMilanka Ringwald l2cap_request_can_send_now_event(connection->control_cid); 78705439aa6SMilanka Ringwald log_info("Incoming interrupt connection opened: set boot mode"); 788fd7ba7a6SMilanka Ringwald break; 789fe493a7cSMilanka Ringwald default: 79001977ed1SMilanka Ringwald // SDP query 79105439aa6SMilanka Ringwald log_info("Incoming interrupt connection opened: start SDP query"); 79205439aa6SMilanka Ringwald connection->state = HID_HOST_W2_SEND_SDP_QUERY; 7935961e155SMilanka Ringwald hid_host_handle_sdp_client_query_request.callback = &hid_host_handle_start_sdp_client_query; 7945961e155SMilanka Ringwald (void) sdp_client_register_query_callback(&hid_host_handle_sdp_client_query_request); 79501977ed1SMilanka Ringwald break; 79601977ed1SMilanka Ringwald } 79701977ed1SMilanka Ringwald break; 79801977ed1SMilanka Ringwald 79901977ed1SMilanka Ringwald default: 8005961e155SMilanka Ringwald btstack_assert(false); 80101977ed1SMilanka Ringwald break; 80201977ed1SMilanka Ringwald } 80301977ed1SMilanka Ringwald break; 80401977ed1SMilanka Ringwald } 80501977ed1SMilanka Ringwald 80601977ed1SMilanka Ringwald // handle outgoing connection 8075961e155SMilanka Ringwald switch (connection->state){ 8085961e155SMilanka Ringwald case HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED: 80905439aa6SMilanka Ringwald log_info("Opened control connection, requested protocol mode %d\n", connection->requested_protocol_mode); 81001977ed1SMilanka Ringwald connection->con_handle = l2cap_event_channel_opened_get_handle(packet); 81101977ed1SMilanka Ringwald connection->state = HID_HOST_CONTROL_CONNECTION_ESTABLISHED; 81201977ed1SMilanka Ringwald 81301977ed1SMilanka Ringwald switch (connection->requested_protocol_mode){ 81401977ed1SMilanka Ringwald case HID_PROTOCOL_MODE_BOOT: 81501977ed1SMilanka Ringwald case HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT: 81601977ed1SMilanka Ringwald connection->set_protocol = true; 8175961e155SMilanka Ringwald connection->interrupt_psm = BLUETOOTH_PSM_HID_INTERRUPT; 81801977ed1SMilanka Ringwald l2cap_request_can_send_now_event(connection->control_cid); 81901977ed1SMilanka Ringwald break; 82001977ed1SMilanka Ringwald default: 82105439aa6SMilanka Ringwald status = l2cap_create_channel(hid_host_packet_handler, address, connection->interrupt_psm, 0xffff, &connection->interrupt_cid); 822fd7ba7a6SMilanka Ringwald if (status){ 823fd7ba7a6SMilanka Ringwald log_info("Connecting to HID Interrupt failed: 0x%02x", status); 82401977ed1SMilanka Ringwald hid_emit_connected_event(connection, status); 825fd7ba7a6SMilanka Ringwald break; 826fd7ba7a6SMilanka Ringwald } 82701977ed1SMilanka Ringwald connection->protocol_mode = connection->requested_protocol_mode; 828fd7ba7a6SMilanka Ringwald connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED; 829fe493a7cSMilanka Ringwald break; 830fd7ba7a6SMilanka Ringwald } 831fd7ba7a6SMilanka Ringwald break; 832fd7ba7a6SMilanka Ringwald 8335961e155SMilanka Ringwald case HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED: 834fd7ba7a6SMilanka Ringwald connection->state = HID_HOST_CONNECTION_ESTABLISHED; 83559a2ea74SMilanka Ringwald log_info("HID host connection established, cids: control 0x%02x, interrupt 0x%02x interrupt, hid 0x%02x", 83659a2ea74SMilanka Ringwald connection->control_cid, connection->interrupt_cid, connection->hid_cid); 837fd7ba7a6SMilanka Ringwald hid_emit_connected_event(connection, ERROR_CODE_SUCCESS); 83805439aa6SMilanka Ringwald hid_emit_descriptor_available_event(connection); 839fd7ba7a6SMilanka Ringwald break; 840fd7ba7a6SMilanka Ringwald 841fd7ba7a6SMilanka Ringwald default: 8425961e155SMilanka Ringwald btstack_assert(false); 843fd7ba7a6SMilanka Ringwald break; 844fd7ba7a6SMilanka Ringwald } 845fd7ba7a6SMilanka Ringwald break; 846fd7ba7a6SMilanka Ringwald 847ab30106eSMilanka Ringwald case L2CAP_EVENT_CHANNEL_CLOSED: 848ab30106eSMilanka Ringwald l2cap_cid = l2cap_event_channel_closed_get_local_cid(packet); 849ab30106eSMilanka Ringwald connection = hid_host_get_connection_for_l2cap_cid(l2cap_cid); 850ab30106eSMilanka Ringwald if (!connection) return; 851ab30106eSMilanka Ringwald 852ab30106eSMilanka Ringwald if (l2cap_cid == connection->interrupt_cid){ 853ab30106eSMilanka Ringwald connection->interrupt_cid = 0; 854ab30106eSMilanka Ringwald if (connection->state == HID_HOST_W4_INTERRUPT_CONNECTION_DISCONNECTED){ 855ab30106eSMilanka Ringwald connection->state = HID_HOST_W4_CONTROL_CONNECTION_DISCONNECTED; 856ab30106eSMilanka Ringwald l2cap_disconnect(connection->control_cid, 0); 857ab30106eSMilanka Ringwald } 858ab30106eSMilanka Ringwald break; 859ab30106eSMilanka Ringwald } 860ab30106eSMilanka Ringwald 861ab30106eSMilanka Ringwald if (l2cap_cid == connection->control_cid){ 862ab30106eSMilanka Ringwald connection->control_cid = 0; 863ab30106eSMilanka Ringwald hid_emit_event(connection, HID_SUBEVENT_CONNECTION_CLOSED); 864ab30106eSMilanka Ringwald hid_descriptor_storage_delete(connection); 865ab30106eSMilanka Ringwald hid_host_finalize_connection(connection); 866ab30106eSMilanka Ringwald break; 867ab30106eSMilanka Ringwald } 868ab30106eSMilanka Ringwald break; 869ab30106eSMilanka Ringwald 870ab30106eSMilanka Ringwald case L2CAP_EVENT_CAN_SEND_NOW: 871ab30106eSMilanka Ringwald l2cap_cid = l2cap_event_can_send_now_get_local_cid(packet); 872ab30106eSMilanka Ringwald connection = hid_host_get_connection_for_l2cap_cid(l2cap_cid); 873ab30106eSMilanka Ringwald if (!connection) return; 874ab30106eSMilanka Ringwald 87501977ed1SMilanka Ringwald 87601977ed1SMilanka Ringwald 87759a2ea74SMilanka Ringwald if (connection->control_cid == l2cap_cid){ 878ab30106eSMilanka Ringwald switch(connection->state){ 87901977ed1SMilanka Ringwald case HID_HOST_CONTROL_CONNECTION_ESTABLISHED: 88001977ed1SMilanka Ringwald case HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED: 88101977ed1SMilanka Ringwald if (connection->set_protocol){ 88201977ed1SMilanka Ringwald connection->set_protocol = false; 88301977ed1SMilanka Ringwald uint8_t header = (HID_MESSAGE_TYPE_SET_PROTOCOL << 4) | connection->requested_protocol_mode; 88401977ed1SMilanka Ringwald uint8_t report[] = {header}; 88501977ed1SMilanka Ringwald connection->w4_set_protocol_response = true; 88601977ed1SMilanka Ringwald l2cap_send(connection->control_cid, (uint8_t*) report, sizeof(report)); 88701977ed1SMilanka Ringwald break; 88801977ed1SMilanka Ringwald } 88901977ed1SMilanka Ringwald break; 89001977ed1SMilanka Ringwald 89159a2ea74SMilanka Ringwald case HID_HOST_CONNECTION_ESTABLISHED: 89259a2ea74SMilanka Ringwald if ((connection->control_tasks & CONTROL_MESSAGE_BITMASK_SUSPEND) != 0){ 89359a2ea74SMilanka Ringwald connection->control_tasks &= ~CONTROL_MESSAGE_BITMASK_SUSPEND; 89459a2ea74SMilanka Ringwald uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_SUSPEND }; 89559a2ea74SMilanka Ringwald l2cap_send(connection->control_cid, (uint8_t*) report, 1); 89659a2ea74SMilanka Ringwald break; 89759a2ea74SMilanka Ringwald } 89859a2ea74SMilanka Ringwald if ((connection->control_tasks & CONTROL_MESSAGE_BITMASK_EXIT_SUSPEND) != 0){ 89959a2ea74SMilanka Ringwald connection->control_tasks &= ~CONTROL_MESSAGE_BITMASK_EXIT_SUSPEND; 90059a2ea74SMilanka Ringwald uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_EXIT_SUSPEND }; 90159a2ea74SMilanka Ringwald l2cap_send(connection->control_cid, (uint8_t*) report, 1); 90259a2ea74SMilanka Ringwald break; 90359a2ea74SMilanka Ringwald } 90459a2ea74SMilanka Ringwald if ((connection->control_tasks & CONTROL_MESSAGE_BITMASK_VIRTUAL_CABLE_UNPLUG) != 0){ 90559a2ea74SMilanka Ringwald connection->control_tasks &= ~CONTROL_MESSAGE_BITMASK_VIRTUAL_CABLE_UNPLUG; 90659a2ea74SMilanka Ringwald uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_VIRTUAL_CABLE_UNPLUG }; 90759a2ea74SMilanka Ringwald l2cap_send(connection->control_cid, (uint8_t*) report, 1); 90859a2ea74SMilanka Ringwald break; 90959a2ea74SMilanka Ringwald } 91001977ed1SMilanka Ringwald 91101977ed1SMilanka Ringwald if (connection->set_protocol){ 91201977ed1SMilanka Ringwald connection->set_protocol = false; 91301977ed1SMilanka Ringwald uint8_t header = (HID_MESSAGE_TYPE_SET_PROTOCOL << 4) | connection->requested_protocol_mode; 91401977ed1SMilanka Ringwald uint8_t report[] = {header}; 91501977ed1SMilanka Ringwald 91601977ed1SMilanka Ringwald connection->w4_set_protocol_response = true; 91701977ed1SMilanka Ringwald l2cap_send(connection->control_cid, (uint8_t*) report, sizeof(report)); 91801977ed1SMilanka Ringwald break; 91901977ed1SMilanka Ringwald } 92059a2ea74SMilanka Ringwald break; 92159a2ea74SMilanka Ringwald 922ab30106eSMilanka Ringwald case HID_HOST_W2_SEND_GET_REPORT:{ 923ab30106eSMilanka Ringwald uint8_t header = (HID_MESSAGE_TYPE_GET_REPORT << 4) | connection->report_type; 924ab30106eSMilanka Ringwald uint8_t report[] = {header, connection->report_id}; 925ab30106eSMilanka Ringwald 926ab30106eSMilanka Ringwald connection->state = HID_HOST_W4_GET_REPORT_RESPONSE; 927ab30106eSMilanka Ringwald l2cap_send(connection->control_cid, (uint8_t*) report, sizeof(report)); 928ab30106eSMilanka Ringwald break; 929ab30106eSMilanka Ringwald } 930a93a968fSMilanka Ringwald 931ab30106eSMilanka Ringwald case HID_HOST_W2_SEND_SET_REPORT:{ 932ab30106eSMilanka Ringwald uint8_t header = (HID_MESSAGE_TYPE_SET_REPORT << 4) | connection->report_type; 933ab30106eSMilanka Ringwald connection->state = HID_HOST_W4_SET_REPORT_RESPONSE; 934ab30106eSMilanka Ringwald 935ab30106eSMilanka Ringwald l2cap_reserve_packet_buffer(); 936ab30106eSMilanka Ringwald uint8_t * out_buffer = l2cap_get_outgoing_buffer(); 937ab30106eSMilanka Ringwald out_buffer[0] = header; 938ab30106eSMilanka Ringwald out_buffer[1] = connection->report_id; 939ab30106eSMilanka Ringwald (void)memcpy(out_buffer + 2, connection->report, connection->report_len); 940ab30106eSMilanka Ringwald l2cap_send_prepared(connection->control_cid, connection->report_len + 2); 941ab30106eSMilanka Ringwald break; 942ab30106eSMilanka Ringwald } 943a93a968fSMilanka Ringwald 944ab30106eSMilanka Ringwald case HID_HOST_W2_SEND_GET_PROTOCOL:{ 945ab30106eSMilanka Ringwald uint8_t header = (HID_MESSAGE_TYPE_GET_PROTOCOL << 4); 946ab30106eSMilanka Ringwald uint8_t report[] = {header}; 947ab30106eSMilanka Ringwald connection->state = HID_HOST_W4_GET_PROTOCOL_RESPONSE; 948ab30106eSMilanka Ringwald l2cap_send(connection->control_cid, (uint8_t*) report, sizeof(report)); 949ab30106eSMilanka Ringwald break; 950ab30106eSMilanka Ringwald } 951a93a968fSMilanka Ringwald 95259a2ea74SMilanka Ringwald default: 95359a2ea74SMilanka Ringwald break; 95459a2ea74SMilanka Ringwald } 95559a2ea74SMilanka Ringwald } 95659a2ea74SMilanka Ringwald 957fe493a7cSMilanka Ringwald if (connection->interrupt_cid == l2cap_cid && connection->state == HID_HOST_W2_SEND_REPORT){ 958fe493a7cSMilanka Ringwald connection->state = HID_HOST_CONNECTION_ESTABLISHED; 959fe493a7cSMilanka Ringwald // there is no response for this type of message 960ab30106eSMilanka Ringwald uint8_t header = (HID_MESSAGE_TYPE_DATA << 4) | connection->report_type; 961ab30106eSMilanka Ringwald 962ab30106eSMilanka Ringwald l2cap_reserve_packet_buffer(); 963ab30106eSMilanka Ringwald uint8_t * out_buffer = l2cap_get_outgoing_buffer(); 964ab30106eSMilanka Ringwald out_buffer[0] = header; 965ab30106eSMilanka Ringwald out_buffer[1] = connection->report_id; 966ab30106eSMilanka Ringwald (void)memcpy(out_buffer + 2, connection->report, connection->report_len); 967ab30106eSMilanka Ringwald l2cap_send_prepared(connection->interrupt_cid, connection->report_len + 2); 968ab30106eSMilanka Ringwald break; 969ab30106eSMilanka Ringwald } 970ab30106eSMilanka Ringwald 97159a2ea74SMilanka Ringwald if (connection->control_tasks != 0){ 97259a2ea74SMilanka Ringwald l2cap_request_can_send_now_event(connection->control_cid); 97359a2ea74SMilanka Ringwald } 97459a2ea74SMilanka Ringwald break; 97563bf37cdSMilanka Ringwald default: 97663bf37cdSMilanka Ringwald break; 97763bf37cdSMilanka Ringwald } 97863bf37cdSMilanka Ringwald default: 97963bf37cdSMilanka Ringwald break; 98063bf37cdSMilanka Ringwald } 98163bf37cdSMilanka Ringwald } 98263bf37cdSMilanka Ringwald 983fd7ba7a6SMilanka Ringwald 984fd7ba7a6SMilanka Ringwald void hid_host_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){ 98563bf37cdSMilanka Ringwald hid_host_descriptor_storage = hid_descriptor_storage; 98663bf37cdSMilanka Ringwald hid_host_descriptor_storage_len = hid_descriptor_storage_len; 98763bf37cdSMilanka Ringwald 98863bf37cdSMilanka Ringwald // register L2CAP Services for reconnections 989fd7ba7a6SMilanka Ringwald l2cap_register_service(hid_host_packet_handler, PSM_HID_INTERRUPT, 0xffff, gap_get_security_level()); 990fd7ba7a6SMilanka Ringwald l2cap_register_service(hid_host_packet_handler, PSM_HID_CONTROL, 0xffff, gap_get_security_level()); 99163bf37cdSMilanka Ringwald } 99263bf37cdSMilanka Ringwald 99363bf37cdSMilanka Ringwald void hid_host_register_packet_handler(btstack_packet_handler_t callback){ 994fd7ba7a6SMilanka Ringwald hid_callback = callback; 99563bf37cdSMilanka Ringwald } 99663bf37cdSMilanka Ringwald 997fd7ba7a6SMilanka Ringwald 998fd7ba7a6SMilanka Ringwald static void hid_host_handle_start_sdp_client_query(void * context){ 999fd7ba7a6SMilanka Ringwald UNUSED(context); 1000fd7ba7a6SMilanka Ringwald btstack_linked_list_iterator_t it; 1001fd7ba7a6SMilanka Ringwald btstack_linked_list_iterator_init(&it, &connections); 10025961e155SMilanka Ringwald 1003fd7ba7a6SMilanka Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 1004fd7ba7a6SMilanka Ringwald hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it); 1005fd7ba7a6SMilanka Ringwald 1006fd7ba7a6SMilanka Ringwald switch (connection->state){ 1007fd7ba7a6SMilanka Ringwald case HID_HOST_W2_SEND_SDP_QUERY: 1008fd7ba7a6SMilanka Ringwald connection->state = HID_HOST_W4_SDP_QUERY_RESULT; 100905439aa6SMilanka Ringwald connection->hid_descriptor_status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 1010fd7ba7a6SMilanka Ringwald break; 1011fd7ba7a6SMilanka Ringwald default: 1012fd7ba7a6SMilanka Ringwald continue; 1013fd7ba7a6SMilanka Ringwald } 10145961e155SMilanka Ringwald 1015fd7ba7a6SMilanka Ringwald hid_descriptor_storage_init(connection); 1016fd7ba7a6SMilanka Ringwald sdp_query_context_hid_host_control_cid = connection->hid_cid; 1017fd7ba7a6SMilanka Ringwald sdp_client_query_uuid16(&hid_host_handle_sdp_client_query_result, (uint8_t *) connection->remote_addr, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE); 1018fd7ba7a6SMilanka Ringwald return; 1019fd7ba7a6SMilanka Ringwald } 1020fd7ba7a6SMilanka Ringwald } 1021fd7ba7a6SMilanka Ringwald 102201977ed1SMilanka Ringwald uint8_t hid_host_accept_connection(uint16_t hid_cid, hid_protocol_mode_t protocol_mode){ 102359a2ea74SMilanka Ringwald hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid); 102457e682feSMilanka Ringwald if (!connection){ 102557e682feSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 102657e682feSMilanka Ringwald } 102757e682feSMilanka Ringwald if (connection->state != HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED){ 102857e682feSMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 102957e682feSMilanka Ringwald } 103057e682feSMilanka Ringwald 103101977ed1SMilanka Ringwald connection->requested_protocol_mode = protocol_mode; 103259a2ea74SMilanka Ringwald l2cap_accept_connection(connection->control_cid); 103359a2ea74SMilanka Ringwald return ERROR_CODE_SUCCESS; 103459a2ea74SMilanka Ringwald } 1035fd7ba7a6SMilanka Ringwald 103659a2ea74SMilanka Ringwald uint8_t hid_host_decline_connection(uint16_t hid_cid){ 103759a2ea74SMilanka Ringwald hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid); 103857e682feSMilanka Ringwald if (!connection){ 103957e682feSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 104057e682feSMilanka Ringwald } 104157e682feSMilanka Ringwald if (connection->state != HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED){ 104257e682feSMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 104357e682feSMilanka Ringwald } 104457e682feSMilanka Ringwald 104559a2ea74SMilanka Ringwald l2cap_decline_connection(connection->control_cid); 104659a2ea74SMilanka Ringwald hid_host_finalize_connection(connection); 104759a2ea74SMilanka Ringwald return ERROR_CODE_SUCCESS; 104859a2ea74SMilanka Ringwald } 104959a2ea74SMilanka Ringwald 105059a2ea74SMilanka Ringwald uint8_t hid_host_connect(bd_addr_t remote_addr, hid_protocol_mode_t protocol_mode, uint16_t * hid_cid){ 1051fd7ba7a6SMilanka Ringwald if (hid_cid == NULL) { 1052fd7ba7a6SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 1053fd7ba7a6SMilanka Ringwald } 1054fd7ba7a6SMilanka Ringwald 1055fd7ba7a6SMilanka Ringwald hid_host_connection_t * connection = hid_host_get_connection_for_bd_addr(remote_addr); 1056fd7ba7a6SMilanka Ringwald if (connection){ 1057fd7ba7a6SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 1058fd7ba7a6SMilanka Ringwald } 1059fd7ba7a6SMilanka Ringwald 1060fd7ba7a6SMilanka Ringwald connection = hid_host_create_connection(remote_addr); 1061fd7ba7a6SMilanka Ringwald if (!connection) return BTSTACK_MEMORY_ALLOC_FAILED; 1062fd7ba7a6SMilanka Ringwald 1063fd7ba7a6SMilanka Ringwald *hid_cid = connection->hid_cid; 1064fd7ba7a6SMilanka Ringwald 1065fd7ba7a6SMilanka Ringwald connection->state = HID_HOST_W2_SEND_SDP_QUERY; 1066fd7ba7a6SMilanka Ringwald connection->incoming = false; 106701977ed1SMilanka Ringwald connection->requested_protocol_mode = protocol_mode; 10682ba92e70SMilanka Ringwald connection->hid_descriptor_status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE; 1069fd7ba7a6SMilanka Ringwald 107001977ed1SMilanka Ringwald uint8_t status = ERROR_CODE_SUCCESS; 107101977ed1SMilanka Ringwald 10725961e155SMilanka Ringwald switch (connection->requested_protocol_mode){ 107301977ed1SMilanka Ringwald case HID_PROTOCOL_MODE_BOOT: 107401977ed1SMilanka Ringwald connection->state = HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED; 107505439aa6SMilanka Ringwald status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, BLUETOOTH_PSM_HID_CONTROL, 0xffff, &connection->control_cid); 107601977ed1SMilanka Ringwald break; 107701977ed1SMilanka Ringwald default: 1078fd7ba7a6SMilanka Ringwald hid_host_handle_sdp_client_query_request.callback = &hid_host_handle_start_sdp_client_query; 1079fd7ba7a6SMilanka Ringwald // ignore ERROR_CODE_COMMAND_DISALLOWED because in that case, we already have requested an SDP callback 1080fd7ba7a6SMilanka Ringwald (void) sdp_client_register_query_callback(&hid_host_handle_sdp_client_query_request); 108101977ed1SMilanka Ringwald break; 108201977ed1SMilanka Ringwald } 108301977ed1SMilanka Ringwald return status; 108463bf37cdSMilanka Ringwald } 108563bf37cdSMilanka Ringwald 108663bf37cdSMilanka Ringwald 108763bf37cdSMilanka Ringwald void hid_host_disconnect(uint16_t hid_cid){ 1088ab30106eSMilanka Ringwald hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid); 1089ab30106eSMilanka Ringwald if (!connection) return; 1090ab30106eSMilanka Ringwald 1091ab30106eSMilanka Ringwald switch (connection->state){ 1092ab30106eSMilanka Ringwald case HID_HOST_IDLE: 1093ab30106eSMilanka Ringwald case HID_HOST_W4_CONTROL_CONNECTION_DISCONNECTED: 1094ab30106eSMilanka Ringwald case HID_HOST_W4_INTERRUPT_CONNECTION_DISCONNECTED: 1095ab30106eSMilanka Ringwald return; 1096ab30106eSMilanka Ringwald default: 1097ab30106eSMilanka Ringwald break; 1098ab30106eSMilanka Ringwald } 1099ab30106eSMilanka Ringwald 1100ab30106eSMilanka Ringwald if (connection->interrupt_cid){ 1101ab30106eSMilanka Ringwald connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_DISCONNECTED; 1102ab30106eSMilanka Ringwald l2cap_disconnect(connection->interrupt_cid, 0); // reason isn't used 1103ab30106eSMilanka Ringwald return; 1104ab30106eSMilanka Ringwald } 1105ab30106eSMilanka Ringwald 1106ab30106eSMilanka Ringwald if (connection->control_cid){ 1107ab30106eSMilanka Ringwald connection->state = HID_HOST_W4_CONTROL_CONNECTION_DISCONNECTED; 1108ab30106eSMilanka Ringwald l2cap_disconnect(connection->control_cid, 0); // reason isn't used 1109ab30106eSMilanka Ringwald return; 1110ab30106eSMilanka Ringwald } 111163bf37cdSMilanka Ringwald } 111263bf37cdSMilanka Ringwald 111359a2ea74SMilanka Ringwald 111459a2ea74SMilanka Ringwald static inline uint8_t hid_host_send_control_message(uint16_t hid_cid, uint8_t control_message_bitmask){ 111559a2ea74SMilanka Ringwald hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid); 111659a2ea74SMilanka Ringwald if (!connection || !connection->control_cid) return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 111759a2ea74SMilanka Ringwald 111859a2ea74SMilanka Ringwald if (connection->state < HID_HOST_CONTROL_CONNECTION_ESTABLISHED) { 111959a2ea74SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 112063bf37cdSMilanka Ringwald } 112159a2ea74SMilanka Ringwald if (connection->state >= HID_HOST_W4_INTERRUPT_CONNECTION_DISCONNECTED){ 112259a2ea74SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 112359a2ea74SMilanka Ringwald } 112459a2ea74SMilanka Ringwald 112559a2ea74SMilanka Ringwald connection->control_tasks |= control_message_bitmask; 112659a2ea74SMilanka Ringwald l2cap_request_can_send_now_event(connection->control_cid); 112759a2ea74SMilanka Ringwald return ERROR_CODE_SUCCESS; 112859a2ea74SMilanka Ringwald } 112959a2ea74SMilanka Ringwald 113059a2ea74SMilanka Ringwald uint8_t hid_host_send_suspend(uint16_t hid_cid){ 113159a2ea74SMilanka Ringwald return hid_host_send_control_message(hid_cid, CONTROL_MESSAGE_BITMASK_SUSPEND); 113259a2ea74SMilanka Ringwald } 113359a2ea74SMilanka Ringwald 113459a2ea74SMilanka Ringwald uint8_t hid_host_send_exit_suspend(uint16_t hid_cid){ 113559a2ea74SMilanka Ringwald return hid_host_send_control_message(hid_cid, CONTROL_MESSAGE_BITMASK_EXIT_SUSPEND); 113659a2ea74SMilanka Ringwald } 113759a2ea74SMilanka Ringwald 113859a2ea74SMilanka Ringwald uint8_t hid_host_send_virtual_cable_unplug(uint16_t hid_cid){ 113959a2ea74SMilanka Ringwald return hid_host_send_control_message(hid_cid, CONTROL_MESSAGE_BITMASK_VIRTUAL_CABLE_UNPLUG); 114059a2ea74SMilanka Ringwald } 114159a2ea74SMilanka Ringwald 1142fe493a7cSMilanka Ringwald uint8_t hid_host_send_get_report(uint16_t hid_cid, hid_report_type_t report_type, uint8_t report_id){ 1143ab30106eSMilanka Ringwald hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid); 1144ab30106eSMilanka Ringwald 1145ab30106eSMilanka Ringwald if (!connection || !connection->control_cid){ 1146ab30106eSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1147ab30106eSMilanka Ringwald } 1148ab30106eSMilanka Ringwald if (connection->state != HID_HOST_CONNECTION_ESTABLISHED){ 1149ab30106eSMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 1150ab30106eSMilanka Ringwald } 1151ab30106eSMilanka Ringwald 1152ab30106eSMilanka Ringwald connection->state = HID_HOST_W2_SEND_GET_REPORT; 1153ab30106eSMilanka Ringwald connection->report_type = report_type; 1154ab30106eSMilanka Ringwald connection->report_id = report_id; 1155ab30106eSMilanka Ringwald 1156ab30106eSMilanka Ringwald l2cap_request_can_send_now_event(connection->control_cid); 1157ab30106eSMilanka Ringwald return ERROR_CODE_SUCCESS; 1158ab30106eSMilanka Ringwald } 1159ab30106eSMilanka Ringwald 1160fe493a7cSMilanka Ringwald uint8_t hid_host_send_set_report(uint16_t hid_cid, hid_report_type_t report_type, uint8_t report_id, const uint8_t * report, uint8_t report_len){ 1161ab30106eSMilanka Ringwald hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid); 1162ab30106eSMilanka Ringwald 1163ab30106eSMilanka Ringwald if (!connection || !connection->control_cid){ 1164ab30106eSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1165ab30106eSMilanka Ringwald } 1166ab30106eSMilanka Ringwald 1167ab30106eSMilanka Ringwald if (connection->state != HID_HOST_CONNECTION_ESTABLISHED){ 11684a4b5586SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 11694a4b5586SMilanka Ringwald } 11704a4b5586SMilanka Ringwald 11714a4b5586SMilanka Ringwald if ((l2cap_max_mtu() - 2) < report_len ){ 1172ab30106eSMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 1173ab30106eSMilanka Ringwald } 1174ab30106eSMilanka Ringwald 1175ab30106eSMilanka Ringwald connection->state = HID_HOST_W2_SEND_SET_REPORT; 1176ab30106eSMilanka Ringwald connection->report_type = report_type; 1177ab30106eSMilanka Ringwald connection->report_id = report_id; 1178ab30106eSMilanka Ringwald connection->report = report; 1179ab30106eSMilanka Ringwald connection->report_len = report_len; 1180ab30106eSMilanka Ringwald 1181ab30106eSMilanka Ringwald l2cap_request_can_send_now_event(connection->control_cid); 1182ab30106eSMilanka Ringwald return ERROR_CODE_SUCCESS; 1183ab30106eSMilanka Ringwald } 1184ab30106eSMilanka Ringwald 1185ab30106eSMilanka Ringwald uint8_t hid_host_send_get_protocol(uint16_t hid_cid){ 1186ab30106eSMilanka Ringwald hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid); 11874a4b5586SMilanka Ringwald if (!connection || !connection->control_cid){ 11884a4b5586SMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 11894a4b5586SMilanka Ringwald } 11904a4b5586SMilanka Ringwald if (connection->state != HID_HOST_CONNECTION_ESTABLISHED){ 11914a4b5586SMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 11924a4b5586SMilanka Ringwald } 1193ab30106eSMilanka Ringwald 1194ab30106eSMilanka Ringwald connection->state = HID_HOST_W2_SEND_GET_PROTOCOL; 1195ab30106eSMilanka Ringwald l2cap_request_can_send_now_event(connection->control_cid); 1196ab30106eSMilanka Ringwald return ERROR_CODE_SUCCESS; 1197ab30106eSMilanka Ringwald } 1198ab30106eSMilanka Ringwald 1199ab30106eSMilanka Ringwald uint8_t hid_host_send_set_protocol_mode(uint16_t hid_cid, hid_protocol_mode_t protocol_mode){ 1200ab30106eSMilanka Ringwald hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid); 1201a93a968fSMilanka Ringwald if (!connection || !connection->control_cid){ 1202a93a968fSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1203a93a968fSMilanka Ringwald } 120401977ed1SMilanka Ringwald if (connection->state != HID_HOST_CONNECTION_ESTABLISHED || connection->set_protocol || connection->w4_set_protocol_response){ 1205ab30106eSMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 1206ab30106eSMilanka Ringwald } 1207ab30106eSMilanka Ringwald 120801977ed1SMilanka Ringwald connection->set_protocol = true; 120901977ed1SMilanka Ringwald connection->requested_protocol_mode = protocol_mode; 1210ab30106eSMilanka Ringwald 1211ab30106eSMilanka Ringwald l2cap_request_can_send_now_event(connection->control_cid); 1212ab30106eSMilanka Ringwald return ERROR_CODE_SUCCESS; 1213ab30106eSMilanka Ringwald } 1214ab30106eSMilanka Ringwald 1215ab30106eSMilanka Ringwald 1216fe493a7cSMilanka Ringwald uint8_t hid_host_send_report(uint16_t hid_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){ 1217a93a968fSMilanka Ringwald hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid); 1218a93a968fSMilanka Ringwald if (!connection || !connection->control_cid || !connection->interrupt_cid) { 1219a93a968fSMilanka Ringwald return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER; 1220a93a968fSMilanka Ringwald } 1221a93a968fSMilanka Ringwald 1222a93a968fSMilanka Ringwald if (connection->state < HID_HOST_CONNECTION_ESTABLISHED) { 1223a93a968fSMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 1224a93a968fSMilanka Ringwald } 1225a93a968fSMilanka Ringwald if (connection->state >= HID_HOST_W4_INTERRUPT_CONNECTION_DISCONNECTED){ 1226a93a968fSMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 1227a93a968fSMilanka Ringwald } 1228a93a968fSMilanka Ringwald 1229a93a968fSMilanka Ringwald if ((l2cap_max_mtu() - 2) < report_len ){ 1230a93a968fSMilanka Ringwald return ERROR_CODE_COMMAND_DISALLOWED; 1231a93a968fSMilanka Ringwald } 1232a93a968fSMilanka Ringwald 1233a93a968fSMilanka Ringwald connection->state = HID_HOST_W2_SEND_REPORT; 1234fe493a7cSMilanka Ringwald connection->report_type = HID_REPORT_TYPE_OUTPUT; 1235a93a968fSMilanka Ringwald connection->report_id = report_id; 1236a93a968fSMilanka Ringwald connection->report = report; 1237a93a968fSMilanka Ringwald connection->report_len = report_len; 1238a93a968fSMilanka Ringwald 1239a93a968fSMilanka Ringwald l2cap_request_can_send_now_event(connection->interrupt_cid); 1240a93a968fSMilanka Ringwald return ERROR_CODE_SUCCESS; 1241a93a968fSMilanka Ringwald } 1242