xref: /btstack/src/ble/gatt-service/hids_client.c (revision 1914678990625d883f1e58fafb697f2c235f8c72)
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 
42708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
43708c69d2SMilanka Ringwald #include <stdio.h>
44708c69d2SMilanka Ringwald #endif
45708c69d2SMilanka Ringwald 
46fc975d0eSMilanka Ringwald #include <stdint.h>
47fc975d0eSMilanka Ringwald #include <string.h>
48fc975d0eSMilanka Ringwald 
49cf26c8fbSMilanka Ringwald #include "ble/gatt-service/hids_client.h"
50fc975d0eSMilanka Ringwald 
51cf26c8fbSMilanka Ringwald #include "btstack_memory.h"
52fc975d0eSMilanka Ringwald #include "ble/att_db.h"
53fc975d0eSMilanka Ringwald #include "ble/core.h"
54fc975d0eSMilanka Ringwald #include "ble/gatt_client.h"
55fc975d0eSMilanka Ringwald #include "ble/sm.h"
56cf26c8fbSMilanka Ringwald #include "bluetooth_gatt.h"
57fc975d0eSMilanka Ringwald #include "btstack_debug.h"
58fc975d0eSMilanka Ringwald #include "btstack_event.h"
59fc975d0eSMilanka Ringwald #include "btstack_run_loop.h"
60fc975d0eSMilanka Ringwald #include "gap.h"
61fc975d0eSMilanka Ringwald 
6270093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
6370093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6470093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6570093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6670093cf5SMilanka Ringwald 
67da142a6fSMilanka Ringwald static btstack_linked_list_t clients;
68da142a6fSMilanka Ringwald static uint16_t hids_cid_counter = 0;
69da142a6fSMilanka Ringwald 
70da142a6fSMilanka Ringwald static uint8_t * hids_client_descriptor_storage;
71da142a6fSMilanka Ringwald static uint16_t  hids_client_descriptor_storage_len;
72da142a6fSMilanka Ringwald 
73cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
74cf26c8fbSMilanka Ringwald 
75556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
76556456ccSMilanka Ringwald static char * hid_characteristic_name(uint16_t uuid){
77556456ccSMilanka Ringwald     switch (uuid){
78556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
79556456ccSMilanka Ringwald             return "PROTOCOL_MODE";
80556456ccSMilanka Ringwald 
81556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
82556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_INPUT_REPORT";
83556456ccSMilanka Ringwald 
84556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
85556456ccSMilanka Ringwald             return "BOOT_MOUSE_INPUT_REPORT";
86556456ccSMilanka Ringwald 
87556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
88556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_OUTPUT_REPORT";
89556456ccSMilanka Ringwald 
90556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
91556456ccSMilanka Ringwald             return "REPORT";
92556456ccSMilanka Ringwald 
93556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
94556456ccSMilanka Ringwald             return "REPORT_MAP";
95556456ccSMilanka Ringwald 
96556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
97556456ccSMilanka Ringwald             return "HID_INFORMATION";
98556456ccSMilanka Ringwald 
99556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
100556456ccSMilanka Ringwald             return "HID_CONTROL_POINT";
101556456ccSMilanka Ringwald         default:
102556456ccSMilanka Ringwald             return "UKNOWN";
103556456ccSMilanka Ringwald     }
104556456ccSMilanka Ringwald }
105556456ccSMilanka Ringwald #endif
106556456ccSMilanka Ringwald 
107da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
108da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
109da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
110da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
111da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
112da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
113da142a6fSMilanka Ringwald         return client;
114da142a6fSMilanka Ringwald     }
115da142a6fSMilanka Ringwald     return NULL;
116da142a6fSMilanka Ringwald }
117da142a6fSMilanka Ringwald 
118da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
119da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
120da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
121da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
122da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
123da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
124da142a6fSMilanka Ringwald         return client;
125da142a6fSMilanka Ringwald     }
126da142a6fSMilanka Ringwald     return NULL;
127da142a6fSMilanka Ringwald }
128da142a6fSMilanka Ringwald 
129da142a6fSMilanka Ringwald 
130da142a6fSMilanka Ringwald // START Descriptor Storage Util
131da142a6fSMilanka Ringwald 
132da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
133da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
134da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
135da142a6fSMilanka Ringwald     uint8_t i;
136da142a6fSMilanka Ringwald 
137da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
138da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
139da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
140da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
141da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
142da142a6fSMilanka Ringwald             free_space -= client->services[i].hid_descriptor_len;
143da142a6fSMilanka Ringwald         }
144da142a6fSMilanka Ringwald     }
145da142a6fSMilanka Ringwald     return free_space;
146da142a6fSMilanka Ringwald }
147da142a6fSMilanka Ringwald 
148da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
149da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
150da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_max_len = hids_client_descriptor_storage_get_available_space();
151da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - client->services[service_index].hid_descriptor_max_len;
152da142a6fSMilanka Ringwald }
153da142a6fSMilanka Ringwald 
154da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
155da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
156da142a6fSMilanka Ringwald 
157da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
158da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
159da142a6fSMilanka Ringwald     return true;
160da142a6fSMilanka Ringwald }
161da142a6fSMilanka Ringwald 
162da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
163da142a6fSMilanka Ringwald     uint8_t service_index = 0;
164da142a6fSMilanka Ringwald     uint16_t next_offset = 0;
165da142a6fSMilanka Ringwald 
166da142a6fSMilanka Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
167da142a6fSMilanka Ringwald         next_offset += client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len;
168da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_len = 0;
169da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_offset = 0;
170da142a6fSMilanka Ringwald     }
171da142a6fSMilanka Ringwald 
172da142a6fSMilanka Ringwald     memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
173da142a6fSMilanka Ringwald             &hids_client_descriptor_storage[next_offset],
174da142a6fSMilanka Ringwald             hids_client_descriptor_storage_len - next_offset);
175da142a6fSMilanka Ringwald 
176da142a6fSMilanka Ringwald     uint8_t i;
177da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
178da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
179da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
180da142a6fSMilanka Ringwald         hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
181da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
182da142a6fSMilanka Ringwald             if (conn->services[i].hid_descriptor_offset >= next_offset){
183da142a6fSMilanka Ringwald                 conn->services[i].hid_descriptor_offset = next_offset;
184da142a6fSMilanka Ringwald                 next_offset += conn->services[service_index].hid_descriptor_len;
185da142a6fSMilanka Ringwald             }
186da142a6fSMilanka Ringwald         }
187da142a6fSMilanka Ringwald     }
188da142a6fSMilanka Ringwald }
189da142a6fSMilanka Ringwald 
190da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
191da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
192021192e1SMilanka Ringwald     if (client == NULL){
193021192e1SMilanka Ringwald         return NULL;
194021192e1SMilanka Ringwald     }
195021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
196da142a6fSMilanka Ringwald         return NULL;
197da142a6fSMilanka Ringwald     }
198da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
199da142a6fSMilanka Ringwald }
200da142a6fSMilanka Ringwald 
201da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
202da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
203021192e1SMilanka Ringwald     if (client == NULL){
204021192e1SMilanka Ringwald         return 0;
205021192e1SMilanka Ringwald     }
206021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
207da142a6fSMilanka Ringwald         return 0;
208da142a6fSMilanka Ringwald     }
209da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
210da142a6fSMilanka Ringwald }
211da142a6fSMilanka Ringwald 
212da142a6fSMilanka Ringwald // END Descriptor Storage Util
213da142a6fSMilanka Ringwald 
214cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
215cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
216cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
217cf26c8fbSMilanka Ringwald     } else {
218cf26c8fbSMilanka Ringwald         hids_cid_counter++;
219cf26c8fbSMilanka Ringwald     }
220cf26c8fbSMilanka Ringwald     return hids_cid_counter;
221fc975d0eSMilanka Ringwald }
222fc975d0eSMilanka Ringwald 
223ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
224ab116b1cSMilanka Ringwald     uint8_t i;
225a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
226ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
227ab116b1cSMilanka Ringwald             return i;
228ab116b1cSMilanka Ringwald         }
229ab116b1cSMilanka Ringwald     }
230ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
231ab116b1cSMilanka Ringwald }
232ab116b1cSMilanka Ringwald 
233*19146789SMilanka Ringwald static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
234*19146789SMilanka Ringwald     uint8_t i;
235*19146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
236*19146789SMilanka Ringwald         if (client->external_reports[i].value_handle == value_handle){
237*19146789SMilanka Ringwald             return i;
238*19146789SMilanka Ringwald         }
239*19146789SMilanka Ringwald     }
240*19146789SMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
241*19146789SMilanka Ringwald }
242*19146789SMilanka Ringwald 
243*19146789SMilanka Ringwald static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
244*19146789SMilanka Ringwald     uint8_t i;
245*19146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
246*19146789SMilanka Ringwald         if (client->external_reports[i].external_report_reference_uuid == uuid16){
247*19146789SMilanka Ringwald             return true;
248*19146789SMilanka Ringwald         }
249*19146789SMilanka Ringwald     }
250*19146789SMilanka Ringwald     return false;
251*19146789SMilanka Ringwald }
252*19146789SMilanka Ringwald 
253*19146789SMilanka Ringwald 
254*19146789SMilanka Ringwald 
255ab116b1cSMilanka 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){
256ab116b1cSMilanka Ringwald     uint8_t i;
257a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
258ab116b1cSMilanka Ringwald         if ( (client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
259ab116b1cSMilanka Ringwald             return i;
260ab116b1cSMilanka Ringwald         }
261ab116b1cSMilanka Ringwald     }
262ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
263ab116b1cSMilanka Ringwald }
264ab116b1cSMilanka Ringwald 
265021192e1SMilanka Ringwald static uint8_t hids_client_add_characteristic(hids_client_t * client, gatt_client_characteristic_t * characteristic, uint8_t report_id, hid_report_type_t report_type, bool boot_report){
266ab116b1cSMilanka Ringwald 
267*19146789SMilanka Ringwald     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
268ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
2693322b222SMilanka Ringwald         return report_index;
270ab116b1cSMilanka Ringwald     }
27128da36a6SMilanka Ringwald     report_index = client->num_reports;
272ab116b1cSMilanka Ringwald 
27328da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
27428da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
27528da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
27628da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
277ab116b1cSMilanka Ringwald 
27828da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
27928da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
28028da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
281021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
28270093cf5SMilanka Ringwald 
283021192e1SMilanka Ringwald         log_info("add index %d, id %d, type %d, value handle 0x%02x, properties 0x%02x", report_index, report_id, report_type, characteristic->value_handle, characteristic->properties);
284ab116b1cSMilanka Ringwald         client->num_reports++;
2853322b222SMilanka Ringwald         return report_index;
286ab116b1cSMilanka Ringwald     } else {
287ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
2883322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
289ab116b1cSMilanka Ringwald     }
290ab116b1cSMilanka Ringwald }
291ab116b1cSMilanka Ringwald 
292*19146789SMilanka Ringwald static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
293556456ccSMilanka Ringwald     uint8_t report_index = client->num_external_reports;
294556456ccSMilanka Ringwald 
295556456ccSMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
296*19146789SMilanka Ringwald         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
297556456ccSMilanka Ringwald         client->external_reports[report_index].service_index = client->service_index;
298*19146789SMilanka Ringwald 
299556456ccSMilanka Ringwald         client->num_external_reports++;
300*19146789SMilanka Ringwald         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
301556456ccSMilanka Ringwald         return report_index;
302556456ccSMilanka Ringwald     } else {
303556456ccSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
304556456ccSMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
30570093cf5SMilanka Ringwald     }
306556456ccSMilanka Ringwald }
307556456ccSMilanka Ringwald 
30870093cf5SMilanka Ringwald 
309021192e1SMilanka Ringwald static bool hid_clients_has_reports_in_report_mode(hids_client_t * client){
310021192e1SMilanka Ringwald     uint8_t i;
311021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
312021192e1SMilanka Ringwald         if (!client->reports[i].boot_report){
313021192e1SMilanka Ringwald             return true;
314ab116b1cSMilanka Ringwald         }
315021192e1SMilanka Ringwald     }
316021192e1SMilanka Ringwald     return false;
317021192e1SMilanka Ringwald }
318021192e1SMilanka Ringwald 
319021192e1SMilanka Ringwald static bool hid_clients_has_reports_in_boot_mode(hids_client_t * client){
320021192e1SMilanka Ringwald     uint8_t i;
321021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
322021192e1SMilanka Ringwald         if (client->reports[i].boot_report){
323021192e1SMilanka Ringwald             return true;
324021192e1SMilanka Ringwald         }
325021192e1SMilanka Ringwald     }
326021192e1SMilanka Ringwald     return false;
327ab116b1cSMilanka Ringwald }
328ab116b1cSMilanka Ringwald 
3297e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
3307e1e6e7dSMilanka Ringwald     uint8_t i;
3313322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
3323322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
333556456ccSMilanka Ringwald             return i;
3343322b222SMilanka Ringwald         }
3353322b222SMilanka Ringwald     }
336556456ccSMilanka Ringwald     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
337556456ccSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
3383322b222SMilanka Ringwald }
3393322b222SMilanka Ringwald 
3403322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
3413322b222SMilanka Ringwald     client->service_index++;
3423322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
343556456ccSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3443322b222SMilanka Ringwald         return true;
3453322b222SMilanka Ringwald     }
3463322b222SMilanka Ringwald     return false;
3473322b222SMilanka Ringwald }
3483322b222SMilanka Ringwald 
3493322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
3503322b222SMilanka Ringwald     client->service_index = 0;
3513322b222SMilanka Ringwald 
3523322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3533322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3543322b222SMilanka Ringwald         return true;
3553322b222SMilanka Ringwald     }
3563322b222SMilanka Ringwald     return false;
3573322b222SMilanka Ringwald }
3583322b222SMilanka Ringwald 
3593322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
360021192e1SMilanka Ringwald     client->report_index++;
361*19146789SMilanka Ringwald     if (client->report_index < client->num_external_reports){
3623322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
363e7bd2dbeSMilanka Ringwald         return true;
364e7bd2dbeSMilanka Ringwald     }
365e7bd2dbeSMilanka Ringwald     return false;
366e7bd2dbeSMilanka Ringwald }
367e7bd2dbeSMilanka Ringwald 
3683322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
369021192e1SMilanka Ringwald     client->report_index = 0;
370*19146789SMilanka Ringwald     if (client->num_external_reports > 0){
3713322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
3727e1e6e7dSMilanka Ringwald         return true;
3737e1e6e7dSMilanka Ringwald     }
3747e1e6e7dSMilanka Ringwald     return false;
3757e1e6e7dSMilanka Ringwald }
3767e1e6e7dSMilanka Ringwald 
3777e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
3787e1e6e7dSMilanka Ringwald     uint8_t i;
3797e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
380021192e1SMilanka Ringwald     switch (client->protocol_mode){
381021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_REPORT:
382021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
38370093cf5SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
38470093cf5SMilanka Ringwald                 if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
3857e1e6e7dSMilanka Ringwald                     index = i;
386021192e1SMilanka Ringwald                     client->service_index = report.service_index;
387021192e1SMilanka Ringwald                 }
388021192e1SMilanka Ringwald             }
389021192e1SMilanka Ringwald             break;
390021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
391021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
392021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
393021192e1SMilanka Ringwald                 if (report.boot_report){
394021192e1SMilanka Ringwald                     index = i;
395021192e1SMilanka Ringwald                     client->service_index = report.service_index;
396021192e1SMilanka Ringwald                 }
397021192e1SMilanka Ringwald             }
398021192e1SMilanka Ringwald             break;
399021192e1SMilanka Ringwald         default:
4007e1e6e7dSMilanka Ringwald             break;
40170093cf5SMilanka Ringwald     }
402021192e1SMilanka Ringwald 
403021192e1SMilanka Ringwald     client->report_index = index;
4047e1e6e7dSMilanka Ringwald     return index;
4057e1e6e7dSMilanka Ringwald }
4067e1e6e7dSMilanka Ringwald 
4077e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
408021192e1SMilanka Ringwald     client->report_index++;
4097e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
41028da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
411e7bd2dbeSMilanka Ringwald         return true;
41270093cf5SMilanka Ringwald     }
4137e1e6e7dSMilanka Ringwald     return false;
4147e1e6e7dSMilanka Ringwald }
4157e1e6e7dSMilanka Ringwald 
4167e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
417021192e1SMilanka Ringwald     client->report_index = 0;
4187e1e6e7dSMilanka Ringwald 
4197e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
42028da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
4217e1e6e7dSMilanka Ringwald         return true;
4227e1e6e7dSMilanka Ringwald     }
423e7bd2dbeSMilanka Ringwald     return false;
42470093cf5SMilanka Ringwald }
425ab116b1cSMilanka Ringwald 
426021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
427021192e1SMilanka Ringwald     uint8_t i;
428021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
429021192e1SMilanka Ringwald 
430021192e1SMilanka Ringwald     switch (client->protocol_mode){
431021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_REPORT:
432021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
433021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
434021192e1SMilanka Ringwald                 if (report.report_type != HID_REPORT_TYPE_INPUT){
435021192e1SMilanka Ringwald                     continue;
436021192e1SMilanka Ringwald                 }
437021192e1SMilanka Ringwald                 if (!report.boot_report){
438021192e1SMilanka Ringwald                     index = i;
439021192e1SMilanka Ringwald                 }
440021192e1SMilanka Ringwald             }
441021192e1SMilanka Ringwald             break;
442021192e1SMilanka Ringwald 
443021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
444021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
445021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
446021192e1SMilanka Ringwald                 if (report.report_type != HID_REPORT_TYPE_INPUT){
447021192e1SMilanka Ringwald                     continue;
448021192e1SMilanka Ringwald                 }
449021192e1SMilanka Ringwald                 if (report.boot_report){
450021192e1SMilanka Ringwald                     index = i;
451021192e1SMilanka Ringwald                 }
452021192e1SMilanka Ringwald             }
453021192e1SMilanka Ringwald             break;
454021192e1SMilanka Ringwald 
455021192e1SMilanka Ringwald         default:
456021192e1SMilanka Ringwald             break;
457021192e1SMilanka Ringwald     }
458021192e1SMilanka Ringwald 
459021192e1SMilanka Ringwald     client->report_index = index;
460021192e1SMilanka Ringwald     return index;
461021192e1SMilanka Ringwald }
462021192e1SMilanka Ringwald 
463021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
464021192e1SMilanka Ringwald     client->report_index++;
465021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
466021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
467021192e1SMilanka Ringwald         return true;
468021192e1SMilanka Ringwald     }
469021192e1SMilanka Ringwald     return false;
470021192e1SMilanka Ringwald }
471021192e1SMilanka Ringwald 
472021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
473021192e1SMilanka Ringwald     client->report_index = 0;
474021192e1SMilanka Ringwald 
475021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
476021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
477021192e1SMilanka Ringwald         return true;
478021192e1SMilanka Ringwald     }
479021192e1SMilanka Ringwald     return false;
480021192e1SMilanka Ringwald }
481021192e1SMilanka Ringwald 
482cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
483cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
484cf26c8fbSMilanka Ringwald     if (!client){
485cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
486cf26c8fbSMilanka Ringwald         return NULL;
487cf26c8fbSMilanka Ringwald     }
488cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
489cf26c8fbSMilanka Ringwald     client->cid = cid;
490cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
491fc975d0eSMilanka Ringwald 
492cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
493cf26c8fbSMilanka Ringwald     return client;
494fc975d0eSMilanka Ringwald }
495fc975d0eSMilanka Ringwald 
496cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
497021192e1SMilanka Ringwald     // stop listening
498021192e1SMilanka Ringwald     uint8_t i;
499021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
500021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
501021192e1SMilanka Ringwald     }
502021192e1SMilanka Ringwald 
503da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
504cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
505cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
506fc975d0eSMilanka Ringwald }
507cf26c8fbSMilanka Ringwald 
508cf26c8fbSMilanka Ringwald 
509cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
5106bcfb631SMilanka Ringwald     uint8_t event[8];
511cf26c8fbSMilanka Ringwald     int pos = 0;
512cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
513cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
514cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
515cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
516cf26c8fbSMilanka Ringwald     pos += 2;
517cf26c8fbSMilanka Ringwald     event[pos++] = status;
5186bcfb631SMilanka Ringwald     event[pos++] = client->protocol_mode;
519cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
520cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
521cf26c8fbSMilanka Ringwald }
522cf26c8fbSMilanka Ringwald 
523021192e1SMilanka Ringwald static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
524021192e1SMilanka Ringwald     uint16_t pos = 0;
525021192e1SMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
526021192e1SMilanka Ringwald     pos++;  // skip len
527021192e1SMilanka Ringwald     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
528021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
529021192e1SMilanka Ringwald     pos += 2;
530021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].service_index;
531021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
532021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, report_len + 1);
533021192e1SMilanka Ringwald     pos += 2;
534021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
535021192e1SMilanka Ringwald     buffer[1] = pos + report_len + 1 - 2;
536021192e1SMilanka Ringwald }
537021192e1SMilanka Ringwald 
538021192e1SMilanka Ringwald static void handle_report_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
539021192e1SMilanka Ringwald     UNUSED(packet_type);
540021192e1SMilanka Ringwald     UNUSED(channel);
541021192e1SMilanka Ringwald     UNUSED(size);
542021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
543021192e1SMilanka Ringwald 
544021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
545021192e1SMilanka Ringwald     btstack_assert(client != NULL);
546021192e1SMilanka Ringwald 
547021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
548021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
549021192e1SMilanka Ringwald         return;
550021192e1SMilanka Ringwald     }
551021192e1SMilanka Ringwald 
552021192e1SMilanka Ringwald     uint8_t * in_place_event = &packet[-1];
553021192e1SMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
554021192e1SMilanka Ringwald 
555021192e1SMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size);
556021192e1SMilanka Ringwald }
557021192e1SMilanka Ringwald 
558cf26c8fbSMilanka Ringwald 
559cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
560cf26c8fbSMilanka Ringwald     uint8_t att_status;
5616bcfb631SMilanka Ringwald     gatt_client_service_t service;
5626bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
563cf26c8fbSMilanka Ringwald 
564cf26c8fbSMilanka Ringwald     switch (client->state){
565cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
566556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
567556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
568556456ccSMilanka Ringwald #endif
569cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
570*19146789SMilanka Ringwald 
571*19146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
572cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
573cf26c8fbSMilanka Ringwald             UNUSED(att_status);
574cf26c8fbSMilanka Ringwald             break;
575cf26c8fbSMilanka Ringwald 
576cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
577556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
578556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
579556456ccSMilanka Ringwald #endif
5806bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
5816bcfb631SMilanka Ringwald 
5826bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
5836bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
584*19146789SMilanka Ringwald 
585*19146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
5866bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
5876bcfb631SMilanka Ringwald 
5886bcfb631SMilanka Ringwald             UNUSED(att_status);
5896bcfb631SMilanka Ringwald             break;
5906bcfb631SMilanka Ringwald 
591021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE:
5922901a9b7SMilanka 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);
5936bcfb631SMilanka Ringwald             UNUSED(att_status);
5942901a9b7SMilanka Ringwald 
5952901a9b7SMilanka Ringwald             client->protocol_mode = client->required_protocol_mode;
596021192e1SMilanka Ringwald             if (hids_client_report_query_init(client)){
597021192e1SMilanka Ringwald                 break;
598021192e1SMilanka Ringwald             }
599021192e1SMilanka Ringwald 
600021192e1SMilanka Ringwald             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
601021192e1SMilanka Ringwald             hids_finalize_client(client);
602cf26c8fbSMilanka Ringwald             break;
603cf26c8fbSMilanka Ringwald 
6041624214bSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_REPORT:{
6051624214bSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
6061624214bSMilanka Ringwald 
6071624214bSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
608021192e1SMilanka Ringwald                 client->reports[client->report_index].value_handle,
6091624214bSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
6101624214bSMilanka Ringwald             UNUSED(att_status);
6111624214bSMilanka Ringwald             break;
6121624214bSMilanka Ringwald         }
61370093cf5SMilanka Ringwald 
61428da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
615556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
616556456ccSMilanka Ringwald             printf("\n\nRead REPORT_MAP (Handle 0x%04X) HID Descriptor of service %d:\n", client->services[client->service_index].report_map_value_handle, client->service_index);
617556456ccSMilanka Ringwald #endif
61828da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
619556456ccSMilanka Ringwald 
620*19146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
621*19146789SMilanka Ringwald             att_status = gatt_client_read_long_value_of_characteristic_using_value_handle(&handle_gatt_client_event, client->con_handle, client->services[client->service_index].report_map_value_handle);
622e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
623e7bd2dbeSMilanka Ringwald             break;
624e7bd2dbeSMilanka Ringwald 
625e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
626556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
627556456ccSMilanka Ringwald             printf("\nDiscover REPORT_MAP (Handle 0x%04X) Characteristic Descriptors of service %d:\n", client->services[client->service_index].report_map_value_handle, client->service_index);
628556456ccSMilanka Ringwald #endif
629e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
630e7bd2dbeSMilanka Ringwald 
6313322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
6323322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
6333322b222SMilanka Ringwald 
634*19146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
635e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
636e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
637e7bd2dbeSMilanka Ringwald             break;
638e7bd2dbeSMilanka Ringwald 
63928da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
640556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
641*19146789SMilanka Ringwald             printf("\nRead external chr UUID (Handle 0x%04X) Characteristic Descriptors, service index %d:\n", client->external_reports[client->report_index].value_handle, client->service_index);
642556456ccSMilanka Ringwald #endif
64328da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
644e7bd2dbeSMilanka Ringwald 
645*19146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
646*19146789SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->external_reports[client->report_index].value_handle);
647e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
648e7bd2dbeSMilanka Ringwald             break;
649e7bd2dbeSMilanka Ringwald 
6503322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
651556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
652*19146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
653556456ccSMilanka Ringwald #endif
6543322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
655e7bd2dbeSMilanka Ringwald 
6563322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
6573322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
658*19146789SMilanka Ringwald 
659*19146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6603322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
661e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
662e7bd2dbeSMilanka Ringwald             break;
663e7bd2dbeSMilanka Ringwald 
66428da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
665556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
666556456ccSMilanka Ringwald             printf("\nQuery Report (Handle 0x%04X, index %d) Characteristic Descriptors of service %d:\n",
667556456ccSMilanka Ringwald                 client->reports[client->report_index].value_handle,
668556456ccSMilanka Ringwald                 client->report_index,
669556456ccSMilanka Ringwald                 client->reports[client->report_index].service_index);
670556456ccSMilanka Ringwald #endif
67128da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
67270093cf5SMilanka Ringwald             client->descriptor_handle = 0;
673556456ccSMilanka Ringwald 
674556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
675556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
676556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
677556456ccSMilanka Ringwald 
678*19146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
67970093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
68070093cf5SMilanka Ringwald             UNUSED(att_status);
68170093cf5SMilanka Ringwald             break;
68270093cf5SMilanka Ringwald 
68328da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
684*19146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
685*19146789SMilanka Ringwald             printf("\nQuery Report ID and Type (Handle 0x%04X, index %d) Characteristic Descriptors of service %d:\n",
686*19146789SMilanka Ringwald                 client->reports[client->report_index].value_handle,
687*19146789SMilanka Ringwald                 client->report_index,
688*19146789SMilanka Ringwald                 client->reports[client->report_index].service_index);
689*19146789SMilanka Ringwald #endif
69028da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
69170093cf5SMilanka Ringwald 
692*19146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
69370093cf5SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
69470093cf5SMilanka Ringwald             client->descriptor_handle = 0;
69570093cf5SMilanka Ringwald             UNUSED(att_status);
69670093cf5SMilanka Ringwald             break;
69770093cf5SMilanka Ringwald 
698021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
699021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
700021192e1SMilanka Ringwald 
701021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
702021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
703021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
704021192e1SMilanka Ringwald 
705*19146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
706021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
707021192e1SMilanka Ringwald 
708021192e1SMilanka Ringwald             if (att_status != ERROR_CODE_SUCCESS){
709021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
710021192e1SMilanka Ringwald                     hids_run_for_client(client);
711021192e1SMilanka Ringwald                     break;
712021192e1SMilanka Ringwald                 }
713021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
714021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
715021192e1SMilanka Ringwald             } else {
716021192e1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
717021192e1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
718021192e1SMilanka Ringwald                     &handle_report_hid_event, client->con_handle, &characteristic);
719021192e1SMilanka Ringwald 
720021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
721021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
722021192e1SMilanka Ringwald             }
723021192e1SMilanka Ringwald             UNUSED(att_status);
724021192e1SMilanka Ringwald             break;
725021192e1SMilanka Ringwald 
7266bcfb631SMilanka Ringwald         default:
7276bcfb631SMilanka Ringwald             break;
7286bcfb631SMilanka Ringwald     }
7296bcfb631SMilanka Ringwald }
7306bcfb631SMilanka Ringwald 
731cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
732cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
733cf26c8fbSMilanka Ringwald     UNUSED(channel);
734cf26c8fbSMilanka Ringwald     UNUSED(size);
735cf26c8fbSMilanka Ringwald 
7366bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
737cf26c8fbSMilanka Ringwald     uint8_t att_status;
7386bcfb631SMilanka Ringwald     gatt_client_service_t service;
7396bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
74070093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
741ab116b1cSMilanka Ringwald 
742021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
743021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
744e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
7453322b222SMilanka Ringwald     uint8_t i;
7463322b222SMilanka Ringwald     uint8_t report_index;
747da142a6fSMilanka Ringwald 
7488cec2b74SMilanka Ringwald     const uint8_t * descriptor_value;
7498cec2b74SMilanka Ringwald     uint16_t descriptor_value_len;
750cf26c8fbSMilanka Ringwald 
751cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
752cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
753cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
754cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
755cf26c8fbSMilanka Ringwald 
7566bcfb631SMilanka Ringwald             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
7576bcfb631SMilanka Ringwald                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
7586bcfb631SMilanka Ringwald                 hids_finalize_client(client);
7596bcfb631SMilanka Ringwald                 break;
7606bcfb631SMilanka Ringwald             }
7616bcfb631SMilanka Ringwald 
762021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
763021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
7646bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
765021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
766021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
7676bcfb631SMilanka Ringwald                 client->num_instances++;
768021192e1SMilanka Ringwald 
769708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
770708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
771708c69d2SMilanka Ringwald #endif
772021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
773021192e1SMilanka Ringwald             }  else {
774021192e1SMilanka Ringwald                 log_info("%d hid services found, only first %d can be stored, increase MAX_NUM_HID_SERVICES", client->num_instances + 1, MAX_NUM_HID_SERVICES);
775da142a6fSMilanka Ringwald             }
7766bcfb631SMilanka Ringwald             break;
7776bcfb631SMilanka Ringwald 
7786bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
7796bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
7806bcfb631SMilanka Ringwald             btstack_assert(client != NULL);
7816bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
782*19146789SMilanka Ringwald 
783*19146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
784*19146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
785*19146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
786*19146789SMilanka Ringwald                     break;
787*19146789SMilanka Ringwald                 }
788*19146789SMilanka Ringwald             }
789*19146789SMilanka Ringwald 
790*19146789SMilanka Ringwald             switch (characteristic.uuid16){
791*19146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
792*19146789SMilanka Ringwald                     client->protocol_mode_value_handle = characteristic.value_handle;
793*19146789SMilanka Ringwald                     break;
794*19146789SMilanka Ringwald 
795*19146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
796*19146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
797*19146789SMilanka Ringwald                     break;
798*19146789SMilanka Ringwald 
799*19146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
800*19146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
801*19146789SMilanka Ringwald                     break;
802*19146789SMilanka Ringwald 
803*19146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
804*19146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, false);
805*19146789SMilanka Ringwald                     break;
806*19146789SMilanka Ringwald 
807*19146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
808*19146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
809*19146789SMilanka Ringwald                     break;
810*19146789SMilanka Ringwald 
811*19146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
812*19146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
813*19146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
814*19146789SMilanka Ringwald                     break;
815*19146789SMilanka Ringwald 
816*19146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
817*19146789SMilanka Ringwald                     break;
818*19146789SMilanka Ringwald 
819*19146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
820*19146789SMilanka Ringwald                     break;
821*19146789SMilanka Ringwald 
822*19146789SMilanka Ringwald                 default:
823*19146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
824*19146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
825*19146789SMilanka Ringwald #endif
826*19146789SMilanka Ringwald                     return;
827*19146789SMilanka Ringwald             }
828*19146789SMilanka Ringwald 
829*19146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
830*19146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
831*19146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
832*19146789SMilanka Ringwald                 characteristic.start_handle,
833*19146789SMilanka Ringwald                 characteristic.properties,
834*19146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
835*19146789SMilanka Ringwald                 client->service_index);
836*19146789SMilanka Ringwald 
837*19146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
838*19146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
839*19146789SMilanka Ringwald             }
840*19146789SMilanka Ringwald             printf("\n");
841*19146789SMilanka Ringwald #endif
842cf26c8fbSMilanka Ringwald             break;
843cf26c8fbSMilanka Ringwald 
8448cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
845e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
8468cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
847e7bd2dbeSMilanka Ringwald             btstack_assert(client != NULL);
848da142a6fSMilanka Ringwald 
8498cec2b74SMilanka Ringwald             descriptor_value = gatt_event_long_characteristic_value_query_result_get_value(packet);
8508cec2b74SMilanka Ringwald             descriptor_value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
8518cec2b74SMilanka Ringwald 
852556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
8538cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
8548cec2b74SMilanka Ringwald             printf_hexdump(descriptor_value, descriptor_value_len);
855556456ccSMilanka Ringwald #endif
8568cec2b74SMilanka Ringwald             for (i = 0; i < descriptor_value_len; i++){
8578cec2b74SMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, descriptor_value[i]);
858da142a6fSMilanka Ringwald                 if (!stored){
859da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
860da142a6fSMilanka Ringwald                     break;
861da142a6fSMilanka Ringwald                 }
862da142a6fSMilanka Ringwald             }
863e7bd2dbeSMilanka Ringwald             break;
864e7bd2dbeSMilanka Ringwald 
86570093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
86670093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
86770093cf5SMilanka Ringwald             btstack_assert(client != NULL);
86870093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
86970093cf5SMilanka Ringwald 
870e7bd2dbeSMilanka Ringwald             switch (client->state) {
871e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
872e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
873e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
874*19146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
8753322b222SMilanka Ringwald 
876556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
877556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
878556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
879556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
880556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
881556456ccSMilanka Ringwald                                 client->service_index, report_index);
882556456ccSMilanka Ringwald                         }
883556456ccSMilanka Ringwald #endif
884e7bd2dbeSMilanka Ringwald                     }
885e7bd2dbeSMilanka Ringwald                     break;
88628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
88770093cf5SMilanka Ringwald                     // setup for descriptor value query
88870093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
88970093cf5SMilanka Ringwald                         client->descriptor_handle = characteristic_descriptor.handle;
890556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
891556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
892556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
893556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
894*19146789SMilanka Ringwald #endif
895556456ccSMilanka Ringwald                     }
896556456ccSMilanka Ringwald 
897*19146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
898556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
899556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
900556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
901556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
902556456ccSMilanka Ringwald                     }
903556456ccSMilanka Ringwald #endif
90470093cf5SMilanka Ringwald                     break;
905556456ccSMilanka Ringwald 
906e7bd2dbeSMilanka Ringwald                 default:
907e7bd2dbeSMilanka Ringwald                     break;
908e7bd2dbeSMilanka Ringwald             }
909e7bd2dbeSMilanka Ringwald             break;
91070093cf5SMilanka Ringwald 
91170093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
91270093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
91370093cf5SMilanka Ringwald             btstack_assert(client != NULL);
914e7bd2dbeSMilanka Ringwald 
915e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
916e7bd2dbeSMilanka Ringwald                 break;
917e7bd2dbeSMilanka Ringwald             }
9187e1e6e7dSMilanka Ringwald 
919e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
920e7bd2dbeSMilanka Ringwald             switch (client->state) {
92128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
922e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
923*19146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
9243322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
925*19146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
926556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
927*19146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
928*19146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
929556456ccSMilanka Ringwald #endif
9303322b222SMilanka Ringwald                     }
931e7bd2dbeSMilanka Ringwald                     break;
932e7bd2dbeSMilanka Ringwald 
93328da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
934*19146789SMilanka Ringwald 
935*19146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
936021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
937021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
938*19146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
939*19146789SMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
940021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
941021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
942*19146789SMilanka Ringwald                             client->report_index, client->service_index,
943*19146789SMilanka Ringwald                             client->external_reports[client->report_index].external_report_reference_uuid);
944*19146789SMilanka Ringwald     #endif
945*19146789SMilanka Ringwald                     }
946e7bd2dbeSMilanka Ringwald                     break;
947e7bd2dbeSMilanka Ringwald 
948e7bd2dbeSMilanka Ringwald                 default:
949*19146789SMilanka Ringwald                     printf("GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT 3");
950e7bd2dbeSMilanka Ringwald                     break;
95170093cf5SMilanka Ringwald             }
95270093cf5SMilanka Ringwald             break;
95370093cf5SMilanka Ringwald 
954cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
955cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
956cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
957cf26c8fbSMilanka Ringwald 
958cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
959cf26c8fbSMilanka Ringwald 
960cf26c8fbSMilanka Ringwald             switch (client->state){
961cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
9626bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
9636bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
9646bcfb631SMilanka Ringwald                         hids_finalize_client(client);
965cf26c8fbSMilanka Ringwald                         break;
9666bcfb631SMilanka Ringwald                     }
9676bcfb631SMilanka Ringwald 
9686bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
9696bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
9706bcfb631SMilanka Ringwald                         hids_finalize_client(client);
9716bcfb631SMilanka Ringwald                         break;
9726bcfb631SMilanka Ringwald                     }
9736bcfb631SMilanka Ringwald 
9746bcfb631SMilanka Ringwald                     client->service_index = 0;
975*19146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
9766bcfb631SMilanka Ringwald                     break;
9776bcfb631SMilanka Ringwald 
9786bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
9796bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
9806bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
9816bcfb631SMilanka Ringwald                         hids_finalize_client(client);
9826bcfb631SMilanka Ringwald                         break;
9836bcfb631SMilanka Ringwald                     }
9846bcfb631SMilanka Ringwald 
98570093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
98670093cf5SMilanka Ringwald                         // discover characteristics of next service
98770093cf5SMilanka Ringwald                         client->service_index++;
98870093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
989021192e1SMilanka Ringwald                         break;
990021192e1SMilanka Ringwald                     }
991021192e1SMilanka Ringwald 
992021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
993021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
994021192e1SMilanka Ringwald                             client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
995021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_report_mode(client)){
996021192e1SMilanka Ringwald                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
997021192e1SMilanka Ringwald                                 break;
998021192e1SMilanka Ringwald                             }
999021192e1SMilanka Ringwald                             hids_emit_connection_established(client, att_status);
1000021192e1SMilanka Ringwald                             hids_finalize_client(client);
1001021192e1SMilanka Ringwald                             return;
1002021192e1SMilanka Ringwald 
1003021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT:
1004021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_report_mode(client)){
1005021192e1SMilanka Ringwald                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1006021192e1SMilanka Ringwald                                 break;
1007021192e1SMilanka Ringwald                             }
1008021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_boot_mode(client)){
1009021192e1SMilanka Ringwald                                 if (client->protocol_mode_value_handle != 0){
1010021192e1SMilanka Ringwald                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
1011021192e1SMilanka Ringwald                                     break;
1012021192e1SMilanka Ringwald                                 }
1013021192e1SMilanka Ringwald                                 hids_emit_connection_established(client, att_status);
1014021192e1SMilanka Ringwald                                 hids_finalize_client(client);
1015021192e1SMilanka Ringwald                                 return;
1016021192e1SMilanka Ringwald                             }
1017021192e1SMilanka Ringwald                             break;
1018021192e1SMilanka Ringwald                         default:
1019021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_boot_mode(client)){
1020021192e1SMilanka Ringwald                                 if (client->protocol_mode_value_handle != 0){
1021021192e1SMilanka Ringwald                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
1022021192e1SMilanka Ringwald                                     break;
1023021192e1SMilanka Ringwald                                 }
1024021192e1SMilanka Ringwald                                 hids_emit_connection_established(client, att_status);
1025021192e1SMilanka Ringwald                                 hids_finalize_client(client);
1026021192e1SMilanka Ringwald                                 return;
1027021192e1SMilanka Ringwald                             }
1028021192e1SMilanka Ringwald                             break;
1029021192e1SMilanka Ringwald                     }
1030021192e1SMilanka Ringwald 
1031021192e1SMilanka Ringwald                     if (client->state == HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE){
1032021192e1SMilanka Ringwald                         break;
1033021192e1SMilanka Ringwald                     }
10343322b222SMilanka Ringwald 
10357e1e6e7dSMilanka Ringwald                     // 1. we need to get HID Descriptor and
10367e1e6e7dSMilanka Ringwald                     // 2. get external Report characteristics if referenced from Report Map
10377e1e6e7dSMilanka Ringwald                     if (hids_client_report_map_query_init(client)){
103870093cf5SMilanka Ringwald                         break;
103970093cf5SMilanka Ringwald                     }
104070093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
104170093cf5SMilanka Ringwald                     hids_finalize_client(client);
10426bcfb631SMilanka Ringwald                     break;
10436bcfb631SMilanka Ringwald 
10443322b222SMilanka Ringwald 
104528da36a6SMilanka Ringwald                 // HID descriptor found
104628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1047e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
1048e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
1049e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
1050e7bd2dbeSMilanka Ringwald                         break;
1051e7bd2dbeSMilanka Ringwald                     }
1052e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1053e7bd2dbeSMilanka Ringwald                     break;
1054e7bd2dbeSMilanka Ringwald 
105528da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1056e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
10573322b222SMilanka Ringwald                     // go for next report map
10583322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1059e7bd2dbeSMilanka Ringwald                         break;
1060e7bd2dbeSMilanka Ringwald                     }
1061e7bd2dbeSMilanka Ringwald 
10623322b222SMilanka Ringwald                     // read UUIDS for external characteristics
10633322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1064e7bd2dbeSMilanka Ringwald                         break;
1065e7bd2dbeSMilanka Ringwald                     }
1066e7bd2dbeSMilanka Ringwald 
1067e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1068e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
10697e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1070e7bd2dbeSMilanka Ringwald                         break;
1071e7bd2dbeSMilanka Ringwald                     }
1072e7bd2dbeSMilanka Ringwald 
1073e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1074e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1075e7bd2dbeSMilanka Ringwald                     break;
1076e7bd2dbeSMilanka Ringwald 
107728da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1078e7bd2dbeSMilanka Ringwald                     // go for next map report
10793322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1080e7bd2dbeSMilanka Ringwald                         break;
1081e7bd2dbeSMilanka Ringwald                     }
1082e7bd2dbeSMilanka Ringwald 
10833322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
10843322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1085e7bd2dbeSMilanka Ringwald                     break;
1086e7bd2dbeSMilanka Ringwald 
10873322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1088e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1089e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
10907e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1091e7bd2dbeSMilanka Ringwald                         break;
1092e7bd2dbeSMilanka Ringwald                     }
1093e7bd2dbeSMilanka Ringwald 
1094e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1095e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1096e7bd2dbeSMilanka Ringwald                     break;
1097e7bd2dbeSMilanka Ringwald 
109828da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
109970093cf5SMilanka Ringwald                     if (client->descriptor_handle != 0){
1100*19146789SMilanka Ringwald                         printf("descriptor found\n");
110128da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
110270093cf5SMilanka Ringwald                         break;
110370093cf5SMilanka Ringwald                     }
110470093cf5SMilanka Ringwald 
110570093cf5SMilanka Ringwald                     // go for next report
11067e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
110770093cf5SMilanka Ringwald                         break;
110870093cf5SMilanka Ringwald                     }
110970093cf5SMilanka Ringwald 
111070093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
111170093cf5SMilanka Ringwald                     break;
111270093cf5SMilanka Ringwald 
111328da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
111470093cf5SMilanka Ringwald                     // go for next report
11157e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
111670093cf5SMilanka Ringwald                         break;
111770093cf5SMilanka Ringwald                     }
111870093cf5SMilanka Ringwald 
1119021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1120021192e1SMilanka Ringwald                         break;
1121021192e1SMilanka Ringwald                     }
112270093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
112370093cf5SMilanka Ringwald                     break;
112470093cf5SMilanka Ringwald 
1125021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1126021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
11272901a9b7SMilanka Ringwald                         break;
11282901a9b7SMilanka Ringwald                     }
11296bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
11306bcfb631SMilanka Ringwald                     break;
11316bcfb631SMilanka Ringwald 
1132cf26c8fbSMilanka Ringwald                 default:
1133cf26c8fbSMilanka Ringwald                     break;
1134cf26c8fbSMilanka Ringwald             }
1135cf26c8fbSMilanka Ringwald             break;
1136cf26c8fbSMilanka Ringwald 
1137cf26c8fbSMilanka Ringwald         default:
1138cf26c8fbSMilanka Ringwald             break;
1139cf26c8fbSMilanka Ringwald     }
11406bcfb631SMilanka Ringwald 
11416bcfb631SMilanka Ringwald     if (client != NULL){
11426bcfb631SMilanka Ringwald         hids_run_for_client(client);
11436bcfb631SMilanka Ringwald     }
1144cf26c8fbSMilanka Ringwald }
1145cf26c8fbSMilanka Ringwald 
11466bcfb631SMilanka 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){
1147cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1148cf26c8fbSMilanka Ringwald 
1149cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1150cf26c8fbSMilanka Ringwald     if (client != NULL){
1151cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1152cf26c8fbSMilanka Ringwald     }
1153cf26c8fbSMilanka Ringwald 
1154cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1155cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1156cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1157cf26c8fbSMilanka Ringwald     }
1158cf26c8fbSMilanka Ringwald 
1159cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1160cf26c8fbSMilanka Ringwald     if (client == NULL) {
1161cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1162cf26c8fbSMilanka Ringwald     }
1163cf26c8fbSMilanka Ringwald 
11646bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1165cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1166cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1167cf26c8fbSMilanka Ringwald 
1168cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1169cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1170cf26c8fbSMilanka Ringwald }
1171cf26c8fbSMilanka Ringwald 
1172cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1173cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1174cf26c8fbSMilanka Ringwald     if (client == NULL){
1175cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1176cf26c8fbSMilanka Ringwald     }
1177cf26c8fbSMilanka Ringwald     // finalize connection
1178cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1179cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1180cf26c8fbSMilanka Ringwald }
1181cf26c8fbSMilanka Ringwald 
11821624214bSMilanka Ringwald uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){
11831624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
11841624214bSMilanka Ringwald     if (client == NULL){
11851624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
11861624214bSMilanka Ringwald     }
11871624214bSMilanka Ringwald 
11881624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
11891624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
11901624214bSMilanka Ringwald     }
11911624214bSMilanka Ringwald 
11922fe59e00SMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT);
11931624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
11941624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
11951624214bSMilanka Ringwald     }
11961624214bSMilanka Ringwald 
11971624214bSMilanka Ringwald     uint16_t mtu;
11981624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
11991624214bSMilanka Ringwald 
12001624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
12011624214bSMilanka Ringwald         return status;
12021624214bSMilanka Ringwald     }
12031624214bSMilanka Ringwald 
12041624214bSMilanka Ringwald     if (mtu - 2 < report_len){
12051624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
12061624214bSMilanka Ringwald     }
12071624214bSMilanka Ringwald 
12081624214bSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_REPORT;
1209021192e1SMilanka Ringwald     client->report_index = report_index;
12101624214bSMilanka Ringwald     client->report = report;
12111624214bSMilanka Ringwald     client->report_len = report_len;
12121624214bSMilanka Ringwald 
12131624214bSMilanka Ringwald     hids_run_for_client(client);
12141624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
12151624214bSMilanka Ringwald }
12161624214bSMilanka Ringwald 
1217021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1218021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1219021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1220021192e1SMilanka Ringwald }
1221cf26c8fbSMilanka Ringwald 
1222cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1223