xref: /btstack/src/ble/gatt-service/hids_client.c (revision d38efbba5c7565403822c9c38ce512c8d5cc1815)
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     uint8_t * in_place_event = &packet[-2];
627     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
628                                                   gatt_event_notification_get_value_length(packet));
629     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
630 }
631 
632 static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
633     UNUSED(packet_type);
634     UNUSED(channel);
635 
636     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
637 
638     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
639     if (client == NULL) return;
640 
641     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
642         return;
643     }
644     client->state = HIDS_CLIENT_STATE_CONNECTED;
645 
646     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
647     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
648         return;
649     }
650 
651     uint8_t * in_place_event = &packet[-2];
652     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
653                                                   gatt_event_characteristic_value_query_result_get_value_length(packet));
654     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
655 }
656 
657 static void hids_run_for_client(hids_client_t * client){
658     uint8_t att_status;
659     gatt_client_service_t service;
660     gatt_client_characteristic_t characteristic;
661 
662     switch (client->state){
663         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
664 #ifdef ENABLE_TESTING_SUPPORT
665             printf("\n\nQuery Services:\n");
666 #endif
667             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
668 
669             // result in GATT_EVENT_SERVICE_QUERY_RESULT
670             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
671             UNUSED(att_status);
672             break;
673 
674         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
675 #ifdef ENABLE_TESTING_SUPPORT
676             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
677 #endif
678             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
679 
680             service.start_group_handle = client->services[client->service_index].start_handle;
681             service.end_group_handle = client->services[client->service_index].end_handle;
682 
683             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
684             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
685 
686             UNUSED(att_status);
687             break;
688 
689         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
690 #ifdef ENABLE_TESTING_SUPPORT
691             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);
692 #endif
693             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
694 
695             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
696             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);
697             UNUSED(att_status);
698             break;
699 
700         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
701 #ifdef ENABLE_TESTING_SUPPORT
702             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);
703 #endif
704             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
705 
706             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
707             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
708 
709             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
710             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
711             UNUSED(att_status);
712             break;
713 
714         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
715 #ifdef ENABLE_TESTING_SUPPORT
716             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);
717 #endif
718             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
719 
720             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
721             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);
722             UNUSED(att_status);
723             break;
724 
725         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
726  #ifdef ENABLE_TESTING_SUPPORT
727             printf("\nDiscover External Report Characteristic:\n");
728 #endif
729             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
730 
731             service.start_group_handle = 0x0001;
732             service.end_group_handle = 0xffff;
733 
734             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
735             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
736             UNUSED(att_status);
737             break;
738 
739         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
740 #ifdef ENABLE_TESTING_SUPPORT
741             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
742                 client->report_index,
743                 client->reports[client->report_index].service_index,
744                 client->reports[client->report_index].value_handle);
745 #endif
746             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
747             client->handle = 0;
748 
749             characteristic.value_handle = client->reports[client->report_index].value_handle;
750             characteristic.end_handle = client->reports[client->report_index].end_handle;
751             characteristic.properties = client->reports[client->report_index].properties;
752 
753             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
754             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
755             UNUSED(att_status);
756             break;
757 
758         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
759             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
760 
761             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
762             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
763             client->handle = 0;
764             UNUSED(att_status);
765             break;
766 
767         case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
768 #ifdef ENABLE_TESTING_SUPPORT
769             if (client->value > 0){
770                 printf("    Notification configuration enable ");
771             } else {
772                 printf("    Notification configuration disable ");
773             }
774             printf("[%d, %d, 0x%04X]:\n",
775                 client->report_index,
776                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
777 #endif
778 
779             client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
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 
787             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
788 
789             if (att_status == ERROR_CODE_SUCCESS){
790                 switch(client->value){
791                     case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
792                         gatt_client_listen_for_characteristic_value_updates(
793                             &client->reports[client->report_index].notification_listener,
794                             &handle_notification_event, client->con_handle, &characteristic);
795                         break;
796                     default:
797                         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
798                         break;
799                 }
800             } else {
801                 if (hids_client_report_next_notifications_configuration_report_index(client)){
802                     hids_run_for_client(client);
803                     break;
804                 }
805                 client->state = HIDS_CLIENT_STATE_CONNECTED;
806             }
807             break;
808 
809         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
810 #ifdef ENABLE_TESTING_SUPPORT
811             printf("    Notification enable [%d, %d, 0x%04X]:\n",
812                 client->report_index,
813                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
814 #endif
815             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
816 
817             characteristic.value_handle = client->reports[client->report_index].value_handle;
818             characteristic.end_handle = client->reports[client->report_index].end_handle;
819             characteristic.properties = client->reports[client->report_index].properties;
820 
821             // end of write marked in GATT_EVENT_QUERY_COMPLETE
822             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
823 
824             if (att_status == ERROR_CODE_SUCCESS){
825                 gatt_client_listen_for_characteristic_value_updates(
826                     &client->reports[client->report_index].notification_listener,
827                     &handle_notification_event, client->con_handle, &characteristic);
828             } else {
829                 if (hids_client_report_next_notification_report_index(client)){
830                     hids_run_for_client(client);
831                     break;
832                 }
833                 client->state = HIDS_CLIENT_STATE_CONNECTED;
834                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
835             }
836             break;
837 
838 
839         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
840 #ifdef ENABLE_TESTING_SUPPORT
841             printf("    Write report [%d, %d, 0x%04X]:\n",
842                 client->report_index,
843                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
844 #endif
845 
846             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
847 
848             // see GATT_EVENT_QUERY_COMPLETE for end of write
849             att_status = gatt_client_write_value_of_characteristic(
850                 &handle_gatt_client_event, client->con_handle,
851                 client->reports[client->report_index].value_handle,
852                 client->report_len, (uint8_t *)client->report);
853             UNUSED(att_status);
854             break;
855 
856         case HIDS_CLIENT_W2_SEND_GET_REPORT:
857 #ifdef ENABLE_TESTING_SUPPORT
858             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
859                 client->report_index,
860                 client->reports[client->report_index].report_id,
861                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
862 #endif
863 
864             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
865             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
866             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
867                 &handle_report_event,
868                 client->con_handle,
869                 client->reports[client->report_index].value_handle);
870             UNUSED(att_status);
871             break;
872 
873 #ifdef ENABLE_TESTING_SUPPORT
874         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
875             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
876 
877             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
878             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
879                 &handle_gatt_client_event,
880                 client->con_handle,
881                 client->reports[client->report_index].ccc_handle);
882 
883             break;
884 #endif
885         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
886             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
887 
888             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
889             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
890                 &handle_gatt_client_event,
891                 client->con_handle,
892                 client->handle);
893             break;
894 
895         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
896         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
897             client->write_without_response_request.callback = &hids_client_handle_can_write_without_reponse;
898             client->write_without_response_request.context = client;
899             (void) gatt_client_request_to_write_without_response(&client->write_without_response_request, client->con_handle);
900             break;
901 
902         default:
903             break;
904     }
905 }
906 
907 static void hids_client_handle_can_write_without_reponse(void * context) {
908     hids_client_t *client = (hids_client_t *) context;
909     uint8_t att_status;
910     switch (client->state){
911         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
912             att_status = gatt_client_write_value_of_characteristic_without_response(
913                 client->con_handle,
914                 client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
915 
916 #ifdef ENABLE_TESTING_SUPPORT
917             printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
918 #endif
919 
920             if (att_status == ATT_ERROR_SUCCESS){
921                 client->services[client->service_index].protocol_mode = client->required_protocol_mode;
922                 if ((client->service_index + 1) < client->num_instances){
923                     client->service_index++;
924                     hids_run_for_client(client);
925                     break;
926                 }
927             }
928 
929             // read UUIDS for external characteristics
930             if (hids_client_report_map_uuid_query_init(client)){
931                 hids_run_for_client(client);
932                 break;
933             }
934 
935             // discover characteristic descriptor for all Report characteristics,
936             // then read value of characteristic descriptor to get Report ID
937             if (hids_client_report_query_init(client)){
938                 hids_run_for_client(client);
939                 break;
940             }
941 
942             client->state = HIDS_CLIENT_STATE_CONNECTED;
943             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
944             break;
945 
946         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
947 #ifdef ENABLE_TESTING_SUPPORT
948             printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
949 #endif
950             client->state = HIDS_CLIENT_STATE_CONNECTED;
951             (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
952             break;
953 
954         default:
955             break;
956     }
957 }
958 
959 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
960     UNUSED(packet_type);
961     UNUSED(channel);
962     UNUSED(size);
963 
964     hids_client_t * client = NULL;
965     uint8_t status;
966     gatt_client_service_t service;
967     gatt_client_characteristic_t characteristic;
968     gatt_client_characteristic_descriptor_t characteristic_descriptor;
969 
970     // hids_client_report_t * boot_keyboard_report;
971     // hids_client_report_t * boot_mouse_report;
972     const uint8_t * characteristic_descriptor_value;
973     uint8_t i;
974     uint8_t report_index;
975 
976     const uint8_t * value;
977     uint16_t value_len;
978 
979     switch(hci_event_packet_get_type(packet)){
980         case GATT_EVENT_SERVICE_QUERY_RESULT:
981             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
982             if (client == NULL) break;
983 
984             if (client->num_instances < MAX_NUM_HID_SERVICES){
985                 uint8_t index = client->num_instances;
986                 gatt_event_service_query_result_get_service(packet, &service);
987                 client->services[index].start_handle = service.start_group_handle;
988                 client->services[index].end_handle = service.end_group_handle;
989                 client->num_instances++;
990 
991 #ifdef ENABLE_TESTING_SUPPORT
992                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
993 #endif
994                 hids_client_descriptor_storage_init(client, index);
995             }  else {
996                 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);
997             }
998             break;
999 
1000         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
1001             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
1002             if (client == NULL) break;
1003 
1004             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
1005 
1006             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
1007             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
1008                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
1009                     break;
1010                 }
1011             }
1012 
1013             switch (characteristic.uuid16){
1014                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
1015                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
1016                     break;
1017 
1018                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
1019                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
1020                     break;
1021 
1022                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
1023                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
1024                     break;
1025 
1026                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
1027                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
1028                     break;
1029 
1030                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
1031                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
1032                     break;
1033 
1034                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
1035                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
1036                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
1037                     break;
1038 
1039                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
1040                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
1041                     break;
1042 
1043                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
1044                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
1045                     break;
1046 
1047                 default:
1048 #ifdef ENABLE_TESTING_SUPPORT
1049                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
1050 #endif
1051                     return;
1052             }
1053 
1054 #ifdef ENABLE_TESTING_SUPPORT
1055             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
1056                 hid_characteristic_name(characteristic.uuid16),
1057                 characteristic.start_handle,
1058                 characteristic.properties,
1059                 characteristic.value_handle, characteristic.uuid16,
1060                 client->service_index);
1061 
1062             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1063                 printf(", report index 0x%02X", report_index);
1064             }
1065             printf("\n");
1066 #endif
1067             break;
1068 
1069         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
1070             // Map Report characteristic value == HID Descriptor
1071             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
1072             if (client == NULL) break;
1073 
1074             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
1075             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
1076 
1077 #ifdef ENABLE_TESTING_SUPPORT
1078             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
1079             printf_hexdump(value, value_len);
1080 #endif
1081             for (i = 0; i < value_len; i++){
1082                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
1083                 if (!stored){
1084                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
1085                     break;
1086                 }
1087             }
1088             break;
1089 
1090         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
1091             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
1092             if (client == NULL) break;
1093 
1094             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
1095 
1096             switch (client->state) {
1097                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1098                     // setup for descriptor value query
1099                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
1100                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
1101 
1102 #ifdef ENABLE_TESTING_SUPPORT
1103                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1104                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1105                                 characteristic_descriptor.handle,
1106                                 characteristic_descriptor.uuid16,
1107                                 client->service_index, report_index);
1108                         }
1109 #endif
1110                     }
1111                     break;
1112                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1113                     // setup for descriptor value query
1114                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1115                         client->handle = characteristic_descriptor.handle;
1116 #ifdef ENABLE_TESTING_SUPPORT
1117                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1118                             characteristic_descriptor.handle,
1119                             characteristic_descriptor.uuid16);
1120 #endif
1121                     }
1122 
1123 #ifdef ENABLE_TESTING_SUPPORT
1124                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
1125                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1126                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1127                             characteristic_descriptor.handle,
1128                             characteristic_descriptor.uuid16);
1129                     }
1130 #endif
1131                     break;
1132 
1133                 default:
1134                     break;
1135             }
1136             break;
1137 
1138         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
1139             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
1140             if (client == NULL) break;
1141 
1142             value = gatt_event_characteristic_value_query_result_get_value(packet);
1143             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1144 
1145 
1146             switch (client->state){
1147 #ifdef ENABLE_TESTING_SUPPORT
1148                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1149                     printf("    Received CCC value: ");
1150                     printf_hexdump(value,  value_len);
1151                     break;
1152 #endif
1153                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1154                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1155                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1156                         hids_client_emit_hid_information_event(client, value, value_len);
1157                         break;
1158                     }
1159                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1160                         hids_client_emit_protocol_mode_event(client, value, value_len);
1161                         break;
1162                     }
1163                     break;
1164                 }
1165                 default:
1166                     break;
1167             }
1168 
1169             break;
1170 
1171         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
1172             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
1173             if (client == NULL) break;
1174 
1175             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1176                 break;
1177             }
1178 
1179             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1180             switch (client->state) {
1181                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1182                     // get external report characteristic uuid
1183                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
1184                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1185                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1186 #ifdef ENABLE_TESTING_SUPPORT
1187                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
1188                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1189 #endif
1190                     }
1191                     break;
1192 
1193                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
1194 
1195                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1196                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1197                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
1198     #ifdef ENABLE_TESTING_SUPPORT
1199                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1200                             client->reports[client->report_index].report_id,
1201                             client->reports[client->report_index].report_type,
1202                             client->report_index, client->service_index);
1203     #endif
1204                     }
1205                     break;
1206 
1207                 default:
1208                     break;
1209             }
1210             break;
1211 
1212         case GATT_EVENT_QUERY_COMPLETE:
1213             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
1214             if (client == NULL) break;
1215 
1216             status = gatt_service_client_att_status_to_error_code(gatt_event_query_complete_get_att_status(packet));
1217 
1218             switch (client->state){
1219                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
1220                     if (status != ERROR_CODE_SUCCESS){
1221                         hids_emit_connection_established(client, status);
1222                         hids_finalize_client(client);
1223                         return;
1224                     }
1225 
1226                     if (client->num_instances == 0){
1227                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1228                         hids_finalize_client(client);
1229                         return;
1230                     }
1231 
1232                     client->service_index = 0;
1233                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1234                     break;
1235 
1236                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
1237                     if (status != ERROR_CODE_SUCCESS){
1238                         hids_emit_connection_established(client, status);
1239                         hids_finalize_client(client);
1240                         return;
1241                     }
1242 
1243                     if ((client->service_index + 1) < client->num_instances){
1244                         // discover characteristics of next service
1245                         client->service_index++;
1246                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1247                         break;
1248                     }
1249 
1250                     btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1251 
1252                     switch (client->required_protocol_mode){
1253                         case HID_PROTOCOL_MODE_REPORT:
1254                             for (i = 0; i < client->num_instances; i++){
1255                                 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1256                             }
1257                             // 1. we need to get HID Descriptor and
1258                             // 2. get external Report characteristics if referenced from Report Map
1259                             if (hids_client_report_map_query_init(client)){
1260                                 break;
1261                             }
1262                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1263                             hids_finalize_client(client);
1264                             return;
1265 
1266                         default:
1267                             // set boot mode
1268                             client->service_index = 0;
1269                             client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1270                             break;
1271                     }
1272                     break;
1273 
1274 
1275                 // HID descriptor found
1276                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1277                     if (status != ERROR_CODE_SUCCESS){
1278                         hids_emit_connection_established(client, status);
1279                         hids_finalize_client(client);
1280                         return;
1281                     }
1282                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1283                     break;
1284 
1285                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1286                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1287                     // go for next report map
1288                     if (hids_client_report_query_next_report_map(client)){
1289                         break;
1290                     }
1291 
1292                     // read UUIDS for external characteristics
1293                     if (hids_client_report_map_uuid_query_init(client)){
1294                         break;
1295                     }
1296 
1297                     // discover characteristic descriptor for all Report characteristics,
1298                     // then read value of characteristic descriptor to get Report ID
1299                     if (hids_client_report_query_init(client)){
1300                         break;
1301                     }
1302 
1303                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1304                     hids_finalize_client(client);
1305                     return;
1306 
1307                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1308                     // go for next map report
1309                     if (hids_client_report_query_next_report_map_uuid(client)){
1310                         break;
1311                     }
1312 
1313                     // update external characteristics with correct value handle and end handle
1314                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1315                     break;
1316 
1317                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1318                     // discover characteristic descriptor for all Report characteristics,
1319                     // then read value of characteristic descriptor to get Report ID
1320                     if (hids_client_report_query_init(client)){
1321                         break;
1322                     }
1323 
1324                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1325                     hids_finalize_client(client);
1326                     return;
1327 
1328                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1329                     if (client->handle != 0){
1330                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
1331                         break;
1332                     }
1333                     // go for next report
1334                     if (hids_client_report_query_next_report(client)){
1335                         break;
1336                     }
1337                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1338                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1339                     break;
1340 
1341                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
1342                     // go for next report
1343                     if (hids_client_report_query_next_report(client)){
1344                         break;
1345                     }
1346                     if (hids_client_report_notifications_init(client)){
1347                         break;
1348                     }
1349                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1350                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1351                     break;
1352 
1353                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1354                     if (hids_client_report_next_notification_report_index(client)){
1355                         break;
1356                     }
1357                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1358                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1359                     break;
1360 
1361                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1362                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1363                         break;
1364                     }
1365                     hids_emit_notifications_configuration(client);
1366                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1367                     break;
1368 
1369 #ifdef ENABLE_TESTING_SUPPORT
1370                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1371                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1372                     break;
1373 #endif
1374 
1375                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
1376                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1377                     break;
1378 
1379                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
1380                     {
1381                         client->state = HIDS_CLIENT_STATE_CONNECTED;
1382 
1383                         // emit empty report to signal done
1384                         uint8_t event[9];
1385                         hids_client_setup_report_event(GATTSERVICE_SUBEVENT_HID_REPORT_WRITTEN, client,
1386                                                        client->report_index, event, 0);
1387                         (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
1388                     }
1389                     break;
1390 
1391                 default:
1392                     break;
1393             }
1394             break;
1395 
1396         default:
1397             break;
1398     }
1399 
1400     if (client != NULL){
1401         hids_run_for_client(client);
1402     }
1403 }
1404 
1405 static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
1406     UNUSED(packet_type); // ok: only hci events
1407     UNUSED(channel);     // ok: there is no channel
1408     UNUSED(size);        // ok: fixed format events read from HCI buffer
1409 
1410     hci_con_handle_t con_handle;
1411     hids_client_t * client;
1412 
1413     switch (hci_event_packet_get_type(packet)) {
1414         case HCI_EVENT_DISCONNECTION_COMPLETE:
1415             con_handle = hci_event_disconnection_complete_get_connection_handle(packet);
1416             client = hids_get_client_for_con_handle(con_handle);
1417             if (client != NULL){
1418                 // emit disconnected
1419                 btstack_packet_handler_t packet_handler = client->client_handler;
1420                 uint16_t cid = client->cid;
1421                 hids_emit_disconnected(packet_handler, cid);
1422                 // finalize
1423                 hids_finalize_client(client);
1424             }
1425             break;
1426         default:
1427             break;
1428     }
1429 }
1430 
1431 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){
1432     btstack_assert(packet_handler != NULL);
1433 
1434     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1435     if (client != NULL){
1436         return ERROR_CODE_COMMAND_DISALLOWED;
1437     }
1438 
1439     uint16_t cid = hids_get_next_cid();
1440     if (hids_cid != NULL) {
1441         *hids_cid = cid;
1442     }
1443 
1444     client = hids_create_client(con_handle, cid);
1445     if (client == NULL) {
1446         return BTSTACK_MEMORY_ALLOC_FAILED;
1447     }
1448 
1449     client->required_protocol_mode = protocol_mode;
1450     client->client_handler = packet_handler;
1451     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1452 
1453     hids_run_for_client(client);
1454     return ERROR_CODE_SUCCESS;
1455 }
1456 
1457 uint8_t hids_client_disconnect(uint16_t hids_cid){
1458     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1459     if (client == NULL){
1460         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1461     }
1462     // finalize connection
1463     hids_finalize_client(client);
1464     return ERROR_CODE_SUCCESS;
1465 }
1466 
1467 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){
1468     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1469     if (client == NULL){
1470         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1471     }
1472 
1473     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1474         return ERROR_CODE_COMMAND_DISALLOWED;
1475     }
1476 
1477     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
1478 
1479     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
1480         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1481     }
1482 
1483     uint16_t mtu;
1484     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
1485 
1486     if (status != ERROR_CODE_SUCCESS){
1487         return status;
1488     }
1489 
1490     if (mtu - 2 < report_len){
1491         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
1492     }
1493 
1494     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1495     client->report_index = report_index;
1496     client->report = report;
1497     client->report_len = report_len;
1498 
1499     hids_run_for_client(client);
1500     return ERROR_CODE_SUCCESS;
1501 }
1502 
1503 uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
1504     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1505     if (client == NULL){
1506         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1507     }
1508 
1509     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1510         return ERROR_CODE_COMMAND_DISALLOWED;
1511     }
1512 
1513     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
1514     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
1515         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1516     }
1517 
1518     client->report_index = report_index;
1519 
1520 #ifdef ENABLE_TESTING_SUPPORT
1521     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
1522 #else
1523     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1524 #endif
1525     hids_run_for_client(client);
1526     return ERROR_CODE_SUCCESS;
1527 }
1528 
1529 
1530 uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1531     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1532     if (client == NULL){
1533         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1534     }
1535 
1536     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1537         return ERROR_CODE_COMMAND_DISALLOWED;
1538     }
1539 
1540     if (service_index >= client->num_instances){
1541         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1542     }
1543 
1544     client->service_index = service_index;
1545     client->handle = client->services[client->service_index].hid_information_value_handle;
1546 
1547     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1548     hids_run_for_client(client);
1549     return ERROR_CODE_SUCCESS;
1550 }
1551 
1552 uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1553     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1554     if (client == NULL){
1555         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1556     }
1557 
1558     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1559         return ERROR_CODE_COMMAND_DISALLOWED;
1560     }
1561 
1562     if (service_index >= client->num_instances){
1563         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1564     }
1565 
1566     client->service_index = service_index;
1567     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1568 
1569     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1570     hids_run_for_client(client);
1571     return ERROR_CODE_SUCCESS;
1572 }
1573 
1574 uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode){
1575     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1576     if (client == NULL){
1577         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1578     }
1579 
1580     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1581         return ERROR_CODE_COMMAND_DISALLOWED;
1582     }
1583 
1584     if (service_index >= client->num_instances){
1585         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1586     }
1587 
1588     client->service_index = service_index;
1589     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1590     client->value = (uint8_t)protocol_mode;
1591 
1592     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
1593     hids_run_for_client(client);
1594     return ERROR_CODE_SUCCESS;
1595 }
1596 
1597 
1598 static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
1599     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1600     if (client == NULL){
1601         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1602     }
1603 
1604     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1605         return ERROR_CODE_COMMAND_DISALLOWED;
1606     }
1607 
1608     if (service_index >= client->num_instances){
1609         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1610     }
1611 
1612     client->service_index = service_index;
1613     client->handle = client->services[client->service_index].control_point_value_handle;
1614     client->value = value;
1615 
1616     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
1617     hids_run_for_client(client);
1618     return ERROR_CODE_SUCCESS;
1619 }
1620 
1621 uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
1622     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
1623 }
1624 
1625 uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
1626     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
1627 }
1628 
1629 uint8_t hids_client_enable_notifications(uint16_t hids_cid){
1630      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1631     if (client == NULL){
1632         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1633     }
1634 
1635     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1636         return ERROR_CODE_COMMAND_DISALLOWED;
1637     }
1638     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1639     if (hids_client_notifications_configuration_init(client)){
1640         hids_run_for_client(client);
1641         return ERROR_CODE_SUCCESS;
1642     }
1643     hids_emit_notifications_configuration(client);
1644     return ERROR_CODE_SUCCESS;
1645 }
1646 
1647 uint8_t hids_client_disable_notifications(uint16_t hids_cid){
1648          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1649     if (client == NULL){
1650         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1651     }
1652 
1653     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1654         return ERROR_CODE_COMMAND_DISALLOWED;
1655     }
1656 
1657     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1658     if (hids_client_notifications_configuration_init(client)){
1659         hids_run_for_client(client);
1660         return ERROR_CODE_SUCCESS;
1661     }
1662     hids_emit_notifications_configuration(client);
1663     return ERROR_CODE_SUCCESS;
1664 }
1665 
1666 void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1667     hids_client_descriptor_storage = hid_descriptor_storage;
1668     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1669 
1670     hci_event_callback_registration.callback = &handle_hci_event;
1671     hci_add_event_handler(&hci_event_callback_registration);
1672 }
1673 
1674 void hids_client_deinit(void){}
1675