xref: /btstack/src/ble/gatt-service/hids_client.c (revision c824d78c0a34df89b57d535abafcc7dacf30bb06)
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 BLUEKITCHEN
24  * GMBH 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     if (client == NULL) return;
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     if (client == NULL) return;
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             if (client == NULL) break;
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             if (client == NULL) break;
911 
912             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
913 
914             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
915             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
916                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
917                     break;
918                 }
919             }
920 
921             switch (characteristic.uuid16){
922                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
923                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
924                     break;
925 
926                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
927                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
928                     break;
929 
930                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
931                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
932                     break;
933 
934                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
935                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
936                     break;
937 
938                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
939                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
940                     break;
941 
942                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
943                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
944                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
945                     break;
946 
947                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
948                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
949                     break;
950 
951                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
952                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
953                     break;
954 
955                 default:
956 #ifdef ENABLE_TESTING_SUPPORT
957                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
958 #endif
959                     return;
960             }
961 
962 #ifdef ENABLE_TESTING_SUPPORT
963             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
964                 hid_characteristic_name(characteristic.uuid16),
965                 characteristic.start_handle,
966                 characteristic.properties,
967                 characteristic.value_handle, characteristic.uuid16,
968                 client->service_index);
969 
970             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
971                 printf(", report index 0x%02X", report_index);
972             }
973             printf("\n");
974 #endif
975             break;
976 
977         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
978             // Map Report characteristic value == HID Descriptor
979             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
980             if (client == NULL) break;
981 
982             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
983             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
984 
985 #ifdef ENABLE_TESTING_SUPPORT
986             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
987             printf_hexdump(value, value_len);
988 #endif
989             for (i = 0; i < value_len; i++){
990                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
991                 if (!stored){
992                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
993                     break;
994                 }
995             }
996             break;
997 
998         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
999             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
1000             if (client == NULL) break;
1001 
1002             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
1003 
1004             switch (client->state) {
1005                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1006                     // setup for descriptor value query
1007                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
1008                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
1009 
1010 #ifdef ENABLE_TESTING_SUPPORT
1011                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1012                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1013                                 characteristic_descriptor.handle,
1014                                 characteristic_descriptor.uuid16,
1015                                 client->service_index, report_index);
1016                         }
1017 #endif
1018                     }
1019                     break;
1020                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1021                     // setup for descriptor value query
1022                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1023                         client->handle = characteristic_descriptor.handle;
1024 #ifdef ENABLE_TESTING_SUPPORT
1025                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1026                             characteristic_descriptor.handle,
1027                             characteristic_descriptor.uuid16);
1028 #endif
1029                     }
1030 
1031 #ifdef ENABLE_TESTING_SUPPORT
1032                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
1033                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1034                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1035                             characteristic_descriptor.handle,
1036                             characteristic_descriptor.uuid16);
1037                     }
1038 #endif
1039                     break;
1040 
1041                 default:
1042                     break;
1043             }
1044             break;
1045 
1046         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
1047             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
1048             if (client == NULL) break;
1049 
1050             value = gatt_event_characteristic_value_query_result_get_value(packet);
1051             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1052 
1053 
1054             switch (client->state){
1055 #ifdef ENABLE_TESTING_SUPPORT
1056                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1057                     printf("    Received CCC value: ");
1058                     printf_hexdump(value,  value_len);
1059                     break;
1060 #endif
1061                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1062                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1063                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1064                         hids_client_emit_hid_information_event(client, value, value_len);
1065                         break;
1066                     }
1067                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1068                         hids_client_emit_protocol_mode_event(client, value, value_len);
1069                         break;
1070                     }
1071                     break;
1072                 }
1073                 default:
1074                     break;
1075             }
1076 
1077             break;
1078 
1079         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
1080             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
1081             if (client == NULL) break;
1082 
1083             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1084                 break;
1085             }
1086 
1087             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1088             switch (client->state) {
1089                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1090                     // get external report characteristic uuid
1091                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
1092                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1093                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1094 #ifdef ENABLE_TESTING_SUPPORT
1095                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
1096                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1097 #endif
1098                     }
1099                     break;
1100 
1101                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
1102 
1103                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1104                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1105                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
1106     #ifdef ENABLE_TESTING_SUPPORT
1107                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1108                             client->reports[client->report_index].report_id,
1109                             client->reports[client->report_index].report_type,
1110                             client->report_index, client->service_index);
1111     #endif
1112                     }
1113                     break;
1114 
1115                 default:
1116                     break;
1117             }
1118             break;
1119 
1120         case GATT_EVENT_CAN_WRITE_WITHOUT_RESPONSE:
1121             client = hids_get_client_for_con_handle(gatt_event_can_write_without_response_get_handle(packet));
1122             if (client == NULL) break;
1123 
1124             switch (client->state){
1125                 case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
1126                     att_status = gatt_client_write_value_of_characteristic_without_response(
1127                         client->con_handle,
1128                         client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
1129 
1130 #ifdef ENABLE_TESTING_SUPPORT
1131                     printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
1132 #endif
1133 
1134                     if (att_status == ATT_ERROR_SUCCESS){
1135                         client->services[client->service_index].protocol_mode = client->required_protocol_mode;
1136                         if ((client->service_index + 1) < client->num_instances){
1137                             client->service_index++;
1138                             hids_run_for_client(client);
1139                             break;
1140                         }
1141                     }
1142 
1143                     // read UUIDS for external characteristics
1144                     if (hids_client_report_map_uuid_query_init(client)){
1145                         hids_run_for_client(client);
1146                         break;
1147                     }
1148 
1149                     // discover characteristic descriptor for all Report characteristics,
1150                     // then read value of characteristic descriptor to get Report ID
1151                     if (hids_client_report_query_init(client)){
1152                         hids_run_for_client(client);
1153                         break;
1154                     }
1155 
1156                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1157                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1158                     break;
1159 
1160                 case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
1161 #ifdef ENABLE_TESTING_SUPPORT
1162                     printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
1163 #endif
1164                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1165                     (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
1166                     break;
1167 
1168                 default:
1169                     break;
1170             }
1171 
1172             break;
1173 
1174         case GATT_EVENT_QUERY_COMPLETE:
1175             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
1176             if (client == NULL) break;
1177 
1178             att_status = gatt_event_query_complete_get_att_status(packet);
1179 
1180             switch (client->state){
1181                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
1182                     if (att_status != ATT_ERROR_SUCCESS){
1183                         hids_emit_connection_established(client, att_status);
1184                         hids_finalize_client(client);
1185                         return;
1186                     }
1187 
1188                     if (client->num_instances == 0){
1189                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1190                         hids_finalize_client(client);
1191                         return;
1192                     }
1193 
1194                     client->service_index = 0;
1195                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1196                     break;
1197 
1198                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
1199                     if (att_status != ATT_ERROR_SUCCESS){
1200                         hids_emit_connection_established(client, att_status);
1201                         hids_finalize_client(client);
1202                         return;
1203                     }
1204 
1205                     if ((client->service_index + 1) < client->num_instances){
1206                         // discover characteristics of next service
1207                         client->service_index++;
1208                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1209                         break;
1210                     }
1211 
1212                     btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1213 
1214                     switch (client->required_protocol_mode){
1215                         case HID_PROTOCOL_MODE_REPORT:
1216                             for (i = 0; i < client->num_instances; i++){
1217                                 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1218                             }
1219                             // 1. we need to get HID Descriptor and
1220                             // 2. get external Report characteristics if referenced from Report Map
1221                             if (hids_client_report_map_query_init(client)){
1222                                 break;
1223                             }
1224                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1225                             hids_finalize_client(client);
1226                             return;
1227 
1228                         default:
1229                             // set boot mode
1230                             client->service_index = 0;
1231                             client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1232                             break;
1233                     }
1234                     break;
1235 
1236 
1237                 // HID descriptor found
1238                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1239                     if (att_status != ATT_ERROR_SUCCESS){
1240                         hids_emit_connection_established(client, att_status);
1241                         hids_finalize_client(client);
1242                         return;
1243                     }
1244                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1245                     break;
1246 
1247                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1248                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1249                     // go for next report map
1250                     if (hids_client_report_query_next_report_map(client)){
1251                         break;
1252                     }
1253 
1254                     // read UUIDS for external characteristics
1255                     if (hids_client_report_map_uuid_query_init(client)){
1256                         break;
1257                     }
1258 
1259                     // discover characteristic descriptor for all Report characteristics,
1260                     // then read value of characteristic descriptor to get Report ID
1261                     if (hids_client_report_query_init(client)){
1262                         break;
1263                     }
1264 
1265                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1266                     hids_finalize_client(client);
1267                     return;
1268 
1269                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1270                     // go for next map report
1271                     if (hids_client_report_query_next_report_map_uuid(client)){
1272                         break;
1273                     }
1274 
1275                     // update external characteristics with correct value handle and end handle
1276                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1277                     break;
1278 
1279                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1280                     // discover characteristic descriptor for all Report characteristics,
1281                     // then read value of characteristic descriptor to get Report ID
1282                     if (hids_client_report_query_init(client)){
1283                         break;
1284                     }
1285 
1286                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1287                     hids_finalize_client(client);
1288                     return;
1289 
1290                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1291                     if (client->handle != 0){
1292                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
1293                         break;
1294                     }
1295                     // go for next report
1296                     if (hids_client_report_query_next_report(client)){
1297                         break;
1298                     }
1299                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1300                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1301                     break;
1302 
1303                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
1304                     // go for next report
1305                     if (hids_client_report_query_next_report(client)){
1306                         break;
1307                     }
1308                     if (hids_client_report_notifications_init(client)){
1309                         break;
1310                     }
1311                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1312                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1313                     break;
1314 
1315                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1316                     if (hids_client_report_next_notification_report_index(client)){
1317                         break;
1318                     }
1319                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1320                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1321                     break;
1322 
1323                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1324                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1325                         break;
1326                     }
1327                     hids_emit_notifications_configuration(client);
1328                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1329                     break;
1330 
1331 #ifdef ENABLE_TESTING_SUPPORT
1332                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1333                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1334                     break;
1335 #endif
1336 
1337                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
1338                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
1339                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1340                     break;
1341 
1342 
1343                 default:
1344                     break;
1345             }
1346             break;
1347 
1348         default:
1349             break;
1350     }
1351 
1352     if (client != NULL){
1353         hids_run_for_client(client);
1354     }
1355 }
1356 
1357 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){
1358     btstack_assert(packet_handler != NULL);
1359 
1360     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1361     if (client != NULL){
1362         return ERROR_CODE_COMMAND_DISALLOWED;
1363     }
1364 
1365     uint16_t cid = hids_get_next_cid();
1366     if (hids_cid != NULL) {
1367         *hids_cid = cid;
1368     }
1369 
1370     client = hids_create_client(con_handle, cid);
1371     if (client == NULL) {
1372         return BTSTACK_MEMORY_ALLOC_FAILED;
1373     }
1374 
1375     client->required_protocol_mode = protocol_mode;
1376     client->client_handler = packet_handler;
1377     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1378 
1379     hids_run_for_client(client);
1380     return ERROR_CODE_SUCCESS;
1381 }
1382 
1383 uint8_t hids_client_disconnect(uint16_t hids_cid){
1384     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1385     if (client == NULL){
1386         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1387     }
1388     // finalize connection
1389     hids_finalize_client(client);
1390     return ERROR_CODE_SUCCESS;
1391 }
1392 
1393 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){
1394     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1395     if (client == NULL){
1396         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1397     }
1398 
1399     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1400         return ERROR_CODE_COMMAND_DISALLOWED;
1401     }
1402 
1403     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
1404 
1405     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
1406         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1407     }
1408 
1409     uint16_t mtu;
1410     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
1411 
1412     if (status != ERROR_CODE_SUCCESS){
1413         return status;
1414     }
1415 
1416     if (mtu - 2 < report_len){
1417         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
1418     }
1419 
1420     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1421     client->report_index = report_index;
1422     client->report = report;
1423     client->report_len = report_len;
1424 
1425     hids_run_for_client(client);
1426     return ERROR_CODE_SUCCESS;
1427 }
1428 
1429 uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
1430     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1431     if (client == NULL){
1432         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1433     }
1434 
1435     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1436         return ERROR_CODE_COMMAND_DISALLOWED;
1437     }
1438 
1439     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
1440     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
1441         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1442     }
1443 
1444     client->report_index = report_index;
1445 
1446 #ifdef ENABLE_TESTING_SUPPORT
1447     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
1448 #else
1449     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1450 #endif
1451     hids_run_for_client(client);
1452     return ERROR_CODE_SUCCESS;
1453 }
1454 
1455 
1456 uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1457     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1458     if (client == NULL){
1459         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1460     }
1461 
1462     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1463         return ERROR_CODE_COMMAND_DISALLOWED;
1464     }
1465 
1466     if (service_index >= client->num_instances){
1467         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1468     }
1469 
1470     client->service_index = service_index;
1471     client->handle = client->services[client->service_index].hid_information_value_handle;
1472 
1473     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1474     hids_run_for_client(client);
1475     return ERROR_CODE_SUCCESS;
1476 }
1477 
1478 uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1479     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1480     if (client == NULL){
1481         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1482     }
1483 
1484     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1485         return ERROR_CODE_COMMAND_DISALLOWED;
1486     }
1487 
1488     if (service_index >= client->num_instances){
1489         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1490     }
1491 
1492     client->service_index = service_index;
1493     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1494 
1495     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1496     hids_run_for_client(client);
1497     return ERROR_CODE_SUCCESS;
1498 }
1499 
1500 uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode){
1501     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1502     if (client == NULL){
1503         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1504     }
1505 
1506     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1507         return ERROR_CODE_COMMAND_DISALLOWED;
1508     }
1509 
1510     if (service_index >= client->num_instances){
1511         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1512     }
1513 
1514     client->service_index = service_index;
1515     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1516     client->value = (uint8_t)protocol_mode;
1517 
1518     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
1519     hids_run_for_client(client);
1520     return ERROR_CODE_SUCCESS;
1521 }
1522 
1523 
1524 static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
1525     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1526     if (client == NULL){
1527         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1528     }
1529 
1530     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1531         return ERROR_CODE_COMMAND_DISALLOWED;
1532     }
1533 
1534     if (service_index >= client->num_instances){
1535         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1536     }
1537 
1538     client->service_index = service_index;
1539     client->handle = client->services[client->service_index].control_point_value_handle;
1540     client->value = value;
1541 
1542     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
1543     hids_run_for_client(client);
1544     return ERROR_CODE_SUCCESS;
1545 }
1546 
1547 uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
1548     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
1549 }
1550 
1551 uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
1552     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
1553 }
1554 
1555 uint8_t hids_client_enable_notifications(uint16_t hids_cid){
1556      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1557     if (client == NULL){
1558         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1559     }
1560 
1561     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1562         return ERROR_CODE_COMMAND_DISALLOWED;
1563     }
1564     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1565     if (hids_client_notifications_configuration_init(client)){
1566         hids_run_for_client(client);
1567         return ERROR_CODE_SUCCESS;
1568     }
1569     hids_emit_notifications_configuration(client);
1570     return ERROR_CODE_SUCCESS;
1571 }
1572 
1573 uint8_t hids_client_disable_notifications(uint16_t hids_cid){
1574          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1575     if (client == NULL){
1576         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1577     }
1578 
1579     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1580         return ERROR_CODE_COMMAND_DISALLOWED;
1581     }
1582 
1583     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1584     if (hids_client_notifications_configuration_init(client)){
1585         hids_run_for_client(client);
1586         return ERROR_CODE_SUCCESS;
1587     }
1588     hids_emit_notifications_configuration(client);
1589     return ERROR_CODE_SUCCESS;
1590 }
1591 
1592 void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1593     hids_client_descriptor_storage = hid_descriptor_storage;
1594     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1595 }
1596 
1597 void hids_client_deinit(void){}
1598