xref: /btstack/src/ble/gatt-service/hids_client.c (revision 02a896b61c3f819cc0bb215b393d48870ff27609)
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->num_instances < MAX_NUM_HID_SERVICES){
895                 uint8_t index = client->num_instances;
896                 gatt_event_service_query_result_get_service(packet, &service);
897                 client->services[index].start_handle = service.start_group_handle;
898                 client->services[index].end_handle = service.end_group_handle;
899                 client->num_instances++;
900 
901 #ifdef ENABLE_TESTING_SUPPORT
902                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
903 #endif
904                 hids_client_descriptor_storage_init(client, index);
905             }  else {
906                 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);
907             }
908             break;
909 
910         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
911             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
912             btstack_assert(client != NULL);
913             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
914 
915             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
916             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
917                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
918                     break;
919                 }
920             }
921 
922             switch (characteristic.uuid16){
923                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
924                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
925                     break;
926 
927                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
928                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
929                     break;
930 
931                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
932                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
933                     break;
934 
935                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
936                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
937                     break;
938 
939                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
940                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
941                     break;
942 
943                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
944                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
945                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
946                     break;
947 
948                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
949                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
950                     break;
951 
952                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
953                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
954                     break;
955 
956                 default:
957 #ifdef ENABLE_TESTING_SUPPORT
958                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
959 #endif
960                     return;
961             }
962 
963 #ifdef ENABLE_TESTING_SUPPORT
964             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
965                 hid_characteristic_name(characteristic.uuid16),
966                 characteristic.start_handle,
967                 characteristic.properties,
968                 characteristic.value_handle, characteristic.uuid16,
969                 client->service_index);
970 
971             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
972                 printf(", report index 0x%02X", report_index);
973             }
974             printf("\n");
975 #endif
976             break;
977 
978         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
979             // Map Report characteristic value == HID Descriptor
980             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
981             btstack_assert(client != NULL);
982 
983             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
984             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
985 
986 #ifdef ENABLE_TESTING_SUPPORT
987             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
988             printf_hexdump(value, value_len);
989 #endif
990             for (i = 0; i < value_len; i++){
991                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
992                 if (!stored){
993                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
994                     break;
995                 }
996             }
997             break;
998 
999         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
1000             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
1001             btstack_assert(client != NULL);
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             btstack_assert(client != NULL);
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             btstack_assert(client != NULL);
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             btstack_assert(client != NULL);
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             btstack_assert(client != NULL);
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                         break;
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                         break;
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                         break;
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                         break;
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                     break;
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                     break;
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