xref: /btstack/src/ble/gatt-service/hids_client.c (revision 28da36a62602cf22aa1822287b284b961e96b24a)
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 
114ab116b1cSMilanka Ringwald static void 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){
117ab116b1cSMilanka Ringwald         return;
118ab116b1cSMilanka Ringwald     }
119*28da36a6SMilanka Ringwald     report_index = client->num_reports;
120ab116b1cSMilanka Ringwald 
121*28da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
122*28da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
123*28da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
124*28da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
125ab116b1cSMilanka Ringwald 
126*28da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
127*28da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
128*28da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
12970093cf5SMilanka Ringwald 
130*28da36a6SMilanka Ringwald         log_info("add index %d, id %d, type %d, value handle 0x%02x", report_index, report_id, report_type, characteristic->value_handle);
131ab116b1cSMilanka Ringwald         client->num_reports++;
132ab116b1cSMilanka Ringwald     } else {
133ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
134ab116b1cSMilanka Ringwald     }
135ab116b1cSMilanka Ringwald }
136ab116b1cSMilanka Ringwald 
13770093cf5SMilanka Ringwald static void hids_client_get_characteristic_for_report_index(hids_client_t * client, uint8_t report_index, gatt_client_characteristic_t * characteristic){
13870093cf5SMilanka Ringwald     characteristic->value_handle = client->reports[report_index].value_handle;
13970093cf5SMilanka Ringwald     characteristic->end_handle = client->reports[report_index].end_handle;
14070093cf5SMilanka Ringwald     characteristic->properties = client->reports[report_index].properties;
14170093cf5SMilanka Ringwald }
14270093cf5SMilanka Ringwald 
143ab116b1cSMilanka 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){
144ab116b1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, report_type);
145ab116b1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
146ab116b1cSMilanka Ringwald         return report_index;
147ab116b1cSMilanka Ringwald     }
14870093cf5SMilanka Ringwald     hids_client_get_characteristic_for_report_index(client, report_index, characteristic);
149ab116b1cSMilanka Ringwald     return report_index;
150ab116b1cSMilanka Ringwald }
151ab116b1cSMilanka Ringwald 
152e7bd2dbeSMilanka Ringwald 
1537e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
1547e1e6e7dSMilanka Ringwald     uint8_t i;
1557e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
156ef102a9eSMilanka Ringwald     for (i = client->active_index; i < client->num_reports; i++){
157e7bd2dbeSMilanka Ringwald         hids_client_report_t report = client->reports[i];
158e7bd2dbeSMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_MAP_ID){
1597e1e6e7dSMilanka Ringwald             index = i;
1607e1e6e7dSMilanka Ringwald             break;
161e7bd2dbeSMilanka Ringwald         }
1627e1e6e7dSMilanka Ringwald     }
1637e1e6e7dSMilanka Ringwald     client->active_index = index;
1647e1e6e7dSMilanka Ringwald     return index;
1657e1e6e7dSMilanka Ringwald }
1667e1e6e7dSMilanka Ringwald 
1677e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
1687e1e6e7dSMilanka Ringwald     client->active_index++;
1697e1e6e7dSMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
1707e1e6e7dSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
171e7bd2dbeSMilanka Ringwald         return true;
172e7bd2dbeSMilanka Ringwald     }
173e7bd2dbeSMilanka Ringwald     return false;
174e7bd2dbeSMilanka Ringwald }
175e7bd2dbeSMilanka Ringwald 
1767e1e6e7dSMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
1777e1e6e7dSMilanka Ringwald     client->service_index = 0;
1787e1e6e7dSMilanka Ringwald     client->active_index = 0;
17970093cf5SMilanka Ringwald 
1807e1e6e7dSMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
181*28da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
1827e1e6e7dSMilanka Ringwald         return true;
1837e1e6e7dSMilanka Ringwald     }
1847e1e6e7dSMilanka Ringwald     return false;
1857e1e6e7dSMilanka Ringwald }
1867e1e6e7dSMilanka Ringwald 
1877e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
1887e1e6e7dSMilanka Ringwald     uint8_t i;
1897e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
190ef102a9eSMilanka Ringwald     for (i = client->active_index; i < client->num_reports; i++){
19170093cf5SMilanka Ringwald         hids_client_report_t report = client->reports[i];
19270093cf5SMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
1937e1e6e7dSMilanka Ringwald             index = i;
1947e1e6e7dSMilanka Ringwald             break;
19570093cf5SMilanka Ringwald         }
1967e1e6e7dSMilanka Ringwald     }
1977e1e6e7dSMilanka Ringwald     client->active_index = index;
1987e1e6e7dSMilanka Ringwald     return index;
1997e1e6e7dSMilanka Ringwald }
2007e1e6e7dSMilanka Ringwald 
2017e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
2027e1e6e7dSMilanka Ringwald     client->active_index++;
2037e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
204*28da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
205e7bd2dbeSMilanka Ringwald         return true;
20670093cf5SMilanka Ringwald     }
2077e1e6e7dSMilanka Ringwald     return false;
2087e1e6e7dSMilanka Ringwald }
2097e1e6e7dSMilanka Ringwald 
2107e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
2117e1e6e7dSMilanka Ringwald     client->service_index = 0;
2127e1e6e7dSMilanka Ringwald     client->active_index = 0;
2137e1e6e7dSMilanka Ringwald 
2147e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
215*28da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
2167e1e6e7dSMilanka Ringwald         return true;
2177e1e6e7dSMilanka Ringwald     }
218e7bd2dbeSMilanka Ringwald     return false;
21970093cf5SMilanka Ringwald }
220ab116b1cSMilanka Ringwald 
221cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
222cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
223cf26c8fbSMilanka Ringwald     if (!client){
224cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
225cf26c8fbSMilanka Ringwald         return NULL;
226cf26c8fbSMilanka Ringwald     }
227cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
228cf26c8fbSMilanka Ringwald     client->cid = cid;
229cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
230fc975d0eSMilanka Ringwald 
231cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
232cf26c8fbSMilanka Ringwald     return client;
233fc975d0eSMilanka Ringwald }
234fc975d0eSMilanka Ringwald 
235cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
236cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
237cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
238fc975d0eSMilanka Ringwald }
239cf26c8fbSMilanka Ringwald 
240cf26c8fbSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
241cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_t it;
242cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
243cf26c8fbSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
244cf26c8fbSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
245cf26c8fbSMilanka Ringwald         if (client->con_handle != con_handle) continue;
246cf26c8fbSMilanka Ringwald         return client;
247cf26c8fbSMilanka Ringwald     }
248cf26c8fbSMilanka Ringwald     return NULL;
249cf26c8fbSMilanka Ringwald }
250cf26c8fbSMilanka Ringwald 
251cf26c8fbSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
252cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_t it;
253cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
254cf26c8fbSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
255cf26c8fbSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
256cf26c8fbSMilanka Ringwald         if (client->cid != hids_cid) continue;
257cf26c8fbSMilanka Ringwald         return client;
258cf26c8fbSMilanka Ringwald     }
259cf26c8fbSMilanka Ringwald     return NULL;
260cf26c8fbSMilanka Ringwald }
261cf26c8fbSMilanka Ringwald 
262cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
2636bcfb631SMilanka Ringwald     uint8_t event[8];
264cf26c8fbSMilanka Ringwald     int pos = 0;
265cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
266cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
267cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
268cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
269cf26c8fbSMilanka Ringwald     pos += 2;
270cf26c8fbSMilanka Ringwald     event[pos++] = status;
2716bcfb631SMilanka Ringwald     event[pos++] = client->protocol_mode;
272cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
273cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
274cf26c8fbSMilanka Ringwald }
275cf26c8fbSMilanka Ringwald 
276cf26c8fbSMilanka Ringwald 
277cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
278cf26c8fbSMilanka Ringwald     uint8_t att_status;
2796bcfb631SMilanka Ringwald     gatt_client_service_t service;
2806bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
281ab116b1cSMilanka Ringwald     uint8_t report_index;
282cf26c8fbSMilanka Ringwald 
283cf26c8fbSMilanka Ringwald     switch (client->state){
284cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
285cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
286cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
287cf26c8fbSMilanka Ringwald             // TODO handle status
288cf26c8fbSMilanka Ringwald             UNUSED(att_status);
289cf26c8fbSMilanka Ringwald             break;
290cf26c8fbSMilanka Ringwald 
291cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
2926bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
2936bcfb631SMilanka Ringwald 
2946bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
2956bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
2966bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
2976bcfb631SMilanka Ringwald 
2986bcfb631SMilanka Ringwald             UNUSED(att_status);
2996bcfb631SMilanka Ringwald             break;
3006bcfb631SMilanka Ringwald 
3012901a9b7SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD:
3022901a9b7SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED;
3032901a9b7SMilanka Ringwald 
304ab116b1cSMilanka Ringwald             report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, &characteristic);
305ab116b1cSMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
306ab116b1cSMilanka Ringwald                 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
307ab116b1cSMilanka Ringwald             }
308ab116b1cSMilanka Ringwald 
309ab116b1cSMilanka Ringwald             if ((report_index == HIDS_CLIENT_INVALID_REPORT_INDEX) || (att_status != ERROR_CODE_SUCCESS)){
310ab116b1cSMilanka Ringwald                 client->reports[report_index].value_handle = 0;
311ab116b1cSMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
3122901a9b7SMilanka Ringwald             }
3136bcfb631SMilanka Ringwald             break;
3146bcfb631SMilanka Ringwald 
3152901a9b7SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_MOUSE:
3162901a9b7SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED;
3172901a9b7SMilanka Ringwald 
318ab116b1cSMilanka Ringwald             report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, &characteristic);
319ab116b1cSMilanka Ringwald 
320ab116b1cSMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
321ab116b1cSMilanka Ringwald                 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
3222901a9b7SMilanka Ringwald             }
323ab116b1cSMilanka Ringwald 
324ab116b1cSMilanka Ringwald             if ((report_index == HIDS_CLIENT_INVALID_REPORT_INDEX) || (att_status != ERROR_CODE_SUCCESS)){
325ab116b1cSMilanka Ringwald                 client->reports[report_index].value_handle = 0;
326ab116b1cSMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
327ab116b1cSMilanka Ringwald             }
328ab116b1cSMilanka Ringwald 
3296bcfb631SMilanka Ringwald             break;
3306bcfb631SMilanka Ringwald 
3312901a9b7SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE:
3322901a9b7SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE;
3332901a9b7SMilanka 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);
3346bcfb631SMilanka Ringwald             UNUSED(att_status);
3352901a9b7SMilanka Ringwald 
3362901a9b7SMilanka Ringwald             client->protocol_mode = client->required_protocol_mode;
3376bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
3386bcfb631SMilanka Ringwald             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
339cf26c8fbSMilanka Ringwald             break;
340cf26c8fbSMilanka Ringwald 
3411624214bSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_REPORT:{
3421624214bSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
3431624214bSMilanka Ringwald 
3441624214bSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
345ef102a9eSMilanka Ringwald                 client->reports[client->active_index].value_handle,
3461624214bSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
3471624214bSMilanka Ringwald             UNUSED(att_status);
3481624214bSMilanka Ringwald             break;
3491624214bSMilanka Ringwald         }
35070093cf5SMilanka Ringwald 
351*28da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
352*28da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
353e7bd2dbeSMilanka Ringwald 
354ef102a9eSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(&handle_gatt_client_event, client->con_handle, client->reports[client->active_index].value_handle);
355e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
356e7bd2dbeSMilanka Ringwald             break;
357e7bd2dbeSMilanka Ringwald 
358e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
359e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
360e7bd2dbeSMilanka Ringwald             client->descriptor_handle = 0;
361e7bd2dbeSMilanka Ringwald 
362ef102a9eSMilanka Ringwald             hids_client_get_characteristic_for_report_index(client, client->active_index, &characteristic);
363e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
364e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
365e7bd2dbeSMilanka Ringwald             break;
366e7bd2dbeSMilanka Ringwald 
367*28da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
368*28da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
369e7bd2dbeSMilanka Ringwald 
370e7bd2dbeSMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
371e7bd2dbeSMilanka Ringwald             client->descriptor_handle = 0;
372e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
373e7bd2dbeSMilanka Ringwald             break;
374e7bd2dbeSMilanka Ringwald 
375e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
376e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
377e7bd2dbeSMilanka Ringwald 
378*28da36a6SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_handle_range_by_uuid16(&handle_gatt_client_event, client->con_handle, 0x0001, 0xffff,
379*28da36a6SMilanka Ringwald                 client->external_report_reference_uuid);
380e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
381e7bd2dbeSMilanka Ringwald             break;
382e7bd2dbeSMilanka Ringwald 
383*28da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
384*28da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
38570093cf5SMilanka Ringwald             client->descriptor_handle = 0;
386ef102a9eSMilanka Ringwald             hids_client_get_characteristic_for_report_index(client, client->active_index, &characteristic);
38770093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
38870093cf5SMilanka Ringwald             UNUSED(att_status);
38970093cf5SMilanka Ringwald             break;
39070093cf5SMilanka Ringwald 
391*28da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
392*28da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
39370093cf5SMilanka Ringwald 
39470093cf5SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
39570093cf5SMilanka Ringwald             client->descriptor_handle = 0;
39670093cf5SMilanka Ringwald             UNUSED(att_status);
39770093cf5SMilanka Ringwald             break;
39870093cf5SMilanka Ringwald 
3996bcfb631SMilanka Ringwald         default:
4006bcfb631SMilanka Ringwald             break;
4016bcfb631SMilanka Ringwald     }
4026bcfb631SMilanka Ringwald }
4036bcfb631SMilanka Ringwald 
40472a8858fSMilanka Ringwald static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_id, uint8_t *buffer, uint16_t report_len){
40572a8858fSMilanka Ringwald     uint16_t pos = 0;
40672a8858fSMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
40772a8858fSMilanka Ringwald     pos++;  // skip len
40872a8858fSMilanka Ringwald     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
40972a8858fSMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
41072a8858fSMilanka Ringwald     pos += 2;
41172a8858fSMilanka Ringwald     buffer[pos++] = report_id;
41272a8858fSMilanka Ringwald     little_endian_store_16(buffer, pos, report_len);
41372a8858fSMilanka Ringwald     pos += 2;
41472a8858fSMilanka Ringwald     buffer[1] = pos + report_len - 2;
41572a8858fSMilanka Ringwald }
41672a8858fSMilanka Ringwald 
41772a8858fSMilanka Ringwald static void handle_boot_keyboard_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
4186bcfb631SMilanka Ringwald     UNUSED(packet_type);
4196bcfb631SMilanka Ringwald     UNUSED(channel);
4206bcfb631SMilanka Ringwald     UNUSED(size);
42172a8858fSMilanka Ringwald 
42272a8858fSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
42372a8858fSMilanka Ringwald 
42472a8858fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
42572a8858fSMilanka Ringwald     btstack_assert(client != NULL);
42672a8858fSMilanka Ringwald 
42772a8858fSMilanka Ringwald     uint8_t * in_place_event = packet;
42872a8858fSMilanka Ringwald     hids_client_setup_report_event(client, HID_BOOT_MODE_KEYBOARD_ID, in_place_event, gatt_event_notification_get_value_length(packet));
42972a8858fSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size);
43072a8858fSMilanka Ringwald }
43172a8858fSMilanka Ringwald 
43272a8858fSMilanka Ringwald static void handle_boot_mouse_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
43372a8858fSMilanka Ringwald     UNUSED(packet_type);
43472a8858fSMilanka Ringwald     UNUSED(channel);
43572a8858fSMilanka Ringwald     UNUSED(size);
43672a8858fSMilanka Ringwald 
43772a8858fSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
43872a8858fSMilanka Ringwald 
43972a8858fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
44072a8858fSMilanka Ringwald     btstack_assert(client != NULL);
44172a8858fSMilanka Ringwald 
44272a8858fSMilanka Ringwald     uint8_t * in_place_event = packet;
44372a8858fSMilanka Ringwald     hids_client_setup_report_event(client, HID_BOOT_MODE_MOUSE_ID, in_place_event, gatt_event_notification_get_value_length(packet));
44472a8858fSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size);
445cf26c8fbSMilanka Ringwald }
446cf26c8fbSMilanka Ringwald 
4471624214bSMilanka Ringwald 
448cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
449cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
450cf26c8fbSMilanka Ringwald     UNUSED(channel);
451cf26c8fbSMilanka Ringwald     UNUSED(size);
452cf26c8fbSMilanka Ringwald 
4536bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
454cf26c8fbSMilanka Ringwald     uint8_t att_status;
4556bcfb631SMilanka Ringwald     gatt_client_service_t service;
4566bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
45770093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
458ab116b1cSMilanka Ringwald 
459ab116b1cSMilanka Ringwald     hids_client_report_t * boot_keyboard_report;
460ab116b1cSMilanka Ringwald     hids_client_report_t * boot_mouse_report;
461e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
462cf26c8fbSMilanka Ringwald 
463cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
464cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
465cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
466cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
467cf26c8fbSMilanka Ringwald 
4686bcfb631SMilanka Ringwald             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
4696bcfb631SMilanka Ringwald                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
4706bcfb631SMilanka Ringwald                 hids_finalize_client(client);
4716bcfb631SMilanka Ringwald                 break;
4726bcfb631SMilanka Ringwald             }
4736bcfb631SMilanka Ringwald 
4746bcfb631SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
4756bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
4766bcfb631SMilanka Ringwald                 client->services[client->num_instances].start_handle = service.start_group_handle;
4776bcfb631SMilanka Ringwald                 client->services[client->num_instances].end_handle = service.end_group_handle;
4786bcfb631SMilanka Ringwald             }
4796bcfb631SMilanka Ringwald             client->num_instances++;
4806bcfb631SMilanka Ringwald             break;
4816bcfb631SMilanka Ringwald 
4826bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
4836bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
4846bcfb631SMilanka Ringwald             btstack_assert(client != NULL);
4856bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
4866bcfb631SMilanka Ringwald             switch (characteristic.uuid16){
48770093cf5SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
48870093cf5SMilanka Ringwald                     client->protocol_mode_value_handle = characteristic.value_handle;
48970093cf5SMilanka Ringwald                     break;
49070093cf5SMilanka Ringwald 
4916bcfb631SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
492ab116b1cSMilanka Ringwald                     hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT);
4936bcfb631SMilanka Ringwald                     break;
4941624214bSMilanka Ringwald 
4956bcfb631SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
496ab116b1cSMilanka Ringwald                     hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT);
4976bcfb631SMilanka Ringwald                     break;
4981624214bSMilanka Ringwald 
4991624214bSMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
500ab116b1cSMilanka Ringwald                     hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT);
5011624214bSMilanka Ringwald                     break;
5021624214bSMilanka Ringwald 
50370093cf5SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
50470093cf5SMilanka Ringwald                     hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED);
50570093cf5SMilanka Ringwald                     break;
50670093cf5SMilanka Ringwald 
50770093cf5SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
508e7bd2dbeSMilanka Ringwald                     hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_MAP_ID, HID_REPORT_TYPE_RESERVED);
50970093cf5SMilanka Ringwald                     break;
51070093cf5SMilanka Ringwald 
51170093cf5SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
51270093cf5SMilanka Ringwald                     // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION\n");
51370093cf5SMilanka Ringwald                     hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_INFORMATION_ID, HID_REPORT_TYPE_INPUT);
51470093cf5SMilanka Ringwald                     break;
51570093cf5SMilanka Ringwald 
51670093cf5SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
51770093cf5SMilanka Ringwald                     // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT\n");
51870093cf5SMilanka Ringwald                     hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_CONTROL_POINT_ID, HID_REPORT_TYPE_INPUT);
51970093cf5SMilanka Ringwald                     break;
5206bcfb631SMilanka Ringwald                 default:
521*28da36a6SMilanka Ringwald                     // check if uuid16 is the external one:
522*28da36a6SMilanka Ringwald                     if ( characteristic.uuid16 == client->external_report_reference_uuid ){
523*28da36a6SMilanka Ringwald                         hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED);
524*28da36a6SMilanka Ringwald                         client->external_report_reference_uuid = 0;
525*28da36a6SMilanka Ringwald                     }
5266bcfb631SMilanka Ringwald                     break;
5276bcfb631SMilanka Ringwald             }
528cf26c8fbSMilanka Ringwald             break;
529cf26c8fbSMilanka Ringwald 
530e7bd2dbeSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
531e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
532e7bd2dbeSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
533e7bd2dbeSMilanka Ringwald             btstack_assert(client != NULL);
534e7bd2dbeSMilanka Ringwald             // TODO get value and store it
535e7bd2dbeSMilanka Ringwald             break;
536e7bd2dbeSMilanka Ringwald 
53770093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
53870093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
53970093cf5SMilanka Ringwald             btstack_assert(client != NULL);
54070093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
54170093cf5SMilanka Ringwald 
542e7bd2dbeSMilanka Ringwald             switch (client->state) {
543e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
544e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
545e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
546e7bd2dbeSMilanka Ringwald                         client->descriptor_handle = characteristic_descriptor.handle;
547e7bd2dbeSMilanka Ringwald                     }
548e7bd2dbeSMilanka Ringwald                     break;
549*28da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
55070093cf5SMilanka Ringwald                     // setup for descriptor value query
55170093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
55270093cf5SMilanka Ringwald                         client->descriptor_handle = characteristic_descriptor.handle;
55370093cf5SMilanka Ringwald                     }
55470093cf5SMilanka Ringwald                     break;
555e7bd2dbeSMilanka Ringwald                 default:
556e7bd2dbeSMilanka Ringwald                     break;
557e7bd2dbeSMilanka Ringwald             }
558e7bd2dbeSMilanka Ringwald             break;
55970093cf5SMilanka Ringwald 
56070093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
56170093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
56270093cf5SMilanka Ringwald             btstack_assert(client != NULL);
563e7bd2dbeSMilanka Ringwald 
564e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
565e7bd2dbeSMilanka Ringwald                 break;
566e7bd2dbeSMilanka Ringwald             }
5677e1e6e7dSMilanka Ringwald 
568e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
569e7bd2dbeSMilanka Ringwald 
570e7bd2dbeSMilanka Ringwald             switch (client->state) {
571*28da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
572e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
573*28da36a6SMilanka Ringwald                     client->descriptor_handle = gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet);
574*28da36a6SMilanka Ringwald                     client->external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
575*28da36a6SMilanka Ringwald                     // printf("found external report characteristic uuid 0x%02x\n", client->external_report_reference_uuid);
576e7bd2dbeSMilanka Ringwald                     break;
577e7bd2dbeSMilanka Ringwald 
578*28da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
579ef102a9eSMilanka Ringwald                     client->reports[client->active_index].report_id = characteristic_descriptor_value[0];
580ef102a9eSMilanka Ringwald                     client->reports[client->active_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
581*28da36a6SMilanka Ringwald                     log_info("update index %d, id %d, type %d, value handle 0x%02x",
582*28da36a6SMilanka Ringwald                         client->active_index,
583*28da36a6SMilanka Ringwald                         client->reports[client->active_index].report_id,
584*28da36a6SMilanka Ringwald                         client->reports[client->active_index].report_type,
585*28da36a6SMilanka Ringwald                         client->reports[client->active_index].value_handle);
5867e1e6e7dSMilanka Ringwald 
587e7bd2dbeSMilanka Ringwald                     break;
588e7bd2dbeSMilanka Ringwald 
589e7bd2dbeSMilanka Ringwald                 default:
590e7bd2dbeSMilanka Ringwald                     break;
59170093cf5SMilanka Ringwald             }
59270093cf5SMilanka Ringwald             break;
59370093cf5SMilanka Ringwald 
594cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
595cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
596cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
597cf26c8fbSMilanka Ringwald 
598cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
599cf26c8fbSMilanka Ringwald 
600cf26c8fbSMilanka Ringwald             switch (client->state){
601cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
6026bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
6036bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
6046bcfb631SMilanka Ringwald                         hids_finalize_client(client);
605cf26c8fbSMilanka Ringwald                         break;
6066bcfb631SMilanka Ringwald                     }
6076bcfb631SMilanka Ringwald 
6086bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
6096bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
6106bcfb631SMilanka Ringwald                         hids_finalize_client(client);
6116bcfb631SMilanka Ringwald                         break;
6126bcfb631SMilanka Ringwald                     }
6136bcfb631SMilanka Ringwald 
6146bcfb631SMilanka Ringwald                     if (client->num_instances > MAX_NUM_HID_SERVICES) {
6156bcfb631SMilanka 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);
6166bcfb631SMilanka Ringwald                         client->num_instances = MAX_NUM_HID_SERVICES;
6176bcfb631SMilanka Ringwald                     }
6186bcfb631SMilanka Ringwald 
6196bcfb631SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
6206bcfb631SMilanka Ringwald                     client->service_index = 0;
6216bcfb631SMilanka Ringwald                     break;
6226bcfb631SMilanka Ringwald 
6236bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
6246bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
6256bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
6266bcfb631SMilanka Ringwald                         hids_finalize_client(client);
6276bcfb631SMilanka Ringwald                         break;
6286bcfb631SMilanka Ringwald                     }
6296bcfb631SMilanka Ringwald 
6306bcfb631SMilanka Ringwald                     switch (client->required_protocol_mode){
6316bcfb631SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
632ab116b1cSMilanka Ringwald                             if (get_boot_keyboard_input_report(client) != NULL){
6332901a9b7SMilanka Ringwald                                 client->state = HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD;
6346bcfb631SMilanka Ringwald                                 break;
6356bcfb631SMilanka Ringwald                             }
636ab116b1cSMilanka Ringwald                             if (get_boot_mouse_input_report(client) != NULL){
6372901a9b7SMilanka Ringwald                                 client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE;
6386bcfb631SMilanka Ringwald                                 break;
6396bcfb631SMilanka Ringwald                             }
6406bcfb631SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
6416bcfb631SMilanka Ringwald                             hids_finalize_client(client);
6426bcfb631SMilanka Ringwald                             break;
6437e1e6e7dSMilanka Ringwald 
64470093cf5SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
64570093cf5SMilanka Ringwald                             if ((client->service_index + 1) < client->num_instances){
64670093cf5SMilanka Ringwald                                 // discover characteristics of next service
64770093cf5SMilanka Ringwald                                 client->service_index++;
64870093cf5SMilanka Ringwald                                 client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
64970093cf5SMilanka Ringwald                             } else {
6507e1e6e7dSMilanka Ringwald                                 // 1. we need to get HID Descriptor and
6517e1e6e7dSMilanka Ringwald                                 // 2. get external Report characteristics if referenced from Report Map
6527e1e6e7dSMilanka Ringwald                                 if (hids_client_report_map_query_init(client)){
65370093cf5SMilanka Ringwald                                     break;
65470093cf5SMilanka Ringwald                                 }
65570093cf5SMilanka Ringwald                                 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
65670093cf5SMilanka Ringwald                                 hids_finalize_client(client);
65770093cf5SMilanka Ringwald                             }
65870093cf5SMilanka Ringwald                             break;
6596bcfb631SMilanka Ringwald                         default:
6606bcfb631SMilanka Ringwald                             break;
6616bcfb631SMilanka Ringwald                     }
6626bcfb631SMilanka Ringwald                     break;
6636bcfb631SMilanka Ringwald 
664*28da36a6SMilanka Ringwald                 // HID descriptor found
665*28da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
666e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
667e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
668e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
669e7bd2dbeSMilanka Ringwald                         break;
670e7bd2dbeSMilanka Ringwald                     }
6717e1e6e7dSMilanka Ringwald                     // printf("HID Descriptor found, check for external Report IDs (read report map descriptor)\n");
672e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
673e7bd2dbeSMilanka Ringwald                     break;
674e7bd2dbeSMilanka Ringwald 
675*28da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
676e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
677e7bd2dbeSMilanka Ringwald                     if (client->descriptor_handle != 0){
678*28da36a6SMilanka Ringwald                         // read EXTERNAL_REPORT_REFERENCE desc. value
679*28da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
680e7bd2dbeSMilanka Ringwald                         break;
681e7bd2dbeSMilanka Ringwald                     }
682e7bd2dbeSMilanka Ringwald 
683e7bd2dbeSMilanka Ringwald                     // go for next report map
6847e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
685e7bd2dbeSMilanka Ringwald                         break;
686e7bd2dbeSMilanka Ringwald                     }
687e7bd2dbeSMilanka Ringwald 
688e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
689e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
6907e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
691e7bd2dbeSMilanka Ringwald                         break;
692e7bd2dbeSMilanka Ringwald                     }
693e7bd2dbeSMilanka Ringwald 
694e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
695e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
696e7bd2dbeSMilanka Ringwald                     break;
697e7bd2dbeSMilanka Ringwald 
698*28da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
699e7bd2dbeSMilanka Ringwald                     if (client->descriptor_handle != 0){
700e7bd2dbeSMilanka Ringwald                         // descriptor found
701e7bd2dbeSMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
702e7bd2dbeSMilanka Ringwald                         break;
703e7bd2dbeSMilanka Ringwald                     }
704e7bd2dbeSMilanka Ringwald 
705e7bd2dbeSMilanka Ringwald                     // go for next map report
7067e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
707e7bd2dbeSMilanka Ringwald                         break;
708e7bd2dbeSMilanka Ringwald                     }
709e7bd2dbeSMilanka Ringwald 
710e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
711e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
7127e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
713e7bd2dbeSMilanka Ringwald                         break;
714e7bd2dbeSMilanka Ringwald                     }
715e7bd2dbeSMilanka Ringwald 
716e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
717e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
718e7bd2dbeSMilanka Ringwald                     break;
719e7bd2dbeSMilanka Ringwald 
720e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
721e7bd2dbeSMilanka Ringwald                     // go for next map report
7227e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
723e7bd2dbeSMilanka Ringwald                         break;
724e7bd2dbeSMilanka Ringwald                     }
725e7bd2dbeSMilanka Ringwald 
726e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
727e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
7287e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
729e7bd2dbeSMilanka Ringwald                         break;
730e7bd2dbeSMilanka Ringwald                     }
731e7bd2dbeSMilanka Ringwald 
732e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
733e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
734e7bd2dbeSMilanka Ringwald                     break;
735e7bd2dbeSMilanka Ringwald 
736*28da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
73770093cf5SMilanka Ringwald                     if (client->descriptor_handle != 0){
73870093cf5SMilanka Ringwald                         // descriptor found
739*28da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
74070093cf5SMilanka Ringwald                         break;
74170093cf5SMilanka Ringwald                     }
74270093cf5SMilanka Ringwald 
74370093cf5SMilanka Ringwald                     // go for next report
7447e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
74570093cf5SMilanka Ringwald                         break;
74670093cf5SMilanka Ringwald                     }
74770093cf5SMilanka Ringwald 
74870093cf5SMilanka Ringwald                     // TODO continue with report mode
74970093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
75070093cf5SMilanka Ringwald                     break;
75170093cf5SMilanka Ringwald 
752*28da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
75370093cf5SMilanka Ringwald                     // go for next report
7547e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
75570093cf5SMilanka Ringwald                         break;
75670093cf5SMilanka Ringwald                     }
75770093cf5SMilanka Ringwald 
75870093cf5SMilanka Ringwald                     // TODO continue with report mode
75970093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
76070093cf5SMilanka Ringwald                     break;
76170093cf5SMilanka Ringwald 
7626bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED:
763ab116b1cSMilanka Ringwald                     boot_keyboard_report = get_boot_keyboard_input_report(client);
764ab116b1cSMilanka Ringwald                     if (boot_keyboard_report != NULL){
7656bcfb631SMilanka Ringwald                         // setup listener
766ab116b1cSMilanka Ringwald                         characteristic.value_handle = boot_keyboard_report->value_handle;
767ab116b1cSMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(&boot_keyboard_report->notifications, &handle_boot_keyboard_hid_event, client->con_handle, &characteristic);
7682901a9b7SMilanka Ringwald                     }
7692901a9b7SMilanka Ringwald 
770ab116b1cSMilanka Ringwald                     // check if there is mouse input report
771ab116b1cSMilanka Ringwald                     boot_mouse_report = get_boot_mouse_input_report(client);
772ab116b1cSMilanka Ringwald                     if (boot_mouse_report != NULL){
7732901a9b7SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE;
7742901a9b7SMilanka Ringwald                         break;
7752901a9b7SMilanka Ringwald                     }
7762901a9b7SMilanka Ringwald 
777ab116b1cSMilanka Ringwald                     // if none of the reports is found bail out
778ab116b1cSMilanka Ringwald                     if (!boot_mouse_report && !boot_keyboard_report){
779ab116b1cSMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
780ab116b1cSMilanka Ringwald                         hids_finalize_client(client);
781ab116b1cSMilanka Ringwald                         break;
782ab116b1cSMilanka Ringwald                     }
783ab116b1cSMilanka Ringwald 
7846bcfb631SMilanka Ringwald                     // set protocol
7856bcfb631SMilanka Ringwald                     if (client->protocol_mode_value_handle != 0){
7862901a9b7SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE;
7876bcfb631SMilanka Ringwald                     } else {
7882901a9b7SMilanka Ringwald                         client->protocol_mode = HID_PROTOCOL_MODE_BOOT;
7896bcfb631SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
7906bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
7916bcfb631SMilanka Ringwald                     }
7922901a9b7SMilanka Ringwald                     hids_run_for_client(client);
7936bcfb631SMilanka Ringwald                     break;
7946bcfb631SMilanka Ringwald 
7956bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_MOUSE_ENABLED:
796ab116b1cSMilanka Ringwald                     boot_mouse_report = get_boot_mouse_input_report(client);
797ab116b1cSMilanka Ringwald                     if (boot_mouse_report != NULL){
7986bcfb631SMilanka Ringwald                         // setup listener
799ab116b1cSMilanka Ringwald                         characteristic.value_handle = boot_mouse_report->value_handle;
800ab116b1cSMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(&boot_mouse_report->notifications, &handle_boot_mouse_hid_event, client->con_handle, &characteristic);
801ab116b1cSMilanka Ringwald                     }
802ab116b1cSMilanka Ringwald 
803ab116b1cSMilanka Ringwald                     boot_keyboard_report = get_boot_keyboard_input_report(client);
804ab116b1cSMilanka Ringwald                     if (!boot_mouse_report && !boot_keyboard_report){
8052901a9b7SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
8062901a9b7SMilanka Ringwald                         hids_finalize_client(client);
8072901a9b7SMilanka Ringwald                         break;
8082901a9b7SMilanka Ringwald                     }
8096bcfb631SMilanka Ringwald 
8106bcfb631SMilanka Ringwald                     if (client->protocol_mode_value_handle != 0){
8112901a9b7SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE;
8126bcfb631SMilanka Ringwald                     } else {
8132901a9b7SMilanka Ringwald                         client->protocol_mode = HID_PROTOCOL_MODE_BOOT;
8146bcfb631SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
8156bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
8166bcfb631SMilanka Ringwald                     }
8176bcfb631SMilanka Ringwald                     break;
818cf26c8fbSMilanka Ringwald                 default:
819cf26c8fbSMilanka Ringwald                     break;
820cf26c8fbSMilanka Ringwald             }
821cf26c8fbSMilanka Ringwald             break;
822cf26c8fbSMilanka Ringwald 
823cf26c8fbSMilanka Ringwald         default:
824cf26c8fbSMilanka Ringwald             break;
825cf26c8fbSMilanka Ringwald     }
8266bcfb631SMilanka Ringwald 
8276bcfb631SMilanka Ringwald     if (client != NULL){
8286bcfb631SMilanka Ringwald         hids_run_for_client(client);
8296bcfb631SMilanka Ringwald     }
830cf26c8fbSMilanka Ringwald }
831cf26c8fbSMilanka Ringwald 
8326bcfb631SMilanka 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){
833cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
834cf26c8fbSMilanka Ringwald 
835cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
836cf26c8fbSMilanka Ringwald     if (client != NULL){
837cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
838cf26c8fbSMilanka Ringwald     }
839cf26c8fbSMilanka Ringwald 
840cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
841cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
842cf26c8fbSMilanka Ringwald         *hids_cid = cid;
843cf26c8fbSMilanka Ringwald     }
844cf26c8fbSMilanka Ringwald 
845cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
846cf26c8fbSMilanka Ringwald     if (client == NULL) {
847cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
848cf26c8fbSMilanka Ringwald     }
849cf26c8fbSMilanka Ringwald 
8506bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
851cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
852cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
853cf26c8fbSMilanka Ringwald 
854cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
855cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
856cf26c8fbSMilanka Ringwald }
857cf26c8fbSMilanka Ringwald 
858cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
859cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
860cf26c8fbSMilanka Ringwald     if (client == NULL){
861cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
862cf26c8fbSMilanka Ringwald     }
863cf26c8fbSMilanka Ringwald     // finalize connection
864cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
865cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
866cf26c8fbSMilanka Ringwald }
867cf26c8fbSMilanka Ringwald 
8681624214bSMilanka Ringwald uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){
8691624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
8701624214bSMilanka Ringwald     if (client == NULL){
8711624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
8721624214bSMilanka Ringwald     }
8731624214bSMilanka Ringwald 
8741624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
8751624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
8761624214bSMilanka Ringwald     }
8771624214bSMilanka Ringwald 
8782fe59e00SMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT);
8791624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
8801624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
8811624214bSMilanka Ringwald     }
8821624214bSMilanka Ringwald 
8831624214bSMilanka Ringwald     uint16_t mtu;
8841624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
8851624214bSMilanka Ringwald 
8861624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
8871624214bSMilanka Ringwald         return status;
8881624214bSMilanka Ringwald     }
8891624214bSMilanka Ringwald 
8901624214bSMilanka Ringwald     if (mtu - 2 < report_len){
8911624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
8921624214bSMilanka Ringwald     }
8931624214bSMilanka Ringwald 
8941624214bSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_REPORT;
895ef102a9eSMilanka Ringwald     client->active_index = report_index;
8961624214bSMilanka Ringwald     client->report = report;
8971624214bSMilanka Ringwald     client->report_len = report_len;
8981624214bSMilanka Ringwald 
8991624214bSMilanka Ringwald     hids_run_for_client(client);
9001624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
9011624214bSMilanka Ringwald }
9021624214bSMilanka Ringwald 
903cf26c8fbSMilanka Ringwald void hids_client_init(void){}
904cf26c8fbSMilanka Ringwald 
905cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
906