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