xref: /btstack/src/ble/gatt-service/hids_client.c (revision 3322b222ec1b74be99aede76b31280a1aff58786)
1fc975d0eSMilanka Ringwald /*
2fc975d0eSMilanka Ringwald  * Copyright (C) 2021 BlueKitchen GmbH
3fc975d0eSMilanka Ringwald  *
4fc975d0eSMilanka Ringwald  * Redistribution and use in source and binary forms, with or without
5fc975d0eSMilanka Ringwald  * modification, are permitted provided that the following conditions
6fc975d0eSMilanka Ringwald  * are met:
7fc975d0eSMilanka Ringwald  *
8fc975d0eSMilanka Ringwald  * 1. Redistributions of source code must retain the above copyright
9fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer.
10fc975d0eSMilanka Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer in the
12fc975d0eSMilanka Ringwald  *    documentation and/or other materials provided with the distribution.
13fc975d0eSMilanka Ringwald  * 3. Neither the name of the copyright holders nor the names of
14fc975d0eSMilanka Ringwald  *    contributors may be used to endorse or promote products derived
15fc975d0eSMilanka Ringwald  *    from this software without specific prior written permission.
16fc975d0eSMilanka Ringwald  * 4. Any redistribution, use, or modification is done solely for
17fc975d0eSMilanka Ringwald  *    personal benefit and not for any commercial purpose or for
18fc975d0eSMilanka Ringwald  *    monetary gain.
19fc975d0eSMilanka Ringwald  *
20fc975d0eSMilanka Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21fc975d0eSMilanka Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22fc975d0eSMilanka Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23fc975d0eSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24fc975d0eSMilanka Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25fc975d0eSMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26fc975d0eSMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27fc975d0eSMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28fc975d0eSMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29fc975d0eSMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30fc975d0eSMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31fc975d0eSMilanka Ringwald  * SUCH DAMAGE.
32fc975d0eSMilanka Ringwald  *
33fc975d0eSMilanka Ringwald  * Please inquire about commercial licensing options at
34fc975d0eSMilanka Ringwald  * [email protected]
35fc975d0eSMilanka Ringwald  *
36fc975d0eSMilanka Ringwald  */
37fc975d0eSMilanka Ringwald 
38fc975d0eSMilanka Ringwald #define BTSTACK_FILE__ "hids_client.c"
39fc975d0eSMilanka Ringwald 
40fc975d0eSMilanka Ringwald #include "btstack_config.h"
41fc975d0eSMilanka Ringwald 
42fc975d0eSMilanka Ringwald #include <stdint.h>
43fc975d0eSMilanka Ringwald #include <string.h>
44fc975d0eSMilanka Ringwald 
45cf26c8fbSMilanka Ringwald #include "ble/gatt-service/hids_client.h"
46fc975d0eSMilanka Ringwald 
47cf26c8fbSMilanka Ringwald #include "btstack_memory.h"
48fc975d0eSMilanka Ringwald #include "ble/att_db.h"
49fc975d0eSMilanka Ringwald #include "ble/core.h"
50fc975d0eSMilanka Ringwald #include "ble/gatt_client.h"
51fc975d0eSMilanka Ringwald #include "ble/sm.h"
52cf26c8fbSMilanka Ringwald #include "bluetooth_gatt.h"
53fc975d0eSMilanka Ringwald #include "btstack_debug.h"
54fc975d0eSMilanka Ringwald #include "btstack_event.h"
55fc975d0eSMilanka Ringwald #include "btstack_run_loop.h"
56fc975d0eSMilanka Ringwald #include "gap.h"
57fc975d0eSMilanka Ringwald 
58cf26c8fbSMilanka Ringwald static btstack_linked_list_t clients;
59cf26c8fbSMilanka Ringwald static uint16_t hids_cid_counter = 0;
60fc975d0eSMilanka Ringwald 
6170093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
6270093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6370093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6470093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6570093cf5SMilanka Ringwald 
66cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
67cf26c8fbSMilanka Ringwald 
68cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
69cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
70cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
71cf26c8fbSMilanka Ringwald     } else {
72cf26c8fbSMilanka Ringwald         hids_cid_counter++;
73cf26c8fbSMilanka Ringwald     }
74cf26c8fbSMilanka Ringwald     return hids_cid_counter;
75fc975d0eSMilanka Ringwald }
76fc975d0eSMilanka Ringwald 
77ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
78ab116b1cSMilanka Ringwald     uint8_t i;
79a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
80ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
81ab116b1cSMilanka Ringwald             return i;
82ab116b1cSMilanka Ringwald         }
83ab116b1cSMilanka Ringwald     }
84ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
85ab116b1cSMilanka Ringwald }
86ab116b1cSMilanka Ringwald 
87ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_report_id_and_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){
88ab116b1cSMilanka Ringwald     uint8_t i;
89a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
90ab116b1cSMilanka Ringwald         if ( (client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
91ab116b1cSMilanka Ringwald             return i;
92ab116b1cSMilanka Ringwald         }
93ab116b1cSMilanka Ringwald     }
94ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
95ab116b1cSMilanka Ringwald }
96ab116b1cSMilanka Ringwald 
97ab116b1cSMilanka Ringwald static hids_client_report_t * find_report_for_report_id_and_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){
98ab116b1cSMilanka Ringwald     uint8_t i;
99a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
100ab116b1cSMilanka Ringwald         if ( (client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
101ab116b1cSMilanka Ringwald             return &client->reports[i];
102ab116b1cSMilanka Ringwald         }
103ab116b1cSMilanka Ringwald     }
104ab116b1cSMilanka Ringwald     return NULL;
105ab116b1cSMilanka Ringwald }
106ab116b1cSMilanka Ringwald 
107ab116b1cSMilanka Ringwald static hids_client_report_t * get_boot_mouse_input_report(hids_client_t * client){
108ab116b1cSMilanka Ringwald     return find_report_for_report_id_and_type(client, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT);
109ab116b1cSMilanka Ringwald }
110ab116b1cSMilanka Ringwald static hids_client_report_t * get_boot_keyboard_input_report(hids_client_t * client){
111ab116b1cSMilanka Ringwald     return find_report_for_report_id_and_type(client, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT);
112ab116b1cSMilanka Ringwald }
113ab116b1cSMilanka Ringwald 
114*3322b222SMilanka Ringwald static uint8_t hids_client_add_characteristic(hids_client_t * client, gatt_client_characteristic_t * characteristic, uint8_t report_id, hid_report_type_t report_type){
115ab116b1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, characteristic->value_handle);
116ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
117*3322b222SMilanka Ringwald         return report_index;
118ab116b1cSMilanka Ringwald     }
11928da36a6SMilanka Ringwald     report_index = client->num_reports;
120ab116b1cSMilanka Ringwald 
12128da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
12228da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
12328da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
12428da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
125ab116b1cSMilanka Ringwald 
12628da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
12728da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
12828da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
12970093cf5SMilanka Ringwald 
130*3322b222SMilanka Ringwald         // log_info("add index %d, id %d, type %d, value handle 0x%02x", report_index, report_id, report_type, characteristic->value_handle);
13128da36a6SMilanka Ringwald         log_info("add index %d, id %d, type %d, value handle 0x%02x", report_index, report_id, report_type, characteristic->value_handle);
132ab116b1cSMilanka Ringwald         client->num_reports++;
133*3322b222SMilanka Ringwald         return report_index;
134ab116b1cSMilanka Ringwald     } else {
135ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
136*3322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
137ab116b1cSMilanka Ringwald     }
138ab116b1cSMilanka Ringwald }
139ab116b1cSMilanka Ringwald 
14070093cf5SMilanka Ringwald static void hids_client_get_characteristic_for_report_index(hids_client_t * client, uint8_t report_index, gatt_client_characteristic_t * characteristic){
14170093cf5SMilanka Ringwald     characteristic->value_handle = client->reports[report_index].value_handle;
14270093cf5SMilanka Ringwald     characteristic->end_handle = client->reports[report_index].end_handle;
14370093cf5SMilanka Ringwald     characteristic->properties = client->reports[report_index].properties;
14470093cf5SMilanka Ringwald }
14570093cf5SMilanka Ringwald 
146ab116b1cSMilanka Ringwald static uint8_t hids_client_get_characteristic(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type, gatt_client_characteristic_t * characteristic){
147ab116b1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, report_type);
148ab116b1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
149ab116b1cSMilanka Ringwald         return report_index;
150ab116b1cSMilanka Ringwald     }
15170093cf5SMilanka Ringwald     hids_client_get_characteristic_for_report_index(client, report_index, characteristic);
152ab116b1cSMilanka Ringwald     return report_index;
153ab116b1cSMilanka Ringwald }
154ab116b1cSMilanka Ringwald 
1557e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
1567e1e6e7dSMilanka Ringwald     uint8_t i;
1577e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
158*3322b222SMilanka Ringwald 
159*3322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
160*3322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
161*3322b222SMilanka Ringwald             index = i;
162*3322b222SMilanka Ringwald             break;
163*3322b222SMilanka Ringwald         }
164*3322b222SMilanka Ringwald     }
165*3322b222SMilanka Ringwald     client->service_index = index;
166*3322b222SMilanka Ringwald     return index;
167*3322b222SMilanka Ringwald }
168*3322b222SMilanka Ringwald 
169*3322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
170*3322b222SMilanka Ringwald     client->service_index++;
171*3322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
172*3322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
173*3322b222SMilanka Ringwald         return true;
174*3322b222SMilanka Ringwald     }
175*3322b222SMilanka Ringwald     return false;
176*3322b222SMilanka Ringwald }
177*3322b222SMilanka Ringwald 
178*3322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
179*3322b222SMilanka Ringwald     client->service_index = 0;
180*3322b222SMilanka Ringwald 
181*3322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
182*3322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
183*3322b222SMilanka Ringwald         return true;
184*3322b222SMilanka Ringwald     }
185*3322b222SMilanka Ringwald     return false;
186*3322b222SMilanka Ringwald }
187*3322b222SMilanka Ringwald 
188*3322b222SMilanka Ringwald 
189*3322b222SMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_uuid_index(hids_client_t * client){
190*3322b222SMilanka Ringwald     uint8_t i;
191*3322b222SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
192*3322b222SMilanka Ringwald 
193ef102a9eSMilanka Ringwald     for (i = client->active_index; i < client->num_reports; i++){
194e7bd2dbeSMilanka Ringwald         hids_client_report_t report = client->reports[i];
195e7bd2dbeSMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_MAP_ID){
1967e1e6e7dSMilanka Ringwald             index = i;
1977e1e6e7dSMilanka Ringwald             break;
198e7bd2dbeSMilanka Ringwald         }
1997e1e6e7dSMilanka Ringwald     }
2007e1e6e7dSMilanka Ringwald     client->active_index = index;
2017e1e6e7dSMilanka Ringwald     return index;
2027e1e6e7dSMilanka Ringwald }
2037e1e6e7dSMilanka Ringwald 
204*3322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
2057e1e6e7dSMilanka Ringwald     client->active_index++;
206*3322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_uuid_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
207*3322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
208e7bd2dbeSMilanka Ringwald         return true;
209e7bd2dbeSMilanka Ringwald     }
210e7bd2dbeSMilanka Ringwald     return false;
211e7bd2dbeSMilanka Ringwald }
212e7bd2dbeSMilanka Ringwald 
213*3322b222SMilanka Ringwald 
214*3322b222SMilanka Ringwald 
215*3322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
2167e1e6e7dSMilanka Ringwald     client->active_index = 0;
21770093cf5SMilanka Ringwald 
218*3322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_uuid_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
219*3322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
2207e1e6e7dSMilanka Ringwald         return true;
2217e1e6e7dSMilanka Ringwald     }
2227e1e6e7dSMilanka Ringwald     return false;
2237e1e6e7dSMilanka Ringwald }
2247e1e6e7dSMilanka Ringwald 
2257e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
2267e1e6e7dSMilanka Ringwald     uint8_t i;
2277e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
228ef102a9eSMilanka Ringwald     for (i = client->active_index; i < client->num_reports; i++){
22970093cf5SMilanka Ringwald         hids_client_report_t report = client->reports[i];
23070093cf5SMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
2317e1e6e7dSMilanka Ringwald             index = i;
2327e1e6e7dSMilanka Ringwald             break;
23370093cf5SMilanka Ringwald         }
2347e1e6e7dSMilanka Ringwald     }
2357e1e6e7dSMilanka Ringwald     client->active_index = index;
2367e1e6e7dSMilanka Ringwald     return index;
2377e1e6e7dSMilanka Ringwald }
2387e1e6e7dSMilanka Ringwald 
2397e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
2407e1e6e7dSMilanka Ringwald     client->active_index++;
2417e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
24228da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
243e7bd2dbeSMilanka Ringwald         return true;
24470093cf5SMilanka Ringwald     }
2457e1e6e7dSMilanka Ringwald     return false;
2467e1e6e7dSMilanka Ringwald }
2477e1e6e7dSMilanka Ringwald 
2487e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
2497e1e6e7dSMilanka Ringwald     client->service_index = 0;
2507e1e6e7dSMilanka Ringwald     client->active_index = 0;
2517e1e6e7dSMilanka Ringwald 
2527e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
25328da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
2547e1e6e7dSMilanka Ringwald         return true;
2557e1e6e7dSMilanka Ringwald     }
256e7bd2dbeSMilanka Ringwald     return false;
25770093cf5SMilanka Ringwald }
258ab116b1cSMilanka Ringwald 
259cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
260cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
261cf26c8fbSMilanka Ringwald     if (!client){
262cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
263cf26c8fbSMilanka Ringwald         return NULL;
264cf26c8fbSMilanka Ringwald     }
265cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
266cf26c8fbSMilanka Ringwald     client->cid = cid;
267cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
268fc975d0eSMilanka Ringwald 
269cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
270cf26c8fbSMilanka Ringwald     return client;
271fc975d0eSMilanka Ringwald }
272fc975d0eSMilanka Ringwald 
273cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
274cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
275cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
276fc975d0eSMilanka Ringwald }
277cf26c8fbSMilanka Ringwald 
278cf26c8fbSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
279cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_t it;
280cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
281cf26c8fbSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
282cf26c8fbSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
283cf26c8fbSMilanka Ringwald         if (client->con_handle != con_handle) continue;
284cf26c8fbSMilanka Ringwald         return client;
285cf26c8fbSMilanka Ringwald     }
286cf26c8fbSMilanka Ringwald     return NULL;
287cf26c8fbSMilanka Ringwald }
288cf26c8fbSMilanka Ringwald 
289cf26c8fbSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
290cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_t it;
291cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
292cf26c8fbSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
293cf26c8fbSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
294cf26c8fbSMilanka Ringwald         if (client->cid != hids_cid) continue;
295cf26c8fbSMilanka Ringwald         return client;
296cf26c8fbSMilanka Ringwald     }
297cf26c8fbSMilanka Ringwald     return NULL;
298cf26c8fbSMilanka Ringwald }
299cf26c8fbSMilanka Ringwald 
300cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
3016bcfb631SMilanka Ringwald     uint8_t event[8];
302cf26c8fbSMilanka Ringwald     int pos = 0;
303cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
304cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
305cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
306cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
307cf26c8fbSMilanka Ringwald     pos += 2;
308cf26c8fbSMilanka Ringwald     event[pos++] = status;
3096bcfb631SMilanka Ringwald     event[pos++] = client->protocol_mode;
310cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
311cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
312cf26c8fbSMilanka Ringwald }
313cf26c8fbSMilanka Ringwald 
314cf26c8fbSMilanka Ringwald 
315cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
316cf26c8fbSMilanka Ringwald     uint8_t att_status;
3176bcfb631SMilanka Ringwald     gatt_client_service_t service;
3186bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
319ab116b1cSMilanka Ringwald     uint8_t report_index;
320cf26c8fbSMilanka Ringwald 
321cf26c8fbSMilanka Ringwald     switch (client->state){
322cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
323cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
324cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
325cf26c8fbSMilanka Ringwald             // TODO handle status
326cf26c8fbSMilanka Ringwald             UNUSED(att_status);
327cf26c8fbSMilanka Ringwald             break;
328cf26c8fbSMilanka Ringwald 
329cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
3306bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
3316bcfb631SMilanka Ringwald 
3326bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
3336bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
3346bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
3356bcfb631SMilanka Ringwald 
3366bcfb631SMilanka Ringwald             UNUSED(att_status);
3376bcfb631SMilanka Ringwald             break;
3386bcfb631SMilanka Ringwald 
3392901a9b7SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD:
3402901a9b7SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED;
3412901a9b7SMilanka Ringwald 
342ab116b1cSMilanka Ringwald             report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, &characteristic);
343ab116b1cSMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
344ab116b1cSMilanka Ringwald                 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
345ab116b1cSMilanka Ringwald             }
346ab116b1cSMilanka Ringwald 
347ab116b1cSMilanka Ringwald             if ((report_index == HIDS_CLIENT_INVALID_REPORT_INDEX) || (att_status != ERROR_CODE_SUCCESS)){
348ab116b1cSMilanka Ringwald                 client->reports[report_index].value_handle = 0;
349ab116b1cSMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
3502901a9b7SMilanka Ringwald             }
3516bcfb631SMilanka Ringwald             break;
3526bcfb631SMilanka Ringwald 
3532901a9b7SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_MOUSE:
3542901a9b7SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED;
3552901a9b7SMilanka Ringwald 
356ab116b1cSMilanka Ringwald             report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, &characteristic);
357ab116b1cSMilanka Ringwald 
358ab116b1cSMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
359ab116b1cSMilanka Ringwald                 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
3602901a9b7SMilanka Ringwald             }
361ab116b1cSMilanka Ringwald 
362ab116b1cSMilanka Ringwald             if ((report_index == HIDS_CLIENT_INVALID_REPORT_INDEX) || (att_status != ERROR_CODE_SUCCESS)){
363ab116b1cSMilanka Ringwald                 client->reports[report_index].value_handle = 0;
364ab116b1cSMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
365ab116b1cSMilanka Ringwald             }
366ab116b1cSMilanka Ringwald 
3676bcfb631SMilanka Ringwald             break;
3686bcfb631SMilanka Ringwald 
3692901a9b7SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE:
3702901a9b7SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE;
3712901a9b7SMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
3726bcfb631SMilanka Ringwald             UNUSED(att_status);
3732901a9b7SMilanka Ringwald 
3742901a9b7SMilanka Ringwald             client->protocol_mode = client->required_protocol_mode;
3756bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
3766bcfb631SMilanka Ringwald             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
377cf26c8fbSMilanka Ringwald             break;
378cf26c8fbSMilanka Ringwald 
3791624214bSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_REPORT:{
3801624214bSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
3811624214bSMilanka Ringwald 
3821624214bSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
383ef102a9eSMilanka Ringwald                 client->reports[client->active_index].value_handle,
3841624214bSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
3851624214bSMilanka Ringwald             UNUSED(att_status);
3861624214bSMilanka Ringwald             break;
3871624214bSMilanka Ringwald         }
38870093cf5SMilanka Ringwald 
38928da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
39028da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
391*3322b222SMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(&handle_gatt_client_event, client->con_handle, client->services[client->service_index].report_map_value_handle);
392e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
393e7bd2dbeSMilanka Ringwald             break;
394e7bd2dbeSMilanka Ringwald 
395e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
396e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
397e7bd2dbeSMilanka Ringwald 
398*3322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
399*3322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
400*3322b222SMilanka Ringwald 
401e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
402e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
403e7bd2dbeSMilanka Ringwald             break;
404e7bd2dbeSMilanka Ringwald 
40528da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
40628da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
407e7bd2dbeSMilanka Ringwald 
408*3322b222SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->reports[client->active_index].value_handle);
409e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
410e7bd2dbeSMilanka Ringwald             break;
411e7bd2dbeSMilanka Ringwald 
412*3322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
413*3322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
414e7bd2dbeSMilanka Ringwald 
415*3322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
416*3322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
417*3322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
418e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
419e7bd2dbeSMilanka Ringwald             break;
420e7bd2dbeSMilanka Ringwald 
42128da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
42228da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
42370093cf5SMilanka Ringwald             client->descriptor_handle = 0;
424ef102a9eSMilanka Ringwald             hids_client_get_characteristic_for_report_index(client, client->active_index, &characteristic);
42570093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
42670093cf5SMilanka Ringwald             UNUSED(att_status);
42770093cf5SMilanka Ringwald             break;
42870093cf5SMilanka Ringwald 
42928da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
43028da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
43170093cf5SMilanka Ringwald 
43270093cf5SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
43370093cf5SMilanka Ringwald             client->descriptor_handle = 0;
43470093cf5SMilanka Ringwald             UNUSED(att_status);
43570093cf5SMilanka Ringwald             break;
43670093cf5SMilanka Ringwald 
4376bcfb631SMilanka Ringwald         default:
4386bcfb631SMilanka Ringwald             break;
4396bcfb631SMilanka Ringwald     }
4406bcfb631SMilanka Ringwald }
4416bcfb631SMilanka Ringwald 
44272a8858fSMilanka Ringwald static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_id, uint8_t *buffer, uint16_t report_len){
44372a8858fSMilanka Ringwald     uint16_t pos = 0;
44472a8858fSMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
44572a8858fSMilanka Ringwald     pos++;  // skip len
44672a8858fSMilanka Ringwald     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
44772a8858fSMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
44872a8858fSMilanka Ringwald     pos += 2;
44972a8858fSMilanka Ringwald     buffer[pos++] = report_id;
45072a8858fSMilanka Ringwald     little_endian_store_16(buffer, pos, report_len);
45172a8858fSMilanka Ringwald     pos += 2;
45272a8858fSMilanka Ringwald     buffer[1] = pos + report_len - 2;
45372a8858fSMilanka Ringwald }
45472a8858fSMilanka Ringwald 
45572a8858fSMilanka Ringwald static void handle_boot_keyboard_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
4566bcfb631SMilanka Ringwald     UNUSED(packet_type);
4576bcfb631SMilanka Ringwald     UNUSED(channel);
4586bcfb631SMilanka Ringwald     UNUSED(size);
45972a8858fSMilanka Ringwald 
46072a8858fSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
46172a8858fSMilanka Ringwald 
46272a8858fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
46372a8858fSMilanka Ringwald     btstack_assert(client != NULL);
46472a8858fSMilanka Ringwald 
46572a8858fSMilanka Ringwald     uint8_t * in_place_event = packet;
46672a8858fSMilanka Ringwald     hids_client_setup_report_event(client, HID_BOOT_MODE_KEYBOARD_ID, in_place_event, gatt_event_notification_get_value_length(packet));
46772a8858fSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size);
46872a8858fSMilanka Ringwald }
46972a8858fSMilanka Ringwald 
47072a8858fSMilanka Ringwald static void handle_boot_mouse_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
47172a8858fSMilanka Ringwald     UNUSED(packet_type);
47272a8858fSMilanka Ringwald     UNUSED(channel);
47372a8858fSMilanka Ringwald     UNUSED(size);
47472a8858fSMilanka Ringwald 
47572a8858fSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
47672a8858fSMilanka Ringwald 
47772a8858fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
47872a8858fSMilanka Ringwald     btstack_assert(client != NULL);
47972a8858fSMilanka Ringwald 
48072a8858fSMilanka Ringwald     uint8_t * in_place_event = packet;
48172a8858fSMilanka Ringwald     hids_client_setup_report_event(client, HID_BOOT_MODE_MOUSE_ID, in_place_event, gatt_event_notification_get_value_length(packet));
48272a8858fSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size);
483cf26c8fbSMilanka Ringwald }
484cf26c8fbSMilanka Ringwald 
4851624214bSMilanka Ringwald 
486cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
487cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
488cf26c8fbSMilanka Ringwald     UNUSED(channel);
489cf26c8fbSMilanka Ringwald     UNUSED(size);
490cf26c8fbSMilanka Ringwald 
4916bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
492cf26c8fbSMilanka Ringwald     uint8_t att_status;
4936bcfb631SMilanka Ringwald     gatt_client_service_t service;
4946bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
49570093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
496ab116b1cSMilanka Ringwald 
497ab116b1cSMilanka Ringwald     hids_client_report_t * boot_keyboard_report;
498ab116b1cSMilanka Ringwald     hids_client_report_t * boot_mouse_report;
499e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
500*3322b222SMilanka Ringwald     uint8_t i;
501*3322b222SMilanka Ringwald     uint8_t report_index;
502cf26c8fbSMilanka Ringwald 
503cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
504cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
505cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
506cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
507cf26c8fbSMilanka Ringwald 
5086bcfb631SMilanka Ringwald             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
5096bcfb631SMilanka Ringwald                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
5106bcfb631SMilanka Ringwald                 hids_finalize_client(client);
5116bcfb631SMilanka Ringwald                 break;
5126bcfb631SMilanka Ringwald             }
5136bcfb631SMilanka Ringwald 
5146bcfb631SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
5156bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
5166bcfb631SMilanka Ringwald                 client->services[client->num_instances].start_handle = service.start_group_handle;
5176bcfb631SMilanka Ringwald                 client->services[client->num_instances].end_handle = service.end_group_handle;
5186bcfb631SMilanka Ringwald             }
5196bcfb631SMilanka Ringwald             client->num_instances++;
5206bcfb631SMilanka Ringwald             break;
5216bcfb631SMilanka Ringwald 
5226bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
5236bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
5246bcfb631SMilanka Ringwald             btstack_assert(client != NULL);
5256bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
526*3322b222SMilanka Ringwald 
527*3322b222SMilanka Ringwald             switch (client->state){
528*3322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
529*3322b222SMilanka Ringwald                     // update value handle od external reports, set type to reserved so that will be inlcuded in ID,TYPE update in hte next step
530*3322b222SMilanka Ringwald                     for (i = 0; i < client->num_reports; i++){
531*3322b222SMilanka Ringwald                         if (client->reports[i].external_report_reference_uuid == characteristic.uuid16){
532*3322b222SMilanka Ringwald                             client->reports[i].report_id = HID_REPORT_MODE_REPORT_ID;
533*3322b222SMilanka Ringwald                             client->reports[i].report_type = HID_REPORT_TYPE_RESERVED;
534*3322b222SMilanka Ringwald                             client->reports[i].properties = characteristic.properties;
535*3322b222SMilanka Ringwald                             client->reports[i].value_handle = characteristic.value_handle;
536*3322b222SMilanka Ringwald                             client->reports[i].end_handle = characteristic.end_handle;
537*3322b222SMilanka Ringwald                         }
538*3322b222SMilanka Ringwald                     }
539*3322b222SMilanka Ringwald                     break;
540*3322b222SMilanka Ringwald 
541*3322b222SMilanka Ringwald                 default:
5426bcfb631SMilanka Ringwald                     switch (characteristic.uuid16){
54370093cf5SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
54470093cf5SMilanka Ringwald                             client->protocol_mode_value_handle = characteristic.value_handle;
54570093cf5SMilanka Ringwald                             break;
54670093cf5SMilanka Ringwald 
5476bcfb631SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
548ab116b1cSMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT);
5496bcfb631SMilanka Ringwald                             break;
5501624214bSMilanka Ringwald 
5516bcfb631SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
552ab116b1cSMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT);
5536bcfb631SMilanka Ringwald                             break;
5541624214bSMilanka Ringwald 
5551624214bSMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
556ab116b1cSMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT);
5571624214bSMilanka Ringwald                             break;
5581624214bSMilanka Ringwald 
55970093cf5SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
56070093cf5SMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED);
56170093cf5SMilanka Ringwald                             break;
56270093cf5SMilanka Ringwald 
56370093cf5SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
564*3322b222SMilanka Ringwald                             client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
565*3322b222SMilanka Ringwald                             client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
56670093cf5SMilanka Ringwald                             break;
56770093cf5SMilanka Ringwald 
56870093cf5SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
56970093cf5SMilanka Ringwald                             // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION\n");
57070093cf5SMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_INFORMATION_ID, HID_REPORT_TYPE_INPUT);
57170093cf5SMilanka Ringwald                             break;
57270093cf5SMilanka Ringwald 
57370093cf5SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
57470093cf5SMilanka Ringwald                             // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT\n");
57570093cf5SMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_CONTROL_POINT_ID, HID_REPORT_TYPE_INPUT);
57670093cf5SMilanka Ringwald                             break;
5776bcfb631SMilanka Ringwald                         default:
5786bcfb631SMilanka Ringwald                             break;
5796bcfb631SMilanka Ringwald                 }
580*3322b222SMilanka Ringwald             }
581*3322b222SMilanka Ringwald 
582cf26c8fbSMilanka Ringwald             break;
583cf26c8fbSMilanka Ringwald 
584e7bd2dbeSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
585e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
586e7bd2dbeSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
587e7bd2dbeSMilanka Ringwald             btstack_assert(client != NULL);
588e7bd2dbeSMilanka Ringwald             // TODO get value and store it
589*3322b222SMilanka Ringwald             // printf("HID descriptor found\n");
590e7bd2dbeSMilanka Ringwald             break;
591e7bd2dbeSMilanka Ringwald 
59270093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
59370093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
59470093cf5SMilanka Ringwald             btstack_assert(client != NULL);
59570093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
59670093cf5SMilanka Ringwald 
597e7bd2dbeSMilanka Ringwald             switch (client->state) {
598e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
599e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
600e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
601*3322b222SMilanka Ringwald                         report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_MAP_ID, HID_REPORT_TYPE_RESERVED);
602*3322b222SMilanka Ringwald 
603*3322b222SMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
604*3322b222SMilanka Ringwald                             client->reports[report_index].value_handle = characteristic_descriptor.handle;
605*3322b222SMilanka Ringwald                             client->reports[report_index].service_index = client->service_index;
606*3322b222SMilanka Ringwald                         }
607*3322b222SMilanka Ringwald 
608e7bd2dbeSMilanka Ringwald                     }
609e7bd2dbeSMilanka Ringwald                     break;
61028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
61170093cf5SMilanka Ringwald                     // setup for descriptor value query
61270093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
61370093cf5SMilanka Ringwald                         client->descriptor_handle = characteristic_descriptor.handle;
61470093cf5SMilanka Ringwald                     }
61570093cf5SMilanka Ringwald                     break;
616e7bd2dbeSMilanka Ringwald                 default:
617e7bd2dbeSMilanka Ringwald                     break;
618e7bd2dbeSMilanka Ringwald             }
619e7bd2dbeSMilanka Ringwald             break;
62070093cf5SMilanka Ringwald 
62170093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
62270093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
62370093cf5SMilanka Ringwald             btstack_assert(client != NULL);
624e7bd2dbeSMilanka Ringwald 
625e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
626e7bd2dbeSMilanka Ringwald                 break;
627e7bd2dbeSMilanka Ringwald             }
6287e1e6e7dSMilanka Ringwald 
629e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
630e7bd2dbeSMilanka Ringwald             switch (client->state) {
63128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
632e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
633*3322b222SMilanka Ringwald                     report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
634*3322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
635*3322b222SMilanka Ringwald                         client->reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
636*3322b222SMilanka Ringwald                     }
637e7bd2dbeSMilanka Ringwald                     break;
638e7bd2dbeSMilanka Ringwald 
63928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
640ef102a9eSMilanka Ringwald                     client->reports[client->active_index].report_id = characteristic_descriptor_value[0];
641ef102a9eSMilanka Ringwald                     client->reports[client->active_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
64228da36a6SMilanka Ringwald                     log_info("update index %d, id %d, type %d, value handle 0x%02x",
64328da36a6SMilanka Ringwald                         client->active_index,
64428da36a6SMilanka Ringwald                         client->reports[client->active_index].report_id,
64528da36a6SMilanka Ringwald                         client->reports[client->active_index].report_type,
64628da36a6SMilanka Ringwald                         client->reports[client->active_index].value_handle);
6477e1e6e7dSMilanka Ringwald 
648e7bd2dbeSMilanka Ringwald                     break;
649e7bd2dbeSMilanka Ringwald 
650e7bd2dbeSMilanka Ringwald                 default:
651e7bd2dbeSMilanka Ringwald                     break;
65270093cf5SMilanka Ringwald             }
65370093cf5SMilanka Ringwald             break;
65470093cf5SMilanka Ringwald 
655cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
656cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
657cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
658cf26c8fbSMilanka Ringwald 
659cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
660cf26c8fbSMilanka Ringwald 
661cf26c8fbSMilanka Ringwald             switch (client->state){
662cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
6636bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
6646bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
6656bcfb631SMilanka Ringwald                         hids_finalize_client(client);
666cf26c8fbSMilanka Ringwald                         break;
6676bcfb631SMilanka Ringwald                     }
6686bcfb631SMilanka Ringwald 
6696bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
6706bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
6716bcfb631SMilanka Ringwald                         hids_finalize_client(client);
6726bcfb631SMilanka Ringwald                         break;
6736bcfb631SMilanka Ringwald                     }
6746bcfb631SMilanka Ringwald 
6756bcfb631SMilanka Ringwald                     if (client->num_instances > MAX_NUM_HID_SERVICES) {
6766bcfb631SMilanka Ringwald                         log_info("%d hid services found, only first %d can be stored, increase MAX_NUM_HID_SERVICES", client->num_instances, MAX_NUM_HID_SERVICES);
6776bcfb631SMilanka Ringwald                         client->num_instances = MAX_NUM_HID_SERVICES;
6786bcfb631SMilanka Ringwald                     }
6796bcfb631SMilanka Ringwald 
6806bcfb631SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
6816bcfb631SMilanka Ringwald                     client->service_index = 0;
6826bcfb631SMilanka Ringwald                     break;
6836bcfb631SMilanka Ringwald 
6846bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
6856bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
6866bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
6876bcfb631SMilanka Ringwald                         hids_finalize_client(client);
6886bcfb631SMilanka Ringwald                         break;
6896bcfb631SMilanka Ringwald                     }
6906bcfb631SMilanka Ringwald 
6916bcfb631SMilanka Ringwald                     switch (client->required_protocol_mode){
6926bcfb631SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
693ab116b1cSMilanka Ringwald                             if (get_boot_keyboard_input_report(client) != NULL){
6942901a9b7SMilanka Ringwald                                 client->state = HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD;
6956bcfb631SMilanka Ringwald                                 break;
6966bcfb631SMilanka Ringwald                             }
697ab116b1cSMilanka Ringwald                             if (get_boot_mouse_input_report(client) != NULL){
6982901a9b7SMilanka Ringwald                                 client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE;
6996bcfb631SMilanka Ringwald                                 break;
7006bcfb631SMilanka Ringwald                             }
7016bcfb631SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
7026bcfb631SMilanka Ringwald                             hids_finalize_client(client);
7036bcfb631SMilanka Ringwald                             break;
7047e1e6e7dSMilanka Ringwald 
70570093cf5SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
70670093cf5SMilanka Ringwald                             if ((client->service_index + 1) < client->num_instances){
70770093cf5SMilanka Ringwald                                 // discover characteristics of next service
70870093cf5SMilanka Ringwald                                 client->service_index++;
70970093cf5SMilanka Ringwald                                 client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
71070093cf5SMilanka Ringwald                             } else {
711*3322b222SMilanka Ringwald 
7127e1e6e7dSMilanka Ringwald                                 // 1. we need to get HID Descriptor and
7137e1e6e7dSMilanka Ringwald                                 // 2. get external Report characteristics if referenced from Report Map
7147e1e6e7dSMilanka Ringwald                                 if (hids_client_report_map_query_init(client)){
71570093cf5SMilanka Ringwald                                     break;
71670093cf5SMilanka Ringwald                                 }
71770093cf5SMilanka Ringwald                                 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
71870093cf5SMilanka Ringwald                                 hids_finalize_client(client);
71970093cf5SMilanka Ringwald                             }
72070093cf5SMilanka Ringwald                             break;
7216bcfb631SMilanka Ringwald                         default:
7226bcfb631SMilanka Ringwald                             break;
7236bcfb631SMilanka Ringwald                     }
7246bcfb631SMilanka Ringwald                     break;
7256bcfb631SMilanka Ringwald 
726*3322b222SMilanka Ringwald 
72728da36a6SMilanka Ringwald                 // HID descriptor found
72828da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
729e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
730e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
731e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
732e7bd2dbeSMilanka Ringwald                         break;
733e7bd2dbeSMilanka Ringwald                     }
734e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
735e7bd2dbeSMilanka Ringwald                     break;
736e7bd2dbeSMilanka Ringwald 
73728da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
738e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
739*3322b222SMilanka Ringwald                     // go for next report map
740*3322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
741e7bd2dbeSMilanka Ringwald                         break;
742e7bd2dbeSMilanka Ringwald                     }
743e7bd2dbeSMilanka Ringwald 
744*3322b222SMilanka Ringwald                     // read UUIDS for external characteristics
745*3322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
746e7bd2dbeSMilanka Ringwald                         break;
747e7bd2dbeSMilanka Ringwald                     }
748e7bd2dbeSMilanka Ringwald 
749e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
750e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
7517e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
752e7bd2dbeSMilanka Ringwald                         break;
753e7bd2dbeSMilanka Ringwald                     }
754e7bd2dbeSMilanka Ringwald 
755e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
756e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
757e7bd2dbeSMilanka Ringwald                     break;
758e7bd2dbeSMilanka Ringwald 
75928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
760e7bd2dbeSMilanka Ringwald                     // go for next map report
761*3322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
762e7bd2dbeSMilanka Ringwald                         break;
763e7bd2dbeSMilanka Ringwald                     }
764e7bd2dbeSMilanka Ringwald 
765*3322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
766*3322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
767e7bd2dbeSMilanka Ringwald                     break;
768e7bd2dbeSMilanka Ringwald 
769*3322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
770e7bd2dbeSMilanka Ringwald 
771e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
772e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
7737e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
774e7bd2dbeSMilanka Ringwald                         break;
775e7bd2dbeSMilanka Ringwald                     }
776e7bd2dbeSMilanka Ringwald 
777e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
778e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
779e7bd2dbeSMilanka Ringwald                     break;
780e7bd2dbeSMilanka Ringwald 
78128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
78270093cf5SMilanka Ringwald                     if (client->descriptor_handle != 0){
78370093cf5SMilanka Ringwald                         // descriptor found
78428da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
78570093cf5SMilanka Ringwald                         break;
78670093cf5SMilanka Ringwald                     }
78770093cf5SMilanka Ringwald 
78870093cf5SMilanka Ringwald                     // go for next report
7897e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
79070093cf5SMilanka Ringwald                         break;
79170093cf5SMilanka Ringwald                     }
79270093cf5SMilanka Ringwald 
79370093cf5SMilanka Ringwald                     // TODO continue with report mode
79470093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
79570093cf5SMilanka Ringwald                     break;
79670093cf5SMilanka Ringwald 
79728da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
79870093cf5SMilanka Ringwald                     // go for next report
7997e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
80070093cf5SMilanka Ringwald                         break;
80170093cf5SMilanka Ringwald                     }
80270093cf5SMilanka Ringwald 
80370093cf5SMilanka Ringwald                     // TODO continue with report mode
80470093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
80570093cf5SMilanka Ringwald                     break;
80670093cf5SMilanka Ringwald 
8076bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED:
808ab116b1cSMilanka Ringwald                     boot_keyboard_report = get_boot_keyboard_input_report(client);
809ab116b1cSMilanka Ringwald                     if (boot_keyboard_report != NULL){
8106bcfb631SMilanka Ringwald                         // setup listener
811ab116b1cSMilanka Ringwald                         characteristic.value_handle = boot_keyboard_report->value_handle;
812ab116b1cSMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(&boot_keyboard_report->notifications, &handle_boot_keyboard_hid_event, client->con_handle, &characteristic);
8132901a9b7SMilanka Ringwald                     }
8142901a9b7SMilanka Ringwald 
815ab116b1cSMilanka Ringwald                     // check if there is mouse input report
816ab116b1cSMilanka Ringwald                     boot_mouse_report = get_boot_mouse_input_report(client);
817ab116b1cSMilanka Ringwald                     if (boot_mouse_report != NULL){
8182901a9b7SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE;
8192901a9b7SMilanka Ringwald                         break;
8202901a9b7SMilanka Ringwald                     }
8212901a9b7SMilanka Ringwald 
822ab116b1cSMilanka Ringwald                     // if none of the reports is found bail out
823ab116b1cSMilanka Ringwald                     if (!boot_mouse_report && !boot_keyboard_report){
824ab116b1cSMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
825ab116b1cSMilanka Ringwald                         hids_finalize_client(client);
826ab116b1cSMilanka Ringwald                         break;
827ab116b1cSMilanka Ringwald                     }
828ab116b1cSMilanka Ringwald 
8296bcfb631SMilanka Ringwald                     // set protocol
8306bcfb631SMilanka Ringwald                     if (client->protocol_mode_value_handle != 0){
8312901a9b7SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE;
8326bcfb631SMilanka Ringwald                     } else {
8332901a9b7SMilanka Ringwald                         client->protocol_mode = HID_PROTOCOL_MODE_BOOT;
8346bcfb631SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
8356bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
8366bcfb631SMilanka Ringwald                     }
8372901a9b7SMilanka Ringwald                     hids_run_for_client(client);
8386bcfb631SMilanka Ringwald                     break;
8396bcfb631SMilanka Ringwald 
8406bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_MOUSE_ENABLED:
841ab116b1cSMilanka Ringwald                     boot_mouse_report = get_boot_mouse_input_report(client);
842ab116b1cSMilanka Ringwald                     if (boot_mouse_report != NULL){
8436bcfb631SMilanka Ringwald                         // setup listener
844ab116b1cSMilanka Ringwald                         characteristic.value_handle = boot_mouse_report->value_handle;
845ab116b1cSMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(&boot_mouse_report->notifications, &handle_boot_mouse_hid_event, client->con_handle, &characteristic);
846ab116b1cSMilanka Ringwald                     }
847ab116b1cSMilanka Ringwald 
848ab116b1cSMilanka Ringwald                     boot_keyboard_report = get_boot_keyboard_input_report(client);
849ab116b1cSMilanka Ringwald                     if (!boot_mouse_report && !boot_keyboard_report){
8502901a9b7SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
8512901a9b7SMilanka Ringwald                         hids_finalize_client(client);
8522901a9b7SMilanka Ringwald                         break;
8532901a9b7SMilanka Ringwald                     }
8546bcfb631SMilanka Ringwald 
8556bcfb631SMilanka Ringwald                     if (client->protocol_mode_value_handle != 0){
8562901a9b7SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE;
8576bcfb631SMilanka Ringwald                     } else {
8582901a9b7SMilanka Ringwald                         client->protocol_mode = HID_PROTOCOL_MODE_BOOT;
8596bcfb631SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
8606bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
8616bcfb631SMilanka Ringwald                     }
8626bcfb631SMilanka Ringwald                     break;
863cf26c8fbSMilanka Ringwald                 default:
864cf26c8fbSMilanka Ringwald                     break;
865cf26c8fbSMilanka Ringwald             }
866cf26c8fbSMilanka Ringwald             break;
867cf26c8fbSMilanka Ringwald 
868cf26c8fbSMilanka Ringwald         default:
869cf26c8fbSMilanka Ringwald             break;
870cf26c8fbSMilanka Ringwald     }
8716bcfb631SMilanka Ringwald 
8726bcfb631SMilanka Ringwald     if (client != NULL){
8736bcfb631SMilanka Ringwald         hids_run_for_client(client);
8746bcfb631SMilanka Ringwald     }
875cf26c8fbSMilanka Ringwald }
876cf26c8fbSMilanka Ringwald 
8776bcfb631SMilanka Ringwald uint8_t hids_client_connect(hci_con_handle_t con_handle, btstack_packet_handler_t packet_handler, hid_protocol_mode_t protocol_mode, uint16_t * hids_cid){
878cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
879cf26c8fbSMilanka Ringwald 
880cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
881cf26c8fbSMilanka Ringwald     if (client != NULL){
882cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
883cf26c8fbSMilanka Ringwald     }
884cf26c8fbSMilanka Ringwald 
885cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
886cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
887cf26c8fbSMilanka Ringwald         *hids_cid = cid;
888cf26c8fbSMilanka Ringwald     }
889cf26c8fbSMilanka Ringwald 
890cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
891cf26c8fbSMilanka Ringwald     if (client == NULL) {
892cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
893cf26c8fbSMilanka Ringwald     }
894cf26c8fbSMilanka Ringwald 
8956bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
896cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
897cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
898cf26c8fbSMilanka Ringwald 
899cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
900cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
901cf26c8fbSMilanka Ringwald }
902cf26c8fbSMilanka Ringwald 
903cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
904cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
905cf26c8fbSMilanka Ringwald     if (client == NULL){
906cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
907cf26c8fbSMilanka Ringwald     }
908cf26c8fbSMilanka Ringwald     // finalize connection
909cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
910cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
911cf26c8fbSMilanka Ringwald }
912cf26c8fbSMilanka Ringwald 
9131624214bSMilanka Ringwald uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){
9141624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
9151624214bSMilanka Ringwald     if (client == NULL){
9161624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
9171624214bSMilanka Ringwald     }
9181624214bSMilanka Ringwald 
9191624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
9201624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
9211624214bSMilanka Ringwald     }
9221624214bSMilanka Ringwald 
9232fe59e00SMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT);
9241624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
9251624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
9261624214bSMilanka Ringwald     }
9271624214bSMilanka Ringwald 
9281624214bSMilanka Ringwald     uint16_t mtu;
9291624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
9301624214bSMilanka Ringwald 
9311624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
9321624214bSMilanka Ringwald         return status;
9331624214bSMilanka Ringwald     }
9341624214bSMilanka Ringwald 
9351624214bSMilanka Ringwald     if (mtu - 2 < report_len){
9361624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
9371624214bSMilanka Ringwald     }
9381624214bSMilanka Ringwald 
9391624214bSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_REPORT;
940ef102a9eSMilanka Ringwald     client->active_index = report_index;
9411624214bSMilanka Ringwald     client->report = report;
9421624214bSMilanka Ringwald     client->report_len = report_len;
9431624214bSMilanka Ringwald 
9441624214bSMilanka Ringwald     hids_run_for_client(client);
9451624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
9461624214bSMilanka Ringwald }
9471624214bSMilanka Ringwald 
948cf26c8fbSMilanka Ringwald void hids_client_init(void){}
949cf26c8fbSMilanka Ringwald 
950cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
951