xref: /btstack/src/ble/gatt-service/hids_client.c (revision 28da36a62602cf22aa1822287b284b961e96b24a)
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 static btstack_linked_list_t clients;
59 static uint16_t hids_cid_counter = 0;
60 
61 #define HID_REPORT_MODE_REPORT_ID               3
62 #define HID_REPORT_MODE_REPORT_MAP_ID           4
63 #define HID_REPORT_MODE_HID_INFORMATION_ID      5
64 #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
65 
66 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
67 
68 static uint16_t hids_get_next_cid(void){
69     if (hids_cid_counter == 0xffff) {
70         hids_cid_counter = 1;
71     } else {
72         hids_cid_counter++;
73     }
74     return hids_cid_counter;
75 }
76 
77 static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
78     uint8_t i;
79     for (i = 0; i < client->num_reports; i++){
80         if (client->reports[i].value_handle == value_handle){
81             return i;
82         }
83     }
84     return HIDS_CLIENT_INVALID_REPORT_INDEX;
85 }
86 
87 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){
88     uint8_t i;
89     for (i = 0; i < client->num_reports; i++){
90         if ( (client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
91             return i;
92         }
93     }
94     return HIDS_CLIENT_INVALID_REPORT_INDEX;
95 }
96 
97 static hids_client_report_t * find_report_for_report_id_and_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){
98     uint8_t i;
99     for (i = 0; i < client->num_reports; i++){
100         if ( (client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
101             return &client->reports[i];
102         }
103     }
104     return NULL;
105 }
106 
107 static hids_client_report_t * get_boot_mouse_input_report(hids_client_t * client){
108     return find_report_for_report_id_and_type(client, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT);
109 }
110 static hids_client_report_t * get_boot_keyboard_input_report(hids_client_t * client){
111     return find_report_for_report_id_and_type(client, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT);
112 }
113 
114 static void hids_client_add_characteristic(hids_client_t * client, gatt_client_characteristic_t * characteristic, uint8_t report_id, hid_report_type_t report_type){
115     uint8_t report_index = find_report_index_for_value_handle(client, characteristic->value_handle);
116     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
117         return;
118     }
119     report_index = client->num_reports;
120 
121     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
122         client->reports[report_index].value_handle = characteristic->value_handle;
123         client->reports[report_index].end_handle = characteristic->end_handle;
124         client->reports[report_index].properties = characteristic->properties;
125 
126         client->reports[report_index].service_index = client->service_index;
127         client->reports[report_index].report_id = report_id;
128         client->reports[report_index].report_type = report_type;
129 
130         log_info("add index %d, id %d, type %d, value handle 0x%02x", report_index, report_id, report_type, characteristic->value_handle);
131         client->num_reports++;
132     } else {
133         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
134     }
135 }
136 
137 static void hids_client_get_characteristic_for_report_index(hids_client_t * client, uint8_t report_index, gatt_client_characteristic_t * characteristic){
138     characteristic->value_handle = client->reports[report_index].value_handle;
139     characteristic->end_handle = client->reports[report_index].end_handle;
140     characteristic->properties = client->reports[report_index].properties;
141 }
142 
143 static uint8_t hids_client_get_characteristic(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type, gatt_client_characteristic_t * characteristic){
144     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, report_type);
145     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
146         return report_index;
147     }
148     hids_client_get_characteristic_for_report_index(client, report_index, characteristic);
149     return report_index;
150 }
151 
152 
153 static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
154     uint8_t i;
155     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
156     for (i = client->active_index; i < client->num_reports; i++){
157         hids_client_report_t report = client->reports[i];
158         if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_MAP_ID){
159             index = i;
160             break;
161         }
162     }
163     client->active_index = index;
164     return index;
165 }
166 
167 static bool hids_client_report_query_next_report_map(hids_client_t * client){
168     client->active_index++;
169     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
170         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
171         return true;
172     }
173     return false;
174 }
175 
176 static bool hids_client_report_map_query_init(hids_client_t * client){
177     client->service_index = 0;
178     client->active_index = 0;
179 
180     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
181         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
182         return true;
183     }
184     return false;
185 }
186 
187 static uint8_t hids_client_get_next_report_index(hids_client_t * client){
188     uint8_t i;
189     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
190     for (i = client->active_index; i < client->num_reports; i++){
191         hids_client_report_t report = client->reports[i];
192         if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
193             index = i;
194             break;
195         }
196     }
197     client->active_index = index;
198     return index;
199 }
200 
201 static bool hids_client_report_query_next_report(hids_client_t * client){
202     client->active_index++;
203     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
204         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
205         return true;
206     }
207     return false;
208 }
209 
210 static bool hids_client_report_query_init(hids_client_t * client){
211     client->service_index = 0;
212     client->active_index = 0;
213 
214     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
215         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
216         return true;
217     }
218     return false;
219 }
220 
221 static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
222     hids_client_t * client = btstack_memory_hids_client_get();
223     if (!client){
224         log_error("Not enough memory to create client");
225         return NULL;
226     }
227     client->state = HIDS_CLIENT_STATE_IDLE;
228     client->cid = cid;
229     client->con_handle = con_handle;
230 
231     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
232     return client;
233 }
234 
235 static void hids_finalize_client(hids_client_t * client){
236     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
237     btstack_memory_hids_client_free(client);
238 }
239 
240 static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
241     btstack_linked_list_iterator_t it;
242     btstack_linked_list_iterator_init(&it, &clients);
243     while (btstack_linked_list_iterator_has_next(&it)){
244         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
245         if (client->con_handle != con_handle) continue;
246         return client;
247     }
248     return NULL;
249 }
250 
251 static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
252     btstack_linked_list_iterator_t it;
253     btstack_linked_list_iterator_init(&it, &clients);
254     while (btstack_linked_list_iterator_has_next(&it)){
255         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
256         if (client->cid != hids_cid) continue;
257         return client;
258     }
259     return NULL;
260 }
261 
262 static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
263     uint8_t event[8];
264     int pos = 0;
265     event[pos++] = HCI_EVENT_GATTSERVICE_META;
266     event[pos++] = sizeof(event) - 2;
267     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
268     little_endian_store_16(event, pos, client->cid);
269     pos += 2;
270     event[pos++] = status;
271     event[pos++] = client->protocol_mode;
272     event[pos++] = client->num_instances;
273     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
274 }
275 
276 
277 static void hids_run_for_client(hids_client_t * client){
278     uint8_t att_status;
279     gatt_client_service_t service;
280     gatt_client_characteristic_t characteristic;
281     uint8_t report_index;
282 
283     switch (client->state){
284         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
285             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
286             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
287             // TODO handle status
288             UNUSED(att_status);
289             break;
290 
291         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
292             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
293 
294             service.start_group_handle = client->services[client->service_index].start_handle;
295             service.end_group_handle = client->services[client->service_index].end_handle;
296             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
297 
298             UNUSED(att_status);
299             break;
300 
301         case HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD:
302             client->state = HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED;
303 
304             report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, &characteristic);
305             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
306                 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
307             }
308 
309             if ((report_index == HIDS_CLIENT_INVALID_REPORT_INDEX) || (att_status != ERROR_CODE_SUCCESS)){
310                 client->reports[report_index].value_handle = 0;
311                 client->state = HIDS_CLIENT_STATE_CONNECTED;
312             }
313             break;
314 
315         case HIDS_CLIENT_STATE_W2_ENABLE_MOUSE:
316             client->state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED;
317 
318             report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, &characteristic);
319 
320             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
321                 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
322             }
323 
324             if ((report_index == HIDS_CLIENT_INVALID_REPORT_INDEX) || (att_status != ERROR_CODE_SUCCESS)){
325                 client->reports[report_index].value_handle = 0;
326                 client->state = HIDS_CLIENT_STATE_CONNECTED;
327             }
328 
329             break;
330 
331         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE:
332             client->state = HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE;
333             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);
334             UNUSED(att_status);
335 
336             client->protocol_mode = client->required_protocol_mode;
337             client->state = HIDS_CLIENT_STATE_CONNECTED;
338             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
339             break;
340 
341         case HIDS_CLIENT_W2_SEND_REPORT:{
342             client->state = HIDS_CLIENT_STATE_CONNECTED;
343 
344             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
345                 client->reports[client->active_index].value_handle,
346                 client->report_len, (uint8_t *)client->report);
347             UNUSED(att_status);
348             break;
349         }
350 
351         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
352             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
353 
354             att_status = gatt_client_read_value_of_characteristic_using_value_handle(&handle_gatt_client_event, client->con_handle, client->reports[client->active_index].value_handle);
355             UNUSED(att_status);
356             break;
357 
358         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
359             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
360             client->descriptor_handle = 0;
361 
362             hids_client_get_characteristic_for_report_index(client, client->active_index, &characteristic);
363             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
364             UNUSED(att_status);
365             break;
366 
367         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
368             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
369 
370             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
371             client->descriptor_handle = 0;
372             UNUSED(att_status);
373             break;
374 
375         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
376             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
377 
378             att_status = gatt_client_discover_characteristics_for_handle_range_by_uuid16(&handle_gatt_client_event, client->con_handle, 0x0001, 0xffff,
379                 client->external_report_reference_uuid);
380             UNUSED(att_status);
381             break;
382 
383         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
384             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
385             client->descriptor_handle = 0;
386             hids_client_get_characteristic_for_report_index(client, client->active_index, &characteristic);
387             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
388             UNUSED(att_status);
389             break;
390 
391         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
392             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
393 
394             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
395             client->descriptor_handle = 0;
396             UNUSED(att_status);
397             break;
398 
399         default:
400             break;
401     }
402 }
403 
404 static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_id, uint8_t *buffer, uint16_t report_len){
405     uint16_t pos = 0;
406     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
407     pos++;  // skip len
408     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
409     little_endian_store_16(buffer, pos, client->cid);
410     pos += 2;
411     buffer[pos++] = report_id;
412     little_endian_store_16(buffer, pos, report_len);
413     pos += 2;
414     buffer[1] = pos + report_len - 2;
415 }
416 
417 static void handle_boot_keyboard_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
418     UNUSED(packet_type);
419     UNUSED(channel);
420     UNUSED(size);
421 
422     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
423 
424     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
425     btstack_assert(client != NULL);
426 
427     uint8_t * in_place_event = packet;
428     hids_client_setup_report_event(client, HID_BOOT_MODE_KEYBOARD_ID, in_place_event, gatt_event_notification_get_value_length(packet));
429     (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size);
430 }
431 
432 static void handle_boot_mouse_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
433     UNUSED(packet_type);
434     UNUSED(channel);
435     UNUSED(size);
436 
437     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
438 
439     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
440     btstack_assert(client != NULL);
441 
442     uint8_t * in_place_event = packet;
443     hids_client_setup_report_event(client, HID_BOOT_MODE_MOUSE_ID, in_place_event, gatt_event_notification_get_value_length(packet));
444     (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size);
445 }
446 
447 
448 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
449     UNUSED(packet_type);
450     UNUSED(channel);
451     UNUSED(size);
452 
453     hids_client_t * client = NULL;
454     uint8_t att_status;
455     gatt_client_service_t service;
456     gatt_client_characteristic_t characteristic;
457     gatt_client_characteristic_descriptor_t characteristic_descriptor;
458 
459     hids_client_report_t * boot_keyboard_report;
460     hids_client_report_t * boot_mouse_report;
461     const uint8_t * characteristic_descriptor_value;
462 
463     switch(hci_event_packet_get_type(packet)){
464         case GATT_EVENT_SERVICE_QUERY_RESULT:
465             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
466             btstack_assert(client != NULL);
467 
468             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
469                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
470                 hids_finalize_client(client);
471                 break;
472             }
473 
474             if (client->num_instances < MAX_NUM_HID_SERVICES){
475                 gatt_event_service_query_result_get_service(packet, &service);
476                 client->services[client->num_instances].start_handle = service.start_group_handle;
477                 client->services[client->num_instances].end_handle = service.end_group_handle;
478             }
479             client->num_instances++;
480             break;
481 
482         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
483             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
484             btstack_assert(client != NULL);
485             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
486             switch (characteristic.uuid16){
487                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
488                     client->protocol_mode_value_handle = characteristic.value_handle;
489                     break;
490 
491                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
492                     hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT);
493                     break;
494 
495                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
496                     hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT);
497                     break;
498 
499                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
500                     hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT);
501                     break;
502 
503                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
504                     hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED);
505                     break;
506 
507                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
508                     hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_MAP_ID, HID_REPORT_TYPE_RESERVED);
509                     break;
510 
511                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
512                     // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION\n");
513                     hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_INFORMATION_ID, HID_REPORT_TYPE_INPUT);
514                     break;
515 
516                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
517                     // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT\n");
518                     hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_CONTROL_POINT_ID, HID_REPORT_TYPE_INPUT);
519                     break;
520                 default:
521                     // check if uuid16 is the external one:
522                     if ( characteristic.uuid16 == client->external_report_reference_uuid ){
523                         hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED);
524                         client->external_report_reference_uuid = 0;
525                     }
526                     break;
527             }
528             break;
529 
530         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
531             // Map Report characteristic value == HID Descriptor
532             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
533             btstack_assert(client != NULL);
534             // TODO get value and store it
535             break;
536 
537         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
538             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
539             btstack_assert(client != NULL);
540             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
541 
542             switch (client->state) {
543                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
544                     // setup for descriptor value query
545                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
546                         client->descriptor_handle = characteristic_descriptor.handle;
547                     }
548                     break;
549                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
550                     // setup for descriptor value query
551                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
552                         client->descriptor_handle = characteristic_descriptor.handle;
553                     }
554                     break;
555                 default:
556                     break;
557             }
558             break;
559 
560         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
561             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
562             btstack_assert(client != NULL);
563 
564             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
565                 break;
566             }
567 
568             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
569 
570             switch (client->state) {
571                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
572                     // get external report characteristic uuid
573                     client->descriptor_handle = gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet);
574                     client->external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
575                     // printf("found external report characteristic uuid 0x%02x\n", client->external_report_reference_uuid);
576                     break;
577 
578                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
579                     client->reports[client->active_index].report_id = characteristic_descriptor_value[0];
580                     client->reports[client->active_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
581                     log_info("update index %d, id %d, type %d, value handle 0x%02x",
582                         client->active_index,
583                         client->reports[client->active_index].report_id,
584                         client->reports[client->active_index].report_type,
585                         client->reports[client->active_index].value_handle);
586 
587                     break;
588 
589                 default:
590                     break;
591             }
592             break;
593 
594         case GATT_EVENT_QUERY_COMPLETE:
595             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
596             btstack_assert(client != NULL);
597 
598             att_status = gatt_event_query_complete_get_att_status(packet);
599 
600             switch (client->state){
601                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
602                     if (att_status != ATT_ERROR_SUCCESS){
603                         hids_emit_connection_established(client, att_status);
604                         hids_finalize_client(client);
605                         break;
606                     }
607 
608                     if (client->num_instances == 0){
609                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
610                         hids_finalize_client(client);
611                         break;
612                     }
613 
614                     if (client->num_instances > MAX_NUM_HID_SERVICES) {
615                         log_info("%d hid services found, only first %d can be stored, increase MAX_NUM_HID_SERVICES", client->num_instances, MAX_NUM_HID_SERVICES);
616                         client->num_instances = MAX_NUM_HID_SERVICES;
617                     }
618 
619                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
620                     client->service_index = 0;
621                     break;
622 
623                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
624                     if (att_status != ATT_ERROR_SUCCESS){
625                         hids_emit_connection_established(client, att_status);
626                         hids_finalize_client(client);
627                         break;
628                     }
629 
630                     switch (client->required_protocol_mode){
631                         case HID_PROTOCOL_MODE_BOOT:
632                             if (get_boot_keyboard_input_report(client) != NULL){
633                                 client->state = HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD;
634                                 break;
635                             }
636                             if (get_boot_mouse_input_report(client) != NULL){
637                                 client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE;
638                                 break;
639                             }
640                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
641                             hids_finalize_client(client);
642                             break;
643 
644                         case HID_PROTOCOL_MODE_REPORT:
645                             if ((client->service_index + 1) < client->num_instances){
646                                 // discover characteristics of next service
647                                 client->service_index++;
648                                 client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
649                             } else {
650                                 // 1. we need to get HID Descriptor and
651                                 // 2. get external Report characteristics if referenced from Report Map
652                                 if (hids_client_report_map_query_init(client)){
653                                     break;
654                                 }
655                                 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
656                                 hids_finalize_client(client);
657                             }
658                             break;
659                         default:
660                             break;
661                     }
662                     break;
663 
664                 // HID descriptor found
665                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
666                     if (att_status != ATT_ERROR_SUCCESS){
667                         hids_emit_connection_established(client, att_status);
668                         hids_finalize_client(client);
669                         break;
670                     }
671                     // printf("HID Descriptor found, check for external Report IDs (read report map descriptor)\n");
672                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
673                     break;
674 
675                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
676                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
677                     if (client->descriptor_handle != 0){
678                         // read EXTERNAL_REPORT_REFERENCE desc. value
679                         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
680                         break;
681                     }
682 
683                     // go for next report map
684                     if (hids_client_report_query_next_report_map(client)){
685                         break;
686                     }
687 
688                     // discover characteristic descriptor for all Report characteristics,
689                     // then read value of characteristic descriptor to get Report ID
690                     if (hids_client_report_query_init(client)){
691                         break;
692                     }
693 
694                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
695                     hids_finalize_client(client);
696                     break;
697 
698                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
699                     if (client->descriptor_handle != 0){
700                         // descriptor found
701                         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
702                         break;
703                     }
704 
705                     // go for next map report
706                     if (hids_client_report_query_next_report_map(client)){
707                         break;
708                     }
709 
710                     // discover characteristic descriptor for all Report characteristics,
711                     // then read value of characteristic descriptor to get Report ID
712                     if (hids_client_report_query_init(client)){
713                         break;
714                     }
715 
716                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
717                     hids_finalize_client(client);
718                     break;
719 
720                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
721                     // go for next map report
722                     if (hids_client_report_query_next_report_map(client)){
723                         break;
724                     }
725 
726                     // discover characteristic descriptor for all Report characteristics,
727                     // then read value of characteristic descriptor to get Report ID
728                     if (hids_client_report_query_init(client)){
729                         break;
730                     }
731 
732                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
733                     hids_finalize_client(client);
734                     break;
735 
736                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
737                     if (client->descriptor_handle != 0){
738                         // descriptor found
739                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
740                         break;
741                     }
742 
743                     // go for next report
744                     if (hids_client_report_query_next_report(client)){
745                         break;
746                     }
747 
748                     // TODO continue with report mode
749                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
750                     break;
751 
752                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
753                     // go for next report
754                     if (hids_client_report_query_next_report(client)){
755                         break;
756                     }
757 
758                     // TODO continue with report mode
759                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
760                     break;
761 
762                 case HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED:
763                     boot_keyboard_report = get_boot_keyboard_input_report(client);
764                     if (boot_keyboard_report != NULL){
765                         // setup listener
766                         characteristic.value_handle = boot_keyboard_report->value_handle;
767                         gatt_client_listen_for_characteristic_value_updates(&boot_keyboard_report->notifications, &handle_boot_keyboard_hid_event, client->con_handle, &characteristic);
768                     }
769 
770                     // check if there is mouse input report
771                     boot_mouse_report = get_boot_mouse_input_report(client);
772                     if (boot_mouse_report != NULL){
773                         client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE;
774                         break;
775                     }
776 
777                     // if none of the reports is found bail out
778                     if (!boot_mouse_report && !boot_keyboard_report){
779                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
780                         hids_finalize_client(client);
781                         break;
782                     }
783 
784                     // set protocol
785                     if (client->protocol_mode_value_handle != 0){
786                         client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE;
787                     } else {
788                         client->protocol_mode = HID_PROTOCOL_MODE_BOOT;
789                         client->state = HIDS_CLIENT_STATE_CONNECTED;
790                         hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
791                     }
792                     hids_run_for_client(client);
793                     break;
794 
795                 case HIDS_CLIENT_STATE_W4_MOUSE_ENABLED:
796                     boot_mouse_report = get_boot_mouse_input_report(client);
797                     if (boot_mouse_report != NULL){
798                         // setup listener
799                         characteristic.value_handle = boot_mouse_report->value_handle;
800                         gatt_client_listen_for_characteristic_value_updates(&boot_mouse_report->notifications, &handle_boot_mouse_hid_event, client->con_handle, &characteristic);
801                     }
802 
803                     boot_keyboard_report = get_boot_keyboard_input_report(client);
804                     if (!boot_mouse_report && !boot_keyboard_report){
805                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
806                         hids_finalize_client(client);
807                         break;
808                     }
809 
810                     if (client->protocol_mode_value_handle != 0){
811                         client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE;
812                     } else {
813                         client->protocol_mode = HID_PROTOCOL_MODE_BOOT;
814                         client->state = HIDS_CLIENT_STATE_CONNECTED;
815                         hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
816                     }
817                     break;
818                 default:
819                     break;
820             }
821             break;
822 
823         default:
824             break;
825     }
826 
827     if (client != NULL){
828         hids_run_for_client(client);
829     }
830 }
831 
832 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){
833     btstack_assert(packet_handler != NULL);
834 
835     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
836     if (client != NULL){
837         return ERROR_CODE_COMMAND_DISALLOWED;
838     }
839 
840     uint16_t cid = hids_get_next_cid();
841     if (hids_cid != NULL) {
842         *hids_cid = cid;
843     }
844 
845     client = hids_create_client(con_handle, cid);
846     if (client == NULL) {
847         return BTSTACK_MEMORY_ALLOC_FAILED;
848     }
849 
850     client->required_protocol_mode = protocol_mode;
851     client->client_handler = packet_handler;
852     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
853 
854     hids_run_for_client(client);
855     return ERROR_CODE_SUCCESS;
856 }
857 
858 uint8_t hids_client_disconnect(uint16_t hids_cid){
859     hids_client_t * client = hids_get_client_for_cid(hids_cid);
860     if (client == NULL){
861         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
862     }
863     // finalize connection
864     hids_finalize_client(client);
865     return ERROR_CODE_SUCCESS;
866 }
867 
868 uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){
869     hids_client_t * client = hids_get_client_for_cid(hids_cid);
870     if (client == NULL){
871         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
872     }
873 
874     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
875         return ERROR_CODE_COMMAND_DISALLOWED;
876     }
877 
878     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT);
879     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
880         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
881     }
882 
883     uint16_t mtu;
884     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
885 
886     if (status != ERROR_CODE_SUCCESS){
887         return status;
888     }
889 
890     if (mtu - 2 < report_len){
891         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
892     }
893 
894     client->state = HIDS_CLIENT_W2_SEND_REPORT;
895     client->active_index = report_index;
896     client->report = report;
897     client->report_len = report_len;
898 
899     hids_run_for_client(client);
900     return ERROR_CODE_SUCCESS;
901 }
902 
903 void hids_client_init(void){}
904 
905 void hids_client_deinit(void){}
906