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