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