xref: /btstack/src/ble/gatt-service/hids_client.c (revision 8cec2b74ed5545c2d74aa6a93cdd6a817b56842a)
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_report_index_for_report_id_and_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){
234     uint8_t i;
235     for (i = 0; i < client->num_reports; i++){
236         if ( (client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
237             return i;
238         }
239     }
240     return HIDS_CLIENT_INVALID_REPORT_INDEX;
241 }
242 
243 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){
244 
245     uint8_t report_index = find_report_index_for_value_handle(client, characteristic->value_handle);
246     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
247         return report_index;
248     }
249     report_index = client->num_reports;
250 
251     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
252         client->reports[report_index].value_handle = characteristic->value_handle;
253         client->reports[report_index].end_handle = characteristic->end_handle;
254         client->reports[report_index].properties = characteristic->properties;
255 
256         client->reports[report_index].service_index = client->service_index;
257         client->reports[report_index].report_id = report_id;
258         client->reports[report_index].report_type = report_type;
259         client->reports[report_index].boot_report = boot_report;
260 
261         // log_info("add index %d, id %d, type %d, value handle 0x%02x", report_index, report_id, report_type, characteristic->value_handle);
262         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);
263         client->num_reports++;
264         return report_index;
265     } else {
266         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
267         return HIDS_CLIENT_INVALID_REPORT_INDEX;
268     }
269 }
270 
271 static uint8_t hids_client_add_external_characteristic(hids_client_t * client, gatt_client_characteristic_t * characteristic){
272     uint8_t report_index = client->num_external_reports;
273 
274     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
275         client->external_reports[report_index].value_handle = characteristic->value_handle;
276         client->external_reports[report_index].end_handle = characteristic->end_handle;
277         client->external_reports[report_index].properties = characteristic->properties;
278 
279         client->external_reports[report_index].service_index = client->service_index;
280         // log_info("add index %d, id %d, type %d, value handle 0x%02x", report_index, report_id, report_type, characteristic->value_handle);
281         printf("add external index %d, value handle 0x%02x, properties 0x%02x\n", report_index, characteristic->value_handle, characteristic->properties);
282         client->num_external_reports++;
283         return report_index;
284     } else {
285         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
286         return HIDS_CLIENT_INVALID_REPORT_INDEX;
287     }
288 }
289 
290 
291 static bool hid_clients_has_reports_in_report_mode(hids_client_t * client){
292     uint8_t i;
293     for (i = 0; i < client->num_reports; i++){
294         if (!client->reports[i].boot_report){
295             return true;
296         }
297     }
298     return false;
299 }
300 
301 static bool hid_clients_has_reports_in_boot_mode(hids_client_t * client){
302     uint8_t i;
303     for (i = 0; i < client->num_reports; i++){
304         if (client->reports[i].boot_report){
305             return true;
306         }
307     }
308     return false;
309 }
310 
311 static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
312     uint8_t i;
313     for (i = client->service_index; i < client->num_instances; i++){
314         if (client->services[i].report_map_value_handle != 0){
315             return i;
316         }
317     }
318     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
319     return HIDS_CLIENT_INVALID_REPORT_INDEX;
320 }
321 
322 static bool hids_client_report_query_next_report_map(hids_client_t * client){
323     client->service_index++;
324     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
325         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
326         return true;
327     }
328     return false;
329 }
330 
331 static bool hids_client_report_map_query_init(hids_client_t * client){
332     client->service_index = 0;
333 
334     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
335         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
336         return true;
337     }
338     return false;
339 }
340 
341 
342 static uint8_t hids_client_get_next_active_report_map_uuid_index(hids_client_t * client){
343     uint8_t i;
344     for (i = client->report_index; i < client->num_reports; i++){
345         hids_client_report_t report = client->external_reports[i];
346         if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_MAP_ID){
347             client->service_index = report.service_index;
348             return i;
349         }
350     }
351     client->report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
352     return HIDS_CLIENT_INVALID_REPORT_INDEX;
353 }
354 
355 static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
356     client->report_index++;
357     if (hids_client_get_next_active_report_map_uuid_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
358         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
359         return true;
360     }
361     return false;
362 }
363 
364 static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
365     client->report_index = 0;
366 
367     if (hids_client_get_next_active_report_map_uuid_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
368         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
369         return true;
370     }
371     return false;
372 }
373 
374 static uint8_t hids_client_get_next_report_index(hids_client_t * client){
375     uint8_t i;
376     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
377     switch (client->protocol_mode){
378         case HID_PROTOCOL_MODE_REPORT:
379             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
380                 hids_client_report_t report = client->reports[i];
381                 if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
382                     index = i;
383                     client->service_index = report.service_index;
384                 }
385             }
386             break;
387         case HID_PROTOCOL_MODE_BOOT:
388             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
389                 hids_client_report_t report = client->reports[i];
390                 if (report.boot_report){
391                     index = i;
392                     client->service_index = report.service_index;
393                 }
394             }
395             break;
396         default:
397             break;
398     }
399 
400     client->report_index = index;
401     return index;
402 }
403 
404 static bool hids_client_report_query_next_report(hids_client_t * client){
405     client->report_index++;
406     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
407         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
408         return true;
409     }
410     return false;
411 }
412 
413 static bool hids_client_report_query_init(hids_client_t * client){
414     client->report_index = 0;
415 
416     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
417         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
418         return true;
419     }
420     return false;
421 }
422 
423 static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
424     uint8_t i;
425     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
426 
427     switch (client->protocol_mode){
428         case HID_PROTOCOL_MODE_REPORT:
429             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
430                 hids_client_report_t report = client->reports[i];
431                 if (report.report_type != HID_REPORT_TYPE_INPUT){
432                     continue;
433                 }
434                 if (!report.boot_report){
435                     index = i;
436                 }
437             }
438             break;
439 
440         case HID_PROTOCOL_MODE_BOOT:
441             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
442                 hids_client_report_t report = client->reports[i];
443                 if (report.report_type != HID_REPORT_TYPE_INPUT){
444                     continue;
445                 }
446                 if (report.boot_report){
447                     index = i;
448                 }
449             }
450             break;
451 
452         default:
453             break;
454     }
455 
456     client->report_index = index;
457     return index;
458 }
459 
460 static bool hids_client_report_next_notification_report_index(hids_client_t * client){
461     client->report_index++;
462     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
463         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
464         return true;
465     }
466     return false;
467 }
468 
469 static bool hids_client_report_notifications_init(hids_client_t * client){
470     client->report_index = 0;
471 
472     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
473         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
474         return true;
475     }
476     return false;
477 }
478 
479 static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
480     hids_client_t * client = btstack_memory_hids_client_get();
481     if (!client){
482         log_error("Not enough memory to create client");
483         return NULL;
484     }
485     client->state = HIDS_CLIENT_STATE_IDLE;
486     client->cid = cid;
487     client->con_handle = con_handle;
488 
489     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
490     return client;
491 }
492 
493 static void hids_finalize_client(hids_client_t * client){
494     // stop listening
495     uint8_t i;
496     for (i = 0; i < client->num_reports; i++){
497         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
498     }
499 
500     hids_client_descriptor_storage_delete(client);
501     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
502     btstack_memory_hids_client_free(client);
503 }
504 
505 
506 static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
507     uint8_t event[8];
508     int pos = 0;
509     event[pos++] = HCI_EVENT_GATTSERVICE_META;
510     event[pos++] = sizeof(event) - 2;
511     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
512     little_endian_store_16(event, pos, client->cid);
513     pos += 2;
514     event[pos++] = status;
515     event[pos++] = client->protocol_mode;
516     event[pos++] = client->num_instances;
517     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
518 }
519 
520 static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
521     uint16_t pos = 0;
522     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
523     pos++;  // skip len
524     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
525     little_endian_store_16(buffer, pos, client->cid);
526     pos += 2;
527     buffer[pos++] = client->reports[report_index].service_index;
528     buffer[pos++] = client->reports[report_index].report_id;
529     little_endian_store_16(buffer, pos, report_len + 1);
530     pos += 2;
531     buffer[pos++] = client->reports[report_index].report_id;
532     buffer[1] = pos + report_len + 1 - 2;
533 }
534 
535 static void handle_report_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
536     UNUSED(packet_type);
537     UNUSED(channel);
538     UNUSED(size);
539     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
540 
541     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
542     btstack_assert(client != NULL);
543 
544     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
545     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
546         return;
547     }
548 
549     uint8_t * in_place_event = &packet[-1];
550     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
551 
552     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size);
553 }
554 
555 
556 static void hids_run_for_client(hids_client_t * client){
557     uint8_t att_status;
558     gatt_client_service_t service;
559     gatt_client_characteristic_t characteristic;
560 
561     switch (client->state){
562         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
563 #ifdef ENABLE_TESTING_SUPPORT
564             printf("\n\nQuery Services:\n");
565 #endif
566             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
567             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
568             // TODO handle status
569             UNUSED(att_status);
570             break;
571 
572         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
573 #ifdef ENABLE_TESTING_SUPPORT
574             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
575 #endif
576             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
577 
578             service.start_group_handle = client->services[client->service_index].start_handle;
579             service.end_group_handle = client->services[client->service_index].end_handle;
580             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
581 
582             UNUSED(att_status);
583             break;
584 
585         case HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE:
586             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);
587             UNUSED(att_status);
588 
589             client->protocol_mode = client->required_protocol_mode;
590             if (hids_client_report_query_init(client)){
591                 break;
592             }
593 
594             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
595             hids_finalize_client(client);
596             break;
597 
598         case HIDS_CLIENT_W2_SEND_REPORT:{
599             client->state = HIDS_CLIENT_STATE_CONNECTED;
600 
601             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
602                 client->reports[client->report_index].value_handle,
603                 client->report_len, (uint8_t *)client->report);
604             UNUSED(att_status);
605             break;
606         }
607 
608         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
609 #ifdef ENABLE_TESTING_SUPPORT
610             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);
611 #endif
612             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
613             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);
614 
615             UNUSED(att_status);
616             break;
617 
618         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
619 #ifdef ENABLE_TESTING_SUPPORT
620             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);
621 #endif
622             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
623 
624             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
625             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
626 
627             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
628             UNUSED(att_status);
629             break;
630 
631         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
632 #ifdef ENABLE_TESTING_SUPPORT
633             printf("\nRead external chr UUID (Handle 0x%04X) Characteristic Descriptors:\n", client->reports[client->report_index].value_handle);
634 #endif
635             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
636 
637             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->reports[client->report_index].value_handle);
638             UNUSED(att_status);
639             break;
640 
641         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
642  #ifdef ENABLE_TESTING_SUPPORT
643             printf("\nDiscover EXTERNAL_REPORT_CHARACTERISTIC:\n");
644 #endif
645             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
646 
647             service.start_group_handle = 0x0001;
648             service.end_group_handle = 0xffff;
649             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
650             UNUSED(att_status);
651             break;
652 
653         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
654 #ifdef ENABLE_TESTING_SUPPORT
655             printf("\nQuery Report (Handle 0x%04X, index %d) Characteristic Descriptors of service %d:\n",
656                 client->reports[client->report_index].value_handle,
657                 client->report_index,
658                 client->reports[client->report_index].service_index);
659 #endif
660             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
661             client->descriptor_handle = 0;
662 
663             characteristic.value_handle = client->reports[client->report_index].value_handle;
664             characteristic.end_handle = client->reports[client->report_index].end_handle;
665             characteristic.properties = client->reports[client->report_index].properties;
666 
667             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
668             UNUSED(att_status);
669             break;
670 
671         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
672             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
673 
674             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
675             client->descriptor_handle = 0;
676             UNUSED(att_status);
677             break;
678 
679         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
680             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
681 
682             characteristic.value_handle = client->reports[client->report_index].value_handle;
683             characteristic.end_handle = client->reports[client->report_index].end_handle;
684             characteristic.properties = client->reports[client->report_index].properties;
685 
686             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
687 
688             if (att_status != ERROR_CODE_SUCCESS){
689                 if (hids_client_report_next_notification_report_index(client)){
690                     hids_run_for_client(client);
691                     break;
692                 }
693                 client->state = HIDS_CLIENT_STATE_CONNECTED;
694                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
695             } else {
696                 gatt_client_listen_for_characteristic_value_updates(
697                     &client->reports[client->report_index].notification_listener,
698                     &handle_report_hid_event, client->con_handle, &characteristic);
699 
700                 client->state = HIDS_CLIENT_STATE_CONNECTED;
701                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
702             }
703             UNUSED(att_status);
704             break;
705 
706         default:
707             break;
708     }
709 }
710 
711 static void hids_client_parse_characteristic(hids_client_t * client, gatt_client_characteristic_t characteristic){
712     uint8_t report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
713 
714     switch (characteristic.uuid16){
715         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
716             client->protocol_mode_value_handle = characteristic.value_handle;
717             break;
718 
719         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
720             report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
721             break;
722 
723         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
724             report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
725             break;
726 
727         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
728             report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, false);
729             break;
730 
731         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
732             report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
733             break;
734 
735         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
736             client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
737             client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
738             break;
739 
740         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
741             // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION\n");
742             break;
743 
744         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
745             // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT\n");
746             break;
747         default:
748             printf("TODO: found CHR 0x%02x\n", characteristic.uuid16);
749             return;
750     }
751 #ifdef ENABLE_TESTING_SUPPORT
752     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
753         printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d\n",
754             hid_characteristic_name(characteristic.uuid16),
755             characteristic.start_handle,
756             characteristic.properties,
757             characteristic.value_handle, characteristic.uuid16,
758             client->service_index);
759     } else {
760         printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
761             hid_characteristic_name(characteristic.uuid16),
762             characteristic.start_handle,
763             characteristic.properties,
764             characteristic.value_handle, characteristic.uuid16,
765             client->service_index, report_index);
766     }
767 #endif
768 }
769 
770 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
771     UNUSED(packet_type);
772     UNUSED(channel);
773     UNUSED(size);
774 
775     hids_client_t * client = NULL;
776     uint8_t att_status;
777     gatt_client_service_t service;
778     gatt_client_characteristic_t characteristic;
779     gatt_client_characteristic_descriptor_t characteristic_descriptor;
780 
781     // hids_client_report_t * boot_keyboard_report;
782     // hids_client_report_t * boot_mouse_report;
783     const uint8_t * characteristic_descriptor_value;
784     uint8_t i;
785     uint8_t report_index;
786 
787     const uint8_t * descriptor_value;
788     uint16_t descriptor_value_len;
789 
790     switch(hci_event_packet_get_type(packet)){
791         case GATT_EVENT_SERVICE_QUERY_RESULT:
792             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
793             btstack_assert(client != NULL);
794 
795             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
796                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
797                 hids_finalize_client(client);
798                 break;
799             }
800 
801             if (client->num_instances < MAX_NUM_HID_SERVICES){
802                 uint8_t index = client->num_instances;
803                 gatt_event_service_query_result_get_service(packet, &service);
804                 client->services[index].start_handle = service.start_group_handle;
805                 client->services[index].end_handle = service.end_group_handle;
806                 client->num_instances++;
807 
808 #ifdef ENABLE_TESTING_SUPPORT
809                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
810 #endif
811                 hids_client_descriptor_storage_init(client, index);
812             }  else {
813                 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);
814             }
815             break;
816 
817         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
818             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
819             btstack_assert(client != NULL);
820             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
821             hids_client_parse_characteristic(client, characteristic);
822             break;
823 
824         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
825             // Map Report characteristic value == HID Descriptor
826             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
827             btstack_assert(client != NULL);
828 
829             descriptor_value = gatt_event_long_characteristic_value_query_result_get_value(packet);
830             descriptor_value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
831 
832 #ifdef ENABLE_TESTING_SUPPORT
833             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
834             printf_hexdump(descriptor_value, descriptor_value_len);
835 #endif
836             for (i = 0; i < descriptor_value_len; i++){
837                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, descriptor_value[i]);
838                 if (!stored){
839                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
840                     break;
841                 }
842             }
843             break;
844 
845         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
846             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
847             btstack_assert(client != NULL);
848             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
849 
850             switch (client->state) {
851                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
852                     // setup for descriptor value query
853                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
854                         report_index = hids_client_add_external_characteristic(client, &characteristic);
855 
856                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
857                             client->external_reports[report_index].value_handle = characteristic_descriptor.handle;
858                             client->external_reports[report_index].service_index = client->service_index;
859                         }
860 
861 #ifdef ENABLE_TESTING_SUPPORT
862                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
863                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
864                                 characteristic_descriptor.handle,
865                                 characteristic_descriptor.uuid16,
866                                 client->service_index, report_index);
867                         }
868 #endif
869                     }
870                     break;
871                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
872                     // setup for descriptor value query
873                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
874                         client->descriptor_handle = characteristic_descriptor.handle;
875                     }
876 
877 #ifdef ENABLE_TESTING_SUPPORT
878                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
879                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
880                             characteristic_descriptor.handle,
881                             characteristic_descriptor.uuid16);
882                     }
883 
884                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
885                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
886                             characteristic_descriptor.handle,
887                             characteristic_descriptor.uuid16);
888                     }
889 #endif
890                     break;
891 
892                 default:
893                     break;
894             }
895             break;
896 
897         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
898             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
899             btstack_assert(client != NULL);
900 
901             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
902                 break;
903             }
904 
905             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
906             switch (client->state) {
907                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
908                     // get external report characteristic uuid
909                     report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
910                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
911                         client->reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
912 #ifdef ENABLE_TESTING_SUPPORT
913                         printf("update external_report_reference_uuid report 0x%02X, index 0x%02X, handle 0%02X\n",
914                             client->reports[report_index].external_report_reference_uuid,
915                             report_index,
916                             gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
917 #endif
918                     }
919                     break;
920 
921                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
922                     client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
923                     client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
924                     log_info("update index %d, id %d, type %d, value handle 0x%02x",
925                         client->report_index,
926                         client->reports[client->report_index].report_id,
927                         client->reports[client->report_index].report_type,
928                         client->reports[client->report_index].value_handle);
929 
930                     break;
931 
932                 default:
933                     break;
934             }
935             break;
936 
937         case GATT_EVENT_QUERY_COMPLETE:
938             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
939             btstack_assert(client != NULL);
940 
941             att_status = gatt_event_query_complete_get_att_status(packet);
942 
943             switch (client->state){
944                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
945                     if (att_status != ATT_ERROR_SUCCESS){
946                         hids_emit_connection_established(client, att_status);
947                         hids_finalize_client(client);
948                         break;
949                     }
950 
951                     if (client->num_instances == 0){
952                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
953                         hids_finalize_client(client);
954                         break;
955                     }
956 
957                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
958                     client->service_index = 0;
959                     break;
960 
961                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
962                     if (att_status != ATT_ERROR_SUCCESS){
963                         hids_emit_connection_established(client, att_status);
964                         hids_finalize_client(client);
965                         break;
966                     }
967 
968                     if ((client->service_index + 1) < client->num_instances){
969                         // discover characteristics of next service
970                         client->service_index++;
971                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
972                         break;
973                     }
974 
975                     switch (client->required_protocol_mode){
976                         case HID_PROTOCOL_MODE_REPORT:
977                             client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
978                             if (hid_clients_has_reports_in_report_mode(client)){
979                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
980                                 break;
981                             }
982                             hids_emit_connection_established(client, att_status);
983                             hids_finalize_client(client);
984                             return;
985 
986                         case HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT:
987                             if (hid_clients_has_reports_in_report_mode(client)){
988                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
989                                 break;
990                             }
991                             if (hid_clients_has_reports_in_boot_mode(client)){
992                                 if (client->protocol_mode_value_handle != 0){
993                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
994                                     break;
995                                 }
996                                 hids_emit_connection_established(client, att_status);
997                                 hids_finalize_client(client);
998                                 return;
999                             }
1000                             break;
1001                         default:
1002                             if (hid_clients_has_reports_in_boot_mode(client)){
1003                                 if (client->protocol_mode_value_handle != 0){
1004                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
1005                                     break;
1006                                 }
1007                                 hids_emit_connection_established(client, att_status);
1008                                 hids_finalize_client(client);
1009                                 return;
1010                             }
1011                             break;
1012                     }
1013 
1014                     if (client->state == HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE){
1015                         break;
1016                     }
1017 
1018                     // 1. we need to get HID Descriptor and
1019                     // 2. get external Report characteristics if referenced from Report Map
1020                     if (hids_client_report_map_query_init(client)){
1021                         break;
1022                     }
1023                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1024                     hids_finalize_client(client);
1025                     break;
1026 
1027 
1028                 // HID descriptor found
1029                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1030                     if (att_status != ATT_ERROR_SUCCESS){
1031                         hids_emit_connection_established(client, att_status);
1032                         hids_finalize_client(client);
1033                         break;
1034                     }
1035                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1036                     break;
1037 
1038                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1039                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1040                     // go for next report map
1041                     if (hids_client_report_query_next_report_map(client)){
1042                         break;
1043                     }
1044 
1045                     // read UUIDS for external characteristics
1046                     if (hids_client_report_map_uuid_query_init(client)){
1047                         break;
1048                     }
1049 
1050                     // discover characteristic descriptor for all Report characteristics,
1051                     // then read value of characteristic descriptor to get Report ID
1052                     if (hids_client_report_query_init(client)){
1053                         break;
1054                     }
1055 
1056                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1057                     hids_finalize_client(client);
1058                     break;
1059 
1060                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1061                     // go for next map report
1062                     if (hids_client_report_query_next_report_map_uuid(client)){
1063                         break;
1064                     }
1065 
1066                     // update external characteristics with correct value handle and end handle
1067                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1068                     break;
1069 
1070                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1071                     // discover characteristic descriptor for all Report characteristics,
1072                     // then read value of characteristic descriptor to get Report ID
1073                     if (hids_client_report_query_init(client)){
1074                         break;
1075                     }
1076 
1077                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1078                     hids_finalize_client(client);
1079                     break;
1080 
1081                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1082                     if (client->descriptor_handle != 0){
1083                         // descriptor found
1084                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
1085                         break;
1086                     }
1087 
1088                     // go for next report
1089                     if (hids_client_report_query_next_report(client)){
1090                         break;
1091                     }
1092 
1093                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1094                     break;
1095 
1096                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
1097                     // go for next report
1098                     if (hids_client_report_query_next_report(client)){
1099                         break;
1100                     }
1101 
1102                     if (hids_client_report_notifications_init(client)){
1103                         break;
1104                     }
1105                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1106                     break;
1107 
1108                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1109                     if (hids_client_report_next_notification_report_index(client)){
1110                         break;
1111                     }
1112                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1113                     break;
1114 
1115                 default:
1116                     break;
1117             }
1118             break;
1119 
1120         default:
1121             break;
1122     }
1123 
1124     if (client != NULL){
1125         hids_run_for_client(client);
1126     }
1127 }
1128 
1129 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){
1130     btstack_assert(packet_handler != NULL);
1131 
1132     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1133     if (client != NULL){
1134         return ERROR_CODE_COMMAND_DISALLOWED;
1135     }
1136 
1137     uint16_t cid = hids_get_next_cid();
1138     if (hids_cid != NULL) {
1139         *hids_cid = cid;
1140     }
1141 
1142     client = hids_create_client(con_handle, cid);
1143     if (client == NULL) {
1144         return BTSTACK_MEMORY_ALLOC_FAILED;
1145     }
1146 
1147     client->required_protocol_mode = protocol_mode;
1148     client->client_handler = packet_handler;
1149     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1150 
1151     hids_run_for_client(client);
1152     return ERROR_CODE_SUCCESS;
1153 }
1154 
1155 uint8_t hids_client_disconnect(uint16_t hids_cid){
1156     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1157     if (client == NULL){
1158         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1159     }
1160     // finalize connection
1161     hids_finalize_client(client);
1162     return ERROR_CODE_SUCCESS;
1163 }
1164 
1165 uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){
1166     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1167     if (client == NULL){
1168         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1169     }
1170 
1171     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1172         return ERROR_CODE_COMMAND_DISALLOWED;
1173     }
1174 
1175     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT);
1176     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
1177         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1178     }
1179 
1180     uint16_t mtu;
1181     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
1182 
1183     if (status != ERROR_CODE_SUCCESS){
1184         return status;
1185     }
1186 
1187     if (mtu - 2 < report_len){
1188         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
1189     }
1190 
1191     client->state = HIDS_CLIENT_W2_SEND_REPORT;
1192     client->report_index = report_index;
1193     client->report = report;
1194     client->report_len = report_len;
1195 
1196     hids_run_for_client(client);
1197     return ERROR_CODE_SUCCESS;
1198 }
1199 
1200 void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1201     hids_client_descriptor_storage = hid_descriptor_storage;
1202     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1203 }
1204 
1205 void hids_client_deinit(void){}
1206