xref: /btstack/src/ble/gatt-service/hids_client.c (revision 021192e149b66ee057b4c4cfc6c2ef699f2c22e5)
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 #include <stdint.h>
43 #include <string.h>
44 
45 #include "ble/gatt-service/hids_client.h"
46 
47 #include "btstack_memory.h"
48 #include "ble/att_db.h"
49 #include "ble/core.h"
50 #include "ble/gatt_client.h"
51 #include "ble/sm.h"
52 #include "bluetooth_gatt.h"
53 #include "btstack_debug.h"
54 #include "btstack_event.h"
55 #include "btstack_run_loop.h"
56 #include "gap.h"
57 
58 #define HID_REPORT_MODE_REPORT_ID               3
59 #define HID_REPORT_MODE_REPORT_MAP_ID           4
60 #define HID_REPORT_MODE_HID_INFORMATION_ID      5
61 #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
62 
63 static btstack_linked_list_t clients;
64 static uint16_t hids_cid_counter = 0;
65 
66 static uint8_t * hids_client_descriptor_storage;
67 static uint16_t  hids_client_descriptor_storage_len;
68 
69 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
70 
71 static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
72     btstack_linked_list_iterator_t it;
73     btstack_linked_list_iterator_init(&it, &clients);
74     while (btstack_linked_list_iterator_has_next(&it)){
75         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
76         if (client->con_handle != con_handle) continue;
77         return client;
78     }
79     return NULL;
80 }
81 
82 static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
83     btstack_linked_list_iterator_t it;
84     btstack_linked_list_iterator_init(&it, &clients);
85     while (btstack_linked_list_iterator_has_next(&it)){
86         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
87         if (client->cid != hids_cid) continue;
88         return client;
89     }
90     return NULL;
91 }
92 
93 
94 // START Descriptor Storage Util
95 
96 static uint16_t hids_client_descriptor_storage_get_available_space(void){
97     // assumes all descriptors are back to back
98     uint16_t free_space = hids_client_descriptor_storage_len;
99     uint8_t i;
100 
101     btstack_linked_list_iterator_t it;
102     btstack_linked_list_iterator_init(&it, &clients);
103     while (btstack_linked_list_iterator_has_next(&it)){
104         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
105         for (i = 0; i < client->num_instances; i++){
106             free_space -= client->services[i].hid_descriptor_len;
107         }
108     }
109     return free_space;
110 }
111 
112 static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
113     client->services[service_index].hid_descriptor_len = 0;
114     client->services[service_index].hid_descriptor_max_len = hids_client_descriptor_storage_get_available_space();
115     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - client->services[service_index].hid_descriptor_max_len;
116 }
117 
118 static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
119     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
120 
121     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
122     client->services[service_index].hid_descriptor_len++;
123     return true;
124 }
125 
126 static void hids_client_descriptor_storage_delete(hids_client_t * client){
127     uint8_t service_index = 0;
128     uint16_t next_offset = 0;
129 
130     for (service_index = 0; service_index < client->num_instances; service_index++){
131         next_offset += client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len;
132         client->services[service_index].hid_descriptor_len = 0;
133         client->services[service_index].hid_descriptor_offset = 0;
134     }
135 
136     memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
137             &hids_client_descriptor_storage[next_offset],
138             hids_client_descriptor_storage_len - next_offset);
139 
140     uint8_t i;
141     btstack_linked_list_iterator_t it;
142     btstack_linked_list_iterator_init(&it, &clients);
143     while (btstack_linked_list_iterator_has_next(&it)){
144         hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
145         for (i = 0; i < client->num_instances; i++){
146             if (conn->services[i].hid_descriptor_offset >= next_offset){
147                 conn->services[i].hid_descriptor_offset = next_offset;
148                 next_offset += conn->services[service_index].hid_descriptor_len;
149             }
150         }
151     }
152 }
153 
154 const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
155     hids_client_t * client = hids_get_client_for_cid(hids_cid);
156     if (client == NULL){
157         return NULL;
158     }
159     if (service_index >= client->num_instances){
160         return NULL;
161     }
162     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
163 }
164 
165 uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
166     hids_client_t * client = hids_get_client_for_cid(hids_cid);
167     if (client == NULL){
168         return 0;
169     }
170     if (service_index >= client->num_instances){
171         return 0;
172     }
173     return client->services[service_index].hid_descriptor_len;
174 }
175 
176 // END Descriptor Storage Util
177 
178 static uint16_t hids_get_next_cid(void){
179     if (hids_cid_counter == 0xffff) {
180         hids_cid_counter = 1;
181     } else {
182         hids_cid_counter++;
183     }
184     return hids_cid_counter;
185 }
186 
187 static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
188     uint8_t i;
189     for (i = 0; i < client->num_reports; i++){
190         if (client->reports[i].value_handle == value_handle){
191             return i;
192         }
193     }
194     return HIDS_CLIENT_INVALID_REPORT_INDEX;
195 }
196 
197 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){
198     uint8_t i;
199     for (i = 0; i < client->num_reports; i++){
200         if ( (client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
201             return i;
202         }
203     }
204     return HIDS_CLIENT_INVALID_REPORT_INDEX;
205 }
206 
207 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){
208 
209     uint8_t report_index = find_report_index_for_value_handle(client, characteristic->value_handle);
210     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
211         return report_index;
212     }
213     report_index = client->num_reports;
214 
215     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
216         client->reports[report_index].value_handle = characteristic->value_handle;
217         client->reports[report_index].end_handle = characteristic->end_handle;
218         client->reports[report_index].properties = characteristic->properties;
219 
220         client->reports[report_index].service_index = client->service_index;
221         client->reports[report_index].report_id = report_id;
222         client->reports[report_index].report_type = report_type;
223         client->reports[report_index].boot_report = boot_report;
224 
225         // log_info("add index %d, id %d, type %d, value handle 0x%02x", report_index, report_id, report_type, characteristic->value_handle);
226         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);
227         client->num_reports++;
228         return report_index;
229     } else {
230         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
231         return HIDS_CLIENT_INVALID_REPORT_INDEX;
232     }
233 }
234 
235 static void hids_client_get_characteristic_for_report_index(hids_client_t * client, uint8_t report_index, gatt_client_characteristic_t * characteristic){
236     characteristic->value_handle = client->reports[report_index].value_handle;
237     characteristic->end_handle = client->reports[report_index].end_handle;
238     characteristic->properties = client->reports[report_index].properties;
239 }
240 
241 static bool hid_clients_has_reports_in_report_mode(hids_client_t * client){
242     uint8_t i;
243     for (i = 0; i < client->num_reports; i++){
244         if (!client->reports[i].boot_report){
245             return true;
246         }
247     }
248     return false;
249 }
250 
251 static bool hid_clients_has_reports_in_boot_mode(hids_client_t * client){
252     uint8_t i;
253     for (i = 0; i < client->num_reports; i++){
254         if (client->reports[i].boot_report){
255             return true;
256         }
257     }
258     return false;
259 }
260 
261 static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
262     uint8_t i;
263     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
264 
265     for (i = client->service_index; i < client->num_instances; i++){
266         if (client->services[i].report_map_value_handle != 0){
267             index = i;
268             break;
269         }
270     }
271     client->service_index = index;
272     return index;
273 }
274 
275 static bool hids_client_report_query_next_report_map(hids_client_t * client){
276     client->service_index++;
277     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
278         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
279         return true;
280     }
281     return false;
282 }
283 
284 static bool hids_client_report_map_query_init(hids_client_t * client){
285     client->service_index = 0;
286 
287     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
288         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
289         return true;
290     }
291     return false;
292 }
293 
294 
295 static uint8_t hids_client_get_next_active_report_map_uuid_index(hids_client_t * client){
296     uint8_t i;
297     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
298 
299     for (i = client->report_index; i < client->num_reports; i++){
300         hids_client_report_t report = client->reports[i];
301         if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_MAP_ID){
302             index = i;
303             break;
304         }
305     }
306     client->report_index = index;
307     return index;
308 }
309 
310 static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
311     client->report_index++;
312     if (hids_client_get_next_active_report_map_uuid_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
313         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
314         return true;
315     }
316     return false;
317 }
318 
319 
320 
321 static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
322     client->report_index = 0;
323 
324     if (hids_client_get_next_active_report_map_uuid_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
325         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
326         return true;
327     }
328     return false;
329 }
330 
331 static uint8_t hids_client_get_next_report_index(hids_client_t * client){
332     uint8_t i;
333     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
334     switch (client->protocol_mode){
335         case HID_PROTOCOL_MODE_REPORT:
336             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
337                 hids_client_report_t report = client->reports[i];
338                 if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
339                     index = i;
340                     client->service_index = report.service_index;
341                 }
342             }
343             break;
344         case HID_PROTOCOL_MODE_BOOT:
345             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
346                 hids_client_report_t report = client->reports[i];
347                 if (report.boot_report){
348                     index = i;
349                     client->service_index = report.service_index;
350                 }
351             }
352             break;
353         default:
354             break;
355     }
356 
357     client->report_index = index;
358     return index;
359 }
360 
361 static bool hids_client_report_query_next_report(hids_client_t * client){
362     client->report_index++;
363     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
364         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
365         return true;
366     }
367     return false;
368 }
369 
370 static bool hids_client_report_query_init(hids_client_t * client){
371     client->report_index = 0;
372 
373     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
374         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
375         return true;
376     }
377     return false;
378 }
379 
380 static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
381     uint8_t i;
382     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
383 
384     switch (client->protocol_mode){
385         case HID_PROTOCOL_MODE_REPORT:
386             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
387                 hids_client_report_t report = client->reports[i];
388                 if (report.report_type != HID_REPORT_TYPE_INPUT){
389                     continue;
390                 }
391                 if (!report.boot_report){
392                     index = i;
393                 }
394             }
395             break;
396 
397         case HID_PROTOCOL_MODE_BOOT:
398             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
399                 hids_client_report_t report = client->reports[i];
400                 if (report.report_type != HID_REPORT_TYPE_INPUT){
401                     continue;
402                 }
403                 if (report.boot_report){
404                     index = i;
405                 }
406             }
407             break;
408 
409         default:
410             break;
411     }
412 
413     client->report_index = index;
414     return index;
415 }
416 
417 static bool hids_client_report_next_notification_report_index(hids_client_t * client){
418     client->report_index++;
419     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
420         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
421         return true;
422     }
423     return false;
424 }
425 
426 static bool hids_client_report_notifications_init(hids_client_t * client){
427     client->report_index = 0;
428 
429     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
430         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
431         return true;
432     }
433     return false;
434 }
435 
436 static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
437     hids_client_t * client = btstack_memory_hids_client_get();
438     if (!client){
439         log_error("Not enough memory to create client");
440         return NULL;
441     }
442     client->state = HIDS_CLIENT_STATE_IDLE;
443     client->cid = cid;
444     client->con_handle = con_handle;
445 
446     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
447     return client;
448 }
449 
450 static void hids_finalize_client(hids_client_t * client){
451     // stop listening
452     uint8_t i;
453     for (i = 0; i < client->num_reports; i++){
454         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
455     }
456 
457     hids_client_descriptor_storage_delete(client);
458     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
459     btstack_memory_hids_client_free(client);
460 }
461 
462 
463 static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
464     uint8_t event[8];
465     int pos = 0;
466     event[pos++] = HCI_EVENT_GATTSERVICE_META;
467     event[pos++] = sizeof(event) - 2;
468     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
469     little_endian_store_16(event, pos, client->cid);
470     pos += 2;
471     event[pos++] = status;
472     event[pos++] = client->protocol_mode;
473     event[pos++] = client->num_instances;
474     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
475 }
476 
477 static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
478     uint16_t pos = 0;
479     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
480     pos++;  // skip len
481     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
482     little_endian_store_16(buffer, pos, client->cid);
483     pos += 2;
484     buffer[pos++] = client->reports[report_index].service_index;
485     buffer[pos++] = client->reports[report_index].report_id;
486     little_endian_store_16(buffer, pos, report_len + 1);
487     pos += 2;
488     buffer[pos++] = client->reports[report_index].report_id;
489     buffer[1] = pos + report_len + 1 - 2;
490 }
491 
492 static void handle_report_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
493     UNUSED(packet_type);
494     UNUSED(channel);
495     UNUSED(size);
496     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
497 
498     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
499     btstack_assert(client != NULL);
500 
501     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
502     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
503         return;
504     }
505 
506     uint8_t * in_place_event = &packet[-1];
507     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
508 
509     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size);
510 }
511 
512 
513 static void hids_run_for_client(hids_client_t * client){
514     uint8_t att_status;
515     gatt_client_service_t service;
516     gatt_client_characteristic_t characteristic;
517 
518     switch (client->state){
519         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
520             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
521             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
522             // TODO handle status
523             UNUSED(att_status);
524             break;
525 
526         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
527             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
528 
529             service.start_group_handle = client->services[client->service_index].start_handle;
530             service.end_group_handle = client->services[client->service_index].end_handle;
531             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
532 
533             UNUSED(att_status);
534             break;
535 
536         case HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE:
537             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);
538             UNUSED(att_status);
539 
540             client->protocol_mode = client->required_protocol_mode;
541             if (hids_client_report_query_init(client)){
542                 break;
543             }
544 
545             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
546             hids_finalize_client(client);
547             break;
548 
549         case HIDS_CLIENT_W2_SEND_REPORT:{
550             client->state = HIDS_CLIENT_STATE_CONNECTED;
551 
552             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
553                 client->reports[client->report_index].value_handle,
554                 client->report_len, (uint8_t *)client->report);
555             UNUSED(att_status);
556             break;
557         }
558 
559         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
560             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
561             att_status = gatt_client_read_value_of_characteristic_using_value_handle(&handle_gatt_client_event, client->con_handle, client->services[client->service_index].report_map_value_handle);
562             UNUSED(att_status);
563             break;
564 
565         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
566             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
567 
568             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
569             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
570 
571             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
572             UNUSED(att_status);
573             break;
574 
575         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
576             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
577 
578             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->reports[client->report_index].value_handle);
579             UNUSED(att_status);
580             break;
581 
582         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
583             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
584 
585             service.start_group_handle = 0x0001;
586             service.end_group_handle = 0xffff;
587             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
588             UNUSED(att_status);
589             break;
590 
591         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
592             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
593             client->descriptor_handle = 0;
594             hids_client_get_characteristic_for_report_index(client, client->report_index, &characteristic);
595             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
596             UNUSED(att_status);
597             break;
598 
599         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
600             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
601 
602             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
603             client->descriptor_handle = 0;
604             UNUSED(att_status);
605             break;
606 
607         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
608             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
609 
610             characteristic.value_handle = client->reports[client->report_index].value_handle;
611             characteristic.end_handle = client->reports[client->report_index].end_handle;
612             characteristic.properties = client->reports[client->report_index].properties;
613 
614             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
615 
616             if (att_status != ERROR_CODE_SUCCESS){
617                 if (hids_client_report_next_notification_report_index(client)){
618                     hids_run_for_client(client);
619                     break;
620                 }
621                 client->state = HIDS_CLIENT_STATE_CONNECTED;
622                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
623             } else {
624                 gatt_client_listen_for_characteristic_value_updates(
625                     &client->reports[client->report_index].notification_listener,
626                     &handle_report_hid_event, client->con_handle, &characteristic);
627 
628                 client->state = HIDS_CLIENT_STATE_CONNECTED;
629                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
630             }
631             UNUSED(att_status);
632             break;
633 
634         default:
635             break;
636     }
637 }
638 
639 
640 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
641     UNUSED(packet_type);
642     UNUSED(channel);
643     UNUSED(size);
644 
645     hids_client_t * client = NULL;
646     uint8_t att_status;
647     gatt_client_service_t service;
648     gatt_client_characteristic_t characteristic;
649     gatt_client_characteristic_descriptor_t characteristic_descriptor;
650 
651     // hids_client_report_t * boot_keyboard_report;
652     // hids_client_report_t * boot_mouse_report;
653     const uint8_t * characteristic_descriptor_value;
654     uint8_t i;
655     uint8_t report_index;
656 
657     const uint8_t * descriptor;
658     uint16_t descriptor_len;
659 
660     switch(hci_event_packet_get_type(packet)){
661         case GATT_EVENT_SERVICE_QUERY_RESULT:
662             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
663             btstack_assert(client != NULL);
664 
665             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
666                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
667                 hids_finalize_client(client);
668                 break;
669             }
670 
671             if (client->num_instances < MAX_NUM_HID_SERVICES){
672                 uint8_t index = client->num_instances;
673                 gatt_event_service_query_result_get_service(packet, &service);
674                 client->services[index].start_handle = service.start_group_handle;
675                 client->services[index].end_handle = service.end_group_handle;
676                 client->num_instances++;
677 
678                 hids_client_descriptor_storage_init(client, index);
679             }  else {
680                 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);
681             }
682             break;
683 
684         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
685             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
686             btstack_assert(client != NULL);
687             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
688 
689             switch (client->state){
690                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
691                     // update value handle od external reports, set type to reserved so that will be inlcuded in ID,TYPE update in hte next step
692                     for (i = 0; i < client->num_reports; i++){
693                         if (client->reports[i].external_report_reference_uuid == characteristic.uuid16){
694                             client->reports[i].report_id = HID_REPORT_MODE_REPORT_ID;
695                             client->reports[i].report_type = HID_REPORT_TYPE_RESERVED;
696                             client->reports[i].properties = characteristic.properties;
697                             client->reports[i].value_handle = characteristic.value_handle;
698                             client->reports[i].end_handle = characteristic.end_handle;
699                         }
700                     }
701                     break;
702 
703                 default:
704                     switch (characteristic.uuid16){
705                         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
706                             client->protocol_mode_value_handle = characteristic.value_handle;
707                             break;
708 
709                         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
710                             hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
711                             break;
712 
713                         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
714                             hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
715                             break;
716 
717                         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
718                             hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, false);
719                             break;
720 
721                         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
722                             hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
723                             break;
724 
725                         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
726                             client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
727                             client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
728                             break;
729 
730                         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
731                             // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION\n");
732                             break;
733 
734                         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
735                             // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT\n");
736                             break;
737                         default:
738                             break;
739                 }
740             }
741 
742             break;
743 
744         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
745             // Map Report characteristic value == HID Descriptor
746             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
747             btstack_assert(client != NULL);
748 
749             descriptor_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
750             descriptor = gatt_event_characteristic_value_query_result_get_value(packet);
751 
752             for (i = 0; i < descriptor_len; i++){
753                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, descriptor[i]);
754                 if (!stored){
755                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
756                     break;
757                 }
758             }
759             break;
760 
761         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
762             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
763             btstack_assert(client != NULL);
764             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
765 
766             switch (client->state) {
767                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
768                     // setup for descriptor value query
769                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
770                         report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_MAP_ID, HID_REPORT_TYPE_RESERVED, false);
771 
772                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
773                             client->reports[report_index].value_handle = characteristic_descriptor.handle;
774                             client->reports[report_index].service_index = client->service_index;
775                         }
776 
777                     }
778                     break;
779                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
780                     // setup for descriptor value query
781                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
782                         client->descriptor_handle = characteristic_descriptor.handle;
783                     }
784                     break;
785                 default:
786                     break;
787             }
788             break;
789 
790         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
791             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
792             btstack_assert(client != NULL);
793 
794             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
795                 break;
796             }
797 
798             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
799             switch (client->state) {
800                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
801                     // get external report characteristic uuid
802                     report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
803                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
804                         client->reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
805                     }
806                     break;
807 
808                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
809                     client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
810                     client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
811                     log_info("update index %d, id %d, type %d, value handle 0x%02x",
812                         client->report_index,
813                         client->reports[client->report_index].report_id,
814                         client->reports[client->report_index].report_type,
815                         client->reports[client->report_index].value_handle);
816 
817                     break;
818 
819                 default:
820                     break;
821             }
822             break;
823 
824         case GATT_EVENT_QUERY_COMPLETE:
825             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
826             btstack_assert(client != NULL);
827 
828             att_status = gatt_event_query_complete_get_att_status(packet);
829 
830             switch (client->state){
831                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
832                     if (att_status != ATT_ERROR_SUCCESS){
833                         hids_emit_connection_established(client, att_status);
834                         hids_finalize_client(client);
835                         break;
836                     }
837 
838                     if (client->num_instances == 0){
839                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
840                         hids_finalize_client(client);
841                         break;
842                     }
843 
844                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
845                     client->service_index = 0;
846                     break;
847 
848                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
849                     if (att_status != ATT_ERROR_SUCCESS){
850                         hids_emit_connection_established(client, att_status);
851                         hids_finalize_client(client);
852                         break;
853                     }
854 
855                     if ((client->service_index + 1) < client->num_instances){
856                         // discover characteristics of next service
857                         client->service_index++;
858                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
859                         break;
860                     }
861 
862                     switch (client->required_protocol_mode){
863                         case HID_PROTOCOL_MODE_REPORT:
864                             client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
865                             if (hid_clients_has_reports_in_report_mode(client)){
866                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
867                                 break;
868                             }
869                             hids_emit_connection_established(client, att_status);
870                             hids_finalize_client(client);
871                             return;
872 
873                         case HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT:
874                             if (hid_clients_has_reports_in_report_mode(client)){
875                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
876                                 break;
877                             }
878                             if (hid_clients_has_reports_in_boot_mode(client)){
879                                 if (client->protocol_mode_value_handle != 0){
880                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
881                                     break;
882                                 }
883                                 hids_emit_connection_established(client, att_status);
884                                 hids_finalize_client(client);
885                                 return;
886                             }
887                             break;
888                         default:
889                             if (hid_clients_has_reports_in_boot_mode(client)){
890                                 if (client->protocol_mode_value_handle != 0){
891                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
892                                     break;
893                                 }
894                                 hids_emit_connection_established(client, att_status);
895                                 hids_finalize_client(client);
896                                 return;
897                             }
898                             break;
899                     }
900 
901                     if (client->state == HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE){
902                         break;
903                     }
904 
905                     // 1. we need to get HID Descriptor and
906                     // 2. get external Report characteristics if referenced from Report Map
907                     if (hids_client_report_map_query_init(client)){
908                         break;
909                     }
910                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
911                     hids_finalize_client(client);
912                     break;
913 
914 
915                 // HID descriptor found
916                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
917                     if (att_status != ATT_ERROR_SUCCESS){
918                         hids_emit_connection_established(client, att_status);
919                         hids_finalize_client(client);
920                         break;
921                     }
922                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
923                     break;
924 
925                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
926                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
927                     // go for next report map
928                     if (hids_client_report_query_next_report_map(client)){
929                         break;
930                     }
931 
932                     // read UUIDS for external characteristics
933                     if (hids_client_report_map_uuid_query_init(client)){
934                         break;
935                     }
936 
937                     // discover characteristic descriptor for all Report characteristics,
938                     // then read value of characteristic descriptor to get Report ID
939                     if (hids_client_report_query_init(client)){
940                         break;
941                     }
942 
943                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
944                     hids_finalize_client(client);
945                     break;
946 
947                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
948                     // go for next map report
949                     if (hids_client_report_query_next_report_map_uuid(client)){
950                         break;
951                     }
952 
953                     // update external characteristics with correct value handle and end handle
954                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
955                     break;
956 
957                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
958                     // discover characteristic descriptor for all Report characteristics,
959                     // then read value of characteristic descriptor to get Report ID
960                     if (hids_client_report_query_init(client)){
961                         break;
962                     }
963 
964                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
965                     hids_finalize_client(client);
966                     break;
967 
968                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
969                     if (client->descriptor_handle != 0){
970                         // descriptor found
971                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
972                         break;
973                     }
974 
975                     // go for next report
976                     if (hids_client_report_query_next_report(client)){
977                         break;
978                     }
979 
980                     // TODO continue with report mode
981                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
982                     break;
983 
984                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
985                     // go for next report
986                     if (hids_client_report_query_next_report(client)){
987                         break;
988                     }
989 
990                     if (hids_client_report_notifications_init(client)){
991                         break;
992                     }
993                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
994                     break;
995 
996                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
997                     if (hids_client_report_next_notification_report_index(client)){
998                         break;
999                     }
1000                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1001                     break;
1002 
1003                 default:
1004                     break;
1005             }
1006             break;
1007 
1008         default:
1009             break;
1010     }
1011 
1012     if (client != NULL){
1013         hids_run_for_client(client);
1014     }
1015 }
1016 
1017 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){
1018     btstack_assert(packet_handler != NULL);
1019 
1020     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1021     if (client != NULL){
1022         return ERROR_CODE_COMMAND_DISALLOWED;
1023     }
1024 
1025     uint16_t cid = hids_get_next_cid();
1026     if (hids_cid != NULL) {
1027         *hids_cid = cid;
1028     }
1029 
1030     client = hids_create_client(con_handle, cid);
1031     if (client == NULL) {
1032         return BTSTACK_MEMORY_ALLOC_FAILED;
1033     }
1034 
1035     client->required_protocol_mode = protocol_mode;
1036     client->client_handler = packet_handler;
1037     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1038 
1039     hids_run_for_client(client);
1040     return ERROR_CODE_SUCCESS;
1041 }
1042 
1043 uint8_t hids_client_disconnect(uint16_t hids_cid){
1044     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1045     if (client == NULL){
1046         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1047     }
1048     // finalize connection
1049     hids_finalize_client(client);
1050     return ERROR_CODE_SUCCESS;
1051 }
1052 
1053 uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){
1054     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1055     if (client == NULL){
1056         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1057     }
1058 
1059     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1060         return ERROR_CODE_COMMAND_DISALLOWED;
1061     }
1062 
1063     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT);
1064     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
1065         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1066     }
1067 
1068     uint16_t mtu;
1069     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
1070 
1071     if (status != ERROR_CODE_SUCCESS){
1072         return status;
1073     }
1074 
1075     if (mtu - 2 < report_len){
1076         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
1077     }
1078 
1079     client->state = HIDS_CLIENT_W2_SEND_REPORT;
1080     client->report_index = report_index;
1081     client->report = report;
1082     client->report_len = report_len;
1083 
1084     hids_run_for_client(client);
1085     return ERROR_CODE_SUCCESS;
1086 }
1087 
1088 void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1089     hids_client_descriptor_storage = hid_descriptor_storage;
1090     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1091 }
1092 
1093 void hids_client_deinit(void){}
1094