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