xref: /btstack/src/classic/hid_host.c (revision 7379ca3deb9401fc5de5bd03acbe874d7841a78e)
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