xref: /btstack/src/classic/hid_host.c (revision 14618ebfef9875b25a069464851370b09f5b07fa)
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
232fca4dadSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
242fca4dadSMilanka Ringwald  * GMBH 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"
473cbedd43SMatthias Ringwald #include "btstack_hid.h"
4863bf37cdSMilanka Ringwald #include "btstack_hid_parser.h"
49fd7ba7a6SMilanka Ringwald #include "btstack_memory.h"
505961e155SMilanka Ringwald #include "l2cap.h"
515961e155SMilanka Ringwald 
5263bf37cdSMilanka Ringwald #include "classic/hid_host.h"
5363bf37cdSMilanka Ringwald #include "classic/sdp_util.h"
54fd7ba7a6SMilanka Ringwald #include "classic/sdp_client.h"
5563bf37cdSMilanka Ringwald 
56d471df90SMatthias Ringwald #ifndef HID_HOST_SDP_MAX_ATTRIBUTE_VALUE_SIZE
57d471df90SMatthias Ringwald #define HID_HOST_SDP_MAX_ATTRIBUTE_VALUE_SIZE 32
58d471df90SMatthias Ringwald #endif
5963bf37cdSMilanka Ringwald 
6059a2ea74SMilanka Ringwald #define CONTROL_MESSAGE_BITMASK_SUSPEND             1
6159a2ea74SMilanka Ringwald #define CONTROL_MESSAGE_BITMASK_EXIT_SUSPEND        2
6259a2ea74SMilanka Ringwald #define CONTROL_MESSAGE_BITMASK_VIRTUAL_CABLE_UNPLUG 4
6359a2ea74SMilanka Ringwald 
64de81b46aSMatthias Ringwald // globals
6559a2ea74SMilanka Ringwald 
66de81b46aSMatthias Ringwald // higher-layer callbacks
67de81b46aSMatthias Ringwald static btstack_packet_handler_t hid_host_callback;
68de81b46aSMatthias Ringwald 
69de81b46aSMatthias Ringwald // descriptor storage
70fd7ba7a6SMilanka Ringwald static uint8_t * hid_host_descriptor_storage;
7163bf37cdSMilanka Ringwald static uint16_t  hid_host_descriptor_storage_len;
7263bf37cdSMilanka Ringwald 
73fd7ba7a6SMilanka Ringwald // SDP
74d471df90SMatthias Ringwald static uint8_t            hid_host_sdp_attribute_value[HID_HOST_SDP_MAX_ATTRIBUTE_VALUE_SIZE];
75d471df90SMatthias Ringwald static const unsigned int hid_host_sdp_attribute_value_buffer_size = HID_HOST_SDP_MAX_ATTRIBUTE_VALUE_SIZE;
76de81b46aSMatthias Ringwald static uint16_t           hid_host_sdp_context_control_cid = 0;
77fd7ba7a6SMilanka Ringwald 
78de81b46aSMatthias Ringwald // connections
79de81b46aSMatthias Ringwald static btstack_linked_list_t hid_host_connections;
8059a2ea74SMilanka Ringwald static uint16_t              hid_host_cid_counter = 0;
8159a2ea74SMilanka Ringwald 
82de81b46aSMatthias Ringwald // lower layer callbacks
83fd7ba7a6SMilanka Ringwald static btstack_context_callback_registration_t hid_host_handle_sdp_client_query_request;
84de81b46aSMatthias Ringwald 
85de81b46aSMatthias Ringwald // prototypes
86de81b46aSMatthias Ringwald 
87fd7ba7a6SMilanka Ringwald static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
885961e155SMilanka Ringwald static void hid_host_handle_start_sdp_client_query(void * context);
89fd7ba7a6SMilanka Ringwald 
hid_descriptor_storage_get_available_space(void)90fd7ba7a6SMilanka Ringwald static uint16_t hid_descriptor_storage_get_available_space(void){
91fd7ba7a6SMilanka Ringwald     // assumes all descriptors are back to back
92fd7ba7a6SMilanka Ringwald     uint16_t free_space = hid_host_descriptor_storage_len;
93fd7ba7a6SMilanka Ringwald 
94fd7ba7a6SMilanka Ringwald     btstack_linked_list_iterator_t it;
95de81b46aSMatthias Ringwald     btstack_linked_list_iterator_init(&it, &hid_host_connections);
96fd7ba7a6SMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
97fd7ba7a6SMilanka Ringwald         hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it);
98fd7ba7a6SMilanka Ringwald         free_space -= connection->hid_descriptor_len;
99fd7ba7a6SMilanka Ringwald     }
100fd7ba7a6SMilanka Ringwald     return free_space;
101fd7ba7a6SMilanka Ringwald }
102fd7ba7a6SMilanka Ringwald 
hid_host_get_connection_for_hid_cid(uint16_t hid_cid)103a93a968fSMilanka Ringwald static hid_host_connection_t * hid_host_get_connection_for_hid_cid(uint16_t hid_cid){
104a93a968fSMilanka Ringwald     btstack_linked_list_iterator_t it;
105de81b46aSMatthias Ringwald     btstack_linked_list_iterator_init(&it, &hid_host_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->hid_cid != hid_cid) continue;
109a93a968fSMilanka Ringwald         return connection;
110a93a968fSMilanka Ringwald     }
111a93a968fSMilanka Ringwald     return NULL;
112a93a968fSMilanka Ringwald }
113a93a968fSMilanka Ringwald 
hid_host_get_connection_for_l2cap_cid(uint16_t l2cap_cid)114a93a968fSMilanka Ringwald static hid_host_connection_t * hid_host_get_connection_for_l2cap_cid(uint16_t l2cap_cid){
115a93a968fSMilanka Ringwald     btstack_linked_list_iterator_t it;
116de81b46aSMatthias Ringwald     btstack_linked_list_iterator_init(&it, &hid_host_connections);
117a93a968fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
118a93a968fSMilanka Ringwald         hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it);
119a93a968fSMilanka Ringwald         if ((connection->interrupt_cid != l2cap_cid) && (connection->control_cid != l2cap_cid)) continue;
120a93a968fSMilanka Ringwald         return connection;
121a93a968fSMilanka Ringwald     }
122a93a968fSMilanka Ringwald     return NULL;
123a93a968fSMilanka Ringwald }
124a93a968fSMilanka Ringwald 
hid_descriptor_storage_init(hid_host_connection_t * connection)125fd7ba7a6SMilanka Ringwald static void hid_descriptor_storage_init(hid_host_connection_t * connection){
12604b99c5bSMatthias Ringwald     // reserve remaining space for this connection
12704b99c5bSMatthias Ringwald     uint16_t available_space = hid_descriptor_storage_get_available_space();
128fd7ba7a6SMilanka Ringwald     connection->hid_descriptor_len = 0;
12904b99c5bSMatthias Ringwald     connection->hid_descriptor_max_len = available_space;
13004b99c5bSMatthias Ringwald     connection->hid_descriptor_offset  = hid_host_descriptor_storage_len - available_space;
131fd7ba7a6SMilanka Ringwald }
132fd7ba7a6SMilanka Ringwald 
hid_descriptor_storage_store(hid_host_connection_t * connection,uint8_t byte)133fd7ba7a6SMilanka Ringwald static bool hid_descriptor_storage_store(hid_host_connection_t * connection, uint8_t byte){
13404b99c5bSMatthias Ringwald     // store single hid descriptor byte
135fd7ba7a6SMilanka Ringwald     if (connection->hid_descriptor_len >= connection->hid_descriptor_max_len) return false;
136fd7ba7a6SMilanka Ringwald 
137fd7ba7a6SMilanka Ringwald     hid_host_descriptor_storage[connection->hid_descriptor_offset + connection->hid_descriptor_len] = byte;
138fd7ba7a6SMilanka Ringwald     connection->hid_descriptor_len++;
139fd7ba7a6SMilanka Ringwald     return true;
140fd7ba7a6SMilanka Ringwald }
141fd7ba7a6SMilanka Ringwald 
hid_descriptor_storage_delete(hid_host_connection_t * connection)142fd7ba7a6SMilanka Ringwald static void hid_descriptor_storage_delete(hid_host_connection_t * connection){
14304b99c5bSMatthias Ringwald     uint16_t descriptor_len = connection->hid_descriptor_len;
14404b99c5bSMatthias Ringwald 
14504b99c5bSMatthias Ringwald     if (descriptor_len > 0){
146fd7ba7a6SMilanka Ringwald         uint16_t next_offset = connection->hid_descriptor_offset + connection->hid_descriptor_len;
147fd7ba7a6SMilanka Ringwald 
14804b99c5bSMatthias Ringwald         // move higher descriptors down
149fd7ba7a6SMilanka Ringwald         memmove(&hid_host_descriptor_storage[connection->hid_descriptor_offset],
150fd7ba7a6SMilanka Ringwald                 &hid_host_descriptor_storage[next_offset],
151fd7ba7a6SMilanka Ringwald                 hid_host_descriptor_storage_len - next_offset);
152fd7ba7a6SMilanka Ringwald 
15304b99c5bSMatthias Ringwald         // fix descriptor offset of higher descriptors
154fd7ba7a6SMilanka Ringwald         btstack_linked_list_iterator_t it;
155de81b46aSMatthias Ringwald         btstack_linked_list_iterator_init(&it, &hid_host_connections);
156fd7ba7a6SMilanka Ringwald         while (btstack_linked_list_iterator_has_next(&it)){
157fd7ba7a6SMilanka Ringwald             hid_host_connection_t * conn = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it);
15804b99c5bSMatthias Ringwald             if (conn == connection) continue;
159fd7ba7a6SMilanka Ringwald             if (conn->hid_descriptor_offset >= next_offset){
16004b99c5bSMatthias Ringwald                 conn->hid_descriptor_offset -= descriptor_len;
161fd7ba7a6SMilanka Ringwald             }
162fd7ba7a6SMilanka Ringwald         }
163fd7ba7a6SMilanka Ringwald     }
164fd7ba7a6SMilanka Ringwald 
16504b99c5bSMatthias Ringwald     // clear descriptor
16604b99c5bSMatthias Ringwald     connection->hid_descriptor_len = 0;
16704b99c5bSMatthias Ringwald     connection->hid_descriptor_offset = 0;
16804b99c5bSMatthias Ringwald }
16904b99c5bSMatthias Ringwald 
hid_descriptor_storage_get_descriptor_data(uint16_t hid_cid)170a93a968fSMilanka Ringwald const uint8_t * hid_descriptor_storage_get_descriptor_data(uint16_t hid_cid){
171a93a968fSMilanka Ringwald     hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid);
172a93a968fSMilanka Ringwald     if (!connection){
173a93a968fSMilanka Ringwald         return NULL;
174a93a968fSMilanka Ringwald     }
17559a2ea74SMilanka Ringwald     return &hid_host_descriptor_storage[connection->hid_descriptor_offset];
17659a2ea74SMilanka Ringwald }
17759a2ea74SMilanka Ringwald 
hid_descriptor_storage_get_descriptor_len(uint16_t hid_cid)1782cfd07faSMatthias Ringwald uint16_t hid_descriptor_storage_get_descriptor_len(uint16_t hid_cid){
179a93a968fSMilanka Ringwald     hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid);
180a93a968fSMilanka Ringwald     if (!connection){
181a93a968fSMilanka Ringwald         return 0;
182a93a968fSMilanka Ringwald     }
18359a2ea74SMilanka Ringwald     return connection->hid_descriptor_len;
18459a2ea74SMilanka Ringwald }
18559a2ea74SMilanka Ringwald 
18659a2ea74SMilanka Ringwald 
187fd7ba7a6SMilanka Ringwald // HID Util
hid_emit_connected_event(hid_host_connection_t * connection,uint8_t status)18831a4e45bSMilanka Ringwald static void hid_emit_connected_event(hid_host_connection_t * connection, uint8_t status){
189fd7ba7a6SMilanka Ringwald     uint8_t event[15];
190dcec10e7SMilanka Ringwald     uint16_t pos = 0;
191fd7ba7a6SMilanka Ringwald     event[pos++] = HCI_EVENT_HID_META;
192fd7ba7a6SMilanka Ringwald     pos++;  // skip len
193fd7ba7a6SMilanka Ringwald     event[pos++] = HID_SUBEVENT_CONNECTION_OPENED;
19431a4e45bSMilanka Ringwald     little_endian_store_16(event, pos, connection->hid_cid);
195fd7ba7a6SMilanka Ringwald     pos+=2;
196fd7ba7a6SMilanka Ringwald     event[pos++] = status;
19731a4e45bSMilanka Ringwald     reverse_bd_addr(connection->remote_addr, &event[pos]);
198fd7ba7a6SMilanka Ringwald     pos += 6;
19931a4e45bSMilanka Ringwald     little_endian_store_16(event,pos,connection->con_handle);
200fd7ba7a6SMilanka Ringwald     pos += 2;
20131a4e45bSMilanka Ringwald     event[pos++] = connection->incoming;
202fd7ba7a6SMilanka Ringwald     event[1] = pos - 2;
203de81b46aSMatthias Ringwald     hid_host_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos);
204fd7ba7a6SMilanka Ringwald }
205fd7ba7a6SMilanka Ringwald 
hid_emit_descriptor_available_event(hid_host_connection_t * connection)20605439aa6SMilanka Ringwald static void hid_emit_descriptor_available_event(hid_host_connection_t * connection){
20705439aa6SMilanka Ringwald     uint8_t event[6];
20805439aa6SMilanka Ringwald     uint16_t pos = 0;
20905439aa6SMilanka Ringwald     event[pos++] = HCI_EVENT_HID_META;
21005439aa6SMilanka Ringwald     pos++;  // skip len
21105439aa6SMilanka Ringwald     event[pos++] = HID_SUBEVENT_DESCRIPTOR_AVAILABLE;
21205439aa6SMilanka Ringwald     little_endian_store_16(event,pos,connection->hid_cid);
21305439aa6SMilanka Ringwald     pos += 2;
21405439aa6SMilanka Ringwald     event[pos++] = connection->hid_descriptor_status;
21505439aa6SMilanka Ringwald     event[1] = pos - 2;
216de81b46aSMatthias Ringwald     hid_host_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos);
21705439aa6SMilanka Ringwald }
21831a4e45bSMilanka Ringwald 
hid_emit_sniff_params_event(hid_host_connection_t * connection)219580d6bb4SMilanka Ringwald static void hid_emit_sniff_params_event(hid_host_connection_t * connection){
220580d6bb4SMilanka Ringwald     uint8_t event[9];
221580d6bb4SMilanka Ringwald     uint16_t pos = 0;
222580d6bb4SMilanka Ringwald     event[pos++] = HCI_EVENT_HID_META;
223580d6bb4SMilanka Ringwald     pos++;  // skip len
224580d6bb4SMilanka Ringwald     event[pos++] = HID_SUBEVENT_SNIFF_SUBRATING_PARAMS;
225580d6bb4SMilanka Ringwald     little_endian_store_16(event,pos,connection->hid_cid);
226580d6bb4SMilanka Ringwald     pos += 2;
227580d6bb4SMilanka Ringwald     little_endian_store_16(event,pos,connection->host_max_latency);
228580d6bb4SMilanka Ringwald     pos += 2;
229580d6bb4SMilanka Ringwald     little_endian_store_16(event,pos,connection->host_min_timeout);
230580d6bb4SMilanka Ringwald     pos += 2;
231580d6bb4SMilanka Ringwald 
232580d6bb4SMilanka Ringwald     event[1] = pos - 2;
233de81b46aSMatthias Ringwald     hid_host_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos);
234580d6bb4SMilanka Ringwald }
235580d6bb4SMilanka Ringwald 
hid_emit_event(hid_host_connection_t * connection,uint8_t subevent_type)23631a4e45bSMilanka Ringwald static void hid_emit_event(hid_host_connection_t * connection, uint8_t subevent_type){
237ab30106eSMilanka Ringwald     uint8_t event[5];
238dcec10e7SMilanka Ringwald     uint16_t pos = 0;
239ab30106eSMilanka Ringwald     event[pos++] = HCI_EVENT_HID_META;
240ab30106eSMilanka Ringwald     pos++;  // skip len
241ab30106eSMilanka Ringwald     event[pos++] = subevent_type;
24231a4e45bSMilanka Ringwald     little_endian_store_16(event,pos,connection->hid_cid);
243ab30106eSMilanka Ringwald     pos += 2;
244ab30106eSMilanka Ringwald     event[1] = pos - 2;
245de81b46aSMatthias Ringwald     hid_host_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos);
246ab30106eSMilanka Ringwald }
247ab30106eSMilanka Ringwald 
hid_emit_event_with_status(hid_host_connection_t * connection,uint8_t subevent_type,hid_handshake_param_type_t status)24831a4e45bSMilanka Ringwald static void hid_emit_event_with_status(hid_host_connection_t * connection, uint8_t subevent_type, hid_handshake_param_type_t status){
2494a4b5586SMilanka Ringwald     uint8_t event[6];
2504a4b5586SMilanka Ringwald     uint16_t pos = 0;
2514a4b5586SMilanka Ringwald     event[pos++] = HCI_EVENT_HID_META;
2524a4b5586SMilanka Ringwald     pos++;  // skip len
2534a4b5586SMilanka Ringwald     event[pos++] = subevent_type;
25431a4e45bSMilanka Ringwald     little_endian_store_16(event,pos,connection->hid_cid);
2554a4b5586SMilanka Ringwald     pos += 2;
2564a4b5586SMilanka Ringwald     event[pos++] = status;
2574a4b5586SMilanka Ringwald     event[1] = pos - 2;
258de81b46aSMatthias Ringwald     hid_host_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos);
2594a4b5586SMilanka Ringwald }
2604a4b5586SMilanka Ringwald 
hid_emit_set_protocol_response_event(hid_host_connection_t * connection,hid_handshake_param_type_t status)261de89a6fcSMilanka Ringwald static void hid_emit_set_protocol_response_event(hid_host_connection_t * connection, hid_handshake_param_type_t status){
262de89a6fcSMilanka Ringwald     uint8_t event[7];
263de89a6fcSMilanka Ringwald     uint16_t pos = 0;
264de89a6fcSMilanka Ringwald     event[pos++] = HCI_EVENT_HID_META;
265de89a6fcSMilanka Ringwald     pos++;  // skip len
266de89a6fcSMilanka Ringwald     event[pos++] = HID_SUBEVENT_SET_PROTOCOL_RESPONSE;
267de89a6fcSMilanka Ringwald     little_endian_store_16(event,pos,connection->hid_cid);
268de89a6fcSMilanka Ringwald     pos += 2;
269de89a6fcSMilanka Ringwald     event[pos++] = status;
270de89a6fcSMilanka Ringwald     event[pos++] = connection->protocol_mode;
271de89a6fcSMilanka Ringwald     event[1] = pos - 2;
272de81b46aSMatthias Ringwald     hid_host_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos);
273de89a6fcSMilanka Ringwald }
274de89a6fcSMilanka Ringwald 
hid_emit_incoming_connection_event(hid_host_connection_t * connection)27531a4e45bSMilanka Ringwald static void hid_emit_incoming_connection_event(hid_host_connection_t * connection){
27659a2ea74SMilanka Ringwald     uint8_t event[13];
277dcec10e7SMilanka Ringwald     uint16_t pos = 0;
27859a2ea74SMilanka Ringwald     event[pos++] = HCI_EVENT_HID_META;
27959a2ea74SMilanka Ringwald     pos++;  // skip len
28059a2ea74SMilanka Ringwald     event[pos++] = HID_SUBEVENT_INCOMING_CONNECTION;
28131a4e45bSMilanka Ringwald     little_endian_store_16(event, pos, connection->hid_cid);
28259a2ea74SMilanka Ringwald     pos += 2;
28331a4e45bSMilanka Ringwald     reverse_bd_addr(connection->remote_addr, &event[pos]);
28459a2ea74SMilanka Ringwald     pos += 6;
28531a4e45bSMilanka Ringwald     little_endian_store_16(event,pos,connection->con_handle);
28659a2ea74SMilanka Ringwald     pos += 2;
28759a2ea74SMilanka Ringwald     event[1] = pos - 2;
288de81b46aSMatthias Ringwald     hid_host_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos);
28959a2ea74SMilanka Ringwald }
29059a2ea74SMilanka Ringwald 
291dcec10e7SMilanka Ringwald // setup get report response event - potentially in-place of original l2cap packet
hid_setup_get_report_event(hid_host_connection_t * connection,hid_handshake_param_type_t status,uint8_t * buffer,uint16_t report_len)29231a4e45bSMilanka 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){
293dcec10e7SMilanka Ringwald     uint16_t pos = 0;
294dcec10e7SMilanka Ringwald     buffer[pos++] = HCI_EVENT_HID_META;
295dcec10e7SMilanka Ringwald     pos++;  // skip len
296dcec10e7SMilanka Ringwald     buffer[pos++] = HID_SUBEVENT_GET_REPORT_RESPONSE;
29731a4e45bSMilanka Ringwald     little_endian_store_16(buffer, pos, connection->hid_cid);
298dcec10e7SMilanka Ringwald     pos += 2;
299dcec10e7SMilanka Ringwald     buffer[pos++] = (uint8_t) status;
300dcec10e7SMilanka Ringwald     little_endian_store_16(buffer, pos, report_len);
301dcec10e7SMilanka Ringwald     pos += 2;
302dcec10e7SMilanka Ringwald     buffer[1] = pos + report_len - 2;
303dcec10e7SMilanka Ringwald }
304dcec10e7SMilanka Ringwald 
305a93a968fSMilanka Ringwald // setup report event - potentially in-place of original l2cap packet
hid_setup_report_event(hid_host_connection_t * connection,uint8_t * buffer,uint16_t report_len)30631a4e45bSMilanka Ringwald static void hid_setup_report_event(hid_host_connection_t * connection, uint8_t *buffer, uint16_t report_len){
307a93a968fSMilanka Ringwald     uint16_t pos = 0;
308a93a968fSMilanka Ringwald     buffer[pos++] = HCI_EVENT_HID_META;
309a93a968fSMilanka Ringwald     pos++;  // skip len
310a93a968fSMilanka Ringwald     buffer[pos++] = HID_SUBEVENT_REPORT;
31131a4e45bSMilanka Ringwald     little_endian_store_16(buffer, pos, connection->hid_cid);
312a93a968fSMilanka Ringwald     pos += 2;
313a93a968fSMilanka Ringwald     little_endian_store_16(buffer, pos, report_len);
314a93a968fSMilanka Ringwald     pos += 2;
315a93a968fSMilanka Ringwald     buffer[1] = pos + report_len - 2;
316a93a968fSMilanka Ringwald }
317a93a968fSMilanka Ringwald 
318a93a968fSMilanka Ringwald 
hid_emit_get_protocol_event(hid_host_connection_t * connection,hid_handshake_param_type_t status,hid_protocol_mode_t protocol_mode)31931a4e45bSMilanka 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){
3204a4b5586SMilanka Ringwald     uint8_t event[7];
3214a4b5586SMilanka Ringwald     uint16_t pos = 0;
3224a4b5586SMilanka Ringwald     event[pos++] = HCI_EVENT_HID_META;
3234a4b5586SMilanka Ringwald     pos++;  // skip len
3244a4b5586SMilanka Ringwald     event[pos++] = HID_SUBEVENT_GET_PROTOCOL_RESPONSE;
32531a4e45bSMilanka Ringwald     little_endian_store_16(event,pos,connection->hid_cid);
3264a4b5586SMilanka Ringwald     pos += 2;
3274a4b5586SMilanka Ringwald     event[pos++] = status;
3284a4b5586SMilanka Ringwald     event[pos++] = protocol_mode;
3294a4b5586SMilanka Ringwald     event[1] = pos - 2;
330de81b46aSMatthias Ringwald     hid_host_callback(HCI_EVENT_PACKET, connection->hid_cid, &event[0], pos);
3314a4b5586SMilanka Ringwald }
3324a4b5586SMilanka Ringwald 
333fd7ba7a6SMilanka Ringwald // HID Host
334fd7ba7a6SMilanka Ringwald 
hid_host_get_next_cid(void)335fd7ba7a6SMilanka Ringwald static uint16_t hid_host_get_next_cid(void){
336fd7ba7a6SMilanka Ringwald     if (hid_host_cid_counter == 0xffff) {
337fd7ba7a6SMilanka Ringwald         hid_host_cid_counter = 1;
338fd7ba7a6SMilanka Ringwald     } else {
339fd7ba7a6SMilanka Ringwald         hid_host_cid_counter++;
340fd7ba7a6SMilanka Ringwald     }
341fd7ba7a6SMilanka Ringwald     return hid_host_cid_counter;
342fd7ba7a6SMilanka Ringwald }
343fd7ba7a6SMilanka Ringwald 
hid_host_create_connection(bd_addr_t remote_addr)344fd7ba7a6SMilanka Ringwald static hid_host_connection_t * hid_host_create_connection(bd_addr_t remote_addr){
345fd7ba7a6SMilanka Ringwald     hid_host_connection_t * connection = btstack_memory_hid_host_connection_get();
346fd7ba7a6SMilanka Ringwald     if (!connection){
347fd7ba7a6SMilanka Ringwald         log_error("Not enough memory to create connection");
348fd7ba7a6SMilanka Ringwald         return NULL;
349fd7ba7a6SMilanka Ringwald     }
350fd7ba7a6SMilanka Ringwald     connection->state = HID_HOST_IDLE;
351fd7ba7a6SMilanka Ringwald     connection->hid_cid = hid_host_get_next_cid();
35259a2ea74SMilanka Ringwald     connection->control_cid = 0;
35359a2ea74SMilanka Ringwald     connection->control_psm = 0;
35459a2ea74SMilanka Ringwald     connection->interrupt_cid = 0;
35559a2ea74SMilanka Ringwald     connection->interrupt_psm = 0;
35659a2ea74SMilanka Ringwald     connection->con_handle = HCI_CON_HANDLE_INVALID;
357fd7ba7a6SMilanka Ringwald 
35859a2ea74SMilanka Ringwald     (void)memcpy(connection->remote_addr, remote_addr, 6);
359113d01ceSMatthias Ringwald     btstack_linked_list_add_tail(&hid_host_connections, (btstack_linked_item_t *) connection);
360fd7ba7a6SMilanka Ringwald     return connection;
361fd7ba7a6SMilanka Ringwald }
362fd7ba7a6SMilanka Ringwald 
hid_host_get_connection_for_bd_addr(bd_addr_t addr)363fd7ba7a6SMilanka Ringwald static hid_host_connection_t * hid_host_get_connection_for_bd_addr(bd_addr_t addr){
364fd7ba7a6SMilanka Ringwald     btstack_linked_list_iterator_t it;
365de81b46aSMatthias Ringwald     btstack_linked_list_iterator_init(&it, &hid_host_connections);
366fd7ba7a6SMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
367fd7ba7a6SMilanka Ringwald         hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it);
368fd7ba7a6SMilanka Ringwald         if (memcmp(addr, connection->remote_addr, 6) != 0) continue;
369fd7ba7a6SMilanka Ringwald         return connection;
370fd7ba7a6SMilanka Ringwald     }
371fd7ba7a6SMilanka Ringwald     return NULL;
372fd7ba7a6SMilanka Ringwald }
373fd7ba7a6SMilanka Ringwald 
374ab30106eSMilanka Ringwald 
hid_host_finalize_connection(hid_host_connection_t * connection)375fd7ba7a6SMilanka Ringwald static void hid_host_finalize_connection(hid_host_connection_t * connection){
3767379ca3dSMilanka Ringwald     uint16_t interrupt_cid = connection->interrupt_cid;
3777379ca3dSMilanka Ringwald     uint16_t control_cid = connection->control_cid;
3787379ca3dSMilanka Ringwald 
3797379ca3dSMilanka Ringwald     connection->interrupt_cid = 0;
3807379ca3dSMilanka Ringwald     connection->control_cid = 0;
3817379ca3dSMilanka Ringwald 
3827379ca3dSMilanka Ringwald     if (interrupt_cid != 0){
383b93f8966SMatthias Ringwald         l2cap_disconnect(interrupt_cid);
3847379ca3dSMilanka Ringwald     }
3857379ca3dSMilanka Ringwald     if (control_cid != 0){
386b93f8966SMatthias Ringwald         l2cap_disconnect(control_cid);
3877379ca3dSMilanka Ringwald     }
388de81b46aSMatthias Ringwald     btstack_linked_list_remove(&hid_host_connections, (btstack_linked_item_t*) connection);
389fd7ba7a6SMilanka Ringwald     btstack_memory_hid_host_connection_free(connection);
390fd7ba7a6SMilanka Ringwald }
391fd7ba7a6SMilanka Ringwald 
hid_host_handle_sdp_hid_descriptor_list(hid_host_connection_t * connection,uint16_t attribute_offset,uint8_t data)3921744e196SMatthias Ringwald static void hid_host_handle_sdp_hid_descriptor_list(hid_host_connection_t * connection, uint16_t attribute_offset, uint8_t data){
3931744e196SMatthias Ringwald     // state machine
394a0a51006SMatthias Ringwald     static uint32_t bytes_needed   = 0;
395a0a51006SMatthias Ringwald     static uint32_t bytes_received = 0;
3961744e196SMatthias Ringwald     static enum {
3971744e196SMatthias Ringwald         HID_DESCRIPTOR_LIST_ERROR,
3981744e196SMatthias Ringwald         HID_DESCRIPTOR_LIST_W4_DES_DESCRIPTOR_LIST_START,
3991744e196SMatthias Ringwald         HID_DESCRIPTOR_LIST_W4_DES_DESCRIPTOR_LIST_HEADER,
4001744e196SMatthias Ringwald         HID_DESCRIPTOR_LIST_W4_DES_DESCRIPTOR_START,
4011744e196SMatthias Ringwald         HID_DESCRIPTOR_LIST_W4_DES_DESCRIPTOR_HEADER,
4021744e196SMatthias Ringwald         HID_DESCRIPTOR_LIST_W4_ITEM_START,
4031744e196SMatthias Ringwald         HID_DESCRIPTOR_LIST_W4_ITEM_HEADER,
4041744e196SMatthias Ringwald         HID_DESCRIPTOR_LIST_W4_ITEM_COMPLETE,
4051744e196SMatthias Ringwald         HID_DESCRIPTOR_LIST_W4_STRING_HEADER,
4061744e196SMatthias Ringwald         HID_DESCRIPTOR_LIST_W4_STRING_COMPLETE,
4071744e196SMatthias Ringwald         HID_DESCRIPTOR_LIST_COMPLETE,
4081744e196SMatthias Ringwald     } state = HID_DESCRIPTOR_LIST_ERROR;
4091744e196SMatthias Ringwald 
4101744e196SMatthias Ringwald     // init state machine
4111744e196SMatthias Ringwald     if (attribute_offset == 0){
4121744e196SMatthias Ringwald         state = HID_DESCRIPTOR_LIST_W4_DES_DESCRIPTOR_LIST_START;
4131744e196SMatthias Ringwald         bytes_received = 0;
4141744e196SMatthias Ringwald     }
4151744e196SMatthias Ringwald 
4161744e196SMatthias Ringwald     // next step
4171744e196SMatthias Ringwald     bool stored;
4181744e196SMatthias Ringwald     bool error = false;
4191744e196SMatthias Ringwald     switch (state){
4201744e196SMatthias Ringwald         case HID_DESCRIPTOR_LIST_W4_DES_DESCRIPTOR_LIST_START:
4211744e196SMatthias Ringwald             hid_host_sdp_attribute_value[bytes_received++] = data;
4221744e196SMatthias Ringwald             if (de_get_element_type(hid_host_sdp_attribute_value) == DE_DES){
4231744e196SMatthias Ringwald                 bytes_needed = de_get_header_size(hid_host_sdp_attribute_value);
4241744e196SMatthias Ringwald                 state = HID_DESCRIPTOR_LIST_W4_DES_DESCRIPTOR_LIST_HEADER;
4251744e196SMatthias Ringwald             } else {
4261744e196SMatthias Ringwald                 error = true;
4271744e196SMatthias Ringwald             }
4281744e196SMatthias Ringwald             break;
4291744e196SMatthias Ringwald         case HID_DESCRIPTOR_LIST_W4_DES_DESCRIPTOR_LIST_HEADER:
4301744e196SMatthias Ringwald             hid_host_sdp_attribute_value[bytes_received++] = data;
431a0a51006SMatthias Ringwald             if (bytes_received >= bytes_needed) {
4321744e196SMatthias Ringwald                 bytes_received = 0;
4331744e196SMatthias Ringwald                 state = HID_DESCRIPTOR_LIST_W4_DES_DESCRIPTOR_START;
4341744e196SMatthias Ringwald             }
4351744e196SMatthias Ringwald             break;
4361744e196SMatthias Ringwald         case HID_DESCRIPTOR_LIST_W4_DES_DESCRIPTOR_START:
4371744e196SMatthias Ringwald             hid_host_sdp_attribute_value[bytes_received++] = data;
4381744e196SMatthias Ringwald             if (de_get_element_type(hid_host_sdp_attribute_value) == DE_DES){
4391744e196SMatthias Ringwald                 bytes_needed = de_get_header_size(hid_host_sdp_attribute_value);
4401744e196SMatthias Ringwald                 state = HID_DESCRIPTOR_LIST_W4_DES_DESCRIPTOR_HEADER;
4411744e196SMatthias Ringwald             } else {
4421744e196SMatthias Ringwald                 error = true;
4431744e196SMatthias Ringwald             }
4441744e196SMatthias Ringwald             break;
4451744e196SMatthias Ringwald         case HID_DESCRIPTOR_LIST_W4_DES_DESCRIPTOR_HEADER:
4461744e196SMatthias Ringwald             hid_host_sdp_attribute_value[bytes_received++] = data;
447a0a51006SMatthias Ringwald             if (bytes_received >= bytes_needed) {
4481744e196SMatthias Ringwald                 bytes_received = 0;
4491744e196SMatthias Ringwald                 state = HID_DESCRIPTOR_LIST_W4_ITEM_START;
4501744e196SMatthias Ringwald             }
4511744e196SMatthias Ringwald             break;
4521744e196SMatthias Ringwald         case HID_DESCRIPTOR_LIST_W4_ITEM_START:
4531744e196SMatthias Ringwald             hid_host_sdp_attribute_value[bytes_received++] = data;
4541744e196SMatthias Ringwald             bytes_needed = de_get_header_size(hid_host_sdp_attribute_value);
455a0a51006SMatthias Ringwald             if (de_get_element_type(hid_host_sdp_attribute_value) == DE_STRING){
4561744e196SMatthias Ringwald                 state = HID_DESCRIPTOR_LIST_W4_STRING_HEADER;
4571744e196SMatthias Ringwald             } else {
4581744e196SMatthias Ringwald                 if (bytes_needed > 1){
4591744e196SMatthias Ringwald                     state = HID_DESCRIPTOR_LIST_W4_ITEM_HEADER;
4601744e196SMatthias Ringwald                 } else {
4611744e196SMatthias Ringwald                     bytes_needed = de_get_len(hid_host_sdp_attribute_value);
4621744e196SMatthias Ringwald                     state = HID_DESCRIPTOR_LIST_W4_ITEM_COMPLETE;
4631744e196SMatthias Ringwald                 }
4641744e196SMatthias Ringwald             }
4651744e196SMatthias Ringwald             break;
4661744e196SMatthias Ringwald         case HID_DESCRIPTOR_LIST_W4_ITEM_HEADER:
4671744e196SMatthias Ringwald             hid_host_sdp_attribute_value[bytes_received++] = data;
468a0a51006SMatthias Ringwald             if (bytes_received >= bytes_needed) {
4691744e196SMatthias Ringwald                 bytes_needed = de_get_len(hid_host_sdp_attribute_value);
4701744e196SMatthias Ringwald                 state = HID_DESCRIPTOR_LIST_W4_ITEM_COMPLETE;
4711744e196SMatthias Ringwald             }
4721744e196SMatthias Ringwald             break;
473a0a51006SMatthias Ringwald         case HID_DESCRIPTOR_LIST_W4_ITEM_COMPLETE:
474a0a51006SMatthias Ringwald             // ignore data for non-string item
475a0a51006SMatthias Ringwald             bytes_received++;
476a0a51006SMatthias Ringwald             if (bytes_received >= bytes_needed) {
477a0a51006SMatthias Ringwald                 bytes_received = 0;
478a0a51006SMatthias Ringwald                 state = HID_DESCRIPTOR_LIST_W4_ITEM_START;
479a0a51006SMatthias Ringwald             }
480a0a51006SMatthias Ringwald             break;
4811744e196SMatthias Ringwald         case HID_DESCRIPTOR_LIST_W4_STRING_HEADER:
4821744e196SMatthias Ringwald             hid_host_sdp_attribute_value[bytes_received++] = data;
483a0a51006SMatthias Ringwald             if (bytes_received >= bytes_needed) {
4841744e196SMatthias Ringwald                 bytes_received = 0;
4851744e196SMatthias Ringwald                 bytes_needed = de_get_data_size(hid_host_sdp_attribute_value);
4861744e196SMatthias Ringwald                 state = HID_DESCRIPTOR_LIST_W4_STRING_COMPLETE;
4871744e196SMatthias Ringwald             }
4881744e196SMatthias Ringwald             break;
4891744e196SMatthias Ringwald         case HID_DESCRIPTOR_LIST_W4_STRING_COMPLETE:
4901744e196SMatthias Ringwald             stored = hid_descriptor_storage_store(connection, data);
4911744e196SMatthias Ringwald             if (stored) {
4921744e196SMatthias Ringwald                 bytes_received++;
493a0a51006SMatthias Ringwald                 if (bytes_received >= bytes_needed) {
4941744e196SMatthias Ringwald                     connection->hid_descriptor_status = ERROR_CODE_SUCCESS;
4951744e196SMatthias Ringwald                     log_info_hexdump(&hid_host_descriptor_storage[connection->hid_descriptor_offset],
4961744e196SMatthias Ringwald                                      connection->hid_descriptor_len);
4971744e196SMatthias Ringwald                     state = HID_DESCRIPTOR_LIST_COMPLETE;
4981744e196SMatthias Ringwald                 }
4991744e196SMatthias Ringwald             } else {
5001744e196SMatthias Ringwald                 error = true;
5011744e196SMatthias Ringwald             }
5021744e196SMatthias Ringwald             break;
5031744e196SMatthias Ringwald         case HID_DESCRIPTOR_LIST_ERROR:
5041744e196SMatthias Ringwald         case HID_DESCRIPTOR_LIST_COMPLETE:
5051744e196SMatthias Ringwald             // ignore data
5061744e196SMatthias Ringwald             break;
5071744e196SMatthias Ringwald         default:
5081744e196SMatthias Ringwald             btstack_unreachable();
5091744e196SMatthias Ringwald             break;
5101744e196SMatthias Ringwald     }
5111744e196SMatthias Ringwald 
5121744e196SMatthias Ringwald     if (error){
5131744e196SMatthias Ringwald         log_info("Descriptor List invalid, state %u", (int) state);
5141744e196SMatthias Ringwald         state = HID_DESCRIPTOR_LIST_ERROR;
5151744e196SMatthias Ringwald         connection->hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
5161744e196SMatthias Ringwald     }
5171744e196SMatthias Ringwald }
5181744e196SMatthias Ringwald 
hid_host_handle_sdp_client_query_result(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)519fd7ba7a6SMilanka Ringwald static void hid_host_handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
520fd7ba7a6SMilanka Ringwald     UNUSED(packet_type);
521fd7ba7a6SMilanka Ringwald     UNUSED(channel);
522fd7ba7a6SMilanka Ringwald     UNUSED(size);
523fd7ba7a6SMilanka Ringwald 
524fd7ba7a6SMilanka Ringwald     des_iterator_t attribute_list_it;
525fd7ba7a6SMilanka Ringwald     des_iterator_t additional_des_it;
526fd7ba7a6SMilanka Ringwald     des_iterator_t prot_it;
527fd7ba7a6SMilanka Ringwald     uint8_t       *des_element;
528fd7ba7a6SMilanka Ringwald     uint8_t       *element;
529fd7ba7a6SMilanka Ringwald     uint32_t       uuid;
530fd7ba7a6SMilanka Ringwald     uint8_t        status = ERROR_CODE_SUCCESS;
53101977ed1SMilanka Ringwald     bool try_fallback_to_boot;
53205439aa6SMilanka Ringwald     bool finalize_connection;
533fd7ba7a6SMilanka Ringwald 
5345961e155SMilanka Ringwald 
535de81b46aSMatthias Ringwald     hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_host_sdp_context_control_cid);
536fd7ba7a6SMilanka Ringwald     if (!connection) {
537de81b46aSMatthias Ringwald         log_error("SDP query, connection with 0x%02x cid not found", hid_host_sdp_context_control_cid);
538fd7ba7a6SMilanka Ringwald         return;
539fd7ba7a6SMilanka Ringwald     }
5405961e155SMilanka Ringwald 
54105439aa6SMilanka Ringwald     btstack_assert(connection->state == HID_HOST_W4_SDP_QUERY_RESULT);
5425961e155SMilanka Ringwald 
543483beab4SMatthias Ringwald     uint16_t  attribute_id;
544483beab4SMatthias Ringwald     uint16_t  attribute_offset;
545483beab4SMatthias Ringwald     uint8_t   attribute_data;
546fd7ba7a6SMilanka Ringwald     switch (hci_event_packet_get_type(packet)){
547fd7ba7a6SMilanka Ringwald         case SDP_EVENT_QUERY_ATTRIBUTE_VALUE:
548fd7ba7a6SMilanka Ringwald 
549483beab4SMatthias Ringwald             attribute_id = sdp_event_query_attribute_byte_get_attribute_id(packet);
550483beab4SMatthias Ringwald             attribute_offset = sdp_event_query_attribute_byte_get_data_offset(packet);
551483beab4SMatthias Ringwald             attribute_data = sdp_event_query_attribute_byte_get_data(packet);
5521744e196SMatthias Ringwald 
553d1b426c4SMatthias Ringwald             // filter attributes
554d1b426c4SMatthias Ringwald             bool process_data = false;
555d1b426c4SMatthias Ringwald             switch (attribute_id){
556d1b426c4SMatthias Ringwald                 case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST:
557d1b426c4SMatthias Ringwald                 case BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
558d1b426c4SMatthias Ringwald                 case BLUETOOTH_ATTRIBUTE_HIDSSR_HOST_MAX_LATENCY:
559d1b426c4SMatthias Ringwald                 case BLUETOOTH_ATTRIBUTE_HIDSSR_HOST_MIN_TIMEOUT:
560d1b426c4SMatthias Ringwald                     process_data = true;
561d1b426c4SMatthias Ringwald                     break;
562d1b426c4SMatthias Ringwald                 case BLUETOOTH_ATTRIBUTE_HID_DESCRIPTOR_LIST:
563d1b426c4SMatthias Ringwald                     // directly process BLUETOOTH_ATTRIBUTE_HID_DESCRIPTOR_LIST with state machine
5641744e196SMatthias Ringwald                     hid_host_handle_sdp_hid_descriptor_list(connection, attribute_offset, attribute_data);
5651744e196SMatthias Ringwald                     break;
566d1b426c4SMatthias Ringwald                 default:
567d1b426c4SMatthias Ringwald                     break;
568d1b426c4SMatthias Ringwald             }
569d1b426c4SMatthias Ringwald             if (process_data == false){
570d1b426c4SMatthias Ringwald                 break;
5711744e196SMatthias Ringwald             }
5721744e196SMatthias Ringwald 
573de81b46aSMatthias Ringwald             if (sdp_event_query_attribute_byte_get_attribute_length(packet) <= hid_host_sdp_attribute_value_buffer_size) {
574fd7ba7a6SMilanka Ringwald 
575483beab4SMatthias Ringwald                 hid_host_sdp_attribute_value[attribute_offset] = attribute_data;
576fd7ba7a6SMilanka Ringwald 
577483beab4SMatthias Ringwald                 if ((uint16_t)(attribute_offset + 1) == sdp_event_query_attribute_byte_get_attribute_length(packet)) {
578483beab4SMatthias Ringwald                     switch(attribute_id) {
579fd7ba7a6SMilanka Ringwald 
580fd7ba7a6SMilanka Ringwald                         case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST:
581de81b46aSMatthias Ringwald                             for (des_iterator_init(&attribute_list_it, hid_host_sdp_attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) {
582fd7ba7a6SMilanka Ringwald                                 if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue;
583fd7ba7a6SMilanka Ringwald                                 des_element = des_iterator_get_element(&attribute_list_it);
584fd7ba7a6SMilanka Ringwald                                 des_iterator_init(&prot_it, des_element);
585fd7ba7a6SMilanka Ringwald                                 element = des_iterator_get_element(&prot_it);
586fd7ba7a6SMilanka Ringwald                                 if (de_get_element_type(element) != DE_UUID) continue;
587fd7ba7a6SMilanka Ringwald                                 uuid = de_get_uuid32(element);
588fd7ba7a6SMilanka Ringwald                                 switch (uuid){
589fd7ba7a6SMilanka Ringwald                                     case BLUETOOTH_PROTOCOL_L2CAP:
590fd7ba7a6SMilanka Ringwald                                         if (!des_iterator_has_more(&prot_it)) continue;
591fd7ba7a6SMilanka Ringwald                                         des_iterator_next(&prot_it);
592fd7ba7a6SMilanka Ringwald                                         de_element_get_uint16(des_iterator_get_element(&prot_it), &connection->control_psm);
5931ddeadfcSMatthias Ringwald                                         log_info("HID Control PSM: 0x%04x", connection->control_psm);
594fd7ba7a6SMilanka Ringwald                                         break;
595fd7ba7a6SMilanka Ringwald                                     default:
596fd7ba7a6SMilanka Ringwald                                         break;
597fd7ba7a6SMilanka Ringwald                                 }
598fd7ba7a6SMilanka Ringwald                             }
599fd7ba7a6SMilanka Ringwald                             break;
600fd7ba7a6SMilanka Ringwald                         case BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
601de81b46aSMatthias Ringwald                             for (des_iterator_init(&attribute_list_it, hid_host_sdp_attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) {
602fd7ba7a6SMilanka Ringwald                                 if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue;
603fd7ba7a6SMilanka Ringwald                                 des_element = des_iterator_get_element(&attribute_list_it);
604fd7ba7a6SMilanka Ringwald                                 for (des_iterator_init(&additional_des_it, des_element); des_iterator_has_more(&additional_des_it); des_iterator_next(&additional_des_it)) {
605fd7ba7a6SMilanka Ringwald                                     if (des_iterator_get_type(&additional_des_it) != DE_DES) continue;
606fd7ba7a6SMilanka Ringwald                                     des_element = des_iterator_get_element(&additional_des_it);
607fd7ba7a6SMilanka Ringwald                                     des_iterator_init(&prot_it, des_element);
608fd7ba7a6SMilanka Ringwald                                     element = des_iterator_get_element(&prot_it);
609fd7ba7a6SMilanka Ringwald                                     if (de_get_element_type(element) != DE_UUID) continue;
610fd7ba7a6SMilanka Ringwald                                     uuid = de_get_uuid32(element);
611fd7ba7a6SMilanka Ringwald                                     switch (uuid){
612fd7ba7a6SMilanka Ringwald                                         case BLUETOOTH_PROTOCOL_L2CAP:
613fd7ba7a6SMilanka Ringwald                                             if (!des_iterator_has_more(&prot_it)) continue;
614fd7ba7a6SMilanka Ringwald                                             des_iterator_next(&prot_it);
615fd7ba7a6SMilanka Ringwald                                             de_element_get_uint16(des_iterator_get_element(&prot_it), &connection->interrupt_psm);
6161ddeadfcSMatthias Ringwald                                             log_info("HID Interrupt PSM: 0x%04x", connection->interrupt_psm);
617fd7ba7a6SMilanka Ringwald                                             break;
618fd7ba7a6SMilanka Ringwald                                         default:
619fd7ba7a6SMilanka Ringwald                                             break;
620fd7ba7a6SMilanka Ringwald                                     }
621fd7ba7a6SMilanka Ringwald                                 }
622fd7ba7a6SMilanka Ringwald                             }
623fd7ba7a6SMilanka Ringwald                             break;
624fd7ba7a6SMilanka Ringwald 
625580d6bb4SMilanka Ringwald                         case BLUETOOTH_ATTRIBUTE_HIDSSR_HOST_MAX_LATENCY:
626de81b46aSMatthias Ringwald                             if (de_get_element_type(hid_host_sdp_attribute_value) == DE_UINT) {
627580d6bb4SMilanka Ringwald                                 uint16_t host_max_latency;
628de81b46aSMatthias Ringwald                                 if (de_element_get_uint16(hid_host_sdp_attribute_value, &host_max_latency) == 1){
629580d6bb4SMilanka Ringwald                                     connection->host_max_latency = host_max_latency;
630580d6bb4SMilanka Ringwald                                 } else {
631580d6bb4SMilanka Ringwald                                     connection->host_max_latency = 0xFFFF;
632580d6bb4SMilanka Ringwald                                 }
633580d6bb4SMilanka Ringwald                             }
634580d6bb4SMilanka Ringwald                             break;
635580d6bb4SMilanka Ringwald 
636580d6bb4SMilanka Ringwald                         case BLUETOOTH_ATTRIBUTE_HIDSSR_HOST_MIN_TIMEOUT:
637de81b46aSMatthias Ringwald                             if (de_get_element_type(hid_host_sdp_attribute_value) == DE_UINT) {
638580d6bb4SMilanka Ringwald                                 uint16_t host_min_timeout;
639de81b46aSMatthias Ringwald                                 if (de_element_get_uint16(hid_host_sdp_attribute_value, &host_min_timeout) == 1){
640580d6bb4SMilanka Ringwald                                     connection->host_min_timeout = host_min_timeout;
641580d6bb4SMilanka Ringwald                                 } else {
642580d6bb4SMilanka Ringwald                                     connection->host_min_timeout = 0xFFFF;
643580d6bb4SMilanka Ringwald                                 }
644580d6bb4SMilanka Ringwald                             }
645580d6bb4SMilanka Ringwald                             break;
646580d6bb4SMilanka Ringwald 
647fd7ba7a6SMilanka Ringwald                         default:
648fd7ba7a6SMilanka Ringwald                             break;
649fd7ba7a6SMilanka Ringwald                     }
650fd7ba7a6SMilanka Ringwald                 }
651fd7ba7a6SMilanka Ringwald             } else {
652de81b46aSMatthias Ringwald                 log_error("SDP attribute value buffer size exceeded: available %d, required %d", hid_host_sdp_attribute_value_buffer_size, sdp_event_query_attribute_byte_get_attribute_length(packet));
653fd7ba7a6SMilanka Ringwald             }
654fd7ba7a6SMilanka Ringwald             break;
655fd7ba7a6SMilanka Ringwald 
656fd7ba7a6SMilanka Ringwald         case SDP_EVENT_QUERY_COMPLETE:
657fe493a7cSMilanka Ringwald             status = sdp_event_query_complete_get_status(packet);
65801977ed1SMilanka Ringwald             try_fallback_to_boot = false;
65905439aa6SMilanka Ringwald             finalize_connection = false;
660fe493a7cSMilanka Ringwald 
66101977ed1SMilanka Ringwald             switch (status){
66201977ed1SMilanka Ringwald                 // remote has SDP server
66301977ed1SMilanka Ringwald                 case ERROR_CODE_SUCCESS:
66401977ed1SMilanka Ringwald                     //  but no HID record
66501977ed1SMilanka Ringwald                     if (!connection->control_psm || !connection->interrupt_psm) {
666fd7ba7a6SMilanka Ringwald                         status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
66705439aa6SMilanka Ringwald                         if (connection->requested_protocol_mode == HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT){
66801977ed1SMilanka Ringwald                             try_fallback_to_boot = true;
66905439aa6SMilanka Ringwald                         } else {
67005439aa6SMilanka Ringwald                             finalize_connection = true;
67105439aa6SMilanka Ringwald                         }
672fd7ba7a6SMilanka Ringwald                         break;
673fd7ba7a6SMilanka Ringwald                     }
67405439aa6SMilanka Ringwald                     // report mode possible
675fd7ba7a6SMilanka Ringwald                     break;
676fd7ba7a6SMilanka Ringwald 
677b4c96950SMatthias Ringwald                 // SDP query incomplete (e.g. disconnect)
678b4c96950SMatthias Ringwald                 case SDP_QUERY_INCOMPLETE:
679b4c96950SMatthias Ringwald                     finalize_connection = true;
680b4c96950SMatthias Ringwald                     break;
681b4c96950SMatthias Ringwald 
68205439aa6SMilanka Ringwald                 // SDP connection failed or remote does not have SDP server
683fd7ba7a6SMilanka Ringwald                 default:
68405439aa6SMilanka Ringwald                     if (connection->requested_protocol_mode == HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT){
68505439aa6SMilanka Ringwald                         try_fallback_to_boot = true;
68605439aa6SMilanka Ringwald                     } else {
68705439aa6SMilanka Ringwald                         finalize_connection = true;
68805439aa6SMilanka Ringwald                     }
68905439aa6SMilanka Ringwald                     break;
69005439aa6SMilanka Ringwald             }
69105439aa6SMilanka Ringwald 
69205439aa6SMilanka Ringwald             if (finalize_connection){
693de81b46aSMatthias Ringwald                 hid_host_sdp_context_control_cid = 0;
694fd7ba7a6SMilanka Ringwald                 hid_emit_connected_event(connection, status);
695fd7ba7a6SMilanka Ringwald                 hid_host_finalize_connection(connection);
6965961e155SMilanka Ringwald                 break;
6975961e155SMilanka Ringwald             }
6985961e155SMilanka Ringwald 
699580d6bb4SMilanka Ringwald             hid_emit_sniff_params_event(connection);
700580d6bb4SMilanka Ringwald 
70101977ed1SMilanka Ringwald             if (try_fallback_to_boot){
70205439aa6SMilanka Ringwald                 if (connection->incoming){
70305439aa6SMilanka Ringwald                     connection->set_protocol = true;
704de89a6fcSMilanka Ringwald                     connection->state = HID_HOST_CONNECTION_ESTABLISHED;
70505439aa6SMilanka Ringwald                     connection->requested_protocol_mode = HID_PROTOCOL_MODE_BOOT;
706de89a6fcSMilanka Ringwald                     hid_emit_descriptor_available_event(connection);
70705439aa6SMilanka Ringwald                     l2cap_request_can_send_now_event(connection->control_cid);
70805439aa6SMilanka Ringwald                 } else {
70901977ed1SMilanka Ringwald                     connection->state = HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED;
71005439aa6SMilanka Ringwald                     status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, BLUETOOTH_PSM_HID_CONTROL, 0xffff, &connection->control_cid);
71101977ed1SMilanka Ringwald                     if (status != ERROR_CODE_SUCCESS){
712de81b46aSMatthias Ringwald                         hid_host_sdp_context_control_cid = 0;
71301977ed1SMilanka Ringwald                         hid_emit_connected_event(connection, status);
71401977ed1SMilanka Ringwald                         hid_host_finalize_connection(connection);
71505439aa6SMilanka Ringwald                     }
71601977ed1SMilanka Ringwald                 }
7175961e155SMilanka Ringwald                 break;
71801977ed1SMilanka Ringwald             }
71905439aa6SMilanka Ringwald 
72005439aa6SMilanka Ringwald             // report mode possible
72105439aa6SMilanka Ringwald             if (connection->incoming) {
72205439aa6SMilanka Ringwald                 connection->set_protocol = true;
723de89a6fcSMilanka Ringwald                 connection->state = HID_HOST_CONNECTION_ESTABLISHED;
72405439aa6SMilanka Ringwald                 connection->requested_protocol_mode = HID_PROTOCOL_MODE_REPORT;
725de89a6fcSMilanka Ringwald                 hid_emit_descriptor_available_event(connection);
72605439aa6SMilanka Ringwald                 l2cap_request_can_send_now_event(connection->control_cid);
72705439aa6SMilanka Ringwald             } else {
72805439aa6SMilanka Ringwald                 connection->state = HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED;
72905439aa6SMilanka Ringwald                 status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, connection->control_psm, 0xffff, &connection->control_cid);
73005439aa6SMilanka Ringwald                 if (status != ERROR_CODE_SUCCESS){
73105439aa6SMilanka Ringwald                     hid_emit_connected_event(connection, status);
73205439aa6SMilanka Ringwald                     hid_host_finalize_connection(connection);
73305439aa6SMilanka Ringwald                 }
73405439aa6SMilanka Ringwald             }
73501977ed1SMilanka Ringwald             break;
73601977ed1SMilanka Ringwald 
73701977ed1SMilanka Ringwald         default:
73801977ed1SMilanka Ringwald             break;
73901977ed1SMilanka Ringwald     }
74001977ed1SMilanka Ringwald 
741fd7ba7a6SMilanka Ringwald }
742fd7ba7a6SMilanka Ringwald 
743ab30106eSMilanka Ringwald 
hid_host_handle_control_packet(hid_host_connection_t * connection,uint8_t * packet,uint16_t size)744ab30106eSMilanka Ringwald static void hid_host_handle_control_packet(hid_host_connection_t * connection, uint8_t *packet, uint16_t size){
745ab30106eSMilanka Ringwald     UNUSED(size);
746ab30106eSMilanka Ringwald     uint8_t param;
747ab30106eSMilanka Ringwald     hid_message_type_t         message_type;
748ab30106eSMilanka Ringwald     hid_handshake_param_type_t message_status;
7494a4b5586SMilanka Ringwald     hid_protocol_mode_t        protocol_mode;
7504a4b5586SMilanka Ringwald 
751dcec10e7SMilanka Ringwald     uint8_t * in_place_event;
752ab30106eSMilanka Ringwald     uint8_t status;
753ab30106eSMilanka Ringwald 
754ab30106eSMilanka Ringwald     message_type   = (hid_message_type_t)(packet[0] >> 4);
755dcec10e7SMilanka Ringwald     if (message_type == HID_MESSAGE_TYPE_HID_CONTROL){
756dcec10e7SMilanka Ringwald         param = packet[0] & 0x0F;
757dcec10e7SMilanka Ringwald         switch ((hid_control_param_t)param){
758dcec10e7SMilanka Ringwald             case HID_CONTROL_PARAM_VIRTUAL_CABLE_UNPLUG:
759dcec10e7SMilanka Ringwald                 hid_emit_event(connection, HID_SUBEVENT_VIRTUAL_CABLE_UNPLUG);
760dcec10e7SMilanka Ringwald                 hid_host_disconnect(connection->hid_cid);
761dcec10e7SMilanka Ringwald                 return;
762dcec10e7SMilanka Ringwald             default:
763dcec10e7SMilanka Ringwald                 break;
764dcec10e7SMilanka Ringwald         }
765dcec10e7SMilanka Ringwald     }
766ab30106eSMilanka Ringwald 
7674a4b5586SMilanka Ringwald     message_status = (hid_handshake_param_type_t)(packet[0] & 0x0F);
7684a4b5586SMilanka Ringwald 
769ab30106eSMilanka Ringwald     switch (connection->state){
77001977ed1SMilanka Ringwald         case HID_HOST_CONNECTION_ESTABLISHED:
77101977ed1SMilanka Ringwald             if (!connection->w4_set_protocol_response) break;
77201977ed1SMilanka Ringwald             connection->w4_set_protocol_response = false;
773de89a6fcSMilanka Ringwald 
77401977ed1SMilanka Ringwald             switch (message_status){
77501977ed1SMilanka Ringwald                 case HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL:
77601977ed1SMilanka Ringwald                     connection->protocol_mode = connection->requested_protocol_mode;
77701977ed1SMilanka Ringwald                     break;
77801977ed1SMilanka Ringwald                 default:
77901977ed1SMilanka Ringwald                     break;
78001977ed1SMilanka Ringwald             }
781de89a6fcSMilanka Ringwald             hid_emit_set_protocol_response_event(connection, message_status);
78201977ed1SMilanka Ringwald             break;
78301977ed1SMilanka Ringwald 
784de89a6fcSMilanka Ringwald         case HID_HOST_CONTROL_CONNECTION_ESTABLISHED:           // outgoing
785de89a6fcSMilanka Ringwald         case HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED:      // outgoing
78601977ed1SMilanka Ringwald             if (!connection->w4_set_protocol_response) break;
78701977ed1SMilanka Ringwald             connection->w4_set_protocol_response = false;
78805439aa6SMilanka Ringwald 
78901977ed1SMilanka Ringwald             switch (message_status){
79001977ed1SMilanka Ringwald                 case HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL:
79101977ed1SMilanka Ringwald                     // we are already connected, here it is only confirmed that we are in required protocol
792de89a6fcSMilanka Ringwald                     btstack_assert(connection->incoming == false);
79305439aa6SMilanka Ringwald                     status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, connection->interrupt_psm, 0xffff, &connection->interrupt_cid);
794de89a6fcSMilanka Ringwald                     if (status != ERROR_CODE_SUCCESS){
7955961e155SMilanka Ringwald                         log_info("HID Interrupt Connection failed: 0x%02x\n", status);
796de89a6fcSMilanka Ringwald                         hid_emit_connected_event(connection, status);
797de89a6fcSMilanka Ringwald                         hid_host_finalize_connection(connection);
79801977ed1SMilanka Ringwald                         break;
79901977ed1SMilanka Ringwald                     }
80001977ed1SMilanka Ringwald                     connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED;
80105439aa6SMilanka Ringwald                     break;
80201977ed1SMilanka Ringwald                 default:
80301977ed1SMilanka Ringwald                     hid_emit_connected_event(connection, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
80405439aa6SMilanka Ringwald                     hid_host_finalize_connection(connection);
80501977ed1SMilanka Ringwald                     break;
80601977ed1SMilanka Ringwald             }
80701977ed1SMilanka Ringwald             break;
80801977ed1SMilanka Ringwald 
809ab30106eSMilanka Ringwald         case HID_HOST_W4_GET_REPORT_RESPONSE:
810dcec10e7SMilanka Ringwald             switch (message_type){
811dcec10e7SMilanka Ringwald                 case HID_MESSAGE_TYPE_HANDSHAKE:{
812dcec10e7SMilanka Ringwald                     uint8_t event[8];
813dcec10e7SMilanka Ringwald                     hid_setup_get_report_event(connection, message_status, event, 0);
814de81b46aSMatthias Ringwald                     hid_host_callback(HCI_EVENT_PACKET, connection->hid_cid, event, sizeof(event));
815dcec10e7SMilanka Ringwald                     break;
816dcec10e7SMilanka Ringwald                 }
817dcec10e7SMilanka Ringwald                 case HID_MESSAGE_TYPE_DATA:
818dcec10e7SMilanka Ringwald                     // reuse hci+l2cap header - max 8 byte (7 bytes + 1 bytes overwriting hid header)
819dcec10e7SMilanka Ringwald                     in_place_event = packet - 7;
820dcec10e7SMilanka Ringwald                     hid_setup_get_report_event(connection, HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL, in_place_event, size-1);
821de81b46aSMatthias Ringwald                     hid_host_callback(HCI_EVENT_PACKET, connection->hid_cid, in_place_event, size + 7);
822dcec10e7SMilanka Ringwald                     break;
823dcec10e7SMilanka Ringwald                 default:
824dcec10e7SMilanka Ringwald                     break;
825dcec10e7SMilanka Ringwald             }
8262ba92e70SMilanka Ringwald             connection->state =  HID_HOST_CONNECTION_ESTABLISHED;
827ab30106eSMilanka Ringwald             break;
8284a4b5586SMilanka Ringwald 
829ab30106eSMilanka Ringwald         case HID_HOST_W4_SET_REPORT_RESPONSE:
8304a4b5586SMilanka Ringwald             hid_emit_event_with_status(connection, HID_SUBEVENT_SET_REPORT_RESPONSE, message_status);
8312ba92e70SMilanka Ringwald             connection->state =  HID_HOST_CONNECTION_ESTABLISHED;
832ab30106eSMilanka Ringwald             break;
8334a4b5586SMilanka Ringwald 
834ab30106eSMilanka Ringwald         case HID_HOST_W4_GET_PROTOCOL_RESPONSE:
8354a4b5586SMilanka Ringwald             protocol_mode = connection->protocol_mode;
8364a4b5586SMilanka Ringwald 
8374a4b5586SMilanka Ringwald             switch (message_type){
8384a4b5586SMilanka Ringwald                 case HID_MESSAGE_TYPE_DATA:
8394a4b5586SMilanka Ringwald                     protocol_mode = (hid_protocol_mode_t)packet[1];
8404a4b5586SMilanka Ringwald                     switch (protocol_mode){
8414a4b5586SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
8424a4b5586SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
8434a4b5586SMilanka Ringwald                             message_status = HID_HANDSHAKE_PARAM_TYPE_SUCCESSFUL;
844ab30106eSMilanka Ringwald                             break;
8454a4b5586SMilanka Ringwald                         default:
8464a4b5586SMilanka Ringwald                             message_status = HID_HANDSHAKE_PARAM_TYPE_ERR_INVALID_PARAMETER;
847ab30106eSMilanka Ringwald                             break;
8484a4b5586SMilanka Ringwald                     }
8494a4b5586SMilanka Ringwald                     break;
8504a4b5586SMilanka Ringwald                 default:
8514a4b5586SMilanka Ringwald                     break;
8524a4b5586SMilanka Ringwald             }
8534a4b5586SMilanka Ringwald             hid_emit_get_protocol_event(connection, message_status, protocol_mode);
8542ba92e70SMilanka Ringwald             connection->state =  HID_HOST_CONNECTION_ESTABLISHED;
8554a4b5586SMilanka Ringwald             break;
8564a4b5586SMilanka Ringwald 
857ab30106eSMilanka Ringwald         default:
858a93a968fSMilanka Ringwald             log_info("ignore invalid HID Control message");
8592ba92e70SMilanka Ringwald             connection->state =  HID_HOST_CONNECTION_ESTABLISHED;
860ab30106eSMilanka Ringwald             break;
861ab30106eSMilanka Ringwald     }
8622ba92e70SMilanka Ringwald 
863ab30106eSMilanka Ringwald }
864ab30106eSMilanka Ringwald 
hid_host_packet_handler(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)865fd7ba7a6SMilanka Ringwald static void hid_host_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
866fd7ba7a6SMilanka Ringwald     UNUSED(channel);
867fd7ba7a6SMilanka Ringwald     UNUSED(size);
868fd7ba7a6SMilanka Ringwald 
86963bf37cdSMilanka Ringwald     uint8_t   event;
870fd7ba7a6SMilanka Ringwald     bd_addr_t address;
871fd7ba7a6SMilanka Ringwald     uint8_t   status;
872ab30106eSMilanka Ringwald     uint16_t  l2cap_cid;
873fd7ba7a6SMilanka Ringwald     hid_host_connection_t * connection;
874fd7ba7a6SMilanka Ringwald 
87563bf37cdSMilanka Ringwald     switch (packet_type) {
876ab30106eSMilanka Ringwald 
877ab30106eSMilanka Ringwald         case L2CAP_DATA_PACKET:
878ab30106eSMilanka Ringwald             connection = hid_host_get_connection_for_l2cap_cid(channel);
879ab30106eSMilanka Ringwald             if (!connection) break;
88059a2ea74SMilanka Ringwald 
881ab30106eSMilanka Ringwald             if (channel == connection->interrupt_cid){
882fe493a7cSMilanka Ringwald                 uint8_t * in_place_event = packet - 7;
883d9d6144eSMatthias Ringwald                 hid_setup_report_event(connection, in_place_event, size);
884de81b46aSMatthias Ringwald                 hid_host_callback(HCI_EVENT_PACKET, connection->hid_cid, in_place_event, size + 7);
885ab30106eSMilanka Ringwald                 break;
886ab30106eSMilanka Ringwald             }
887a93a968fSMilanka Ringwald 
888ab30106eSMilanka Ringwald             if (channel == connection->control_cid){
889ab30106eSMilanka Ringwald                 hid_host_handle_control_packet(connection, packet, size);
890ab30106eSMilanka Ringwald                 break;
891ab30106eSMilanka Ringwald             }
892ab30106eSMilanka Ringwald             break;
893ab30106eSMilanka Ringwald 
89463bf37cdSMilanka Ringwald         case HCI_EVENT_PACKET:
89563bf37cdSMilanka Ringwald             event = hci_event_packet_get_type(packet);
89663bf37cdSMilanka Ringwald             switch (event) {
897fd7ba7a6SMilanka Ringwald                 case L2CAP_EVENT_INCOMING_CONNECTION:
898fd7ba7a6SMilanka Ringwald                     l2cap_event_incoming_connection_get_address(packet, address);
8995961e155SMilanka Ringwald                     // connection should exist if psm == PSM_HID_INTERRUPT
900fd7ba7a6SMilanka Ringwald                     connection = hid_host_get_connection_for_bd_addr(address);
901fd7ba7a6SMilanka Ringwald 
902fd7ba7a6SMilanka Ringwald                     switch (l2cap_event_incoming_connection_get_psm(packet)){
903fd7ba7a6SMilanka Ringwald                         case PSM_HID_CONTROL:
904fd7ba7a6SMilanka Ringwald                             if (connection){
905fd7ba7a6SMilanka Ringwald                                 l2cap_decline_connection(channel);
906fd7ba7a6SMilanka Ringwald                                 break;
907fd7ba7a6SMilanka Ringwald                             }
908fd7ba7a6SMilanka Ringwald 
909fd7ba7a6SMilanka Ringwald                             connection = hid_host_create_connection(address);
910fd7ba7a6SMilanka Ringwald                             if (!connection){
911fd7ba7a6SMilanka Ringwald                                 log_error("Cannot create connection for %s", bd_addr_to_str(address));
912fd7ba7a6SMilanka Ringwald                                 l2cap_decline_connection(channel);
913fd7ba7a6SMilanka Ringwald                                 break;
914fd7ba7a6SMilanka Ringwald                             }
915fd7ba7a6SMilanka Ringwald 
916fd7ba7a6SMilanka Ringwald                             connection->state = HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED;
917e615b0ecSMilanka Ringwald                             connection->hid_descriptor_status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
918fd7ba7a6SMilanka Ringwald                             connection->con_handle = l2cap_event_incoming_connection_get_handle(packet);
919fd7ba7a6SMilanka Ringwald                             connection->control_cid = l2cap_event_incoming_connection_get_local_cid(packet);
920fd7ba7a6SMilanka Ringwald                             connection->incoming = true;
92159a2ea74SMilanka Ringwald 
9225961e155SMilanka Ringwald                             // emit connection request
92357e682feSMilanka Ringwald                             // user calls either hid_host_accept_connection or hid_host_decline_connection
92459a2ea74SMilanka Ringwald                             hid_emit_incoming_connection_event(connection);
925fd7ba7a6SMilanka Ringwald                             break;
926fd7ba7a6SMilanka Ringwald 
927fd7ba7a6SMilanka Ringwald                         case PSM_HID_INTERRUPT:
9285961e155SMilanka Ringwald                             if (!connection || (connection->state != HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED)){
929fd7ba7a6SMilanka Ringwald                                 log_error("Decline connection for %s", bd_addr_to_str(address));
930fd7ba7a6SMilanka Ringwald                                 l2cap_decline_connection(channel);
931fd7ba7a6SMilanka Ringwald                                 break;
932fd7ba7a6SMilanka Ringwald                             }
933fd7ba7a6SMilanka Ringwald 
934fd7ba7a6SMilanka Ringwald                             connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED;
935fd7ba7a6SMilanka Ringwald                             connection->interrupt_cid = l2cap_event_incoming_connection_get_local_cid(packet);
936fd7ba7a6SMilanka Ringwald                             log_info("Accept connection on Interrupt channel %s", bd_addr_to_str(address));
937fd7ba7a6SMilanka Ringwald                             l2cap_accept_connection(channel);
938fd7ba7a6SMilanka Ringwald                             break;
939fd7ba7a6SMilanka Ringwald 
940fd7ba7a6SMilanka Ringwald                         default:
941fd7ba7a6SMilanka Ringwald                             log_info("Decline connection for %s", bd_addr_to_str(address));
942fd7ba7a6SMilanka Ringwald                             l2cap_decline_connection(channel);
943fd7ba7a6SMilanka Ringwald                             break;
94463bf37cdSMilanka Ringwald                     }
94563bf37cdSMilanka Ringwald                     break;
946fd7ba7a6SMilanka Ringwald 
947fd7ba7a6SMilanka Ringwald                 case L2CAP_EVENT_CHANNEL_OPENED:
948fd7ba7a6SMilanka Ringwald                     l2cap_event_channel_opened_get_address(packet, address);
949fd7ba7a6SMilanka Ringwald 
950fd7ba7a6SMilanka Ringwald                     connection = hid_host_get_connection_for_bd_addr(address);
951fd7ba7a6SMilanka Ringwald                     if (!connection){
952fd7ba7a6SMilanka Ringwald                         log_error("Connection does not exist %s", bd_addr_to_str(address));
953fd7ba7a6SMilanka Ringwald                         break;
954fd7ba7a6SMilanka Ringwald                     }
955fd7ba7a6SMilanka Ringwald 
956c71a99a4SMilanka Ringwald                     status = l2cap_event_channel_opened_get_status(packet);
95705439aa6SMilanka Ringwald                     if (status != ERROR_CODE_SUCCESS){
958c71a99a4SMilanka Ringwald                         log_info("L2CAP connection %s failed: 0x%02xn", bd_addr_to_str(address), status);
959c71a99a4SMilanka Ringwald                         hid_emit_connected_event(connection, status);
96005439aa6SMilanka Ringwald                         hid_host_finalize_connection(connection);
961c71a99a4SMilanka Ringwald                         break;
962c71a99a4SMilanka Ringwald                     }
963c71a99a4SMilanka Ringwald 
96401977ed1SMilanka Ringwald                     // handle incoming connection:
96501977ed1SMilanka Ringwald                     if (connection->incoming){
9665961e155SMilanka Ringwald                         switch (connection->state){
9675961e155SMilanka Ringwald                             case HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED:
96801977ed1SMilanka 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)
96901977ed1SMilanka Ringwald                                 // We expect incomming interrupt connection from remote HID device
97001977ed1SMilanka Ringwald                                 connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED;
97105439aa6SMilanka Ringwald                                 log_info("Incoming control connection opened: w4 interrupt");
97201977ed1SMilanka Ringwald                                 break;
97301977ed1SMilanka Ringwald 
9745961e155SMilanka Ringwald                             case HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED:
975de89a6fcSMilanka Ringwald                                 hid_emit_connected_event(connection, ERROR_CODE_SUCCESS);
976de89a6fcSMilanka Ringwald                                 connection->state = HID_HOST_CONNECTION_ESTABLISHED;
977fd7ba7a6SMilanka Ringwald 
97805439aa6SMilanka Ringwald                                 switch (connection->requested_protocol_mode){
979fe493a7cSMilanka Ringwald                                     case HID_PROTOCOL_MODE_BOOT:
980e615b0ecSMilanka Ringwald                                         hid_emit_descriptor_available_event(connection);
98101977ed1SMilanka Ringwald                                         connection->set_protocol = true;
982fe493a7cSMilanka Ringwald                                         l2cap_request_can_send_now_event(connection->control_cid);
98305439aa6SMilanka Ringwald                                         log_info("Incoming interrupt connection opened: set boot mode");
984fd7ba7a6SMilanka Ringwald                                         break;
985fe493a7cSMilanka Ringwald                                     default:
98601977ed1SMilanka Ringwald                                         // SDP query
98705439aa6SMilanka Ringwald                                         log_info("Incoming interrupt connection opened: start SDP query");
98805439aa6SMilanka Ringwald                                         connection->state = HID_HOST_W2_SEND_SDP_QUERY;
9895961e155SMilanka Ringwald                                         hid_host_handle_sdp_client_query_request.callback = &hid_host_handle_start_sdp_client_query;
9905961e155SMilanka Ringwald                                         (void) sdp_client_register_query_callback(&hid_host_handle_sdp_client_query_request);
99101977ed1SMilanka Ringwald                                         break;
99201977ed1SMilanka Ringwald                                 }
99301977ed1SMilanka Ringwald                                 break;
99401977ed1SMilanka Ringwald 
99501977ed1SMilanka Ringwald                             default:
9965961e155SMilanka Ringwald                                 btstack_assert(false);
99701977ed1SMilanka Ringwald                                 break;
99801977ed1SMilanka Ringwald                         }
99901977ed1SMilanka Ringwald                         break;
100001977ed1SMilanka Ringwald                     }
100101977ed1SMilanka Ringwald 
100201977ed1SMilanka Ringwald                     // handle outgoing connection
10035961e155SMilanka Ringwald                     switch (connection->state){
10045961e155SMilanka Ringwald                         case HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED:
100505439aa6SMilanka Ringwald                             log_info("Opened control connection, requested protocol mode %d\n", connection->requested_protocol_mode);
100601977ed1SMilanka Ringwald                             connection->con_handle = l2cap_event_channel_opened_get_handle(packet);
100701977ed1SMilanka Ringwald                             connection->state = HID_HOST_CONTROL_CONNECTION_ESTABLISHED;
100801977ed1SMilanka Ringwald 
100901977ed1SMilanka Ringwald                             switch (connection->requested_protocol_mode){
101001977ed1SMilanka Ringwald                                 case HID_PROTOCOL_MODE_BOOT:
101101977ed1SMilanka Ringwald                                 case HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT:
101201977ed1SMilanka Ringwald                                     connection->set_protocol = true;
10135961e155SMilanka Ringwald                                     connection->interrupt_psm = BLUETOOTH_PSM_HID_INTERRUPT;
101401977ed1SMilanka Ringwald                                     l2cap_request_can_send_now_event(connection->control_cid);
101501977ed1SMilanka Ringwald                                     break;
101601977ed1SMilanka Ringwald                                 default:
101705439aa6SMilanka Ringwald                                     status = l2cap_create_channel(hid_host_packet_handler, address, connection->interrupt_psm, 0xffff, &connection->interrupt_cid);
1018fd7ba7a6SMilanka Ringwald                                     if (status){
1019fd7ba7a6SMilanka Ringwald                                         log_info("Connecting to HID Interrupt failed: 0x%02x", status);
102001977ed1SMilanka Ringwald                                         hid_emit_connected_event(connection, status);
1021fd7ba7a6SMilanka Ringwald                                         break;
1022fd7ba7a6SMilanka Ringwald                                     }
102301977ed1SMilanka Ringwald                                     connection->protocol_mode = connection->requested_protocol_mode;
1024fd7ba7a6SMilanka Ringwald                                     connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED;
1025fe493a7cSMilanka Ringwald                                     break;
1026fd7ba7a6SMilanka Ringwald                             }
1027fd7ba7a6SMilanka Ringwald                             break;
1028fd7ba7a6SMilanka Ringwald 
10295961e155SMilanka Ringwald                         case HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED:
1030fd7ba7a6SMilanka Ringwald                             connection->state = HID_HOST_CONNECTION_ESTABLISHED;
103159a2ea74SMilanka Ringwald                             log_info("HID host connection established, cids: control 0x%02x, interrupt 0x%02x interrupt, hid 0x%02x",
103259a2ea74SMilanka Ringwald                                 connection->control_cid, connection->interrupt_cid, connection->hid_cid);
1033fd7ba7a6SMilanka Ringwald                             hid_emit_connected_event(connection, ERROR_CODE_SUCCESS);
103405439aa6SMilanka Ringwald                             hid_emit_descriptor_available_event(connection);
1035fd7ba7a6SMilanka Ringwald                             break;
1036fd7ba7a6SMilanka Ringwald 
1037fd7ba7a6SMilanka Ringwald                         default:
10385961e155SMilanka Ringwald                             btstack_assert(false);
1039fd7ba7a6SMilanka Ringwald                             break;
1040fd7ba7a6SMilanka Ringwald                     }
1041fd7ba7a6SMilanka Ringwald                     break;
1042fd7ba7a6SMilanka Ringwald 
1043ab30106eSMilanka Ringwald                 case L2CAP_EVENT_CHANNEL_CLOSED:
1044ab30106eSMilanka Ringwald                     l2cap_cid  = l2cap_event_channel_closed_get_local_cid(packet);
1045ab30106eSMilanka Ringwald                     connection = hid_host_get_connection_for_l2cap_cid(l2cap_cid);
1046ab30106eSMilanka Ringwald                     if (!connection) return;
1047ab30106eSMilanka Ringwald 
1048ab30106eSMilanka Ringwald                     if (l2cap_cid == connection->interrupt_cid){
1049ab30106eSMilanka Ringwald                         connection->interrupt_cid = 0;
1050ab30106eSMilanka Ringwald                         if (connection->state == HID_HOST_W4_INTERRUPT_CONNECTION_DISCONNECTED){
1051ab30106eSMilanka Ringwald                             connection->state = HID_HOST_W4_CONTROL_CONNECTION_DISCONNECTED;
1052b93f8966SMatthias Ringwald                             l2cap_disconnect(connection->control_cid);
1053ab30106eSMilanka Ringwald                         }
1054ab30106eSMilanka Ringwald                         break;
1055ab30106eSMilanka Ringwald                     }
1056ab30106eSMilanka Ringwald 
1057ab30106eSMilanka Ringwald                     if (l2cap_cid == connection->control_cid){
1058ab30106eSMilanka Ringwald                         connection->control_cid = 0;
1059ab30106eSMilanka Ringwald                         hid_emit_event(connection, HID_SUBEVENT_CONNECTION_CLOSED);
1060ab30106eSMilanka Ringwald                         hid_descriptor_storage_delete(connection);
1061ab30106eSMilanka Ringwald                         hid_host_finalize_connection(connection);
1062ab30106eSMilanka Ringwald                         break;
1063ab30106eSMilanka Ringwald                     }
1064ab30106eSMilanka Ringwald                     break;
1065ab30106eSMilanka Ringwald 
1066ab30106eSMilanka Ringwald                 case L2CAP_EVENT_CAN_SEND_NOW:
1067ab30106eSMilanka Ringwald                     l2cap_cid  = l2cap_event_can_send_now_get_local_cid(packet);
1068ab30106eSMilanka Ringwald                     connection = hid_host_get_connection_for_l2cap_cid(l2cap_cid);
1069ab30106eSMilanka Ringwald                     if (!connection) return;
1070ab30106eSMilanka Ringwald 
107101977ed1SMilanka Ringwald 
107201977ed1SMilanka Ringwald 
107359a2ea74SMilanka Ringwald                     if (connection->control_cid == l2cap_cid){
1074ab30106eSMilanka Ringwald                         switch(connection->state){
107501977ed1SMilanka Ringwald                             case HID_HOST_CONTROL_CONNECTION_ESTABLISHED:
107601977ed1SMilanka Ringwald                             case HID_HOST_W4_INTERRUPT_CONNECTION_ESTABLISHED:
107701977ed1SMilanka Ringwald                                 if (connection->set_protocol){
107801977ed1SMilanka Ringwald                                     connection->set_protocol = false;
1079bcaa0699SMatthias Ringwald                                     uint8_t protocol_mode = connection->requested_protocol_mode == HID_PROTOCOL_MODE_BOOT ? 0 : 1;
1080bcaa0699SMatthias Ringwald                                     uint8_t header = (HID_MESSAGE_TYPE_SET_PROTOCOL << 4) | protocol_mode;
108101977ed1SMilanka Ringwald                                     uint8_t report[] = {header};
108201977ed1SMilanka Ringwald                                     connection->w4_set_protocol_response = true;
108301977ed1SMilanka Ringwald                                     l2cap_send(connection->control_cid, (uint8_t*) report, sizeof(report));
108401977ed1SMilanka Ringwald                                     break;
108501977ed1SMilanka Ringwald                                 }
108601977ed1SMilanka Ringwald                                 break;
108701977ed1SMilanka Ringwald 
108859a2ea74SMilanka Ringwald                             case HID_HOST_CONNECTION_ESTABLISHED:
108959a2ea74SMilanka Ringwald                                 if ((connection->control_tasks & CONTROL_MESSAGE_BITMASK_SUSPEND) != 0){
109059a2ea74SMilanka Ringwald                                     connection->control_tasks &= ~CONTROL_MESSAGE_BITMASK_SUSPEND;
109159a2ea74SMilanka Ringwald                                     uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_SUSPEND };
109259a2ea74SMilanka Ringwald                                     l2cap_send(connection->control_cid, (uint8_t*) report, 1);
109359a2ea74SMilanka Ringwald                                     break;
109459a2ea74SMilanka Ringwald                                 }
109559a2ea74SMilanka Ringwald                                 if ((connection->control_tasks & CONTROL_MESSAGE_BITMASK_EXIT_SUSPEND) != 0){
109659a2ea74SMilanka Ringwald                                     connection->control_tasks &= ~CONTROL_MESSAGE_BITMASK_EXIT_SUSPEND;
109759a2ea74SMilanka Ringwald                                     uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_EXIT_SUSPEND };
109859a2ea74SMilanka Ringwald                                     l2cap_send(connection->control_cid, (uint8_t*) report, 1);
109959a2ea74SMilanka Ringwald                                     break;
110059a2ea74SMilanka Ringwald                                 }
110159a2ea74SMilanka Ringwald                                 if ((connection->control_tasks & CONTROL_MESSAGE_BITMASK_VIRTUAL_CABLE_UNPLUG) != 0){
110259a2ea74SMilanka Ringwald                                     connection->control_tasks &= ~CONTROL_MESSAGE_BITMASK_VIRTUAL_CABLE_UNPLUG;
110359a2ea74SMilanka Ringwald                                     uint8_t report[] = { (HID_MESSAGE_TYPE_HID_CONTROL << 4) | HID_CONTROL_PARAM_VIRTUAL_CABLE_UNPLUG };
110459a2ea74SMilanka Ringwald                                     l2cap_send(connection->control_cid, (uint8_t*) report, 1);
110559a2ea74SMilanka Ringwald                                     break;
110659a2ea74SMilanka Ringwald                                 }
110701977ed1SMilanka Ringwald 
110801977ed1SMilanka Ringwald                                 if (connection->set_protocol){
110901977ed1SMilanka Ringwald                                     connection->set_protocol = false;
111001977ed1SMilanka Ringwald                                     uint8_t header = (HID_MESSAGE_TYPE_SET_PROTOCOL << 4) | connection->requested_protocol_mode;
111101977ed1SMilanka Ringwald                                     uint8_t report[] = {header};
111201977ed1SMilanka Ringwald 
111301977ed1SMilanka Ringwald                                     connection->w4_set_protocol_response = true;
111401977ed1SMilanka Ringwald                                     l2cap_send(connection->control_cid, (uint8_t*) report, sizeof(report));
111501977ed1SMilanka Ringwald                                     break;
111601977ed1SMilanka Ringwald                                 }
111759a2ea74SMilanka Ringwald                                 break;
111859a2ea74SMilanka Ringwald 
1119ab30106eSMilanka Ringwald                             case HID_HOST_W2_SEND_GET_REPORT:{
1120ab30106eSMilanka Ringwald                                 uint8_t header = (HID_MESSAGE_TYPE_GET_REPORT << 4) | connection->report_type;
1121*14618ebfSMatthias Ringwald                                 uint8_t report[2];
1122*14618ebfSMatthias Ringwald                                 uint16_t pos = 0;
1123*14618ebfSMatthias Ringwald                                 report[pos++] = header;
1124*14618ebfSMatthias Ringwald                                 if (connection->report_id != HID_REPORT_ID_UNDEFINED){
1125*14618ebfSMatthias Ringwald                                     report[pos++] = (uint8_t) connection->report_id;
1126*14618ebfSMatthias Ringwald                                 }
1127ab30106eSMilanka Ringwald 
1128ab30106eSMilanka Ringwald                                 connection->state = HID_HOST_W4_GET_REPORT_RESPONSE;
1129*14618ebfSMatthias Ringwald                                 l2cap_send(connection->control_cid, (uint8_t*) report, pos);
1130ab30106eSMilanka Ringwald                                 break;
1131ab30106eSMilanka Ringwald                             }
1132a93a968fSMilanka Ringwald 
1133ab30106eSMilanka Ringwald                             case HID_HOST_W2_SEND_SET_REPORT:{
1134ab30106eSMilanka Ringwald                                 uint8_t header = (HID_MESSAGE_TYPE_SET_REPORT << 4) | connection->report_type;
1135ab30106eSMilanka Ringwald                                 connection->state = HID_HOST_W4_SET_REPORT_RESPONSE;
1136ab30106eSMilanka Ringwald 
1137ab30106eSMilanka Ringwald                                 l2cap_reserve_packet_buffer();
1138ab30106eSMilanka Ringwald                                 uint8_t * out_buffer = l2cap_get_outgoing_buffer();
1139*14618ebfSMatthias Ringwald                                 uint16_t pos = 0;
1140*14618ebfSMatthias Ringwald                                 out_buffer[pos++] = header;
1141*14618ebfSMatthias Ringwald                                 if (connection->report_id != HID_REPORT_ID_UNDEFINED){
1142*14618ebfSMatthias Ringwald                                     out_buffer[pos++] = (uint8_t) connection->report_id;
1143*14618ebfSMatthias Ringwald                                 }
1144*14618ebfSMatthias Ringwald                                 (void)memcpy(&out_buffer[pos], connection->report, connection->report_len);
1145*14618ebfSMatthias Ringwald                                 pos += connection->report_len;
1146*14618ebfSMatthias Ringwald                                 l2cap_send_prepared(connection->control_cid, pos);
1147ab30106eSMilanka Ringwald                                 break;
1148ab30106eSMilanka Ringwald                             }
1149a93a968fSMilanka Ringwald 
1150ab30106eSMilanka Ringwald                             case HID_HOST_W2_SEND_GET_PROTOCOL:{
1151ab30106eSMilanka Ringwald                                 uint8_t header = (HID_MESSAGE_TYPE_GET_PROTOCOL << 4);
1152ab30106eSMilanka Ringwald                                 uint8_t report[] = {header};
1153ab30106eSMilanka Ringwald                                 connection->state = HID_HOST_W4_GET_PROTOCOL_RESPONSE;
1154ab30106eSMilanka Ringwald                                 l2cap_send(connection->control_cid, (uint8_t*) report, sizeof(report));
1155ab30106eSMilanka Ringwald                                 break;
1156ab30106eSMilanka Ringwald                             }
1157a93a968fSMilanka Ringwald 
115859a2ea74SMilanka Ringwald                             default:
115959a2ea74SMilanka Ringwald                                 break;
116059a2ea74SMilanka Ringwald                         }
116159a2ea74SMilanka Ringwald                     }
116259a2ea74SMilanka Ringwald 
1163fe493a7cSMilanka Ringwald                     if (connection->interrupt_cid == l2cap_cid && connection->state == HID_HOST_W2_SEND_REPORT){
1164fe493a7cSMilanka Ringwald                         connection->state = HID_HOST_CONNECTION_ESTABLISHED;
1165fe493a7cSMilanka Ringwald                         // there is no response for this type of message
1166ab30106eSMilanka Ringwald                         uint8_t header = (HID_MESSAGE_TYPE_DATA << 4) | connection->report_type;
1167ab30106eSMilanka Ringwald 
1168ab30106eSMilanka Ringwald                         l2cap_reserve_packet_buffer();
1169ab30106eSMilanka Ringwald                         uint8_t * out_buffer = l2cap_get_outgoing_buffer();
1170*14618ebfSMatthias Ringwald                         uint16_t pos = 0;
1171*14618ebfSMatthias Ringwald                         out_buffer[pos++] = header;
1172*14618ebfSMatthias Ringwald                         if (connection->report_id != HID_REPORT_ID_UNDEFINED){
1173*14618ebfSMatthias Ringwald                             out_buffer[pos++] = (uint8_t) connection->report_id;
1174*14618ebfSMatthias Ringwald                         }
1175*14618ebfSMatthias Ringwald                         (void)memcpy(&out_buffer[pos], connection->report, connection->report_len);
1176*14618ebfSMatthias Ringwald                         pos += connection->report_len;
1177*14618ebfSMatthias Ringwald                         l2cap_send_prepared(connection->interrupt_cid, pos);
1178ab30106eSMilanka Ringwald                         break;
1179ab30106eSMilanka Ringwald                     }
1180ab30106eSMilanka Ringwald 
118159a2ea74SMilanka Ringwald                     if (connection->control_tasks != 0){
118259a2ea74SMilanka Ringwald                         l2cap_request_can_send_now_event(connection->control_cid);
118359a2ea74SMilanka Ringwald                     }
118459a2ea74SMilanka Ringwald                     break;
118563bf37cdSMilanka Ringwald                 default:
118663bf37cdSMilanka Ringwald                     break;
118763bf37cdSMilanka Ringwald             }
118863bf37cdSMilanka Ringwald         default:
118963bf37cdSMilanka Ringwald             break;
119063bf37cdSMilanka Ringwald     }
119163bf37cdSMilanka Ringwald }
119263bf37cdSMilanka Ringwald 
1193fd7ba7a6SMilanka Ringwald 
hid_host_init(uint8_t * hid_descriptor_storage,uint16_t hid_descriptor_storage_len)1194fd7ba7a6SMilanka Ringwald void hid_host_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
119563bf37cdSMilanka Ringwald     hid_host_descriptor_storage = hid_descriptor_storage;
119663bf37cdSMilanka Ringwald     hid_host_descriptor_storage_len = hid_descriptor_storage_len;
119763bf37cdSMilanka Ringwald 
119863bf37cdSMilanka Ringwald     // register L2CAP Services for reconnections
1199fd7ba7a6SMilanka Ringwald     l2cap_register_service(hid_host_packet_handler, PSM_HID_INTERRUPT, 0xffff, gap_get_security_level());
1200fd7ba7a6SMilanka Ringwald     l2cap_register_service(hid_host_packet_handler, PSM_HID_CONTROL, 0xffff, gap_get_security_level());
120163bf37cdSMilanka Ringwald }
120263bf37cdSMilanka Ringwald 
hid_host_deinit(void)1203de81b46aSMatthias Ringwald void hid_host_deinit(void){
1204de81b46aSMatthias Ringwald     hid_host_callback = NULL;
1205de81b46aSMatthias Ringwald     hid_host_descriptor_storage = NULL;
1206de81b46aSMatthias Ringwald     hid_host_sdp_context_control_cid = 0;
1207de81b46aSMatthias Ringwald     hid_host_connections = NULL;
1208de81b46aSMatthias Ringwald     hid_host_cid_counter = 0;
1209de81b46aSMatthias Ringwald     (void) memset(&hid_host_handle_sdp_client_query_request, 0, sizeof(hid_host_handle_sdp_client_query_request));
121063bf37cdSMilanka Ringwald }
121163bf37cdSMilanka Ringwald 
hid_host_register_packet_handler(btstack_packet_handler_t callback)1212de81b46aSMatthias Ringwald void hid_host_register_packet_handler(btstack_packet_handler_t callback){
1213de81b46aSMatthias Ringwald     hid_host_callback = callback;
1214de81b46aSMatthias Ringwald }
1215fd7ba7a6SMilanka Ringwald 
hid_host_handle_start_sdp_client_query(void * context)1216fd7ba7a6SMilanka Ringwald static void hid_host_handle_start_sdp_client_query(void * context){
1217fd7ba7a6SMilanka Ringwald     UNUSED(context);
1218fd7ba7a6SMilanka Ringwald     btstack_linked_list_iterator_t it;
1219de81b46aSMatthias Ringwald     btstack_linked_list_iterator_init(&it, &hid_host_connections);
12205961e155SMilanka Ringwald 
1221fd7ba7a6SMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
1222fd7ba7a6SMilanka Ringwald         hid_host_connection_t * connection = (hid_host_connection_t *)btstack_linked_list_iterator_next(&it);
1223fd7ba7a6SMilanka Ringwald 
1224fd7ba7a6SMilanka Ringwald         switch (connection->state){
1225fd7ba7a6SMilanka Ringwald             case HID_HOST_W2_SEND_SDP_QUERY:
1226fd7ba7a6SMilanka Ringwald                 connection->state = HID_HOST_W4_SDP_QUERY_RESULT;
122705439aa6SMilanka Ringwald                 connection->hid_descriptor_status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1228fd7ba7a6SMilanka Ringwald                 break;
1229fd7ba7a6SMilanka Ringwald             default:
1230fd7ba7a6SMilanka Ringwald                 continue;
1231fd7ba7a6SMilanka Ringwald         }
12325961e155SMilanka Ringwald 
1233fd7ba7a6SMilanka Ringwald         hid_descriptor_storage_init(connection);
1234de81b46aSMatthias Ringwald         hid_host_sdp_context_control_cid = connection->hid_cid;
1235fd7ba7a6SMilanka Ringwald         sdp_client_query_uuid16(&hid_host_handle_sdp_client_query_result, (uint8_t *) connection->remote_addr, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE);
1236fd7ba7a6SMilanka Ringwald         return;
1237fd7ba7a6SMilanka Ringwald     }
1238fd7ba7a6SMilanka Ringwald }
1239fd7ba7a6SMilanka Ringwald 
hid_host_accept_connection(uint16_t hid_cid,hid_protocol_mode_t protocol_mode)124001977ed1SMilanka Ringwald uint8_t hid_host_accept_connection(uint16_t hid_cid, hid_protocol_mode_t protocol_mode){
124159a2ea74SMilanka Ringwald     hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid);
124257e682feSMilanka Ringwald     if (!connection){
124357e682feSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
124457e682feSMilanka Ringwald     }
124557e682feSMilanka Ringwald     if (connection->state != HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED){
124657e682feSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
124757e682feSMilanka Ringwald     }
124857e682feSMilanka Ringwald 
124901977ed1SMilanka Ringwald     connection->requested_protocol_mode = protocol_mode;
125059a2ea74SMilanka Ringwald     l2cap_accept_connection(connection->control_cid);
125159a2ea74SMilanka Ringwald     return ERROR_CODE_SUCCESS;
125259a2ea74SMilanka Ringwald }
1253fd7ba7a6SMilanka Ringwald 
hid_host_decline_connection(uint16_t hid_cid)125459a2ea74SMilanka Ringwald uint8_t hid_host_decline_connection(uint16_t hid_cid){
125559a2ea74SMilanka Ringwald     hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid);
125657e682feSMilanka Ringwald     if (!connection){
125757e682feSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
125857e682feSMilanka Ringwald     }
125957e682feSMilanka Ringwald     if (connection->state != HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED){
126057e682feSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
126157e682feSMilanka Ringwald     }
126257e682feSMilanka Ringwald 
126359a2ea74SMilanka Ringwald     l2cap_decline_connection(connection->control_cid);
126459a2ea74SMilanka Ringwald     hid_host_finalize_connection(connection);
126559a2ea74SMilanka Ringwald     return ERROR_CODE_SUCCESS;
126659a2ea74SMilanka Ringwald }
126759a2ea74SMilanka Ringwald 
hid_host_connect(bd_addr_t remote_addr,hid_protocol_mode_t protocol_mode,uint16_t * hid_cid)126859a2ea74SMilanka Ringwald uint8_t hid_host_connect(bd_addr_t remote_addr, hid_protocol_mode_t protocol_mode, uint16_t * hid_cid){
1269fd7ba7a6SMilanka Ringwald     if (hid_cid == NULL) {
1270fd7ba7a6SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1271fd7ba7a6SMilanka Ringwald     }
1272fd7ba7a6SMilanka Ringwald 
1273fd7ba7a6SMilanka Ringwald     hid_host_connection_t * connection = hid_host_get_connection_for_bd_addr(remote_addr);
1274fd7ba7a6SMilanka Ringwald     if (connection){
1275fd7ba7a6SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1276fd7ba7a6SMilanka Ringwald     }
1277fd7ba7a6SMilanka Ringwald 
1278fd7ba7a6SMilanka Ringwald     connection = hid_host_create_connection(remote_addr);
1279fd7ba7a6SMilanka Ringwald     if (!connection) return BTSTACK_MEMORY_ALLOC_FAILED;
1280fd7ba7a6SMilanka Ringwald 
1281fd7ba7a6SMilanka Ringwald     *hid_cid = connection->hid_cid;
1282fd7ba7a6SMilanka Ringwald 
1283fd7ba7a6SMilanka Ringwald     connection->state = HID_HOST_W2_SEND_SDP_QUERY;
1284fd7ba7a6SMilanka Ringwald     connection->incoming = false;
128501977ed1SMilanka Ringwald     connection->requested_protocol_mode = protocol_mode;
12862ba92e70SMilanka Ringwald     connection->hid_descriptor_status = ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1287fd7ba7a6SMilanka Ringwald 
128801977ed1SMilanka Ringwald     uint8_t status = ERROR_CODE_SUCCESS;
128901977ed1SMilanka Ringwald 
12905961e155SMilanka Ringwald     switch (connection->requested_protocol_mode){
129101977ed1SMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
129201977ed1SMilanka Ringwald             connection->state = HID_HOST_W4_CONTROL_CONNECTION_ESTABLISHED;
129305439aa6SMilanka Ringwald             status = l2cap_create_channel(hid_host_packet_handler, connection->remote_addr, BLUETOOTH_PSM_HID_CONTROL, 0xffff, &connection->control_cid);
129401977ed1SMilanka Ringwald             break;
129501977ed1SMilanka Ringwald         default:
1296fd7ba7a6SMilanka Ringwald             hid_host_handle_sdp_client_query_request.callback = &hid_host_handle_start_sdp_client_query;
1297fd7ba7a6SMilanka Ringwald             // ignore ERROR_CODE_COMMAND_DISALLOWED because in that case, we already have requested an SDP callback
1298fd7ba7a6SMilanka Ringwald             (void) sdp_client_register_query_callback(&hid_host_handle_sdp_client_query_request);
129901977ed1SMilanka Ringwald             break;
130001977ed1SMilanka Ringwald     }
130101977ed1SMilanka Ringwald     return status;
130263bf37cdSMilanka Ringwald }
130363bf37cdSMilanka Ringwald 
130463bf37cdSMilanka Ringwald 
hid_host_disconnect(uint16_t hid_cid)130563bf37cdSMilanka Ringwald void hid_host_disconnect(uint16_t hid_cid){
1306ab30106eSMilanka Ringwald     hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid);
1307ab30106eSMilanka Ringwald     if (!connection) return;
1308ab30106eSMilanka Ringwald 
1309ab30106eSMilanka Ringwald     switch (connection->state){
1310ab30106eSMilanka Ringwald         case HID_HOST_IDLE:
1311ab30106eSMilanka Ringwald         case HID_HOST_W4_CONTROL_CONNECTION_DISCONNECTED:
1312ab30106eSMilanka Ringwald         case HID_HOST_W4_INTERRUPT_CONNECTION_DISCONNECTED:
1313ab30106eSMilanka Ringwald             return;
1314ab30106eSMilanka Ringwald         default:
1315ab30106eSMilanka Ringwald             break;
1316ab30106eSMilanka Ringwald     }
1317ab30106eSMilanka Ringwald 
1318ab30106eSMilanka Ringwald     if (connection->interrupt_cid){
1319ab30106eSMilanka Ringwald         connection->state = HID_HOST_W4_INTERRUPT_CONNECTION_DISCONNECTED;
1320b93f8966SMatthias Ringwald         l2cap_disconnect(connection->interrupt_cid);
1321ab30106eSMilanka Ringwald         return;
1322ab30106eSMilanka Ringwald     }
1323ab30106eSMilanka Ringwald 
1324ab30106eSMilanka Ringwald     if (connection->control_cid){
1325ab30106eSMilanka Ringwald         connection->state = HID_HOST_W4_CONTROL_CONNECTION_DISCONNECTED;
1326b93f8966SMatthias Ringwald         l2cap_disconnect(connection->control_cid);
1327ab30106eSMilanka Ringwald         return;
1328ab30106eSMilanka Ringwald     }
132963bf37cdSMilanka Ringwald }
133063bf37cdSMilanka Ringwald 
133159a2ea74SMilanka Ringwald 
hid_host_send_control_message(uint16_t hid_cid,uint8_t control_message_bitmask)133259a2ea74SMilanka Ringwald static inline uint8_t hid_host_send_control_message(uint16_t hid_cid, uint8_t control_message_bitmask){
133359a2ea74SMilanka Ringwald     hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid);
133459a2ea74SMilanka Ringwald     if (!connection || !connection->control_cid) return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
133559a2ea74SMilanka Ringwald 
133659a2ea74SMilanka Ringwald     if (connection->state < HID_HOST_CONTROL_CONNECTION_ESTABLISHED) {
133759a2ea74SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
133863bf37cdSMilanka Ringwald     }
133959a2ea74SMilanka Ringwald     if (connection->state >= HID_HOST_W4_INTERRUPT_CONNECTION_DISCONNECTED){
134059a2ea74SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
134159a2ea74SMilanka Ringwald     }
134259a2ea74SMilanka Ringwald 
134359a2ea74SMilanka Ringwald     connection->control_tasks |= control_message_bitmask;
134459a2ea74SMilanka Ringwald     l2cap_request_can_send_now_event(connection->control_cid);
134559a2ea74SMilanka Ringwald     return ERROR_CODE_SUCCESS;
134659a2ea74SMilanka Ringwald }
134759a2ea74SMilanka Ringwald 
hid_host_send_suspend(uint16_t hid_cid)134859a2ea74SMilanka Ringwald uint8_t hid_host_send_suspend(uint16_t hid_cid){
134959a2ea74SMilanka Ringwald     return hid_host_send_control_message(hid_cid, CONTROL_MESSAGE_BITMASK_SUSPEND);
135059a2ea74SMilanka Ringwald }
135159a2ea74SMilanka Ringwald 
hid_host_send_exit_suspend(uint16_t hid_cid)135259a2ea74SMilanka Ringwald uint8_t hid_host_send_exit_suspend(uint16_t hid_cid){
135359a2ea74SMilanka Ringwald     return hid_host_send_control_message(hid_cid, CONTROL_MESSAGE_BITMASK_EXIT_SUSPEND);
135459a2ea74SMilanka Ringwald }
135559a2ea74SMilanka Ringwald 
hid_host_send_virtual_cable_unplug(uint16_t hid_cid)135659a2ea74SMilanka Ringwald uint8_t hid_host_send_virtual_cable_unplug(uint16_t hid_cid){
135759a2ea74SMilanka Ringwald     return hid_host_send_control_message(hid_cid, CONTROL_MESSAGE_BITMASK_VIRTUAL_CABLE_UNPLUG);
135859a2ea74SMilanka Ringwald }
135959a2ea74SMilanka Ringwald 
hid_host_send_get_report(uint16_t hid_cid,hid_report_type_t report_type,uint16_t report_id)1360*14618ebfSMatthias Ringwald uint8_t hid_host_send_get_report(uint16_t hid_cid,  hid_report_type_t report_type, uint16_t report_id){
1361ab30106eSMilanka Ringwald     hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid);
1362ab30106eSMilanka Ringwald 
1363ab30106eSMilanka Ringwald     if (!connection || !connection->control_cid){
1364ab30106eSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1365ab30106eSMilanka Ringwald     }
1366ab30106eSMilanka Ringwald     if (connection->state != HID_HOST_CONNECTION_ESTABLISHED){
1367ab30106eSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1368ab30106eSMilanka Ringwald     }
1369ab30106eSMilanka Ringwald 
1370ab30106eSMilanka Ringwald     connection->state = HID_HOST_W2_SEND_GET_REPORT;
1371ab30106eSMilanka Ringwald     connection->report_type = report_type;
1372ab30106eSMilanka Ringwald     connection->report_id = report_id;
1373ab30106eSMilanka Ringwald 
1374ab30106eSMilanka Ringwald     l2cap_request_can_send_now_event(connection->control_cid);
1375ab30106eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1376ab30106eSMilanka Ringwald }
1377ab30106eSMilanka Ringwald 
hid_host_send_set_report(uint16_t hid_cid,hid_report_type_t report_type,uint16_t report_id,const uint8_t * report,uint8_t report_len)1378*14618ebfSMatthias Ringwald uint8_t hid_host_send_set_report(uint16_t hid_cid, hid_report_type_t report_type, uint16_t report_id, const uint8_t * report, uint8_t report_len){
1379ab30106eSMilanka Ringwald     hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid);
1380ab30106eSMilanka Ringwald 
1381ab30106eSMilanka Ringwald     if (!connection || !connection->control_cid){
1382ab30106eSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1383ab30106eSMilanka Ringwald     }
1384ab30106eSMilanka Ringwald 
1385ab30106eSMilanka Ringwald     if (connection->state != HID_HOST_CONNECTION_ESTABLISHED){
13864a4b5586SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
13874a4b5586SMilanka Ringwald     }
13884a4b5586SMilanka Ringwald 
13894a4b5586SMilanka Ringwald     if ((l2cap_max_mtu() - 2) < report_len ){
1390ab30106eSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1391ab30106eSMilanka Ringwald     }
1392ab30106eSMilanka Ringwald 
1393ab30106eSMilanka Ringwald     connection->state = HID_HOST_W2_SEND_SET_REPORT;
1394ab30106eSMilanka Ringwald     connection->report_type = report_type;
1395ab30106eSMilanka Ringwald     connection->report_id = report_id;
1396ab30106eSMilanka Ringwald     connection->report = report;
1397ab30106eSMilanka Ringwald     connection->report_len = report_len;
1398ab30106eSMilanka Ringwald 
1399ab30106eSMilanka Ringwald     l2cap_request_can_send_now_event(connection->control_cid);
1400ab30106eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1401ab30106eSMilanka Ringwald }
1402ab30106eSMilanka Ringwald 
hid_host_send_get_protocol(uint16_t hid_cid)1403ab30106eSMilanka Ringwald uint8_t hid_host_send_get_protocol(uint16_t hid_cid){
1404ab30106eSMilanka Ringwald     hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid);
14054a4b5586SMilanka Ringwald     if (!connection || !connection->control_cid){
14064a4b5586SMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
14074a4b5586SMilanka Ringwald     }
14084a4b5586SMilanka Ringwald     if (connection->state != HID_HOST_CONNECTION_ESTABLISHED){
14094a4b5586SMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
14104a4b5586SMilanka Ringwald     }
1411ab30106eSMilanka Ringwald 
1412ab30106eSMilanka Ringwald     connection->state = HID_HOST_W2_SEND_GET_PROTOCOL;
1413ab30106eSMilanka Ringwald     l2cap_request_can_send_now_event(connection->control_cid);
1414ab30106eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1415ab30106eSMilanka Ringwald }
1416ab30106eSMilanka Ringwald 
hid_host_send_set_protocol_mode(uint16_t hid_cid,hid_protocol_mode_t protocol_mode)1417ab30106eSMilanka Ringwald uint8_t hid_host_send_set_protocol_mode(uint16_t hid_cid, hid_protocol_mode_t protocol_mode){
1418ab30106eSMilanka Ringwald     hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid);
1419a93a968fSMilanka Ringwald     if (!connection || !connection->control_cid){
1420a93a968fSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1421a93a968fSMilanka Ringwald     }
142201977ed1SMilanka Ringwald     if (connection->state != HID_HOST_CONNECTION_ESTABLISHED || connection->set_protocol || connection->w4_set_protocol_response){
1423ab30106eSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1424ab30106eSMilanka Ringwald     }
1425ab30106eSMilanka Ringwald 
142601977ed1SMilanka Ringwald     connection->set_protocol = true;
142701977ed1SMilanka Ringwald     connection->requested_protocol_mode = protocol_mode;
1428ab30106eSMilanka Ringwald 
1429ab30106eSMilanka Ringwald     l2cap_request_can_send_now_event(connection->control_cid);
1430ab30106eSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1431ab30106eSMilanka Ringwald }
1432ab30106eSMilanka Ringwald 
1433ab30106eSMilanka Ringwald 
hid_host_send_report(uint16_t hid_cid,uint16_t report_id,const uint8_t * report,uint8_t report_len)1434*14618ebfSMatthias Ringwald uint8_t hid_host_send_report(uint16_t hid_cid, uint16_t report_id, const uint8_t * report, uint8_t report_len){
1435a93a968fSMilanka Ringwald     hid_host_connection_t * connection = hid_host_get_connection_for_hid_cid(hid_cid);
1436a93a968fSMilanka Ringwald     if (!connection || !connection->control_cid || !connection->interrupt_cid) {
1437a93a968fSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1438a93a968fSMilanka Ringwald     }
1439a93a968fSMilanka Ringwald 
1440a93a968fSMilanka Ringwald     if (connection->state < HID_HOST_CONNECTION_ESTABLISHED) {
1441a93a968fSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1442a93a968fSMilanka Ringwald     }
1443a93a968fSMilanka Ringwald     if (connection->state >= HID_HOST_W4_INTERRUPT_CONNECTION_DISCONNECTED){
1444a93a968fSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1445a93a968fSMilanka Ringwald     }
1446a93a968fSMilanka Ringwald 
1447a93a968fSMilanka Ringwald     if ((l2cap_max_mtu() - 2) < report_len ){
1448a93a968fSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1449a93a968fSMilanka Ringwald     }
1450a93a968fSMilanka Ringwald 
1451a93a968fSMilanka Ringwald     connection->state = HID_HOST_W2_SEND_REPORT;
1452fe493a7cSMilanka Ringwald     connection->report_type = HID_REPORT_TYPE_OUTPUT;
1453a93a968fSMilanka Ringwald     connection->report_id = report_id;
1454a93a968fSMilanka Ringwald     connection->report = report;
1455a93a968fSMilanka Ringwald     connection->report_len = report_len;
1456a93a968fSMilanka Ringwald 
1457a93a968fSMilanka Ringwald     l2cap_request_can_send_now_event(connection->interrupt_cid);
1458a93a968fSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1459a93a968fSMilanka Ringwald }
1460