xref: /btstack/src/ble/gatt-service/hids_client.c (revision 1914678990625d883f1e58fafb697f2c235f8c72)
1 /*
2  * Copyright (C) 2021 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "hids_client.c"
39 
40 #include "btstack_config.h"
41 
42 #ifdef ENABLE_TESTING_SUPPORT
43 #include <stdio.h>
44 #endif
45 
46 #include <stdint.h>
47 #include <string.h>
48 
49 #include "ble/gatt-service/hids_client.h"
50 
51 #include "btstack_memory.h"
52 #include "ble/att_db.h"
53 #include "ble/core.h"
54 #include "ble/gatt_client.h"
55 #include "ble/sm.h"
56 #include "bluetooth_gatt.h"
57 #include "btstack_debug.h"
58 #include "btstack_event.h"
59 #include "btstack_run_loop.h"
60 #include "gap.h"
61 
62 #define HID_REPORT_MODE_REPORT_ID               3
63 #define HID_REPORT_MODE_REPORT_MAP_ID           4
64 #define HID_REPORT_MODE_HID_INFORMATION_ID      5
65 #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
66 
67 static btstack_linked_list_t clients;
68 static uint16_t hids_cid_counter = 0;
69 
70 static uint8_t * hids_client_descriptor_storage;
71 static uint16_t  hids_client_descriptor_storage_len;
72 
73 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
74 
75 #ifdef ENABLE_TESTING_SUPPORT
76 static char * hid_characteristic_name(uint16_t uuid){
77     switch (uuid){
78         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
79             return "PROTOCOL_MODE";
80 
81         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
82             return "BOOT_KEYBOARD_INPUT_REPORT";
83 
84         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
85             return "BOOT_MOUSE_INPUT_REPORT";
86 
87         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
88             return "BOOT_KEYBOARD_OUTPUT_REPORT";
89 
90         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
91             return "REPORT";
92 
93         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
94             return "REPORT_MAP";
95 
96         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
97             return "HID_INFORMATION";
98 
99         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
100             return "HID_CONTROL_POINT";
101         default:
102             return "UKNOWN";
103     }
104 }
105 #endif
106 
107 static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
108     btstack_linked_list_iterator_t it;
109     btstack_linked_list_iterator_init(&it, &clients);
110     while (btstack_linked_list_iterator_has_next(&it)){
111         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
112         if (client->con_handle != con_handle) continue;
113         return client;
114     }
115     return NULL;
116 }
117 
118 static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
119     btstack_linked_list_iterator_t it;
120     btstack_linked_list_iterator_init(&it, &clients);
121     while (btstack_linked_list_iterator_has_next(&it)){
122         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
123         if (client->cid != hids_cid) continue;
124         return client;
125     }
126     return NULL;
127 }
128 
129 
130 // START Descriptor Storage Util
131 
132 static uint16_t hids_client_descriptor_storage_get_available_space(void){
133     // assumes all descriptors are back to back
134     uint16_t free_space = hids_client_descriptor_storage_len;
135     uint8_t i;
136 
137     btstack_linked_list_iterator_t it;
138     btstack_linked_list_iterator_init(&it, &clients);
139     while (btstack_linked_list_iterator_has_next(&it)){
140         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
141         for (i = 0; i < client->num_instances; i++){
142             free_space -= client->services[i].hid_descriptor_len;
143         }
144     }
145     return free_space;
146 }
147 
148 static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
149     client->services[service_index].hid_descriptor_len = 0;
150     client->services[service_index].hid_descriptor_max_len = hids_client_descriptor_storage_get_available_space();
151     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - client->services[service_index].hid_descriptor_max_len;
152 }
153 
154 static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
155     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
156 
157     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
158     client->services[service_index].hid_descriptor_len++;
159     return true;
160 }
161 
162 static void hids_client_descriptor_storage_delete(hids_client_t * client){
163     uint8_t service_index = 0;
164     uint16_t next_offset = 0;
165 
166     for (service_index = 0; service_index < client->num_instances; service_index++){
167         next_offset += client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len;
168         client->services[service_index].hid_descriptor_len = 0;
169         client->services[service_index].hid_descriptor_offset = 0;
170     }
171 
172     memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
173             &hids_client_descriptor_storage[next_offset],
174             hids_client_descriptor_storage_len - next_offset);
175 
176     uint8_t i;
177     btstack_linked_list_iterator_t it;
178     btstack_linked_list_iterator_init(&it, &clients);
179     while (btstack_linked_list_iterator_has_next(&it)){
180         hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
181         for (i = 0; i < client->num_instances; i++){
182             if (conn->services[i].hid_descriptor_offset >= next_offset){
183                 conn->services[i].hid_descriptor_offset = next_offset;
184                 next_offset += conn->services[service_index].hid_descriptor_len;
185             }
186         }
187     }
188 }
189 
190 const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
191     hids_client_t * client = hids_get_client_for_cid(hids_cid);
192     if (client == NULL){
193         return NULL;
194     }
195     if (service_index >= client->num_instances){
196         return NULL;
197     }
198     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
199 }
200 
201 uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
202     hids_client_t * client = hids_get_client_for_cid(hids_cid);
203     if (client == NULL){
204         return 0;
205     }
206     if (service_index >= client->num_instances){
207         return 0;
208     }
209     return client->services[service_index].hid_descriptor_len;
210 }
211 
212 // END Descriptor Storage Util
213 
214 static uint16_t hids_get_next_cid(void){
215     if (hids_cid_counter == 0xffff) {
216         hids_cid_counter = 1;
217     } else {
218         hids_cid_counter++;
219     }
220     return hids_cid_counter;
221 }
222 
223 static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
224     uint8_t i;
225     for (i = 0; i < client->num_reports; i++){
226         if (client->reports[i].value_handle == value_handle){
227             return i;
228         }
229     }
230     return HIDS_CLIENT_INVALID_REPORT_INDEX;
231 }
232 
233 static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
234     uint8_t i;
235     for (i = 0; i < client->num_external_reports; i++){
236         if (client->external_reports[i].value_handle == value_handle){
237             return i;
238         }
239     }
240     return HIDS_CLIENT_INVALID_REPORT_INDEX;
241 }
242 
243 static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
244     uint8_t i;
245     for (i = 0; i < client->num_external_reports; i++){
246         if (client->external_reports[i].external_report_reference_uuid == uuid16){
247             return true;
248         }
249     }
250     return false;
251 }
252 
253 
254 
255 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){
256     uint8_t i;
257     for (i = 0; i < client->num_reports; i++){
258         if ( (client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
259             return i;
260         }
261     }
262     return HIDS_CLIENT_INVALID_REPORT_INDEX;
263 }
264 
265 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){
266 
267     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
268     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
269         return report_index;
270     }
271     report_index = client->num_reports;
272 
273     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
274         client->reports[report_index].value_handle = characteristic->value_handle;
275         client->reports[report_index].end_handle = characteristic->end_handle;
276         client->reports[report_index].properties = characteristic->properties;
277 
278         client->reports[report_index].service_index = client->service_index;
279         client->reports[report_index].report_id = report_id;
280         client->reports[report_index].report_type = report_type;
281         client->reports[report_index].boot_report = boot_report;
282 
283         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);
284         client->num_reports++;
285         return report_index;
286     } else {
287         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
288         return HIDS_CLIENT_INVALID_REPORT_INDEX;
289     }
290 }
291 
292 static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
293     uint8_t report_index = client->num_external_reports;
294 
295     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
296         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
297         client->external_reports[report_index].service_index = client->service_index;
298 
299         client->num_external_reports++;
300         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
301         return report_index;
302     } else {
303         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
304         return HIDS_CLIENT_INVALID_REPORT_INDEX;
305     }
306 }
307 
308 
309 static bool hid_clients_has_reports_in_report_mode(hids_client_t * client){
310     uint8_t i;
311     for (i = 0; i < client->num_reports; i++){
312         if (!client->reports[i].boot_report){
313             return true;
314         }
315     }
316     return false;
317 }
318 
319 static bool hid_clients_has_reports_in_boot_mode(hids_client_t * client){
320     uint8_t i;
321     for (i = 0; i < client->num_reports; i++){
322         if (client->reports[i].boot_report){
323             return true;
324         }
325     }
326     return false;
327 }
328 
329 static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
330     uint8_t i;
331     for (i = client->service_index; i < client->num_instances; i++){
332         if (client->services[i].report_map_value_handle != 0){
333             return i;
334         }
335     }
336     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
337     return HIDS_CLIENT_INVALID_REPORT_INDEX;
338 }
339 
340 static bool hids_client_report_query_next_report_map(hids_client_t * client){
341     client->service_index++;
342     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
343         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
344         return true;
345     }
346     return false;
347 }
348 
349 static bool hids_client_report_map_query_init(hids_client_t * client){
350     client->service_index = 0;
351 
352     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
353         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
354         return true;
355     }
356     return false;
357 }
358 
359 static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
360     client->report_index++;
361     if (client->report_index < client->num_external_reports){
362         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
363         return true;
364     }
365     return false;
366 }
367 
368 static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
369     client->report_index = 0;
370     if (client->num_external_reports > 0){
371         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
372         return true;
373     }
374     return false;
375 }
376 
377 static uint8_t hids_client_get_next_report_index(hids_client_t * client){
378     uint8_t i;
379     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
380     switch (client->protocol_mode){
381         case HID_PROTOCOL_MODE_REPORT:
382             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
383                 hids_client_report_t report = client->reports[i];
384                 if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
385                     index = i;
386                     client->service_index = report.service_index;
387                 }
388             }
389             break;
390         case HID_PROTOCOL_MODE_BOOT:
391             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
392                 hids_client_report_t report = client->reports[i];
393                 if (report.boot_report){
394                     index = i;
395                     client->service_index = report.service_index;
396                 }
397             }
398             break;
399         default:
400             break;
401     }
402 
403     client->report_index = index;
404     return index;
405 }
406 
407 static bool hids_client_report_query_next_report(hids_client_t * client){
408     client->report_index++;
409     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
410         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
411         return true;
412     }
413     return false;
414 }
415 
416 static bool hids_client_report_query_init(hids_client_t * client){
417     client->report_index = 0;
418 
419     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
420         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
421         return true;
422     }
423     return false;
424 }
425 
426 static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
427     uint8_t i;
428     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
429 
430     switch (client->protocol_mode){
431         case HID_PROTOCOL_MODE_REPORT:
432             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
433                 hids_client_report_t report = client->reports[i];
434                 if (report.report_type != HID_REPORT_TYPE_INPUT){
435                     continue;
436                 }
437                 if (!report.boot_report){
438                     index = i;
439                 }
440             }
441             break;
442 
443         case HID_PROTOCOL_MODE_BOOT:
444             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
445                 hids_client_report_t report = client->reports[i];
446                 if (report.report_type != HID_REPORT_TYPE_INPUT){
447                     continue;
448                 }
449                 if (report.boot_report){
450                     index = i;
451                 }
452             }
453             break;
454 
455         default:
456             break;
457     }
458 
459     client->report_index = index;
460     return index;
461 }
462 
463 static bool hids_client_report_next_notification_report_index(hids_client_t * client){
464     client->report_index++;
465     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
466         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
467         return true;
468     }
469     return false;
470 }
471 
472 static bool hids_client_report_notifications_init(hids_client_t * client){
473     client->report_index = 0;
474 
475     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
476         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
477         return true;
478     }
479     return false;
480 }
481 
482 static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
483     hids_client_t * client = btstack_memory_hids_client_get();
484     if (!client){
485         log_error("Not enough memory to create client");
486         return NULL;
487     }
488     client->state = HIDS_CLIENT_STATE_IDLE;
489     client->cid = cid;
490     client->con_handle = con_handle;
491 
492     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
493     return client;
494 }
495 
496 static void hids_finalize_client(hids_client_t * client){
497     // stop listening
498     uint8_t i;
499     for (i = 0; i < client->num_reports; i++){
500         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
501     }
502 
503     hids_client_descriptor_storage_delete(client);
504     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
505     btstack_memory_hids_client_free(client);
506 }
507 
508 
509 static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
510     uint8_t event[8];
511     int pos = 0;
512     event[pos++] = HCI_EVENT_GATTSERVICE_META;
513     event[pos++] = sizeof(event) - 2;
514     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
515     little_endian_store_16(event, pos, client->cid);
516     pos += 2;
517     event[pos++] = status;
518     event[pos++] = client->protocol_mode;
519     event[pos++] = client->num_instances;
520     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
521 }
522 
523 static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
524     uint16_t pos = 0;
525     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
526     pos++;  // skip len
527     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
528     little_endian_store_16(buffer, pos, client->cid);
529     pos += 2;
530     buffer[pos++] = client->reports[report_index].service_index;
531     buffer[pos++] = client->reports[report_index].report_id;
532     little_endian_store_16(buffer, pos, report_len + 1);
533     pos += 2;
534     buffer[pos++] = client->reports[report_index].report_id;
535     buffer[1] = pos + report_len + 1 - 2;
536 }
537 
538 static void handle_report_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
539     UNUSED(packet_type);
540     UNUSED(channel);
541     UNUSED(size);
542     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
543 
544     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
545     btstack_assert(client != NULL);
546 
547     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
548     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
549         return;
550     }
551 
552     uint8_t * in_place_event = &packet[-1];
553     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
554 
555     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size);
556 }
557 
558 
559 static void hids_run_for_client(hids_client_t * client){
560     uint8_t att_status;
561     gatt_client_service_t service;
562     gatt_client_characteristic_t characteristic;
563 
564     switch (client->state){
565         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
566 #ifdef ENABLE_TESTING_SUPPORT
567             printf("\n\nQuery Services:\n");
568 #endif
569             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
570 
571             // result in GATT_EVENT_SERVICE_QUERY_RESULT
572             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
573             UNUSED(att_status);
574             break;
575 
576         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
577 #ifdef ENABLE_TESTING_SUPPORT
578             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
579 #endif
580             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
581 
582             service.start_group_handle = client->services[client->service_index].start_handle;
583             service.end_group_handle = client->services[client->service_index].end_handle;
584 
585             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
586             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
587 
588             UNUSED(att_status);
589             break;
590 
591         case HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE:
592             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);
593             UNUSED(att_status);
594 
595             client->protocol_mode = client->required_protocol_mode;
596             if (hids_client_report_query_init(client)){
597                 break;
598             }
599 
600             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
601             hids_finalize_client(client);
602             break;
603 
604         case HIDS_CLIENT_W2_SEND_REPORT:{
605             client->state = HIDS_CLIENT_STATE_CONNECTED;
606 
607             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
608                 client->reports[client->report_index].value_handle,
609                 client->report_len, (uint8_t *)client->report);
610             UNUSED(att_status);
611             break;
612         }
613 
614         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
615 #ifdef ENABLE_TESTING_SUPPORT
616             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);
617 #endif
618             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
619 
620             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
621             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);
622             UNUSED(att_status);
623             break;
624 
625         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
626 #ifdef ENABLE_TESTING_SUPPORT
627             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);
628 #endif
629             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
630 
631             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
632             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
633 
634             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
635             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
636             UNUSED(att_status);
637             break;
638 
639         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
640 #ifdef ENABLE_TESTING_SUPPORT
641             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);
642 #endif
643             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
644 
645             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
646             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);
647             UNUSED(att_status);
648             break;
649 
650         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
651  #ifdef ENABLE_TESTING_SUPPORT
652             printf("\nDiscover External Report Characteristic:\n");
653 #endif
654             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
655 
656             service.start_group_handle = 0x0001;
657             service.end_group_handle = 0xffff;
658 
659             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
660             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
661             UNUSED(att_status);
662             break;
663 
664         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
665 #ifdef ENABLE_TESTING_SUPPORT
666             printf("\nQuery Report (Handle 0x%04X, index %d) Characteristic Descriptors of service %d:\n",
667                 client->reports[client->report_index].value_handle,
668                 client->report_index,
669                 client->reports[client->report_index].service_index);
670 #endif
671             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
672             client->descriptor_handle = 0;
673 
674             characteristic.value_handle = client->reports[client->report_index].value_handle;
675             characteristic.end_handle = client->reports[client->report_index].end_handle;
676             characteristic.properties = client->reports[client->report_index].properties;
677 
678             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
679             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
680             UNUSED(att_status);
681             break;
682 
683         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
684 #ifdef ENABLE_TESTING_SUPPORT
685             printf("\nQuery Report ID and Type (Handle 0x%04X, index %d) Characteristic Descriptors of service %d:\n",
686                 client->reports[client->report_index].value_handle,
687                 client->report_index,
688                 client->reports[client->report_index].service_index);
689 #endif
690             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
691 
692             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
693             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
694             client->descriptor_handle = 0;
695             UNUSED(att_status);
696             break;
697 
698         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
699             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
700 
701             characteristic.value_handle = client->reports[client->report_index].value_handle;
702             characteristic.end_handle = client->reports[client->report_index].end_handle;
703             characteristic.properties = client->reports[client->report_index].properties;
704 
705             // end of write marked in GATT_EVENT_QUERY_COMPLETE
706             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
707 
708             if (att_status != ERROR_CODE_SUCCESS){
709                 if (hids_client_report_next_notification_report_index(client)){
710                     hids_run_for_client(client);
711                     break;
712                 }
713                 client->state = HIDS_CLIENT_STATE_CONNECTED;
714                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
715             } else {
716                 gatt_client_listen_for_characteristic_value_updates(
717                     &client->reports[client->report_index].notification_listener,
718                     &handle_report_hid_event, client->con_handle, &characteristic);
719 
720                 client->state = HIDS_CLIENT_STATE_CONNECTED;
721                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
722             }
723             UNUSED(att_status);
724             break;
725 
726         default:
727             break;
728     }
729 }
730 
731 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
732     UNUSED(packet_type);
733     UNUSED(channel);
734     UNUSED(size);
735 
736     hids_client_t * client = NULL;
737     uint8_t att_status;
738     gatt_client_service_t service;
739     gatt_client_characteristic_t characteristic;
740     gatt_client_characteristic_descriptor_t characteristic_descriptor;
741 
742     // hids_client_report_t * boot_keyboard_report;
743     // hids_client_report_t * boot_mouse_report;
744     const uint8_t * characteristic_descriptor_value;
745     uint8_t i;
746     uint8_t report_index;
747 
748     const uint8_t * descriptor_value;
749     uint16_t descriptor_value_len;
750 
751     switch(hci_event_packet_get_type(packet)){
752         case GATT_EVENT_SERVICE_QUERY_RESULT:
753             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
754             btstack_assert(client != NULL);
755 
756             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
757                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
758                 hids_finalize_client(client);
759                 break;
760             }
761 
762             if (client->num_instances < MAX_NUM_HID_SERVICES){
763                 uint8_t index = client->num_instances;
764                 gatt_event_service_query_result_get_service(packet, &service);
765                 client->services[index].start_handle = service.start_group_handle;
766                 client->services[index].end_handle = service.end_group_handle;
767                 client->num_instances++;
768 
769 #ifdef ENABLE_TESTING_SUPPORT
770                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
771 #endif
772                 hids_client_descriptor_storage_init(client, index);
773             }  else {
774                 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);
775             }
776             break;
777 
778         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
779             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
780             btstack_assert(client != NULL);
781             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
782 
783             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
784             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
785                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
786                     break;
787                 }
788             }
789 
790             switch (characteristic.uuid16){
791                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
792                     client->protocol_mode_value_handle = characteristic.value_handle;
793                     break;
794 
795                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
796                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
797                     break;
798 
799                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
800                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
801                     break;
802 
803                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
804                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, false);
805                     break;
806 
807                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
808                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
809                     break;
810 
811                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
812                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
813                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
814                     break;
815 
816                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
817                     break;
818 
819                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
820                     break;
821 
822                 default:
823 #ifdef ENABLE_TESTING_SUPPORT
824                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
825 #endif
826                     return;
827             }
828 
829 #ifdef ENABLE_TESTING_SUPPORT
830             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
831                 hid_characteristic_name(characteristic.uuid16),
832                 characteristic.start_handle,
833                 characteristic.properties,
834                 characteristic.value_handle, characteristic.uuid16,
835                 client->service_index);
836 
837             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
838                 printf(", report index 0x%02X", report_index);
839             }
840             printf("\n");
841 #endif
842             break;
843 
844         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
845             // Map Report characteristic value == HID Descriptor
846             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
847             btstack_assert(client != NULL);
848 
849             descriptor_value = gatt_event_long_characteristic_value_query_result_get_value(packet);
850             descriptor_value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
851 
852 #ifdef ENABLE_TESTING_SUPPORT
853             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
854             printf_hexdump(descriptor_value, descriptor_value_len);
855 #endif
856             for (i = 0; i < descriptor_value_len; i++){
857                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, descriptor_value[i]);
858                 if (!stored){
859                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
860                     break;
861                 }
862             }
863             break;
864 
865         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
866             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
867             btstack_assert(client != NULL);
868             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
869 
870             switch (client->state) {
871                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
872                     // setup for descriptor value query
873                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
874                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
875 
876 #ifdef ENABLE_TESTING_SUPPORT
877                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
878                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
879                                 characteristic_descriptor.handle,
880                                 characteristic_descriptor.uuid16,
881                                 client->service_index, report_index);
882                         }
883 #endif
884                     }
885                     break;
886                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
887                     // setup for descriptor value query
888                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
889                         client->descriptor_handle = characteristic_descriptor.handle;
890 #ifdef ENABLE_TESTING_SUPPORT
891                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
892                             characteristic_descriptor.handle,
893                             characteristic_descriptor.uuid16);
894 #endif
895                     }
896 
897 #ifdef ENABLE_TESTING_SUPPORT
898                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
899                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
900                             characteristic_descriptor.handle,
901                             characteristic_descriptor.uuid16);
902                     }
903 #endif
904                     break;
905 
906                 default:
907                     break;
908             }
909             break;
910 
911         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
912             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
913             btstack_assert(client != NULL);
914 
915             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
916                 break;
917             }
918 
919             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
920             switch (client->state) {
921                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
922                     // get external report characteristic uuid
923                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
924                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
925                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
926 #ifdef ENABLE_TESTING_SUPPORT
927                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
928                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
929 #endif
930                     }
931                     break;
932 
933                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
934 
935                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
936                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
937                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
938     #ifdef ENABLE_TESTING_SUPPORT
939                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
940                             client->reports[client->report_index].report_id,
941                             client->reports[client->report_index].report_type,
942                             client->report_index, client->service_index,
943                             client->external_reports[client->report_index].external_report_reference_uuid);
944     #endif
945                     }
946                     break;
947 
948                 default:
949                     printf("GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT 3");
950                     break;
951             }
952             break;
953 
954         case GATT_EVENT_QUERY_COMPLETE:
955             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
956             btstack_assert(client != NULL);
957 
958             att_status = gatt_event_query_complete_get_att_status(packet);
959 
960             switch (client->state){
961                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
962                     if (att_status != ATT_ERROR_SUCCESS){
963                         hids_emit_connection_established(client, att_status);
964                         hids_finalize_client(client);
965                         break;
966                     }
967 
968                     if (client->num_instances == 0){
969                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
970                         hids_finalize_client(client);
971                         break;
972                     }
973 
974                     client->service_index = 0;
975                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
976                     break;
977 
978                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
979                     if (att_status != ATT_ERROR_SUCCESS){
980                         hids_emit_connection_established(client, att_status);
981                         hids_finalize_client(client);
982                         break;
983                     }
984 
985                     if ((client->service_index + 1) < client->num_instances){
986                         // discover characteristics of next service
987                         client->service_index++;
988                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
989                         break;
990                     }
991 
992                     switch (client->required_protocol_mode){
993                         case HID_PROTOCOL_MODE_REPORT:
994                             client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
995                             if (hid_clients_has_reports_in_report_mode(client)){
996                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
997                                 break;
998                             }
999                             hids_emit_connection_established(client, att_status);
1000                             hids_finalize_client(client);
1001                             return;
1002 
1003                         case HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT:
1004                             if (hid_clients_has_reports_in_report_mode(client)){
1005                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1006                                 break;
1007                             }
1008                             if (hid_clients_has_reports_in_boot_mode(client)){
1009                                 if (client->protocol_mode_value_handle != 0){
1010                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
1011                                     break;
1012                                 }
1013                                 hids_emit_connection_established(client, att_status);
1014                                 hids_finalize_client(client);
1015                                 return;
1016                             }
1017                             break;
1018                         default:
1019                             if (hid_clients_has_reports_in_boot_mode(client)){
1020                                 if (client->protocol_mode_value_handle != 0){
1021                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
1022                                     break;
1023                                 }
1024                                 hids_emit_connection_established(client, att_status);
1025                                 hids_finalize_client(client);
1026                                 return;
1027                             }
1028                             break;
1029                     }
1030 
1031                     if (client->state == HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE){
1032                         break;
1033                     }
1034 
1035                     // 1. we need to get HID Descriptor and
1036                     // 2. get external Report characteristics if referenced from Report Map
1037                     if (hids_client_report_map_query_init(client)){
1038                         break;
1039                     }
1040                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1041                     hids_finalize_client(client);
1042                     break;
1043 
1044 
1045                 // HID descriptor found
1046                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1047                     if (att_status != ATT_ERROR_SUCCESS){
1048                         hids_emit_connection_established(client, att_status);
1049                         hids_finalize_client(client);
1050                         break;
1051                     }
1052                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1053                     break;
1054 
1055                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1056                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1057                     // go for next report map
1058                     if (hids_client_report_query_next_report_map(client)){
1059                         break;
1060                     }
1061 
1062                     // read UUIDS for external characteristics
1063                     if (hids_client_report_map_uuid_query_init(client)){
1064                         break;
1065                     }
1066 
1067                     // discover characteristic descriptor for all Report characteristics,
1068                     // then read value of characteristic descriptor to get Report ID
1069                     if (hids_client_report_query_init(client)){
1070                         break;
1071                     }
1072 
1073                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1074                     hids_finalize_client(client);
1075                     break;
1076 
1077                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1078                     // go for next map report
1079                     if (hids_client_report_query_next_report_map_uuid(client)){
1080                         break;
1081                     }
1082 
1083                     // update external characteristics with correct value handle and end handle
1084                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1085                     break;
1086 
1087                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1088                     // discover characteristic descriptor for all Report characteristics,
1089                     // then read value of characteristic descriptor to get Report ID
1090                     if (hids_client_report_query_init(client)){
1091                         break;
1092                     }
1093 
1094                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1095                     hids_finalize_client(client);
1096                     break;
1097 
1098                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1099                     if (client->descriptor_handle != 0){
1100                         printf("descriptor found\n");
1101                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
1102                         break;
1103                     }
1104 
1105                     // go for next report
1106                     if (hids_client_report_query_next_report(client)){
1107                         break;
1108                     }
1109 
1110                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1111                     break;
1112 
1113                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
1114                     // go for next report
1115                     if (hids_client_report_query_next_report(client)){
1116                         break;
1117                     }
1118 
1119                     if (hids_client_report_notifications_init(client)){
1120                         break;
1121                     }
1122                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1123                     break;
1124 
1125                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1126                     if (hids_client_report_next_notification_report_index(client)){
1127                         break;
1128                     }
1129                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1130                     break;
1131 
1132                 default:
1133                     break;
1134             }
1135             break;
1136 
1137         default:
1138             break;
1139     }
1140 
1141     if (client != NULL){
1142         hids_run_for_client(client);
1143     }
1144 }
1145 
1146 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){
1147     btstack_assert(packet_handler != NULL);
1148 
1149     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1150     if (client != NULL){
1151         return ERROR_CODE_COMMAND_DISALLOWED;
1152     }
1153 
1154     uint16_t cid = hids_get_next_cid();
1155     if (hids_cid != NULL) {
1156         *hids_cid = cid;
1157     }
1158 
1159     client = hids_create_client(con_handle, cid);
1160     if (client == NULL) {
1161         return BTSTACK_MEMORY_ALLOC_FAILED;
1162     }
1163 
1164     client->required_protocol_mode = protocol_mode;
1165     client->client_handler = packet_handler;
1166     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1167 
1168     hids_run_for_client(client);
1169     return ERROR_CODE_SUCCESS;
1170 }
1171 
1172 uint8_t hids_client_disconnect(uint16_t hids_cid){
1173     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1174     if (client == NULL){
1175         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1176     }
1177     // finalize connection
1178     hids_finalize_client(client);
1179     return ERROR_CODE_SUCCESS;
1180 }
1181 
1182 uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){
1183     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1184     if (client == NULL){
1185         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1186     }
1187 
1188     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1189         return ERROR_CODE_COMMAND_DISALLOWED;
1190     }
1191 
1192     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT);
1193     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
1194         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1195     }
1196 
1197     uint16_t mtu;
1198     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
1199 
1200     if (status != ERROR_CODE_SUCCESS){
1201         return status;
1202     }
1203 
1204     if (mtu - 2 < report_len){
1205         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
1206     }
1207 
1208     client->state = HIDS_CLIENT_W2_SEND_REPORT;
1209     client->report_index = report_index;
1210     client->report = report;
1211     client->report_len = report_len;
1212 
1213     hids_run_for_client(client);
1214     return ERROR_CODE_SUCCESS;
1215 }
1216 
1217 void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1218     hids_client_descriptor_storage = hid_descriptor_storage;
1219     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1220 }
1221 
1222 void hids_client_deinit(void){}
1223