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