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