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