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