xref: /btstack/src/ble/gatt-service/hids_client.c (revision e7bd2dbefa658cd576e672f06b1d5cccbfddc9cb)
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     }
119ab116b1cSMilanka Ringwald 
120a381a464SMilanka Ringwald     if (client->active_report_index < HIDS_CLIENT_NUM_REPORTS) {
121a381a464SMilanka Ringwald         client->reports[client->active_report_index].value_handle = characteristic->value_handle;
122a381a464SMilanka Ringwald         client->reports[client->active_report_index].end_handle = characteristic->end_handle;
123a381a464SMilanka Ringwald         client->reports[client->active_report_index].properties = characteristic->properties;
124ab116b1cSMilanka Ringwald 
12570093cf5SMilanka Ringwald         client->reports[client->active_report_index].service_index = client->service_index;
126a381a464SMilanka Ringwald         client->reports[client->active_report_index].report_id = report_id;
127a381a464SMilanka Ringwald         client->reports[client->active_report_index].report_type = report_type;
12870093cf5SMilanka Ringwald 
12970093cf5SMilanka Ringwald         client->active_report_index++;
130ab116b1cSMilanka Ringwald         client->num_reports++;
131ab116b1cSMilanka Ringwald     } else {
132ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
133ab116b1cSMilanka Ringwald     }
134ab116b1cSMilanka Ringwald }
135ab116b1cSMilanka Ringwald 
13670093cf5SMilanka Ringwald static void hids_client_get_characteristic_for_report_index(hids_client_t * client, uint8_t report_index, gatt_client_characteristic_t * characteristic){
13770093cf5SMilanka Ringwald     characteristic->value_handle = client->reports[report_index].value_handle;
13870093cf5SMilanka Ringwald     characteristic->end_handle = client->reports[report_index].end_handle;
13970093cf5SMilanka Ringwald     characteristic->properties = client->reports[report_index].properties;
14070093cf5SMilanka Ringwald }
14170093cf5SMilanka Ringwald 
142ab116b1cSMilanka 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){
143ab116b1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, report_type);
144ab116b1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
145ab116b1cSMilanka Ringwald         return report_index;
146ab116b1cSMilanka Ringwald     }
14770093cf5SMilanka Ringwald     hids_client_get_characteristic_for_report_index(client, report_index, characteristic);
148ab116b1cSMilanka Ringwald     return report_index;
149ab116b1cSMilanka Ringwald }
150ab116b1cSMilanka Ringwald 
151*e7bd2dbeSMilanka Ringwald static bool hids_client_get_next_active_report_map_index(hids_client_t * client){
152*e7bd2dbeSMilanka Ringwald     uint8_t i;
153*e7bd2dbeSMilanka Ringwald 
154*e7bd2dbeSMilanka Ringwald     for (i = client->active_report_index; i < client->num_reports; i++){
155*e7bd2dbeSMilanka Ringwald         hids_client_report_t report = client->reports[i];
156*e7bd2dbeSMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_MAP_ID){
157*e7bd2dbeSMilanka Ringwald             client->active_report_index = i;
158*e7bd2dbeSMilanka Ringwald         }
159*e7bd2dbeSMilanka Ringwald         return true;
160*e7bd2dbeSMilanka Ringwald     }
161*e7bd2dbeSMilanka Ringwald     client->active_report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
162*e7bd2dbeSMilanka Ringwald     return false;
163*e7bd2dbeSMilanka Ringwald }
164*e7bd2dbeSMilanka Ringwald 
165*e7bd2dbeSMilanka Ringwald static bool hids_client_get_next_active_report_index(hids_client_t * client){
16670093cf5SMilanka Ringwald     uint8_t i;
16770093cf5SMilanka Ringwald 
16870093cf5SMilanka Ringwald     for (i = client->active_report_index; i < client->num_reports; i++){
16970093cf5SMilanka Ringwald         hids_client_report_t report = client->reports[i];
17070093cf5SMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
17170093cf5SMilanka Ringwald             client->active_report_index = i;
17270093cf5SMilanka Ringwald         }
173*e7bd2dbeSMilanka Ringwald         return true;
17470093cf5SMilanka Ringwald     }
17570093cf5SMilanka Ringwald     client->active_report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
176*e7bd2dbeSMilanka Ringwald     return false;
17770093cf5SMilanka Ringwald }
178ab116b1cSMilanka Ringwald 
179cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
180cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
181cf26c8fbSMilanka Ringwald     if (!client){
182cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
183cf26c8fbSMilanka Ringwald         return NULL;
184cf26c8fbSMilanka Ringwald     }
185cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
186cf26c8fbSMilanka Ringwald     client->cid = cid;
187cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
188fc975d0eSMilanka Ringwald 
189cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
190cf26c8fbSMilanka Ringwald     return client;
191fc975d0eSMilanka Ringwald }
192fc975d0eSMilanka Ringwald 
193cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
194cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
195cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
196fc975d0eSMilanka Ringwald }
197cf26c8fbSMilanka Ringwald 
198cf26c8fbSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
199cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_t it;
200cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
201cf26c8fbSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
202cf26c8fbSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
203cf26c8fbSMilanka Ringwald         if (client->con_handle != con_handle) continue;
204cf26c8fbSMilanka Ringwald         return client;
205cf26c8fbSMilanka Ringwald     }
206cf26c8fbSMilanka Ringwald     return NULL;
207cf26c8fbSMilanka Ringwald }
208cf26c8fbSMilanka Ringwald 
209cf26c8fbSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
210cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_t it;
211cf26c8fbSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
212cf26c8fbSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
213cf26c8fbSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
214cf26c8fbSMilanka Ringwald         if (client->cid != hids_cid) continue;
215cf26c8fbSMilanka Ringwald         return client;
216cf26c8fbSMilanka Ringwald     }
217cf26c8fbSMilanka Ringwald     return NULL;
218cf26c8fbSMilanka Ringwald }
219cf26c8fbSMilanka Ringwald 
220cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
2216bcfb631SMilanka Ringwald     uint8_t event[8];
222cf26c8fbSMilanka Ringwald     int pos = 0;
223cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
224cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
225cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
226cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
227cf26c8fbSMilanka Ringwald     pos += 2;
228cf26c8fbSMilanka Ringwald     event[pos++] = status;
2296bcfb631SMilanka Ringwald     event[pos++] = client->protocol_mode;
230cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
231cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
232cf26c8fbSMilanka Ringwald }
233cf26c8fbSMilanka Ringwald 
234cf26c8fbSMilanka Ringwald 
235cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
236cf26c8fbSMilanka Ringwald     uint8_t att_status;
2376bcfb631SMilanka Ringwald     gatt_client_service_t service;
2386bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
239ab116b1cSMilanka Ringwald     uint8_t report_index;
240cf26c8fbSMilanka Ringwald 
241cf26c8fbSMilanka Ringwald     switch (client->state){
242cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
243cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
244cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
245cf26c8fbSMilanka Ringwald             // TODO handle status
246cf26c8fbSMilanka Ringwald             UNUSED(att_status);
247cf26c8fbSMilanka Ringwald             break;
248cf26c8fbSMilanka Ringwald 
249cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
2506bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
2516bcfb631SMilanka Ringwald 
2526bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
2536bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
2546bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
2556bcfb631SMilanka Ringwald 
2566bcfb631SMilanka Ringwald             UNUSED(att_status);
2576bcfb631SMilanka Ringwald             break;
2586bcfb631SMilanka Ringwald 
2592901a9b7SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD:
2602901a9b7SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED;
2612901a9b7SMilanka Ringwald 
262ab116b1cSMilanka Ringwald             report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, &characteristic);
263ab116b1cSMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
264ab116b1cSMilanka Ringwald                 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
265ab116b1cSMilanka Ringwald             }
266ab116b1cSMilanka Ringwald 
267ab116b1cSMilanka Ringwald             if ((report_index == HIDS_CLIENT_INVALID_REPORT_INDEX) || (att_status != ERROR_CODE_SUCCESS)){
268ab116b1cSMilanka Ringwald                 client->reports[report_index].value_handle = 0;
269ab116b1cSMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
2702901a9b7SMilanka Ringwald             }
2716bcfb631SMilanka Ringwald             break;
2726bcfb631SMilanka Ringwald 
2732901a9b7SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_MOUSE:
2742901a9b7SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED;
2752901a9b7SMilanka Ringwald 
276ab116b1cSMilanka Ringwald             report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, &characteristic);
277ab116b1cSMilanka Ringwald 
278ab116b1cSMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
279ab116b1cSMilanka Ringwald                 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
2802901a9b7SMilanka Ringwald             }
281ab116b1cSMilanka Ringwald 
282ab116b1cSMilanka Ringwald             if ((report_index == HIDS_CLIENT_INVALID_REPORT_INDEX) || (att_status != ERROR_CODE_SUCCESS)){
283ab116b1cSMilanka Ringwald                 client->reports[report_index].value_handle = 0;
284ab116b1cSMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
285ab116b1cSMilanka Ringwald             }
286ab116b1cSMilanka Ringwald 
2876bcfb631SMilanka Ringwald             break;
2886bcfb631SMilanka Ringwald 
2892901a9b7SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE:
2902901a9b7SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE;
2912901a9b7SMilanka 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);
2926bcfb631SMilanka Ringwald             UNUSED(att_status);
2932901a9b7SMilanka Ringwald 
2942901a9b7SMilanka Ringwald             client->protocol_mode = client->required_protocol_mode;
2956bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
2966bcfb631SMilanka Ringwald             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
297cf26c8fbSMilanka Ringwald             break;
298cf26c8fbSMilanka Ringwald 
2991624214bSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_REPORT:{
3001624214bSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
3011624214bSMilanka Ringwald 
3021624214bSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
3031624214bSMilanka Ringwald                 client->reports[client->active_report_index].value_handle,
3041624214bSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
3051624214bSMilanka Ringwald             UNUSED(att_status);
3061624214bSMilanka Ringwald             break;
3071624214bSMilanka Ringwald         }
30870093cf5SMilanka Ringwald 
309*e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_CHARACTERISTIC_VALUE:
310*e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_VALUE_RESULT;
311*e7bd2dbeSMilanka Ringwald 
312*e7bd2dbeSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(&handle_gatt_client_event, client->con_handle, client->reports[client->active_report_index].value_handle);
313*e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
314*e7bd2dbeSMilanka Ringwald             break;
315*e7bd2dbeSMilanka Ringwald 
316*e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
317*e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
318*e7bd2dbeSMilanka Ringwald             client->descriptor_handle = 0;
319*e7bd2dbeSMilanka Ringwald 
320*e7bd2dbeSMilanka Ringwald             hids_client_get_characteristic_for_report_index(client, client->active_report_index, &characteristic);
321*e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
322*e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
323*e7bd2dbeSMilanka Ringwald             break;
324*e7bd2dbeSMilanka Ringwald 
325*e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_CHARACTERISTIC_DESCRIPTOR_VALUE:
326*e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTOR_VALUE_RESULT;
327*e7bd2dbeSMilanka Ringwald 
328*e7bd2dbeSMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
329*e7bd2dbeSMilanka Ringwald             client->descriptor_handle = 0;
330*e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
331*e7bd2dbeSMilanka Ringwald             break;
332*e7bd2dbeSMilanka Ringwald 
333*e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
334*e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
335*e7bd2dbeSMilanka Ringwald 
336*e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_handle_range_by_uuid16(&handle_gatt_client_event, client->con_handle, 0, 0xffff,
337*e7bd2dbeSMilanka Ringwald                 client->reports[client->active_report_index].external_report_reference_uuid);
338*e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
339*e7bd2dbeSMilanka Ringwald             break;
340*e7bd2dbeSMilanka Ringwald 
34170093cf5SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_QUERY_CHARACTERISTIC_DESCRIPTORS:
34270093cf5SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTORS_RESULT;
34370093cf5SMilanka Ringwald             client->descriptor_handle = 0;
344*e7bd2dbeSMilanka Ringwald 
34570093cf5SMilanka Ringwald             hids_client_get_characteristic_for_report_index(client, client->active_report_index, &characteristic);
34670093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
34770093cf5SMilanka Ringwald             UNUSED(att_status);
34870093cf5SMilanka Ringwald             break;
34970093cf5SMilanka Ringwald 
35070093cf5SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_READ_CHARACTERISTIC_DESCRIPTOR_VALUE:
35170093cf5SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTOR_VALUE_RESULT;
35270093cf5SMilanka Ringwald 
35370093cf5SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
35470093cf5SMilanka Ringwald             client->descriptor_handle = 0;
35570093cf5SMilanka Ringwald             UNUSED(att_status);
35670093cf5SMilanka Ringwald             break;
35770093cf5SMilanka Ringwald 
3586bcfb631SMilanka Ringwald         default:
3596bcfb631SMilanka Ringwald             break;
3606bcfb631SMilanka Ringwald     }
3616bcfb631SMilanka Ringwald }
3626bcfb631SMilanka Ringwald 
36372a8858fSMilanka Ringwald static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_id, uint8_t *buffer, uint16_t report_len){
36472a8858fSMilanka Ringwald     uint16_t pos = 0;
36572a8858fSMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
36672a8858fSMilanka Ringwald     pos++;  // skip len
36772a8858fSMilanka Ringwald     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
36872a8858fSMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
36972a8858fSMilanka Ringwald     pos += 2;
37072a8858fSMilanka Ringwald     buffer[pos++] = report_id;
37172a8858fSMilanka Ringwald     little_endian_store_16(buffer, pos, report_len);
37272a8858fSMilanka Ringwald     pos += 2;
37372a8858fSMilanka Ringwald     buffer[1] = pos + report_len - 2;
37472a8858fSMilanka Ringwald }
37572a8858fSMilanka Ringwald 
37672a8858fSMilanka Ringwald static void handle_boot_keyboard_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
3776bcfb631SMilanka Ringwald     UNUSED(packet_type);
3786bcfb631SMilanka Ringwald     UNUSED(channel);
3796bcfb631SMilanka Ringwald     UNUSED(size);
38072a8858fSMilanka Ringwald 
38172a8858fSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
38272a8858fSMilanka Ringwald 
38372a8858fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
38472a8858fSMilanka Ringwald     btstack_assert(client != NULL);
38572a8858fSMilanka Ringwald 
38672a8858fSMilanka Ringwald     uint8_t * in_place_event = packet;
38772a8858fSMilanka Ringwald     hids_client_setup_report_event(client, HID_BOOT_MODE_KEYBOARD_ID, in_place_event, gatt_event_notification_get_value_length(packet));
38872a8858fSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size);
38972a8858fSMilanka Ringwald }
39072a8858fSMilanka Ringwald 
39172a8858fSMilanka Ringwald static void handle_boot_mouse_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
39272a8858fSMilanka Ringwald     UNUSED(packet_type);
39372a8858fSMilanka Ringwald     UNUSED(channel);
39472a8858fSMilanka Ringwald     UNUSED(size);
39572a8858fSMilanka Ringwald 
39672a8858fSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
39772a8858fSMilanka Ringwald 
39872a8858fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
39972a8858fSMilanka Ringwald     btstack_assert(client != NULL);
40072a8858fSMilanka Ringwald 
40172a8858fSMilanka Ringwald     uint8_t * in_place_event = packet;
40272a8858fSMilanka Ringwald     hids_client_setup_report_event(client, HID_BOOT_MODE_MOUSE_ID, in_place_event, gatt_event_notification_get_value_length(packet));
40372a8858fSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size);
404cf26c8fbSMilanka Ringwald }
405cf26c8fbSMilanka Ringwald 
4061624214bSMilanka Ringwald 
407cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
408cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
409cf26c8fbSMilanka Ringwald     UNUSED(channel);
410cf26c8fbSMilanka Ringwald     UNUSED(size);
411cf26c8fbSMilanka Ringwald 
4126bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
413cf26c8fbSMilanka Ringwald     uint8_t att_status;
4146bcfb631SMilanka Ringwald     gatt_client_service_t service;
4156bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
41670093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
417ab116b1cSMilanka Ringwald 
418ab116b1cSMilanka Ringwald     hids_client_report_t * boot_keyboard_report;
419ab116b1cSMilanka Ringwald     hids_client_report_t * boot_mouse_report;
420*e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
421cf26c8fbSMilanka Ringwald 
422cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
423cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
424cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
425cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
426cf26c8fbSMilanka Ringwald 
4276bcfb631SMilanka Ringwald             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
4286bcfb631SMilanka Ringwald                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
4296bcfb631SMilanka Ringwald                 hids_finalize_client(client);
4306bcfb631SMilanka Ringwald                 break;
4316bcfb631SMilanka Ringwald             }
4326bcfb631SMilanka Ringwald 
4336bcfb631SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
4346bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
4356bcfb631SMilanka Ringwald                 client->services[client->num_instances].start_handle = service.start_group_handle;
4366bcfb631SMilanka Ringwald                 client->services[client->num_instances].end_handle = service.end_group_handle;
4376bcfb631SMilanka Ringwald             }
4386bcfb631SMilanka Ringwald             client->num_instances++;
4396bcfb631SMilanka Ringwald             break;
4406bcfb631SMilanka Ringwald 
4416bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
4426bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
4436bcfb631SMilanka Ringwald             btstack_assert(client != NULL);
4446bcfb631SMilanka Ringwald 
4456bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
4466bcfb631SMilanka Ringwald             switch (characteristic.uuid16){
44770093cf5SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
44870093cf5SMilanka Ringwald                     client->protocol_mode_value_handle = characteristic.value_handle;
44970093cf5SMilanka Ringwald                     break;
45070093cf5SMilanka Ringwald 
4516bcfb631SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
452ab116b1cSMilanka Ringwald                     hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT);
4536bcfb631SMilanka Ringwald                     break;
4541624214bSMilanka Ringwald 
4556bcfb631SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
456ab116b1cSMilanka Ringwald                     hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT);
4576bcfb631SMilanka Ringwald                     break;
4581624214bSMilanka Ringwald 
4591624214bSMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
460ab116b1cSMilanka Ringwald                     hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT);
4611624214bSMilanka Ringwald                     break;
4621624214bSMilanka Ringwald 
46370093cf5SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
46470093cf5SMilanka Ringwald                     hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED);
46570093cf5SMilanka Ringwald                     break;
46670093cf5SMilanka Ringwald 
46770093cf5SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
46870093cf5SMilanka Ringwald                     // printf("ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP\n");
469*e7bd2dbeSMilanka Ringwald                     hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_MAP_ID, HID_REPORT_TYPE_RESERVED);
47070093cf5SMilanka Ringwald                     break;
47170093cf5SMilanka Ringwald 
47270093cf5SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
47370093cf5SMilanka Ringwald                     // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION\n");
47470093cf5SMilanka Ringwald                     hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_INFORMATION_ID, HID_REPORT_TYPE_INPUT);
47570093cf5SMilanka Ringwald                     break;
47670093cf5SMilanka Ringwald 
47770093cf5SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
47870093cf5SMilanka Ringwald                     // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT\n");
47970093cf5SMilanka Ringwald                     hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_CONTROL_POINT_ID, HID_REPORT_TYPE_INPUT);
48070093cf5SMilanka Ringwald                     break;
4816bcfb631SMilanka Ringwald                 default:
4826bcfb631SMilanka Ringwald                     break;
4836bcfb631SMilanka Ringwald             }
484cf26c8fbSMilanka Ringwald             break;
485cf26c8fbSMilanka Ringwald 
486*e7bd2dbeSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
487*e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
488*e7bd2dbeSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
489*e7bd2dbeSMilanka Ringwald             btstack_assert(client != NULL);
490*e7bd2dbeSMilanka Ringwald             // TODO get value and store it
491*e7bd2dbeSMilanka Ringwald             break;
492*e7bd2dbeSMilanka Ringwald 
49370093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
49470093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
49570093cf5SMilanka Ringwald             btstack_assert(client != NULL);
49670093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
49770093cf5SMilanka Ringwald 
498*e7bd2dbeSMilanka Ringwald             switch (client->state) {
499*e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
500*e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
501*e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
502*e7bd2dbeSMilanka Ringwald                         client->descriptor_handle = characteristic_descriptor.handle;
503*e7bd2dbeSMilanka Ringwald                     }
504*e7bd2dbeSMilanka Ringwald                     break;
505*e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTORS_RESULT:
50670093cf5SMilanka Ringwald                     // setup for descriptor value query
50770093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
50870093cf5SMilanka Ringwald                         client->descriptor_handle = characteristic_descriptor.handle;
50970093cf5SMilanka Ringwald                     }
51070093cf5SMilanka Ringwald                     break;
511*e7bd2dbeSMilanka Ringwald                 default:
512*e7bd2dbeSMilanka Ringwald                     break;
513*e7bd2dbeSMilanka Ringwald             }
514*e7bd2dbeSMilanka Ringwald             break;
51570093cf5SMilanka Ringwald 
51670093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
51770093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
51870093cf5SMilanka Ringwald             btstack_assert(client != NULL);
519*e7bd2dbeSMilanka Ringwald 
520*e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
521*e7bd2dbeSMilanka Ringwald                 break;
522*e7bd2dbeSMilanka Ringwald             }
523*e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
524*e7bd2dbeSMilanka Ringwald 
525*e7bd2dbeSMilanka Ringwald             switch (client->state) {
526*e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
527*e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
528*e7bd2dbeSMilanka Ringwald                     client->reports[client->active_report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
529*e7bd2dbeSMilanka Ringwald                     break;
530*e7bd2dbeSMilanka Ringwald 
531*e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTORS_RESULT:
532*e7bd2dbeSMilanka Ringwald                     client->reports[client->active_report_index].report_id = characteristic_descriptor_value[0];
533*e7bd2dbeSMilanka Ringwald                     client->reports[client->active_report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
534*e7bd2dbeSMilanka Ringwald                     break;
535*e7bd2dbeSMilanka Ringwald 
536*e7bd2dbeSMilanka Ringwald                 default:
537*e7bd2dbeSMilanka Ringwald                     break;
53870093cf5SMilanka Ringwald             }
53970093cf5SMilanka Ringwald             break;
54070093cf5SMilanka Ringwald 
541cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
542cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
543cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
544cf26c8fbSMilanka Ringwald 
545cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
546cf26c8fbSMilanka Ringwald 
547cf26c8fbSMilanka Ringwald             switch (client->state){
548cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
5496bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
5506bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
5516bcfb631SMilanka Ringwald                         hids_finalize_client(client);
552cf26c8fbSMilanka Ringwald                         break;
5536bcfb631SMilanka Ringwald                     }
5546bcfb631SMilanka Ringwald 
5556bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
5566bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
5576bcfb631SMilanka Ringwald                         hids_finalize_client(client);
5586bcfb631SMilanka Ringwald                         break;
5596bcfb631SMilanka Ringwald                     }
5606bcfb631SMilanka Ringwald 
5616bcfb631SMilanka Ringwald                     if (client->num_instances > MAX_NUM_HID_SERVICES) {
5626bcfb631SMilanka 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);
5636bcfb631SMilanka Ringwald                         client->num_instances = MAX_NUM_HID_SERVICES;
5646bcfb631SMilanka Ringwald                     }
5656bcfb631SMilanka Ringwald 
5666bcfb631SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
5676bcfb631SMilanka Ringwald                     client->service_index = 0;
5686bcfb631SMilanka Ringwald                     break;
5696bcfb631SMilanka Ringwald 
5706bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
5716bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
5726bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
5736bcfb631SMilanka Ringwald                         hids_finalize_client(client);
5746bcfb631SMilanka Ringwald                         break;
5756bcfb631SMilanka Ringwald                     }
5766bcfb631SMilanka Ringwald 
5776bcfb631SMilanka Ringwald                     switch (client->required_protocol_mode){
5786bcfb631SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
579ab116b1cSMilanka Ringwald                             if (get_boot_keyboard_input_report(client) != NULL){
5802901a9b7SMilanka Ringwald                                 client->state = HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD;
5816bcfb631SMilanka Ringwald                                 break;
5826bcfb631SMilanka Ringwald                             }
583ab116b1cSMilanka Ringwald                             if (get_boot_mouse_input_report(client) != NULL){
5842901a9b7SMilanka Ringwald                                 client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE;
5856bcfb631SMilanka Ringwald                                 break;
5866bcfb631SMilanka Ringwald                             }
5876bcfb631SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
5886bcfb631SMilanka Ringwald                             hids_finalize_client(client);
5896bcfb631SMilanka Ringwald                             break;
59070093cf5SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
59170093cf5SMilanka Ringwald                             if ((client->service_index + 1) < client->num_instances){
59270093cf5SMilanka Ringwald                                 // discover characteristics of next service
59370093cf5SMilanka Ringwald                                 client->service_index++;
59470093cf5SMilanka Ringwald                                 client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
59570093cf5SMilanka Ringwald                             } else {
59670093cf5SMilanka Ringwald 
59770093cf5SMilanka Ringwald                                 // discover characteristic descriptor for all Report characteristics,
59870093cf5SMilanka Ringwald                                 // then read value of characteristic descriptor to get Report ID
59970093cf5SMilanka Ringwald                                 client->active_report_index = 0;
60070093cf5SMilanka Ringwald                                 client->service_index = 0;
60170093cf5SMilanka Ringwald 
602*e7bd2dbeSMilanka Ringwald                                 hids_client_get_next_active_report_map_index(client);
60370093cf5SMilanka Ringwald                                 if (client->active_report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
604*e7bd2dbeSMilanka Ringwald                                     client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_CHARACTERISTIC_VALUE;
60570093cf5SMilanka Ringwald                                     break;
60670093cf5SMilanka Ringwald                                 }
60770093cf5SMilanka Ringwald                                 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
60870093cf5SMilanka Ringwald                                 hids_finalize_client(client);
60970093cf5SMilanka Ringwald                             }
61070093cf5SMilanka Ringwald                             break;
6116bcfb631SMilanka Ringwald                         default:
6126bcfb631SMilanka Ringwald                             break;
6136bcfb631SMilanka Ringwald                     }
6146bcfb631SMilanka Ringwald                     break;
6156bcfb631SMilanka Ringwald 
616*e7bd2dbeSMilanka Ringwald 
617*e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_VALUE_RESULT:
618*e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
619*e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
620*e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
621*e7bd2dbeSMilanka Ringwald                         break;
622*e7bd2dbeSMilanka Ringwald                     }
623*e7bd2dbeSMilanka Ringwald                     // HID Descriptor found, check for external Report IDs (read report map descriptor)
624*e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
625*e7bd2dbeSMilanka Ringwald                     break;
626*e7bd2dbeSMilanka Ringwald 
627*e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
628*e7bd2dbeSMilanka Ringwald                     if (client->descriptor_handle != 0){
629*e7bd2dbeSMilanka Ringwald                         // descriptor found
630*e7bd2dbeSMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_CHARACTERISTIC_DESCRIPTOR_VALUE;
631*e7bd2dbeSMilanka Ringwald                         break;
632*e7bd2dbeSMilanka Ringwald                     }
633*e7bd2dbeSMilanka Ringwald 
634*e7bd2dbeSMilanka Ringwald                     // go for next report map
635*e7bd2dbeSMilanka Ringwald                     client->active_report_index++;
636*e7bd2dbeSMilanka Ringwald                     if (hids_client_get_next_active_report_map_index(client)){
637*e7bd2dbeSMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
638*e7bd2dbeSMilanka Ringwald                         break;
639*e7bd2dbeSMilanka Ringwald                     }
640*e7bd2dbeSMilanka Ringwald 
641*e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
642*e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
643*e7bd2dbeSMilanka Ringwald                     client->active_report_index = 0;
644*e7bd2dbeSMilanka Ringwald                     client->service_index = 0;
645*e7bd2dbeSMilanka Ringwald                     if (hids_client_get_next_active_report_index(client)){
646*e7bd2dbeSMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_REPORT_QUERY_CHARACTERISTIC_DESCRIPTORS;
647*e7bd2dbeSMilanka Ringwald                         break;
648*e7bd2dbeSMilanka Ringwald                     }
649*e7bd2dbeSMilanka Ringwald 
650*e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
651*e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
652*e7bd2dbeSMilanka Ringwald                     break;
653*e7bd2dbeSMilanka Ringwald 
654*e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTOR_VALUE_RESULT:
655*e7bd2dbeSMilanka Ringwald                     if (client->descriptor_handle != 0){
656*e7bd2dbeSMilanka Ringwald                         // descriptor found
657*e7bd2dbeSMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
658*e7bd2dbeSMilanka Ringwald                         break;
659*e7bd2dbeSMilanka Ringwald                     }
660*e7bd2dbeSMilanka Ringwald 
661*e7bd2dbeSMilanka Ringwald                     // go for next map report
662*e7bd2dbeSMilanka Ringwald                     client->active_report_index++;
663*e7bd2dbeSMilanka Ringwald                     if (hids_client_get_next_active_report_map_index(client)){
664*e7bd2dbeSMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
665*e7bd2dbeSMilanka Ringwald                         break;
666*e7bd2dbeSMilanka Ringwald                     }
667*e7bd2dbeSMilanka Ringwald 
668*e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
669*e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
670*e7bd2dbeSMilanka Ringwald                     client->active_report_index = 0;
671*e7bd2dbeSMilanka Ringwald                     client->service_index = 0;
672*e7bd2dbeSMilanka Ringwald                     if (hids_client_get_next_active_report_index(client)){
673*e7bd2dbeSMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_REPORT_QUERY_CHARACTERISTIC_DESCRIPTORS;
674*e7bd2dbeSMilanka Ringwald                         break;
675*e7bd2dbeSMilanka Ringwald                     }
676*e7bd2dbeSMilanka Ringwald 
677*e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
678*e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
679*e7bd2dbeSMilanka Ringwald                     break;
680*e7bd2dbeSMilanka Ringwald 
681*e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
682*e7bd2dbeSMilanka Ringwald                     // go for next map report
683*e7bd2dbeSMilanka Ringwald                     client->active_report_index++;
684*e7bd2dbeSMilanka Ringwald                     if (hids_client_get_next_active_report_map_index(client)){
685*e7bd2dbeSMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
686*e7bd2dbeSMilanka Ringwald                         break;
687*e7bd2dbeSMilanka Ringwald                     }
688*e7bd2dbeSMilanka Ringwald 
689*e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
690*e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
691*e7bd2dbeSMilanka Ringwald                     client->active_report_index = 0;
692*e7bd2dbeSMilanka Ringwald                     client->service_index = 0;
693*e7bd2dbeSMilanka Ringwald                     if (hids_client_get_next_active_report_index(client)){
694*e7bd2dbeSMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_REPORT_QUERY_CHARACTERISTIC_DESCRIPTORS;
695*e7bd2dbeSMilanka Ringwald                         break;
696*e7bd2dbeSMilanka Ringwald                     }
697*e7bd2dbeSMilanka Ringwald 
698*e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
699*e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
700*e7bd2dbeSMilanka Ringwald                     break;
701*e7bd2dbeSMilanka Ringwald 
70270093cf5SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTORS_RESULT:
70370093cf5SMilanka Ringwald                     if (client->descriptor_handle != 0){
70470093cf5SMilanka Ringwald                         // descriptor found
70570093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_REPORT_READ_CHARACTERISTIC_DESCRIPTOR_VALUE;
70670093cf5SMilanka Ringwald                         break;
70770093cf5SMilanka Ringwald                     }
70870093cf5SMilanka Ringwald 
70970093cf5SMilanka Ringwald                     // go for next report
71070093cf5SMilanka Ringwald                     client->active_report_index++;
711*e7bd2dbeSMilanka Ringwald                     if (hids_client_get_next_active_report_index(client)){
71270093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_REPORT_QUERY_CHARACTERISTIC_DESCRIPTORS;
71370093cf5SMilanka Ringwald                         break;
71470093cf5SMilanka Ringwald                     }
71570093cf5SMilanka Ringwald 
71670093cf5SMilanka Ringwald                     // TODO continue with report mode
71770093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
71870093cf5SMilanka Ringwald                     break;
71970093cf5SMilanka Ringwald 
72070093cf5SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_CHARACTERISTIC_DESCRIPTOR_VALUE_RESULT:
72170093cf5SMilanka Ringwald                     // go for next report
72270093cf5SMilanka Ringwald                     client->active_report_index++;
723*e7bd2dbeSMilanka Ringwald                     if (hids_client_get_next_active_report_index(client)){
72470093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_REPORT_QUERY_CHARACTERISTIC_DESCRIPTORS;
72570093cf5SMilanka Ringwald                         break;
72670093cf5SMilanka Ringwald                     }
72770093cf5SMilanka Ringwald 
72870093cf5SMilanka Ringwald                     // TODO continue with report mode
72970093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
73070093cf5SMilanka Ringwald                     break;
73170093cf5SMilanka Ringwald 
7326bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED:
733ab116b1cSMilanka Ringwald                     boot_keyboard_report = get_boot_keyboard_input_report(client);
734ab116b1cSMilanka Ringwald                     if (boot_keyboard_report != NULL){
7356bcfb631SMilanka Ringwald                         // setup listener
736ab116b1cSMilanka Ringwald                         characteristic.value_handle = boot_keyboard_report->value_handle;
737ab116b1cSMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(&boot_keyboard_report->notifications, &handle_boot_keyboard_hid_event, client->con_handle, &characteristic);
7382901a9b7SMilanka Ringwald                     }
7392901a9b7SMilanka Ringwald 
740ab116b1cSMilanka Ringwald                     // check if there is mouse input report
741ab116b1cSMilanka Ringwald                     boot_mouse_report = get_boot_mouse_input_report(client);
742ab116b1cSMilanka Ringwald                     if (boot_mouse_report != NULL){
7432901a9b7SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE;
7442901a9b7SMilanka Ringwald                         break;
7452901a9b7SMilanka Ringwald                     }
7462901a9b7SMilanka Ringwald 
747ab116b1cSMilanka Ringwald                     // if none of the reports is found bail out
748ab116b1cSMilanka Ringwald                     if (!boot_mouse_report && !boot_keyboard_report){
749ab116b1cSMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
750ab116b1cSMilanka Ringwald                         hids_finalize_client(client);
751ab116b1cSMilanka Ringwald                         break;
752ab116b1cSMilanka Ringwald                     }
753ab116b1cSMilanka Ringwald 
7546bcfb631SMilanka Ringwald                     // set protocol
7556bcfb631SMilanka Ringwald                     if (client->protocol_mode_value_handle != 0){
7562901a9b7SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE;
7576bcfb631SMilanka Ringwald                     } else {
7582901a9b7SMilanka Ringwald                         client->protocol_mode = HID_PROTOCOL_MODE_BOOT;
7596bcfb631SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
7606bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
7616bcfb631SMilanka Ringwald                     }
7622901a9b7SMilanka Ringwald                     hids_run_for_client(client);
7636bcfb631SMilanka Ringwald                     break;
7646bcfb631SMilanka Ringwald 
7656bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_MOUSE_ENABLED:
766ab116b1cSMilanka Ringwald                     boot_mouse_report = get_boot_mouse_input_report(client);
767ab116b1cSMilanka Ringwald                     if (boot_mouse_report != NULL){
7686bcfb631SMilanka Ringwald                         // setup listener
769ab116b1cSMilanka Ringwald                         characteristic.value_handle = boot_mouse_report->value_handle;
770ab116b1cSMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(&boot_mouse_report->notifications, &handle_boot_mouse_hid_event, client->con_handle, &characteristic);
771ab116b1cSMilanka Ringwald                     }
772ab116b1cSMilanka Ringwald 
773ab116b1cSMilanka Ringwald                     boot_keyboard_report = get_boot_keyboard_input_report(client);
774ab116b1cSMilanka Ringwald                     if (!boot_mouse_report && !boot_keyboard_report){
7752901a9b7SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
7762901a9b7SMilanka Ringwald                         hids_finalize_client(client);
7772901a9b7SMilanka Ringwald                         break;
7782901a9b7SMilanka Ringwald                     }
7796bcfb631SMilanka Ringwald 
7806bcfb631SMilanka Ringwald                     if (client->protocol_mode_value_handle != 0){
7812901a9b7SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE;
7826bcfb631SMilanka Ringwald                     } else {
7832901a9b7SMilanka Ringwald                         client->protocol_mode = HID_PROTOCOL_MODE_BOOT;
7846bcfb631SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
7856bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
7866bcfb631SMilanka Ringwald                     }
7876bcfb631SMilanka Ringwald                     break;
788cf26c8fbSMilanka Ringwald                 default:
789cf26c8fbSMilanka Ringwald                     break;
790cf26c8fbSMilanka Ringwald             }
791cf26c8fbSMilanka Ringwald             break;
792cf26c8fbSMilanka Ringwald 
793cf26c8fbSMilanka Ringwald         default:
794cf26c8fbSMilanka Ringwald             break;
795cf26c8fbSMilanka Ringwald     }
7966bcfb631SMilanka Ringwald 
7976bcfb631SMilanka Ringwald     if (client != NULL){
7986bcfb631SMilanka Ringwald         hids_run_for_client(client);
7996bcfb631SMilanka Ringwald     }
800cf26c8fbSMilanka Ringwald }
801cf26c8fbSMilanka Ringwald 
8026bcfb631SMilanka 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){
803cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
804cf26c8fbSMilanka Ringwald 
805cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
806cf26c8fbSMilanka Ringwald     if (client != NULL){
807cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
808cf26c8fbSMilanka Ringwald     }
809cf26c8fbSMilanka Ringwald 
810cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
811cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
812cf26c8fbSMilanka Ringwald         *hids_cid = cid;
813cf26c8fbSMilanka Ringwald     }
814cf26c8fbSMilanka Ringwald 
815cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
816cf26c8fbSMilanka Ringwald     if (client == NULL) {
817cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
818cf26c8fbSMilanka Ringwald     }
819cf26c8fbSMilanka Ringwald 
8206bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
821cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
822cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
823cf26c8fbSMilanka Ringwald 
824cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
825cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
826cf26c8fbSMilanka Ringwald }
827cf26c8fbSMilanka Ringwald 
828cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
829cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
830cf26c8fbSMilanka Ringwald     if (client == NULL){
831cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
832cf26c8fbSMilanka Ringwald     }
833cf26c8fbSMilanka Ringwald     // finalize connection
834cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
835cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
836cf26c8fbSMilanka Ringwald }
837cf26c8fbSMilanka Ringwald 
8381624214bSMilanka Ringwald uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){
8391624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
8401624214bSMilanka Ringwald     if (client == NULL){
8411624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
8421624214bSMilanka Ringwald     }
8431624214bSMilanka Ringwald 
8441624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
8451624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
8461624214bSMilanka Ringwald     }
8471624214bSMilanka Ringwald 
8482fe59e00SMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT);
8491624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
8501624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
8511624214bSMilanka Ringwald     }
8521624214bSMilanka Ringwald 
8531624214bSMilanka Ringwald     uint16_t mtu;
8541624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
8551624214bSMilanka Ringwald 
8561624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
8571624214bSMilanka Ringwald         return status;
8581624214bSMilanka Ringwald     }
8591624214bSMilanka Ringwald 
8601624214bSMilanka Ringwald     if (mtu - 2 < report_len){
8611624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
8621624214bSMilanka Ringwald     }
8631624214bSMilanka Ringwald 
8641624214bSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_REPORT;
8651624214bSMilanka Ringwald     client->active_report_index = report_index;
8661624214bSMilanka Ringwald     client->report = report;
8671624214bSMilanka Ringwald     client->report_len = report_len;
8681624214bSMilanka Ringwald 
8691624214bSMilanka Ringwald     hids_run_for_client(client);
8701624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
8711624214bSMilanka Ringwald }
8721624214bSMilanka Ringwald 
873cf26c8fbSMilanka Ringwald void hids_client_init(void){}
874cf26c8fbSMilanka Ringwald 
875cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
876