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