xref: /btstack/src/ble/gatt-service/hids_client.c (revision f11fd9a990fedbf11b7c70e38b9da44019506e13)
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/core.h"
53 #include "ble/gatt_client.h"
54 #include "bluetooth_gatt.h"
55 #include "btstack_debug.h"
56 #include "btstack_event.h"
57 #include "btstack_run_loop.h"
58 #include "gap.h"
59 
60 #define HID_REPORT_MODE_REPORT_ID               3
61 #define HID_REPORT_MODE_REPORT_MAP_ID           4
62 #define HID_REPORT_MODE_HID_INFORMATION_ID      5
63 #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
64 
65 static btstack_linked_list_t clients;
66 static uint16_t hids_cid_counter = 0;
67 
68 static uint8_t * hids_client_descriptor_storage;
69 static uint16_t  hids_client_descriptor_storage_len;
70 
71 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
72 
73 #ifdef ENABLE_TESTING_SUPPORT
74 static char * hid_characteristic_name(uint16_t uuid){
75     switch (uuid){
76         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
77             return "PROTOCOL_MODE";
78 
79         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
80             return "BOOT_KEYBOARD_INPUT_REPORT";
81 
82         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
83             return "BOOT_MOUSE_INPUT_REPORT";
84 
85         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
86             return "BOOT_KEYBOARD_OUTPUT_REPORT";
87 
88         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
89             return "REPORT";
90 
91         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
92             return "REPORT_MAP";
93 
94         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
95             return "HID_INFORMATION";
96 
97         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
98             return "HID_CONTROL_POINT";
99         default:
100             return "UKNOWN";
101     }
102 }
103 #endif
104 
105 static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
106     btstack_linked_list_iterator_t it;
107     btstack_linked_list_iterator_init(&it, &clients);
108     while (btstack_linked_list_iterator_has_next(&it)){
109         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
110         if (client->con_handle != con_handle) continue;
111         return client;
112     }
113     return NULL;
114 }
115 
116 static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
117     btstack_linked_list_iterator_t it;
118     btstack_linked_list_iterator_init(&it, &clients);
119     while (btstack_linked_list_iterator_has_next(&it)){
120         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
121         if (client->cid != hids_cid) continue;
122         return client;
123     }
124     return NULL;
125 }
126 
127 
128 // START Descriptor Storage Util
129 
130 static uint16_t hids_client_descriptor_storage_get_available_space(void){
131     // assumes all descriptors are back to back
132     uint16_t free_space = hids_client_descriptor_storage_len;
133     uint8_t i;
134 
135     btstack_linked_list_iterator_t it;
136     btstack_linked_list_iterator_init(&it, &clients);
137     while (btstack_linked_list_iterator_has_next(&it)){
138         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
139         for (i = 0; i < client->num_instances; i++){
140             free_space -= client->services[i].hid_descriptor_len;
141         }
142     }
143     return free_space;
144 }
145 
146 static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
147     client->services[service_index].hid_descriptor_len = 0;
148     client->services[service_index].hid_descriptor_max_len = hids_client_descriptor_storage_get_available_space();
149     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - client->services[service_index].hid_descriptor_max_len;
150 }
151 
152 static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
153     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
154 
155     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
156     client->services[service_index].hid_descriptor_len++;
157     return true;
158 }
159 
160 static void hids_client_descriptor_storage_delete(hids_client_t * client){
161     uint8_t service_index = 0;
162     uint16_t next_offset = 0;
163 
164     for (service_index = 0; service_index < client->num_instances; service_index++){
165         next_offset += client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len;
166         client->services[service_index].hid_descriptor_len = 0;
167         client->services[service_index].hid_descriptor_offset = 0;
168     }
169 
170     memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
171             &hids_client_descriptor_storage[next_offset],
172             hids_client_descriptor_storage_len - next_offset);
173 
174     uint8_t i;
175     btstack_linked_list_iterator_t it;
176     btstack_linked_list_iterator_init(&it, &clients);
177     while (btstack_linked_list_iterator_has_next(&it)){
178         hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
179         for (i = 0; i < client->num_instances; i++){
180             if (conn->services[i].hid_descriptor_offset >= next_offset){
181                 conn->services[i].hid_descriptor_offset = next_offset;
182                 next_offset += conn->services[service_index].hid_descriptor_len;
183             }
184         }
185     }
186 }
187 
188 const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
189     hids_client_t * client = hids_get_client_for_cid(hids_cid);
190     if (client == NULL){
191         return NULL;
192     }
193     if (service_index >= client->num_instances){
194         return NULL;
195     }
196     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
197 }
198 
199 uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
200     hids_client_t * client = hids_get_client_for_cid(hids_cid);
201     if (client == NULL){
202         return 0;
203     }
204     if (service_index >= client->num_instances){
205         return 0;
206     }
207     return client->services[service_index].hid_descriptor_len;
208 }
209 
210 // END Descriptor Storage Util
211 
212 static uint16_t hids_get_next_cid(void){
213     if (hids_cid_counter == 0xffff) {
214         hids_cid_counter = 1;
215     } else {
216         hids_cid_counter++;
217     }
218     return hids_cid_counter;
219 }
220 
221 static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
222     uint8_t i;
223     for (i = 0; i < client->num_reports; i++){
224         if (client->reports[i].value_handle == value_handle){
225             return i;
226         }
227     }
228     return HIDS_CLIENT_INVALID_REPORT_INDEX;
229 }
230 
231 static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
232     uint8_t i;
233     for (i = 0; i < client->num_external_reports; i++){
234         if (client->external_reports[i].value_handle == value_handle){
235             return i;
236         }
237     }
238     return HIDS_CLIENT_INVALID_REPORT_INDEX;
239 }
240 
241 static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
242     uint8_t i;
243     for (i = 0; i < client->num_external_reports; i++){
244         if (client->external_reports[i].external_report_reference_uuid == uuid16){
245             return true;
246         }
247     }
248     return false;
249 }
250 
251 static uint8_t find_report_index_for_report_id_and_report_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){
252     uint8_t i;
253 
254     for (i = 0; i < client->num_reports; i++){
255         hids_client_report_t report = client->reports[i];
256         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
257 
258         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
259             if (!client->reports[i].boot_report){
260                 continue;
261             }
262         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
263             if (client->reports[i].boot_report){
264                 continue;
265             }
266         }
267 
268         if (report.report_id == report_id && report.report_type == report_type){
269             return i;
270         }
271     }
272     return HIDS_CLIENT_INVALID_REPORT_INDEX;
273 }
274 
275 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){
276 
277     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
278     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
279         return report_index;
280     }
281     report_index = client->num_reports;
282 
283     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
284         client->reports[report_index].value_handle = characteristic->value_handle;
285         client->reports[report_index].end_handle = characteristic->end_handle;
286         client->reports[report_index].properties = characteristic->properties;
287 
288         client->reports[report_index].service_index = client->service_index;
289         client->reports[report_index].report_id = report_id;
290         client->reports[report_index].report_type = report_type;
291         client->reports[report_index].boot_report = boot_report;
292 
293         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);
294         client->num_reports++;
295         return report_index;
296     } else {
297         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
298         return HIDS_CLIENT_INVALID_REPORT_INDEX;
299     }
300 }
301 
302 static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
303     uint8_t report_index = client->num_external_reports;
304 
305     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
306         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
307         client->external_reports[report_index].service_index = client->service_index;
308 
309         client->num_external_reports++;
310         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
311         return report_index;
312     } else {
313         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
314         return HIDS_CLIENT_INVALID_REPORT_INDEX;
315     }
316 }
317 
318 static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
319     uint8_t i;
320     for (i = client->service_index; i < client->num_instances; i++){
321         if (client->services[i].report_map_value_handle != 0){
322             return i;
323         }
324     }
325     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
326     return HIDS_CLIENT_INVALID_REPORT_INDEX;
327 }
328 
329 static bool hids_client_report_query_next_report_map(hids_client_t * client){
330     client->service_index++;
331     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
332         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
333         return true;
334     }
335     return false;
336 }
337 
338 static bool hids_client_report_map_query_init(hids_client_t * client){
339     client->service_index = 0;
340 
341     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
342         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
343         return true;
344     }
345     return false;
346 }
347 
348 static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
349     client->report_index++;
350     if (client->report_index < client->num_external_reports){
351         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
352         return true;
353     }
354     return false;
355 }
356 
357 static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
358     client->report_index = 0;
359     if (client->num_external_reports > 0){
360         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
361         return true;
362     }
363     return false;
364 }
365 
366 static uint8_t hids_client_get_next_report_index(hids_client_t * client){
367     uint8_t i;
368     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
369 
370     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
371         hids_client_report_t report = client->reports[i];
372         if (!report.boot_report){
373             if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
374                 index = i;
375                 client->service_index = report.service_index;
376                 break;
377             }
378         }
379     }
380     client->report_index = index;
381     return index;
382 }
383 
384 static bool hids_client_report_query_next_report(hids_client_t * client){
385     client->report_index++;
386     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
387         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
388         return true;
389     }
390     return false;
391 }
392 
393 static bool hids_client_report_query_init(hids_client_t * client){
394     client->report_index = 0;
395 
396     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
397         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
398         return true;
399     }
400     return false;
401 }
402 
403 static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
404     uint8_t i;
405     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
406 
407     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
408         hids_client_report_t report = client->reports[i];
409         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
410 
411         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
412             if (!client->reports[i].boot_report){
413                 continue;
414             }
415         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
416             if (client->reports[i].boot_report){
417                 continue;
418             }
419         }
420         if (report.report_type == HID_REPORT_TYPE_INPUT){
421             index = i;
422         }
423     }
424     client->report_index = index;
425     return index;
426 }
427 
428 static bool hids_client_report_next_notification_report_index(hids_client_t * client){
429     client->report_index++;
430     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
431         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
432         return true;
433     }
434     return false;
435 }
436 
437 static bool hids_client_report_notifications_init(hids_client_t * client){
438 #ifdef ENABLE_TESTING_SUPPORT
439     printf("\nRegister for Notifications: \n");
440 #endif
441     client->report_index = 0;
442 
443     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
444         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
445         return true;
446     }
447     return false;
448 }
449 
450 static bool hids_client_report_next_notifications_configuration_report_index(hids_client_t * client){
451     client->report_index++;
452     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
453         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
454         return true;
455     }
456     return false;
457 }
458 
459 static bool hids_client_notifications_configuration_init(hids_client_t * client){
460 #ifdef ENABLE_TESTING_SUPPORT
461     printf("\nConfigure for Notifications: \n");
462 #endif
463     client->report_index = 0;
464 
465     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
466         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
467         return true;
468     }
469     return false;
470 }
471 
472 static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
473     hids_client_t * client = btstack_memory_hids_client_get();
474     if (!client){
475         log_error("Not enough memory to create client");
476         return NULL;
477     }
478     client->state = HIDS_CLIENT_STATE_IDLE;
479     client->cid = cid;
480     client->con_handle = con_handle;
481 
482     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
483     return client;
484 }
485 
486 static void hids_finalize_client(hids_client_t * client){
487     // stop listening
488     uint8_t i;
489     for (i = 0; i < client->num_reports; i++){
490         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
491     }
492 
493     hids_client_descriptor_storage_delete(client);
494     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
495     btstack_memory_hids_client_free(client);
496 }
497 
498 
499 static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
500     uint8_t event[8];
501     int pos = 0;
502     event[pos++] = HCI_EVENT_GATTSERVICE_META;
503     event[pos++] = sizeof(event) - 2;
504     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
505     little_endian_store_16(event, pos, client->cid);
506     pos += 2;
507     event[pos++] = status;
508     event[pos++] = client->services[0].protocol_mode;
509     event[pos++] = client->num_instances;
510     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
511 }
512 
513 static void hids_emit_notifications_configuration(hids_client_t * client){
514     uint8_t event[6];
515     int pos = 0;
516     event[pos++] = HCI_EVENT_GATTSERVICE_META;
517     event[pos++] = sizeof(event) - 2;
518     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION;
519     little_endian_store_16(event, pos, client->cid);
520     pos += 2;
521     event[pos++] = client->value;
522     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
523 }
524 
525 static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
526     uint16_t pos = 0;
527     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
528     pos++;  // skip len
529     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
530     little_endian_store_16(buffer, pos, client->cid);
531     pos += 2;
532     buffer[pos++] = client->reports[report_index].service_index;
533     buffer[pos++] = client->reports[report_index].report_id;
534     little_endian_store_16(buffer, pos, report_len + 1);
535     pos += 2;
536     buffer[pos++] = client->reports[report_index].report_id;
537     buffer[1] = pos + (report_len + 1) - 2;
538 
539 }
540 
541 static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
542     if (value_len != 4) return;
543 
544     uint8_t event[11];
545     int pos = 0;
546     event[pos++] = HCI_EVENT_GATTSERVICE_META;
547     event[pos++] = sizeof(event) - 2;
548     event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
549     little_endian_store_16(event, pos, client->cid);
550     pos += 2;
551     event[pos++] = client->service_index;
552 
553     memcpy(event+pos, value, 3);
554     pos += 3;
555     event[pos++] = (value[3] & 0x02) >> 1;
556     event[pos++] = value[3] & 0x01;
557 
558     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
559 }
560 
561 static void hids_client_emit_protocol_mode_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
562     if (value_len != 1) return;
563 
564     uint8_t event[11];
565     int pos = 0;
566     event[pos++] = HCI_EVENT_GATTSERVICE_META;
567     event[pos++] = sizeof(event) - 2;
568     event[pos++] = GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE;
569     little_endian_store_16(event, pos, client->cid);
570     pos += 2;
571     event[pos++] = client->service_index;
572     event[pos++] = value[0];
573     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
574 }
575 
576 static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
577     UNUSED(packet_type);
578     UNUSED(channel);
579 
580     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
581 
582     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
583     btstack_assert(client != NULL);
584 
585     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
586     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
587         return;
588     }
589 
590     uint8_t * in_place_event = &packet[-2];
591     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
592     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
593 }
594 
595 static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
596     UNUSED(packet_type);
597     UNUSED(channel);
598 
599     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
600 
601     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
602     btstack_assert(client != NULL);
603 
604     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
605         return;
606     }
607     client->state = HIDS_CLIENT_STATE_CONNECTED;
608 
609     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
610     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
611         return;
612     }
613 
614     uint8_t * in_place_event = &packet[-2];
615     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_characteristic_value_query_result_get_value_length(packet));
616     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
617 }
618 
619 static void hids_run_for_client(hids_client_t * client){
620     uint8_t att_status;
621     gatt_client_service_t service;
622     gatt_client_characteristic_t characteristic;
623 
624     switch (client->state){
625         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
626 #ifdef ENABLE_TESTING_SUPPORT
627             printf("\n\nQuery Services:\n");
628 #endif
629             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
630 
631             // result in GATT_EVENT_SERVICE_QUERY_RESULT
632             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
633             UNUSED(att_status);
634             break;
635 
636         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
637 #ifdef ENABLE_TESTING_SUPPORT
638             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
639 #endif
640             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
641 
642             service.start_group_handle = client->services[client->service_index].start_handle;
643             service.end_group_handle = client->services[client->service_index].end_handle;
644 
645             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
646             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
647 
648             UNUSED(att_status);
649             break;
650 
651         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
652 #ifdef ENABLE_TESTING_SUPPORT
653             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);
654 #endif
655             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
656 
657             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
658             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);
659             UNUSED(att_status);
660             break;
661 
662         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
663 #ifdef ENABLE_TESTING_SUPPORT
664             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);
665 #endif
666             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
667 
668             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
669             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
670 
671             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
672             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
673             UNUSED(att_status);
674             break;
675 
676         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
677 #ifdef ENABLE_TESTING_SUPPORT
678             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);
679 #endif
680             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
681 
682             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
683             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);
684             UNUSED(att_status);
685             break;
686 
687         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
688  #ifdef ENABLE_TESTING_SUPPORT
689             printf("\nDiscover External Report Characteristic:\n");
690 #endif
691             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
692 
693             service.start_group_handle = 0x0001;
694             service.end_group_handle = 0xffff;
695 
696             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
697             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
698             UNUSED(att_status);
699             break;
700 
701         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
702 #ifdef ENABLE_TESTING_SUPPORT
703             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
704                 client->report_index,
705                 client->reports[client->report_index].service_index,
706                 client->reports[client->report_index].value_handle);
707 #endif
708             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
709             client->handle = 0;
710 
711             characteristic.value_handle = client->reports[client->report_index].value_handle;
712             characteristic.end_handle = client->reports[client->report_index].end_handle;
713             characteristic.properties = client->reports[client->report_index].properties;
714 
715             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
716             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
717             UNUSED(att_status);
718             break;
719 
720         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
721             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
722 
723             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
724             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
725             client->handle = 0;
726             UNUSED(att_status);
727             break;
728 
729         case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
730 #ifdef ENABLE_TESTING_SUPPORT
731             if (client->value > 0){
732                 printf("    Notification configuration enable ");
733             } else {
734                 printf("    Notification configuration disable ");
735             }
736             printf("[%d, %d, 0x%04X]:\n",
737                 client->report_index,
738                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
739 #endif
740 
741             client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
742 
743             characteristic.value_handle = client->reports[client->report_index].value_handle;
744             characteristic.end_handle = client->reports[client->report_index].end_handle;
745             characteristic.properties = client->reports[client->report_index].properties;
746 
747             // end of write marked in GATT_EVENT_QUERY_COMPLETE
748 
749             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
750 
751             if (att_status == ERROR_CODE_SUCCESS){
752                 switch(client->value){
753                     case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
754                         gatt_client_listen_for_characteristic_value_updates(
755                             &client->reports[client->report_index].notification_listener,
756                             &handle_notification_event, client->con_handle, &characteristic);
757                         break;
758                     default:
759                         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
760                         break;
761                 }
762             } else {
763                 if (hids_client_report_next_notifications_configuration_report_index(client)){
764                     hids_run_for_client(client);
765                     break;
766                 }
767                 client->state = HIDS_CLIENT_STATE_CONNECTED;
768             }
769             break;
770 
771         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
772 #ifdef ENABLE_TESTING_SUPPORT
773             printf("    Notification enable [%d, %d, 0x%04X]:\n",
774                 client->report_index,
775                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
776 #endif
777             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
778 
779             characteristic.value_handle = client->reports[client->report_index].value_handle;
780             characteristic.end_handle = client->reports[client->report_index].end_handle;
781             characteristic.properties = client->reports[client->report_index].properties;
782 
783             // end of write marked in GATT_EVENT_QUERY_COMPLETE
784             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
785 
786             if (att_status == ERROR_CODE_SUCCESS){
787                 gatt_client_listen_for_characteristic_value_updates(
788                     &client->reports[client->report_index].notification_listener,
789                     &handle_notification_event, client->con_handle, &characteristic);
790             } else {
791                 if (hids_client_report_next_notification_report_index(client)){
792                     hids_run_for_client(client);
793                     break;
794                 }
795                 client->state = HIDS_CLIENT_STATE_CONNECTED;
796                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
797             }
798             break;
799 
800 
801         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
802 #ifdef ENABLE_TESTING_SUPPORT
803             printf("    Write report [%d, %d, 0x%04X]:\n",
804                 client->report_index,
805                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
806 #endif
807 
808             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
809 
810             // see GATT_EVENT_QUERY_COMPLETE for end of write
811             att_status = gatt_client_write_value_of_characteristic(
812                 &handle_report_event, client->con_handle,
813                 client->reports[client->report_index].value_handle,
814                 client->report_len, (uint8_t *)client->report);
815             UNUSED(att_status);
816             break;
817 
818         case HIDS_CLIENT_W2_SEND_GET_REPORT:
819 #ifdef ENABLE_TESTING_SUPPORT
820             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
821                 client->report_index,
822                 client->reports[client->report_index].report_id,
823                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
824 #endif
825 
826             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
827             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
828             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
829                 &handle_report_event,
830                 client->con_handle,
831                 client->reports[client->report_index].value_handle);
832             UNUSED(att_status);
833             break;
834 
835 #ifdef ENABLE_TESTING_SUPPORT
836         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
837             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
838 
839             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
840             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
841                 &handle_gatt_client_event,
842                 client->con_handle,
843                 client->reports[client->report_index].ccc_handle);
844 
845             break;
846 #endif
847         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
848             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
849 
850             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
851             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
852                 &handle_gatt_client_event,
853                 client->con_handle,
854                 client->handle);
855             break;
856 
857         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
858         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
859             (void) gatt_client_request_can_write_without_response_event(&handle_gatt_client_event, client->con_handle);
860             break;
861 
862         default:
863             break;
864     }
865 }
866 
867 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
868     UNUSED(packet_type);
869     UNUSED(channel);
870     UNUSED(size);
871 
872     hids_client_t * client = NULL;
873     uint8_t att_status;
874     gatt_client_service_t service;
875     gatt_client_characteristic_t characteristic;
876     gatt_client_characteristic_descriptor_t characteristic_descriptor;
877 
878     // hids_client_report_t * boot_keyboard_report;
879     // hids_client_report_t * boot_mouse_report;
880     const uint8_t * characteristic_descriptor_value;
881     uint8_t i;
882     uint8_t report_index;
883 
884     const uint8_t * value;
885     uint16_t value_len;
886 
887     switch(hci_event_packet_get_type(packet)){
888         case GATT_EVENT_SERVICE_QUERY_RESULT:
889             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
890             btstack_assert(client != NULL);
891 
892             if (client->num_instances < MAX_NUM_HID_SERVICES){
893                 uint8_t index = client->num_instances;
894                 gatt_event_service_query_result_get_service(packet, &service);
895                 client->services[index].start_handle = service.start_group_handle;
896                 client->services[index].end_handle = service.end_group_handle;
897                 client->num_instances++;
898 
899 #ifdef ENABLE_TESTING_SUPPORT
900                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
901 #endif
902                 hids_client_descriptor_storage_init(client, index);
903             }  else {
904                 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);
905             }
906             break;
907 
908         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
909             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
910             btstack_assert(client != NULL);
911             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
912 
913             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
914             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
915                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
916                     break;
917                 }
918             }
919 
920             switch (characteristic.uuid16){
921                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
922                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
923                     break;
924 
925                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
926                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
927                     break;
928 
929                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
930                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
931                     break;
932 
933                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
934                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
935                     break;
936 
937                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
938                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
939                     break;
940 
941                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
942                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
943                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
944                     break;
945 
946                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
947                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
948                     break;
949 
950                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
951                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
952                     break;
953 
954                 default:
955 #ifdef ENABLE_TESTING_SUPPORT
956                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
957 #endif
958                     return;
959             }
960 
961 #ifdef ENABLE_TESTING_SUPPORT
962             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
963                 hid_characteristic_name(characteristic.uuid16),
964                 characteristic.start_handle,
965                 characteristic.properties,
966                 characteristic.value_handle, characteristic.uuid16,
967                 client->service_index);
968 
969             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
970                 printf(", report index 0x%02X", report_index);
971             }
972             printf("\n");
973 #endif
974             break;
975 
976         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
977             // Map Report characteristic value == HID Descriptor
978             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
979             btstack_assert(client != NULL);
980 
981             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
982             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
983 
984 #ifdef ENABLE_TESTING_SUPPORT
985             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
986             printf_hexdump(value, value_len);
987 #endif
988             for (i = 0; i < value_len; i++){
989                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
990                 if (!stored){
991                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
992                     break;
993                 }
994             }
995             break;
996 
997         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
998             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
999             btstack_assert(client != NULL);
1000             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
1001 
1002             switch (client->state) {
1003                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1004                     // setup for descriptor value query
1005                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
1006                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
1007 
1008 #ifdef ENABLE_TESTING_SUPPORT
1009                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1010                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1011                                 characteristic_descriptor.handle,
1012                                 characteristic_descriptor.uuid16,
1013                                 client->service_index, report_index);
1014                         }
1015 #endif
1016                     }
1017                     break;
1018                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1019                     // setup for descriptor value query
1020                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1021                         client->handle = characteristic_descriptor.handle;
1022 #ifdef ENABLE_TESTING_SUPPORT
1023                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1024                             characteristic_descriptor.handle,
1025                             characteristic_descriptor.uuid16);
1026 #endif
1027                     }
1028 
1029 #ifdef ENABLE_TESTING_SUPPORT
1030                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
1031                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1032                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1033                             characteristic_descriptor.handle,
1034                             characteristic_descriptor.uuid16);
1035                     }
1036 #endif
1037                     break;
1038 
1039                 default:
1040                     break;
1041             }
1042             break;
1043 
1044         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
1045             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
1046             btstack_assert(client != NULL);
1047 
1048             value = gatt_event_characteristic_value_query_result_get_value(packet);
1049             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1050 
1051 
1052             switch (client->state){
1053 #ifdef ENABLE_TESTING_SUPPORT
1054                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1055                     printf("    Received CCC value: ");
1056                     printf_hexdump(value,  value_len);
1057                     break;
1058 #endif
1059                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1060                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1061                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1062                         hids_client_emit_hid_information_event(client, value, value_len);
1063                         break;
1064                     }
1065                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1066                         hids_client_emit_protocol_mode_event(client, value, value_len);
1067                         break;
1068                     }
1069                     break;
1070                 }
1071                 default:
1072                     break;
1073             }
1074 
1075             break;
1076 
1077         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
1078             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
1079             btstack_assert(client != NULL);
1080 
1081             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1082                 break;
1083             }
1084 
1085             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1086             switch (client->state) {
1087                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1088                     // get external report characteristic uuid
1089                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
1090                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1091                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1092 #ifdef ENABLE_TESTING_SUPPORT
1093                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
1094                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1095 #endif
1096                     }
1097                     break;
1098 
1099                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
1100 
1101                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1102                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1103                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
1104     #ifdef ENABLE_TESTING_SUPPORT
1105                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1106                             client->reports[client->report_index].report_id,
1107                             client->reports[client->report_index].report_type,
1108                             client->report_index, client->service_index);
1109     #endif
1110                     }
1111                     break;
1112 
1113                 default:
1114                     break;
1115             }
1116             break;
1117 
1118         case GATT_EVENT_CAN_WRITE_WITHOUT_RESPONSE:
1119             client = hids_get_client_for_con_handle(gatt_event_can_write_without_response_get_handle(packet));
1120             btstack_assert(client != NULL);
1121 
1122             switch (client->state){
1123                 case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
1124                     att_status = gatt_client_write_value_of_characteristic_without_response(
1125                         client->con_handle,
1126                         client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
1127 
1128 #ifdef ENABLE_TESTING_SUPPORT
1129                     printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
1130 #endif
1131 
1132                     if (att_status == ATT_ERROR_SUCCESS){
1133                         client->services[client->service_index].protocol_mode = client->required_protocol_mode;
1134                         if ((client->service_index + 1) < client->num_instances){
1135                             client->service_index++;
1136                             hids_run_for_client(client);
1137                             break;
1138                         }
1139                     }
1140 
1141                     // read UUIDS for external characteristics
1142                     if (hids_client_report_map_uuid_query_init(client)){
1143                         hids_run_for_client(client);
1144                         break;
1145                     }
1146 
1147                     // discover characteristic descriptor for all Report characteristics,
1148                     // then read value of characteristic descriptor to get Report ID
1149                     if (hids_client_report_query_init(client)){
1150                         hids_run_for_client(client);
1151                         break;
1152                     }
1153 
1154                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1155                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1156                     break;
1157 
1158                 case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
1159 #ifdef ENABLE_TESTING_SUPPORT
1160                     printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
1161 #endif
1162                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1163                     (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
1164                     break;
1165 
1166                 default:
1167                     break;
1168             }
1169 
1170             break;
1171 
1172         case GATT_EVENT_QUERY_COMPLETE:
1173             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
1174             btstack_assert(client != NULL);
1175 
1176             att_status = gatt_event_query_complete_get_att_status(packet);
1177 
1178             switch (client->state){
1179                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
1180                     if (att_status != ATT_ERROR_SUCCESS){
1181                         hids_emit_connection_established(client, att_status);
1182                         hids_finalize_client(client);
1183                         return;
1184                     }
1185 
1186                     if (client->num_instances == 0){
1187                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1188                         hids_finalize_client(client);
1189                         return;
1190                     }
1191 
1192                     client->service_index = 0;
1193                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1194                     break;
1195 
1196                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
1197                     if (att_status != ATT_ERROR_SUCCESS){
1198                         hids_emit_connection_established(client, att_status);
1199                         hids_finalize_client(client);
1200                         return;
1201                     }
1202 
1203                     if ((client->service_index + 1) < client->num_instances){
1204                         // discover characteristics of next service
1205                         client->service_index++;
1206                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1207                         break;
1208                     }
1209 
1210                     btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1211 
1212                     switch (client->required_protocol_mode){
1213                         case HID_PROTOCOL_MODE_REPORT:
1214                             for (i = 0; i < client->num_instances; i++){
1215                                 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1216                             }
1217                             // 1. we need to get HID Descriptor and
1218                             // 2. get external Report characteristics if referenced from Report Map
1219                             if (hids_client_report_map_query_init(client)){
1220                                 break;
1221                             }
1222                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1223                             hids_finalize_client(client);
1224                             return;
1225 
1226                         default:
1227                             // set boot mode
1228                             client->service_index = 0;
1229                             client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1230                             break;
1231                     }
1232                     break;
1233 
1234 
1235                 // HID descriptor found
1236                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1237                     if (att_status != ATT_ERROR_SUCCESS){
1238                         hids_emit_connection_established(client, att_status);
1239                         hids_finalize_client(client);
1240                         return;
1241                     }
1242                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1243                     break;
1244 
1245                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1246                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1247                     // go for next report map
1248                     if (hids_client_report_query_next_report_map(client)){
1249                         break;
1250                     }
1251 
1252                     // read UUIDS for external characteristics
1253                     if (hids_client_report_map_uuid_query_init(client)){
1254                         break;
1255                     }
1256 
1257                     // discover characteristic descriptor for all Report characteristics,
1258                     // then read value of characteristic descriptor to get Report ID
1259                     if (hids_client_report_query_init(client)){
1260                         break;
1261                     }
1262 
1263                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1264                     hids_finalize_client(client);
1265                     return;
1266 
1267                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1268                     // go for next map report
1269                     if (hids_client_report_query_next_report_map_uuid(client)){
1270                         break;
1271                     }
1272 
1273                     // update external characteristics with correct value handle and end handle
1274                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1275                     break;
1276 
1277                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1278                     // discover characteristic descriptor for all Report characteristics,
1279                     // then read value of characteristic descriptor to get Report ID
1280                     if (hids_client_report_query_init(client)){
1281                         break;
1282                     }
1283 
1284                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1285                     hids_finalize_client(client);
1286                     return;
1287 
1288                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1289                     if (client->handle != 0){
1290                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
1291                         break;
1292                     }
1293                     // go for next report
1294                     if (hids_client_report_query_next_report(client)){
1295                         break;
1296                     }
1297                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1298                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1299                     break;
1300 
1301                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
1302                     // go for next report
1303                     if (hids_client_report_query_next_report(client)){
1304                         break;
1305                     }
1306                     if (hids_client_report_notifications_init(client)){
1307                         break;
1308                     }
1309                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1310                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1311                     break;
1312 
1313                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1314                     if (hids_client_report_next_notification_report_index(client)){
1315                         break;
1316                     }
1317                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1318                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1319                     break;
1320 
1321                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1322                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1323                         break;
1324                     }
1325                     hids_emit_notifications_configuration(client);
1326                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1327                     break;
1328 
1329 #ifdef ENABLE_TESTING_SUPPORT
1330                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1331                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1332                     break;
1333 #endif
1334 
1335                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
1336                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
1337                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1338                     break;
1339 
1340 
1341                 default:
1342                     break;
1343             }
1344             break;
1345 
1346         default:
1347             break;
1348     }
1349 
1350     if (client != NULL){
1351         hids_run_for_client(client);
1352     }
1353 }
1354 
1355 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){
1356     btstack_assert(packet_handler != NULL);
1357 
1358     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1359     if (client != NULL){
1360         return ERROR_CODE_COMMAND_DISALLOWED;
1361     }
1362 
1363     uint16_t cid = hids_get_next_cid();
1364     if (hids_cid != NULL) {
1365         *hids_cid = cid;
1366     }
1367 
1368     client = hids_create_client(con_handle, cid);
1369     if (client == NULL) {
1370         return BTSTACK_MEMORY_ALLOC_FAILED;
1371     }
1372 
1373     client->required_protocol_mode = protocol_mode;
1374     client->client_handler = packet_handler;
1375     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1376 
1377     hids_run_for_client(client);
1378     return ERROR_CODE_SUCCESS;
1379 }
1380 
1381 uint8_t hids_client_disconnect(uint16_t hids_cid){
1382     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1383     if (client == NULL){
1384         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1385     }
1386     // finalize connection
1387     hids_finalize_client(client);
1388     return ERROR_CODE_SUCCESS;
1389 }
1390 
1391 uint8_t hids_client_send_write_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type, const uint8_t * report, uint8_t report_len){
1392     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1393     if (client == NULL){
1394         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1395     }
1396 
1397     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1398         return ERROR_CODE_COMMAND_DISALLOWED;
1399     }
1400 
1401     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
1402 
1403     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
1404         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1405     }
1406 
1407     uint16_t mtu;
1408     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
1409 
1410     if (status != ERROR_CODE_SUCCESS){
1411         return status;
1412     }
1413 
1414     if (mtu - 2 < report_len){
1415         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
1416     }
1417 
1418     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1419     client->report_index = report_index;
1420     client->report = report;
1421     client->report_len = report_len;
1422 
1423     hids_run_for_client(client);
1424     return ERROR_CODE_SUCCESS;
1425 }
1426 
1427 uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
1428     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1429     if (client == NULL){
1430         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1431     }
1432 
1433     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1434         return ERROR_CODE_COMMAND_DISALLOWED;
1435     }
1436 
1437     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
1438     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
1439         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1440     }
1441 
1442     client->report_index = report_index;
1443 
1444 #ifdef ENABLE_TESTING_SUPPORT
1445     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
1446 #else
1447     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1448 #endif
1449     hids_run_for_client(client);
1450     return ERROR_CODE_SUCCESS;
1451 }
1452 
1453 
1454 uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1455     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1456     if (client == NULL){
1457         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1458     }
1459 
1460     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1461         return ERROR_CODE_COMMAND_DISALLOWED;
1462     }
1463 
1464     if (service_index >= client->num_instances){
1465         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1466     }
1467 
1468     client->service_index = service_index;
1469     client->handle = client->services[client->service_index].hid_information_value_handle;
1470 
1471     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1472     hids_run_for_client(client);
1473     return ERROR_CODE_SUCCESS;
1474 }
1475 
1476 uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1477     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1478     if (client == NULL){
1479         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1480     }
1481 
1482     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1483         return ERROR_CODE_COMMAND_DISALLOWED;
1484     }
1485 
1486     if (service_index >= client->num_instances){
1487         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1488     }
1489 
1490     client->service_index = service_index;
1491     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1492 
1493     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1494     hids_run_for_client(client);
1495     return ERROR_CODE_SUCCESS;
1496 }
1497 
1498 uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode){
1499     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1500     if (client == NULL){
1501         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1502     }
1503 
1504     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1505         return ERROR_CODE_COMMAND_DISALLOWED;
1506     }
1507 
1508     if (service_index >= client->num_instances){
1509         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1510     }
1511 
1512     client->service_index = service_index;
1513     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1514     client->value = (uint8_t)protocol_mode;
1515 
1516     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
1517     hids_run_for_client(client);
1518     return ERROR_CODE_SUCCESS;
1519 }
1520 
1521 
1522 static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
1523     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1524     if (client == NULL){
1525         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1526     }
1527 
1528     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1529         return ERROR_CODE_COMMAND_DISALLOWED;
1530     }
1531 
1532     if (service_index >= client->num_instances){
1533         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1534     }
1535 
1536     client->service_index = service_index;
1537     client->handle = client->services[client->service_index].control_point_value_handle;
1538     client->value = value;
1539 
1540     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
1541     hids_run_for_client(client);
1542     return ERROR_CODE_SUCCESS;
1543 }
1544 
1545 uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
1546     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
1547 }
1548 
1549 uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
1550     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
1551 }
1552 
1553 uint8_t hids_client_enable_notifications(uint16_t hids_cid){
1554      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1555     if (client == NULL){
1556         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1557     }
1558 
1559     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1560         return ERROR_CODE_COMMAND_DISALLOWED;
1561     }
1562     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1563     if (hids_client_notifications_configuration_init(client)){
1564         hids_run_for_client(client);
1565         return ERROR_CODE_SUCCESS;
1566     }
1567     hids_emit_notifications_configuration(client);
1568     return ERROR_CODE_SUCCESS;
1569 }
1570 
1571 uint8_t hids_client_disable_notifications(uint16_t hids_cid){
1572          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1573     if (client == NULL){
1574         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1575     }
1576 
1577     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1578         return ERROR_CODE_COMMAND_DISALLOWED;
1579     }
1580 
1581     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1582     if (hids_client_notifications_configuration_init(client)){
1583         hids_run_for_client(client);
1584         return ERROR_CODE_SUCCESS;
1585     }
1586     hids_emit_notifications_configuration(client);
1587     return ERROR_CODE_SUCCESS;
1588 }
1589 
1590 void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1591     hids_client_descriptor_storage = hid_descriptor_storage;
1592     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1593 }
1594 
1595 void hids_client_deinit(void){}
1596