xref: /btstack/src/ble/gatt-service/hids_client.c (revision 61776ecd4af57608daff473952738d9670d262ca)
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 BLUEKITCHEN
24  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "hids_client.c"
39 
40 #include "btstack_config.h"
41 
42 #ifdef ENABLE_TESTING_SUPPORT
43 #include <stdio.h>
44 #endif
45 
46 #include <stdint.h>
47 #include <string.h>
48 
49 #include "ble/gatt-service/hids_client.h"
50 
51 #include "btstack_memory.h"
52 #include "ble/core.h"
53 #include "ble/gatt_client.h"
54 #include "bluetooth_gatt.h"
55 #include "btstack_debug.h"
56 #include "btstack_event.h"
57 #include "btstack_run_loop.h"
58 #include "gap.h"
59 #include "ble/gatt_service_client.h"
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 btstack_packet_callback_registration_t hci_event_callback_registration;
67 
68 static btstack_linked_list_t clients;
69 static uint16_t hids_cid_counter = 0;
70 
71 static uint8_t * hids_client_descriptor_storage;
72 static uint16_t  hids_client_descriptor_storage_len;
73 
74 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
75 static void hids_client_handle_can_write_without_reponse(void * context);
76 
77 #ifdef ENABLE_TESTING_SUPPORT
hid_characteristic_name(uint16_t uuid)78 static char * hid_characteristic_name(uint16_t uuid){
79     switch (uuid){
80         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
81             return "PROTOCOL_MODE";
82 
83         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
84             return "BOOT_KEYBOARD_INPUT_REPORT";
85 
86         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
87             return "BOOT_MOUSE_INPUT_REPORT";
88 
89         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
90             return "BOOT_KEYBOARD_OUTPUT_REPORT";
91 
92         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
93             return "REPORT";
94 
95         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
96             return "REPORT_MAP";
97 
98         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
99             return "HID_INFORMATION";
100 
101         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
102             return "HID_CONTROL_POINT";
103         default:
104             return "UKNOWN";
105     }
106 }
107 #endif
108 
hids_get_client_for_con_handle(hci_con_handle_t con_handle)109 static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
110     btstack_linked_list_iterator_t it;
111     btstack_linked_list_iterator_init(&it, &clients);
112     while (btstack_linked_list_iterator_has_next(&it)){
113         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
114         if (client->con_handle != con_handle) continue;
115         return client;
116     }
117     return NULL;
118 }
119 
hids_get_client_for_cid(uint16_t hids_cid)120 static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
121     btstack_linked_list_iterator_t it;
122     btstack_linked_list_iterator_init(&it, &clients);
123     while (btstack_linked_list_iterator_has_next(&it)){
124         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
125         if (client->cid != hids_cid) continue;
126         return client;
127     }
128     return NULL;
129 }
130 
131 
132 // START Descriptor Storage Util
133 
hids_client_descriptors_len(hids_client_t * client)134 static uint16_t hids_client_descriptors_len(hids_client_t * client){
135     uint16_t descriptors_len = 0;
136     uint8_t service_index;
137     for (service_index = 0; service_index < client->num_instances; service_index++){
138         descriptors_len += client->services[service_index].hid_descriptor_len;
139     }
140     return descriptors_len;
141 }
142 
hids_client_descriptor_storage_get_available_space(void)143 static uint16_t hids_client_descriptor_storage_get_available_space(void){
144     // assumes all descriptors are back to back
145     uint16_t free_space = hids_client_descriptor_storage_len;
146     btstack_linked_list_iterator_t it;
147     btstack_linked_list_iterator_init(&it, &clients);
148     while (btstack_linked_list_iterator_has_next(&it)){
149         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
150         free_space -= hids_client_descriptors_len(client);
151     }
152     return free_space;
153 }
154 
hids_client_descriptor_storage_init(hids_client_t * client,uint8_t service_index)155 static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
156     // reserve remaining space for this connection
157     uint16_t available_space = hids_client_descriptor_storage_get_available_space();
158     client->services[service_index].hid_descriptor_len = 0;
159     client->services[service_index].hid_descriptor_max_len = available_space;
160     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - available_space;
161 }
162 
hids_client_descriptor_storage_store(hids_client_t * client,uint8_t service_index,uint8_t byte)163 static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
164     // store single hid descriptor byte
165     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
166 
167     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
168     client->services[service_index].hid_descriptor_len++;
169     return true;
170 }
171 
hids_client_descriptor_storage_delete(hids_client_t * client)172 static void hids_client_descriptor_storage_delete(hids_client_t * client){
173     uint8_t service_index;
174 
175     // calculate descriptors len
176     uint16_t descriptors_len = hids_client_descriptors_len(client);
177 
178     if (descriptors_len > 0){
179         // move higher descriptors down
180         uint16_t next_offset = client->services[0].hid_descriptor_offset + descriptors_len;
181         memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
182                 &hids_client_descriptor_storage[next_offset],
183                 hids_client_descriptor_storage_len - next_offset);
184 
185         // fix descriptor offset of higher descriptors
186         btstack_linked_list_iterator_t it;
187         btstack_linked_list_iterator_init(&it, &clients);
188         while (btstack_linked_list_iterator_has_next(&it)){
189             hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
190             if (conn == client) continue;
191             for (service_index = 0; service_index < client->num_instances; service_index++){
192                 if (conn->services[service_index].hid_descriptor_offset >= next_offset){
193                     conn->services[service_index].hid_descriptor_offset -= descriptors_len;
194                 }
195             }
196         }
197     }
198 
199     // clear descriptors
200     for (service_index = 0; service_index < client->num_instances; service_index++){
201         client->services[service_index].hid_descriptor_len = 0;
202         client->services[service_index].hid_descriptor_offset = 0;
203     }
204 }
205 
hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid,uint8_t service_index)206 const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
207     hids_client_t * client = hids_get_client_for_cid(hids_cid);
208     if (client == NULL){
209         return NULL;
210     }
211     if (service_index >= client->num_instances){
212         return NULL;
213     }
214     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
215 }
216 
hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid,uint8_t service_index)217 uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
218     hids_client_t * client = hids_get_client_for_cid(hids_cid);
219     if (client == NULL){
220         return 0;
221     }
222     if (service_index >= client->num_instances){
223         return 0;
224     }
225     return client->services[service_index].hid_descriptor_len;
226 }
227 
228 // END Descriptor Storage Util
229 
hids_get_next_cid(void)230 static uint16_t hids_get_next_cid(void){
231     if (hids_cid_counter == 0xffff) {
232         hids_cid_counter = 1;
233     } else {
234         hids_cid_counter++;
235     }
236     return hids_cid_counter;
237 }
238 
find_report_index_for_value_handle(hids_client_t * client,uint16_t value_handle)239 static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
240     uint8_t i;
241     for (i = 0; i < client->num_reports; i++){
242         if (client->reports[i].value_handle == value_handle){
243             return i;
244         }
245     }
246     return HIDS_CLIENT_INVALID_REPORT_INDEX;
247 }
248 
find_external_report_index_for_value_handle(hids_client_t * client,uint16_t value_handle)249 static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
250     uint8_t i;
251     for (i = 0; i < client->num_external_reports; i++){
252         if (client->external_reports[i].value_handle == value_handle){
253             return i;
254         }
255     }
256     return HIDS_CLIENT_INVALID_REPORT_INDEX;
257 }
258 
external_report_index_for_uuid_exists(hids_client_t * client,uint16_t uuid16)259 static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
260     uint8_t i;
261     for (i = 0; i < client->num_external_reports; i++){
262         if (client->external_reports[i].external_report_reference_uuid == uuid16){
263             return true;
264         }
265     }
266     return false;
267 }
268 
find_report_index_for_report_id_and_report_type(hids_client_t * client,uint8_t report_id,hid_report_type_t report_type)269 static uint8_t find_report_index_for_report_id_and_report_type(hids_client_t * client, uint8_t report_id, hid_report_type_t report_type){
270     uint8_t i;
271 
272     for (i = 0; i < client->num_reports; i++){
273         hids_client_report_t report = client->reports[i];
274         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
275 
276         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
277             if (!client->reports[i].boot_report){
278                 continue;
279             }
280         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
281             if (client->reports[i].boot_report){
282                 continue;
283             }
284         }
285 
286         if (report.report_id == report_id && report.report_type == report_type){
287             return i;
288         }
289     }
290     return HIDS_CLIENT_INVALID_REPORT_INDEX;
291 }
292 
hids_client_add_characteristic(hids_client_t * client,gatt_client_characteristic_t * characteristic,uint8_t report_id,hid_report_type_t report_type,bool boot_report)293 static uint8_t hids_client_add_characteristic(hids_client_t * client, gatt_client_characteristic_t * characteristic, uint8_t report_id, hid_report_type_t report_type, bool boot_report){
294 
295     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
296     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
297         return report_index;
298     }
299     report_index = client->num_reports;
300 
301     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
302         client->reports[report_index].value_handle = characteristic->value_handle;
303         client->reports[report_index].end_handle = characteristic->end_handle;
304         client->reports[report_index].properties = characteristic->properties;
305 
306         client->reports[report_index].service_index = client->service_index;
307         client->reports[report_index].report_id = report_id;
308         client->reports[report_index].report_type = report_type;
309         client->reports[report_index].boot_report = boot_report;
310 
311         log_info("add index %d, id %d, type %d, value handle 0x%02x, properties 0x%02x", report_index, report_id, report_type, characteristic->value_handle, characteristic->properties);
312         client->num_reports++;
313         return report_index;
314     } else {
315         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
316         return HIDS_CLIENT_INVALID_REPORT_INDEX;
317     }
318 }
319 
hids_client_add_external_report(hids_client_t * client,gatt_client_characteristic_descriptor_t * characteristic_descriptor)320 static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
321     uint8_t report_index = client->num_external_reports;
322 
323     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
324         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
325         client->external_reports[report_index].service_index = client->service_index;
326 
327         client->num_external_reports++;
328         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
329         return report_index;
330     } else {
331         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
332         return HIDS_CLIENT_INVALID_REPORT_INDEX;
333     }
334 }
335 
hids_client_get_next_active_report_map_index(hids_client_t * client)336 static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
337     uint8_t i;
338     for (i = client->service_index; i < client->num_instances; i++){
339         if (client->services[i].report_map_value_handle != 0){
340             return i;
341         }
342     }
343     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
344     return HIDS_CLIENT_INVALID_REPORT_INDEX;
345 }
346 
hids_client_report_query_next_report_map(hids_client_t * client)347 static bool hids_client_report_query_next_report_map(hids_client_t * client){
348     client->service_index++;
349     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
350         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
351         return true;
352     }
353     return false;
354 }
355 
hids_client_report_map_query_init(hids_client_t * client)356 static bool hids_client_report_map_query_init(hids_client_t * client){
357     client->service_index = 0;
358 
359     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
360         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
361         return true;
362     }
363     return false;
364 }
365 
hids_client_report_query_next_report_map_uuid(hids_client_t * client)366 static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
367     client->report_index++;
368     if (client->report_index < client->num_external_reports){
369         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
370         return true;
371     }
372     return false;
373 }
374 
hids_client_report_map_uuid_query_init(hids_client_t * client)375 static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
376     client->report_index = 0;
377     if (client->num_external_reports > 0){
378         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
379         return true;
380     }
381     return false;
382 }
383 
hids_client_get_next_report_index(hids_client_t * client)384 static uint8_t hids_client_get_next_report_index(hids_client_t * client){
385     uint8_t i;
386     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
387 
388     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
389         hids_client_report_t report = client->reports[i];
390         if (!report.boot_report){
391             if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
392                 index = i;
393                 client->service_index = report.service_index;
394                 break;
395             }
396         }
397     }
398     client->report_index = index;
399     return index;
400 }
401 
hids_client_report_query_next_report(hids_client_t * client)402 static bool hids_client_report_query_next_report(hids_client_t * client){
403     client->report_index++;
404     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
405         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
406         return true;
407     }
408     return false;
409 }
410 
hids_client_report_query_init(hids_client_t * client)411 static bool hids_client_report_query_init(hids_client_t * client){
412     client->report_index = 0;
413 
414     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
415         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
416         return true;
417     }
418     return false;
419 }
420 
hids_client_get_next_notification_report_index(hids_client_t * client)421 static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
422     uint8_t i;
423     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
424 
425     for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
426         hids_client_report_t report = client->reports[i];
427         hid_protocol_mode_t  protocol_mode = client->services[report.service_index].protocol_mode;
428 
429         if (protocol_mode == HID_PROTOCOL_MODE_BOOT){
430             if (!client->reports[i].boot_report){
431                 continue;
432             }
433         } else if (protocol_mode == HID_PROTOCOL_MODE_REPORT){
434             if (client->reports[i].boot_report){
435                 continue;
436             }
437         }
438         if (report.report_type == HID_REPORT_TYPE_INPUT){
439             index = i;
440         }
441     }
442     client->report_index = index;
443     return index;
444 }
445 
hids_client_report_next_notification_report_index(hids_client_t * client)446 static bool hids_client_report_next_notification_report_index(hids_client_t * client){
447     client->report_index++;
448     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
449         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
450         return true;
451     }
452     return false;
453 }
454 
hids_client_report_notifications_init(hids_client_t * client)455 static bool hids_client_report_notifications_init(hids_client_t * client){
456 #ifdef ENABLE_TESTING_SUPPORT
457     printf("\nRegister for Notifications: \n");
458 #endif
459     client->report_index = 0;
460 
461     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
462         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
463         return true;
464     }
465     return false;
466 }
467 
hids_client_report_next_notifications_configuration_report_index(hids_client_t * client)468 static bool hids_client_report_next_notifications_configuration_report_index(hids_client_t * client){
469     client->report_index++;
470     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
471         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
472         return true;
473     }
474     return false;
475 }
476 
hids_client_notifications_configuration_init(hids_client_t * client)477 static bool hids_client_notifications_configuration_init(hids_client_t * client){
478 #ifdef ENABLE_TESTING_SUPPORT
479     printf("\nConfigure for Notifications: \n");
480 #endif
481     client->report_index = 0;
482 
483     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
484         client->state = HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS;
485         return true;
486     }
487     return false;
488 }
489 
hids_create_client(hci_con_handle_t con_handle,uint16_t cid)490 static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
491     hids_client_t * client = btstack_memory_hids_client_get();
492     if (!client){
493         log_error("Not enough memory to create client");
494         return NULL;
495     }
496     client->state = HIDS_CLIENT_STATE_IDLE;
497     client->cid = cid;
498     client->con_handle = con_handle;
499 
500     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
501     return client;
502 }
503 
hids_finalize_client(hids_client_t * client)504 static void hids_finalize_client(hids_client_t * client){
505     // stop listening
506     uint8_t i;
507     for (i = 0; i < client->num_reports; i++){
508         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
509     }
510 
511     hids_client_descriptor_storage_delete(client);
512     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
513     btstack_memory_hids_client_free(client);
514 }
515 
516 
hids_emit_connection_established(hids_client_t * client,uint8_t status)517 static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
518     uint8_t event[8];
519     int pos = 0;
520     event[pos++] = HCI_EVENT_GATTSERVICE_META;
521     event[pos++] = sizeof(event) - 2;
522     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
523     little_endian_store_16(event, pos, client->cid);
524     pos += 2;
525     event[pos++] = status;
526     event[pos++] = client->services[0].protocol_mode;
527     event[pos++] = client->num_instances;
528     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
529 }
530 
hids_emit_disconnected(btstack_packet_handler_t packet_handler,uint16_t cid)531 static void hids_emit_disconnected(btstack_packet_handler_t packet_handler, uint16_t cid){
532     uint8_t event[5];
533     int pos = 0;
534     event[pos++] = HCI_EVENT_GATTSERVICE_META;
535     event[pos++] = sizeof(event) - 2;
536     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_DISCONNECTED;
537     little_endian_store_16(event, pos, cid);
538     pos += 2;
539     btstack_assert(pos == sizeof(event));
540     (*packet_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
541 }
542 
hids_emit_notifications_configuration(hids_client_t * client)543 static void hids_emit_notifications_configuration(hids_client_t * client){
544     uint8_t event[6];
545     int pos = 0;
546     event[pos++] = HCI_EVENT_GATTSERVICE_META;
547     event[pos++] = sizeof(event) - 2;
548     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_REPORTS_NOTIFICATION;
549     little_endian_store_16(event, pos, client->cid);
550     pos += 2;
551     event[pos++] = client->value;
552     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
553 }
554 
hids_client_setup_report_event(uint8_t subevent,hids_client_t * client,uint8_t report_index,uint8_t * buffer,uint16_t report_len)555 static uint16_t hids_client_setup_report_event(uint8_t subevent, hids_client_t *client, uint8_t report_index, uint8_t *buffer,
556                                uint16_t report_len) {
557     uint16_t pos = 0;
558     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
559     pos++;  // skip len
560     buffer[pos++] = subevent;
561     little_endian_store_16(buffer, pos, client->cid);
562     pos += 2;
563     buffer[pos++] = client->reports[report_index].service_index;
564     buffer[pos++] = client->reports[report_index].report_id;
565     little_endian_store_16(buffer, pos, report_len);
566     pos += 2;
567     buffer[1] = pos + report_len - 2;
568     return pos;
569 }
570 
hids_client_setup_report_event_with_report_id(hids_client_t * client,uint8_t report_index,uint8_t * buffer,uint16_t report_len)571 static void hids_client_setup_report_event_with_report_id(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
572     uint16_t pos = hids_client_setup_report_event(GATTSERVICE_SUBEVENT_HID_REPORT, client, report_index, buffer,
573                                                   report_len + 1);
574     buffer[pos] = client->reports[report_index].report_id;
575 }
576 
hids_client_emit_hid_information_event(hids_client_t * client,const uint8_t * value,uint16_t value_len)577 static void hids_client_emit_hid_information_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
578     if (value_len != 4) return;
579 
580     uint8_t event[11];
581     int pos = 0;
582     event[pos++] = HCI_EVENT_GATTSERVICE_META;
583     event[pos++] = sizeof(event) - 2;
584     event[pos++] = GATTSERVICE_SUBEVENT_HID_INFORMATION;
585     little_endian_store_16(event, pos, client->cid);
586     pos += 2;
587     event[pos++] = client->service_index;
588 
589     memcpy(event+pos, value, 3);
590     pos += 3;
591     event[pos++] = (value[3] & 0x02) >> 1;
592     event[pos++] = value[3] & 0x01;
593 
594     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
595 }
596 
hids_client_emit_protocol_mode_event(hids_client_t * client,const uint8_t * value,uint16_t value_len)597 static void hids_client_emit_protocol_mode_event(hids_client_t * client, const uint8_t *value, uint16_t value_len){
598     if (value_len != 1) return;
599 
600     uint8_t event[11];
601     int pos = 0;
602     event[pos++] = HCI_EVENT_GATTSERVICE_META;
603     event[pos++] = sizeof(event) - 2;
604     event[pos++] = GATTSERVICE_SUBEVENT_HID_PROTOCOL_MODE;
605     little_endian_store_16(event, pos, client->cid);
606     pos += 2;
607     event[pos++] = client->service_index;
608     event[pos++] = value[0];
609     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
610 }
611 
handle_notification_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)612 static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
613     UNUSED(packet_type);
614     UNUSED(channel);
615 
616     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
617 
618     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
619     if (client == NULL) return;
620 
621     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
622     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
623         return;
624     }
625 
626     // GATT_EVENT_NOTIFICATION has HID report data at offset 12
627     // - see gatt_event_notification_get_value()
628     //
629     // GATTSERVICE_SUBEVENT_HID_REPORT has HID report data at offset 10
630     // - see gattservice_subevent_hid_report_get_report() and add 1 for the inserted Report ID
631     //
632     // => use existing packet from offset 2 = 12 - 10 to setup event
633     const int16_t offset = 2;
634 
635     uint8_t * in_place_event = &packet[offset];
636     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
637                                                   gatt_event_notification_get_value_length(packet));
638     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size - offset);
639 }
640 
handle_report_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)641 static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
642     UNUSED(packet_type);
643     UNUSED(channel);
644 
645     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
646 
647     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
648     if (client == NULL) return;
649 
650     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
651         return;
652     }
653     client->state = HIDS_CLIENT_STATE_CONNECTED;
654 
655     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
656     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
657         return;
658     }
659 
660     // GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT has HID report data at offset 12
661     // - see gatt_event_characteristic_value_query_result_get_value()
662     //
663     // GATTSERVICE_SUBEVENT_HID_REPORT has HID report data at offset 10
664     // - see gattservice_subevent_hid_report_get_report() and add 1 for the inserted Report ID
665     //
666     // => use existing packet from offset 2 = 12 - 10 to setup event
667     const int16_t offset = 2;
668 
669     uint8_t * in_place_event = &packet[offset];
670     hids_client_setup_report_event_with_report_id(client, report_index, in_place_event,
671                                                   gatt_event_characteristic_value_query_result_get_value_length(packet));
672     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size - offset);
673 }
674 
hids_run_for_client(hids_client_t * client)675 static void hids_run_for_client(hids_client_t * client){
676     uint8_t att_status;
677     gatt_client_service_t service;
678     gatt_client_characteristic_t characteristic;
679 
680     switch (client->state){
681         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
682 #ifdef ENABLE_TESTING_SUPPORT
683             printf("\n\nQuery Services:\n");
684 #endif
685             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
686 
687             // result in GATT_EVENT_SERVICE_QUERY_RESULT
688             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
689             UNUSED(att_status);
690             break;
691 
692         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
693 #ifdef ENABLE_TESTING_SUPPORT
694             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
695 #endif
696             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
697 
698             service.start_group_handle = client->services[client->service_index].start_handle;
699             service.end_group_handle = client->services[client->service_index].end_handle;
700 
701             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
702             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
703 
704             UNUSED(att_status);
705             break;
706 
707         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
708 #ifdef ENABLE_TESTING_SUPPORT
709             printf("\n\nRead REPORT_MAP (Handle 0x%04X) HID Descriptor of service %d:\n", client->services[client->service_index].report_map_value_handle, client->service_index);
710 #endif
711             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
712 
713             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
714             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);
715             UNUSED(att_status);
716             break;
717 
718         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
719 #ifdef ENABLE_TESTING_SUPPORT
720             printf("\nDiscover REPORT_MAP (Handle 0x%04X) Characteristic Descriptors of service %d:\n", client->services[client->service_index].report_map_value_handle, client->service_index);
721 #endif
722             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
723 
724             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
725             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
726 
727             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
728             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
729             UNUSED(att_status);
730             break;
731 
732         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
733 #ifdef ENABLE_TESTING_SUPPORT
734             printf("\nRead external chr UUID (Handle 0x%04X) Characteristic Descriptors, service index %d:\n", client->external_reports[client->report_index].value_handle, client->service_index);
735 #endif
736             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
737 
738             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
739             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->external_reports[client->report_index].value_handle);
740             UNUSED(att_status);
741             break;
742 
743         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
744  #ifdef ENABLE_TESTING_SUPPORT
745             printf("\nDiscover External Report Characteristic:\n");
746 #endif
747             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
748 
749             service.start_group_handle = 0x0001;
750             service.end_group_handle = 0xffff;
751 
752             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
753             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
754             UNUSED(att_status);
755             break;
756 
757         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
758 #ifdef ENABLE_TESTING_SUPPORT
759             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
760                 client->report_index,
761                 client->reports[client->report_index].service_index,
762                 client->reports[client->report_index].value_handle);
763 #endif
764             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
765             client->handle = 0;
766 
767             characteristic.value_handle = client->reports[client->report_index].value_handle;
768             characteristic.end_handle = client->reports[client->report_index].end_handle;
769             characteristic.properties = client->reports[client->report_index].properties;
770 
771             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
772             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
773             UNUSED(att_status);
774             break;
775 
776         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
777             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
778 
779             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
780             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->handle);
781             client->handle = 0;
782             UNUSED(att_status);
783             break;
784 
785         case HIDS_CLIENT_STATE_W2_CONFIGURE_NOTIFICATIONS:
786 #ifdef ENABLE_TESTING_SUPPORT
787             if (client->value > 0){
788                 printf("    Notification configuration enable ");
789             } else {
790                 printf("    Notification configuration disable ");
791             }
792             printf("[%d, %d, 0x%04X]:\n",
793                 client->report_index,
794                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
795 #endif
796 
797             client->state = HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED;
798 
799             characteristic.value_handle = client->reports[client->report_index].value_handle;
800             characteristic.end_handle = client->reports[client->report_index].end_handle;
801             characteristic.properties = client->reports[client->report_index].properties;
802 
803             // end of write marked in GATT_EVENT_QUERY_COMPLETE
804 
805             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, client->value);
806 
807             if (att_status == ERROR_CODE_SUCCESS){
808                 switch(client->value){
809                     case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION:
810                         gatt_client_listen_for_characteristic_value_updates(
811                             &client->reports[client->report_index].notification_listener,
812                             &handle_notification_event, client->con_handle, &characteristic);
813                         break;
814                     default:
815                         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[client->report_index].notification_listener);
816                         break;
817                 }
818             } else {
819                 if (hids_client_report_next_notifications_configuration_report_index(client)){
820                     hids_run_for_client(client);
821                     break;
822                 }
823                 client->state = HIDS_CLIENT_STATE_CONNECTED;
824             }
825             break;
826 
827         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
828 #ifdef ENABLE_TESTING_SUPPORT
829             printf("    Notification enable [%d, %d, 0x%04X]:\n",
830                 client->report_index,
831                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
832 #endif
833             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
834 
835             characteristic.value_handle = client->reports[client->report_index].value_handle;
836             characteristic.end_handle = client->reports[client->report_index].end_handle;
837             characteristic.properties = client->reports[client->report_index].properties;
838 
839             // end of write marked in GATT_EVENT_QUERY_COMPLETE
840             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
841 
842             if (att_status == ERROR_CODE_SUCCESS){
843                 gatt_client_listen_for_characteristic_value_updates(
844                     &client->reports[client->report_index].notification_listener,
845                     &handle_notification_event, client->con_handle, &characteristic);
846             } else {
847                 if (hids_client_report_next_notification_report_index(client)){
848                     hids_run_for_client(client);
849                     break;
850                 }
851                 client->state = HIDS_CLIENT_STATE_CONNECTED;
852                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
853             }
854             break;
855 
856 
857         case HIDS_CLIENT_W2_SEND_WRITE_REPORT:
858 #ifdef ENABLE_TESTING_SUPPORT
859             printf("    Write report [%d, %d, 0x%04X]:\n",
860                 client->report_index,
861                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
862 #endif
863 
864             client->state = HIDS_CLIENT_W4_WRITE_REPORT_DONE;
865 
866             // see GATT_EVENT_QUERY_COMPLETE for end of write
867             att_status = gatt_client_write_value_of_characteristic(
868                 &handle_gatt_client_event, client->con_handle,
869                 client->reports[client->report_index].value_handle,
870                 client->report_len, (uint8_t *)client->report);
871             UNUSED(att_status);
872             break;
873 
874         case HIDS_CLIENT_W2_SEND_GET_REPORT:
875 #ifdef ENABLE_TESTING_SUPPORT
876             printf("    Get report [index %d, ID %d, Service %d, handle 0x%04X]:\n",
877                 client->report_index,
878                 client->reports[client->report_index].report_id,
879                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
880 #endif
881 
882             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
883             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
884             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
885                 &handle_report_event,
886                 client->con_handle,
887                 client->reports[client->report_index].value_handle);
888             UNUSED(att_status);
889             break;
890 
891 #ifdef ENABLE_TESTING_SUPPORT
892         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
893             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
894 
895             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
896             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
897                 &handle_gatt_client_event,
898                 client->con_handle,
899                 client->reports[client->report_index].ccc_handle);
900 
901             break;
902 #endif
903         case HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC:
904             client->state = HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT;
905 
906             // result in GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT
907             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
908                 &handle_gatt_client_event,
909                 client->con_handle,
910                 client->handle);
911             break;
912 
913         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
914         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
915             client->write_without_response_request.callback = &hids_client_handle_can_write_without_reponse;
916             client->write_without_response_request.context = client;
917             (void) gatt_client_request_to_write_without_response(&client->write_without_response_request, client->con_handle);
918             break;
919 
920         default:
921             break;
922     }
923 }
924 
hids_client_handle_can_write_without_reponse(void * context)925 static void hids_client_handle_can_write_without_reponse(void * context) {
926     hids_client_t *client = (hids_client_t *) context;
927     uint8_t att_status;
928     switch (client->state){
929         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE:
930             att_status = gatt_client_write_value_of_characteristic_without_response(
931                 client->con_handle,
932                 client->services[client->service_index].protocol_mode_value_handle, 1, (uint8_t *)&client->required_protocol_mode);
933 
934 #ifdef ENABLE_TESTING_SUPPORT
935             printf("\n\nSet report mode %d of service %d, status 0x%02x", client->required_protocol_mode, client->service_index, att_status);
936 #endif
937 
938             if (att_status == ATT_ERROR_SUCCESS){
939                 client->services[client->service_index].protocol_mode = client->required_protocol_mode;
940                 if ((client->service_index + 1) < client->num_instances){
941                     client->service_index++;
942                     hids_run_for_client(client);
943                     break;
944                 }
945             }
946 
947             // read UUIDS for external characteristics
948             if (hids_client_report_map_uuid_query_init(client)){
949                 hids_run_for_client(client);
950                 break;
951             }
952 
953             // discover characteristic descriptor for all Report characteristics,
954             // then read value of characteristic descriptor to get Report ID
955             if (hids_client_report_query_init(client)){
956                 hids_run_for_client(client);
957                 break;
958             }
959 
960             client->state = HIDS_CLIENT_STATE_CONNECTED;
961             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
962             break;
963 
964         case HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE:
965 #ifdef ENABLE_TESTING_SUPPORT
966             printf("    Write characteristic [service %d, handle 0x%04X]:\n", client->service_index, client->handle);
967 #endif
968             client->state = HIDS_CLIENT_STATE_CONNECTED;
969             (void) gatt_client_write_value_of_characteristic_without_response(client->con_handle, client->handle, 1, (uint8_t *) &client->value);
970             break;
971 
972         default:
973             break;
974     }
975 }
976 
handle_gatt_client_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)977 static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
978     UNUSED(packet_type);
979     UNUSED(channel);
980     UNUSED(size);
981 
982     hids_client_t * client = NULL;
983     uint8_t status;
984     gatt_client_service_t service;
985     gatt_client_characteristic_t characteristic;
986     gatt_client_characteristic_descriptor_t characteristic_descriptor;
987 
988     // hids_client_report_t * boot_keyboard_report;
989     // hids_client_report_t * boot_mouse_report;
990     const uint8_t * characteristic_descriptor_value;
991     uint8_t i;
992     uint8_t report_index;
993 
994     const uint8_t * value;
995     uint16_t value_len;
996 
997     switch(hci_event_packet_get_type(packet)){
998         case GATT_EVENT_SERVICE_QUERY_RESULT:
999             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
1000             if (client == NULL) break;
1001 
1002             if (client->num_instances < MAX_NUM_HID_SERVICES){
1003                 uint8_t index = client->num_instances;
1004                 gatt_event_service_query_result_get_service(packet, &service);
1005                 client->services[index].start_handle = service.start_group_handle;
1006                 client->services[index].end_handle = service.end_group_handle;
1007                 client->num_instances++;
1008 
1009 #ifdef ENABLE_TESTING_SUPPORT
1010                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
1011 #endif
1012                 hids_client_descriptor_storage_init(client, index);
1013             }  else {
1014                 log_info("%d hid services found, only first %d can be stored, increase MAX_NUM_HID_SERVICES", client->num_instances + 1, MAX_NUM_HID_SERVICES);
1015             }
1016             break;
1017 
1018         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
1019             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
1020             if (client == NULL) break;
1021 
1022             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
1023 
1024             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
1025             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
1026                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
1027                     break;
1028                 }
1029             }
1030 
1031             switch (characteristic.uuid16){
1032                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
1033                     client->services[client->service_index].protocol_mode_value_handle = characteristic.value_handle;
1034                     break;
1035 
1036                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
1037                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
1038                     break;
1039 
1040                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
1041                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
1042                     break;
1043 
1044                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
1045                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, true);
1046                     break;
1047 
1048                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
1049                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
1050                     break;
1051 
1052                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
1053                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
1054                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
1055                     break;
1056 
1057                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
1058                     client->services[client->service_index].hid_information_value_handle = characteristic.value_handle;
1059                     break;
1060 
1061                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
1062                     client->services[client->service_index].control_point_value_handle = characteristic.value_handle;
1063                     break;
1064 
1065                 default:
1066 #ifdef ENABLE_TESTING_SUPPORT
1067                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
1068 #endif
1069                     return;
1070             }
1071 
1072 #ifdef ENABLE_TESTING_SUPPORT
1073             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
1074                 hid_characteristic_name(characteristic.uuid16),
1075                 characteristic.start_handle,
1076                 characteristic.properties,
1077                 characteristic.value_handle, characteristic.uuid16,
1078                 client->service_index);
1079 
1080             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1081                 printf(", report index 0x%02X", report_index);
1082             }
1083             printf("\n");
1084 #endif
1085             break;
1086 
1087         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
1088             // Map Report characteristic value == HID Descriptor
1089             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
1090             if (client == NULL) break;
1091 
1092             value = gatt_event_long_characteristic_value_query_result_get_value(packet);
1093             value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
1094 
1095 #ifdef ENABLE_TESTING_SUPPORT
1096             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
1097             printf_hexdump(value, value_len);
1098 #endif
1099             for (i = 0; i < value_len; i++){
1100                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, value[i]);
1101                 if (!stored){
1102                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
1103                     break;
1104                 }
1105             }
1106             break;
1107 
1108         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
1109             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
1110             if (client == NULL) break;
1111 
1112             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
1113 
1114             switch (client->state) {
1115                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1116                     // setup for descriptor value query
1117                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
1118                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
1119 
1120 #ifdef ENABLE_TESTING_SUPPORT
1121                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1122                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
1123                                 characteristic_descriptor.handle,
1124                                 characteristic_descriptor.uuid16,
1125                                 client->service_index, report_index);
1126                         }
1127 #endif
1128                     }
1129                     break;
1130                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1131                     // setup for descriptor value query
1132                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
1133                         client->handle = characteristic_descriptor.handle;
1134 #ifdef ENABLE_TESTING_SUPPORT
1135                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
1136                             characteristic_descriptor.handle,
1137                             characteristic_descriptor.uuid16);
1138 #endif
1139                     }
1140 
1141 #ifdef ENABLE_TESTING_SUPPORT
1142                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
1143                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
1144                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
1145                             characteristic_descriptor.handle,
1146                             characteristic_descriptor.uuid16);
1147                     }
1148 #endif
1149                     break;
1150 
1151                 default:
1152                     break;
1153             }
1154             break;
1155 
1156         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
1157             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
1158             if (client == NULL) break;
1159 
1160             value = gatt_event_characteristic_value_query_result_get_value(packet);
1161             value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
1162 
1163 
1164             switch (client->state){
1165 #ifdef ENABLE_TESTING_SUPPORT
1166                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1167                     printf("    Received CCC value: ");
1168                     printf_hexdump(value,  value_len);
1169                     break;
1170 #endif
1171                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:{
1172                     uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet);
1173                     if (value_handle == client->services[client->service_index].hid_information_value_handle){
1174                         hids_client_emit_hid_information_event(client, value, value_len);
1175                         break;
1176                     }
1177                     if (value_handle == client->services[client->service_index].protocol_mode_value_handle){
1178                         hids_client_emit_protocol_mode_event(client, value, value_len);
1179                         break;
1180                     }
1181                     break;
1182                 }
1183                 default:
1184                     break;
1185             }
1186 
1187             break;
1188 
1189         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
1190             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
1191             if (client == NULL) break;
1192 
1193             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1194                 break;
1195             }
1196 
1197             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1198             switch (client->state) {
1199                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1200                     // get external report characteristic uuid
1201                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
1202                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1203                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1204 #ifdef ENABLE_TESTING_SUPPORT
1205                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
1206                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1207 #endif
1208                     }
1209                     break;
1210 
1211                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
1212 
1213                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1214                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1215                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
1216     #ifdef ENABLE_TESTING_SUPPORT
1217                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1218                             client->reports[client->report_index].report_id,
1219                             client->reports[client->report_index].report_type,
1220                             client->report_index, client->service_index);
1221     #endif
1222                     }
1223                     break;
1224 
1225                 default:
1226                     break;
1227             }
1228             break;
1229 
1230         case GATT_EVENT_QUERY_COMPLETE:
1231             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
1232             if (client == NULL) break;
1233 
1234             status = gatt_client_att_status_to_error_code(gatt_event_query_complete_get_att_status(packet));
1235 
1236             switch (client->state){
1237                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
1238                     if (status != ERROR_CODE_SUCCESS){
1239                         hids_emit_connection_established(client, status);
1240                         hids_finalize_client(client);
1241                         return;
1242                     }
1243 
1244                     if (client->num_instances == 0){
1245                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1246                         hids_finalize_client(client);
1247                         return;
1248                     }
1249 
1250                     client->service_index = 0;
1251                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1252                     break;
1253 
1254                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
1255                     if (status != ERROR_CODE_SUCCESS){
1256                         hids_emit_connection_established(client, status);
1257                         hids_finalize_client(client);
1258                         return;
1259                     }
1260 
1261                     if ((client->service_index + 1) < client->num_instances){
1262                         // discover characteristics of next service
1263                         client->service_index++;
1264                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1265                         break;
1266                     }
1267 
1268                     btstack_assert(client->required_protocol_mode != HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT);
1269 
1270                     switch (client->required_protocol_mode){
1271                         case HID_PROTOCOL_MODE_REPORT:
1272                             for (i = 0; i < client->num_instances; i++){
1273                                 client->services[i].protocol_mode = HID_PROTOCOL_MODE_REPORT;
1274                             }
1275                             // 1. we need to get HID Descriptor and
1276                             // 2. get external Report characteristics if referenced from Report Map
1277                             if (hids_client_report_map_query_init(client)){
1278                                 break;
1279                             }
1280                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1281                             hids_finalize_client(client);
1282                             return;
1283 
1284                         default:
1285                             // set boot mode
1286                             client->service_index = 0;
1287                             client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE_WITHOUT_RESPONSE;
1288                             break;
1289                     }
1290                     break;
1291 
1292 
1293                 // HID descriptor found
1294                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1295                     if (status != ERROR_CODE_SUCCESS){
1296                         hids_emit_connection_established(client, status);
1297                         hids_finalize_client(client);
1298                         return;
1299                     }
1300                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1301                     break;
1302 
1303                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1304                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
1305                     // go for next report map
1306                     if (hids_client_report_query_next_report_map(client)){
1307                         break;
1308                     }
1309 
1310                     // read UUIDS for external characteristics
1311                     if (hids_client_report_map_uuid_query_init(client)){
1312                         break;
1313                     }
1314 
1315                     // discover characteristic descriptor for all Report characteristics,
1316                     // then read value of characteristic descriptor to get Report ID
1317                     if (hids_client_report_query_init(client)){
1318                         break;
1319                     }
1320 
1321                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1322                     hids_finalize_client(client);
1323                     return;
1324 
1325                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1326                     // go for next map report
1327                     if (hids_client_report_query_next_report_map_uuid(client)){
1328                         break;
1329                     }
1330 
1331                     // update external characteristics with correct value handle and end handle
1332                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1333                     break;
1334 
1335                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1336                     // discover characteristic descriptor for all Report characteristics,
1337                     // then read value of characteristic descriptor to get Report ID
1338                     if (hids_client_report_query_init(client)){
1339                         break;
1340                     }
1341 
1342                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1343                     hids_finalize_client(client);
1344                     return;
1345 
1346                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
1347                     if (client->handle != 0){
1348                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
1349                         break;
1350                     }
1351                     // go for next report
1352                     if (hids_client_report_query_next_report(client)){
1353                         break;
1354                     }
1355                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1356                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1357                     break;
1358 
1359                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
1360                     // go for next report
1361                     if (hids_client_report_query_next_report(client)){
1362                         break;
1363                     }
1364                     if (hids_client_report_notifications_init(client)){
1365                         break;
1366                     }
1367                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1368                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1369                     break;
1370 
1371                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1372                     if (hids_client_report_next_notification_report_index(client)){
1373                         break;
1374                     }
1375                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1376                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
1377                     break;
1378 
1379                 case HIDS_CLIENT_STATE_W4_NOTIFICATIONS_CONFIGURED:
1380                     if (hids_client_report_next_notifications_configuration_report_index(client)){
1381                         break;
1382                     }
1383                     hids_emit_notifications_configuration(client);
1384                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1385                     break;
1386 
1387 #ifdef ENABLE_TESTING_SUPPORT
1388                 case HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT:
1389                     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1390                     break;
1391 #endif
1392 
1393                 case HIDS_CLIENT_W4_VALUE_OF_CHARACTERISTIC_RESULT:
1394                     client->state = HIDS_CLIENT_STATE_CONNECTED;
1395                     break;
1396 
1397                 case HIDS_CLIENT_W4_WRITE_REPORT_DONE:
1398                     {
1399                         client->state = HIDS_CLIENT_STATE_CONNECTED;
1400 
1401                         // emit empty report to signal done
1402                         uint8_t event[9];
1403                         hids_client_setup_report_event(GATTSERVICE_SUBEVENT_HID_REPORT_WRITTEN, client,
1404                                                        client->report_index, event, 0);
1405                         (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
1406                     }
1407                     break;
1408 
1409                 default:
1410                     break;
1411             }
1412             break;
1413 
1414         default:
1415             break;
1416     }
1417 
1418     if (client != NULL){
1419         hids_run_for_client(client);
1420     }
1421 }
1422 
handle_hci_event(uint8_t packet_type,uint16_t channel,uint8_t * packet,uint16_t size)1423 static void handle_hci_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
1424     UNUSED(packet_type); // ok: only hci events
1425     UNUSED(channel);     // ok: there is no channel
1426     UNUSED(size);        // ok: fixed format events read from HCI buffer
1427 
1428     hci_con_handle_t con_handle;
1429     hids_client_t * client;
1430 
1431     switch (hci_event_packet_get_type(packet)) {
1432         case HCI_EVENT_DISCONNECTION_COMPLETE:
1433             con_handle = hci_event_disconnection_complete_get_connection_handle(packet);
1434             client = hids_get_client_for_con_handle(con_handle);
1435             if (client != NULL){
1436                 // emit disconnected
1437                 btstack_packet_handler_t packet_handler = client->client_handler;
1438                 uint16_t cid = client->cid;
1439                 hids_emit_disconnected(packet_handler, cid);
1440                 // finalize
1441                 hids_finalize_client(client);
1442             }
1443             break;
1444         default:
1445             break;
1446     }
1447 }
1448 
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)1449 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){
1450     btstack_assert(packet_handler != NULL);
1451 
1452     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1453     if (client != NULL){
1454         return ERROR_CODE_COMMAND_DISALLOWED;
1455     }
1456 
1457     uint16_t cid = hids_get_next_cid();
1458     if (hids_cid != NULL) {
1459         *hids_cid = cid;
1460     }
1461 
1462     client = hids_create_client(con_handle, cid);
1463     if (client == NULL) {
1464         return BTSTACK_MEMORY_ALLOC_FAILED;
1465     }
1466 
1467     client->required_protocol_mode = protocol_mode;
1468     client->client_handler = packet_handler;
1469     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1470 
1471     hids_run_for_client(client);
1472     return ERROR_CODE_SUCCESS;
1473 }
1474 
hids_client_disconnect(uint16_t hids_cid)1475 uint8_t hids_client_disconnect(uint16_t hids_cid){
1476     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1477     if (client == NULL){
1478         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1479     }
1480     // finalize connection
1481     hids_finalize_client(client);
1482     return ERROR_CODE_SUCCESS;
1483 }
1484 
hids_client_send_write_report(uint16_t hids_cid,uint8_t report_id,hid_report_type_t report_type,const uint8_t * report,uint8_t report_len)1485 uint8_t hids_client_send_write_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type, const uint8_t * report, uint8_t report_len){
1486     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1487     if (client == NULL){
1488         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1489     }
1490 
1491     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1492         return ERROR_CODE_COMMAND_DISALLOWED;
1493     }
1494 
1495     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
1496 
1497     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
1498         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1499     }
1500 
1501     uint16_t mtu;
1502     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
1503 
1504     if (status != ERROR_CODE_SUCCESS){
1505         return status;
1506     }
1507 
1508     if (mtu - 2 < report_len){
1509         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
1510     }
1511 
1512     client->state = HIDS_CLIENT_W2_SEND_WRITE_REPORT;
1513     client->report_index = report_index;
1514     client->report = report;
1515     client->report_len = report_len;
1516 
1517     hids_run_for_client(client);
1518     return ERROR_CODE_SUCCESS;
1519 }
1520 
hids_client_send_get_report(uint16_t hids_cid,uint8_t report_id,hid_report_type_t report_type)1521 uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id, hid_report_type_t report_type){
1522     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1523     if (client == NULL){
1524         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1525     }
1526 
1527     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1528         return ERROR_CODE_COMMAND_DISALLOWED;
1529     }
1530 
1531     uint8_t report_index = find_report_index_for_report_id_and_report_type(client, report_id, report_type);
1532     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
1533         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1534     }
1535 
1536     client->report_index = report_index;
1537 
1538 #ifdef ENABLE_TESTING_SUPPORT
1539     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
1540 #else
1541     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1542 #endif
1543     hids_run_for_client(client);
1544     return ERROR_CODE_SUCCESS;
1545 }
1546 
1547 
hids_client_get_hid_information(uint16_t hids_cid,uint8_t service_index)1548 uint8_t hids_client_get_hid_information(uint16_t hids_cid, uint8_t service_index){
1549     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1550     if (client == NULL){
1551         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1552     }
1553 
1554     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1555         return ERROR_CODE_COMMAND_DISALLOWED;
1556     }
1557 
1558     if (service_index >= client->num_instances){
1559         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1560     }
1561 
1562     client->service_index = service_index;
1563     client->handle = client->services[client->service_index].hid_information_value_handle;
1564 
1565     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1566     hids_run_for_client(client);
1567     return ERROR_CODE_SUCCESS;
1568 }
1569 
hids_client_get_protocol_mode(uint16_t hids_cid,uint8_t service_index)1570 uint8_t hids_client_get_protocol_mode(uint16_t hids_cid, uint8_t service_index){
1571     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1572     if (client == NULL){
1573         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1574     }
1575 
1576     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1577         return ERROR_CODE_COMMAND_DISALLOWED;
1578     }
1579 
1580     if (service_index >= client->num_instances){
1581         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1582     }
1583 
1584     client->service_index = service_index;
1585     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1586 
1587     client->state = HIDS_CLIENT_W2_READ_VALUE_OF_CHARACTERISTIC;
1588     hids_run_for_client(client);
1589     return ERROR_CODE_SUCCESS;
1590 }
1591 
hids_client_send_set_protocol_mode(uint16_t hids_cid,uint8_t service_index,hid_protocol_mode_t protocol_mode)1592 uint8_t hids_client_send_set_protocol_mode(uint16_t hids_cid, uint8_t service_index, hid_protocol_mode_t protocol_mode){
1593     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1594     if (client == NULL){
1595         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1596     }
1597 
1598     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1599         return ERROR_CODE_COMMAND_DISALLOWED;
1600     }
1601 
1602     if (service_index >= client->num_instances){
1603         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1604     }
1605 
1606     client->service_index = service_index;
1607     client->handle = client->services[client->service_index].protocol_mode_value_handle;
1608     client->value = (uint8_t)protocol_mode;
1609 
1610     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
1611     hids_run_for_client(client);
1612     return ERROR_CODE_SUCCESS;
1613 }
1614 
1615 
hids_client_send_control_point_cmd(uint16_t hids_cid,uint8_t service_index,uint8_t value)1616 static uint8_t hids_client_send_control_point_cmd(uint16_t hids_cid, uint8_t service_index, uint8_t value){
1617     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1618     if (client == NULL){
1619         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1620     }
1621 
1622     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1623         return ERROR_CODE_COMMAND_DISALLOWED;
1624     }
1625 
1626     if (service_index >= client->num_instances){
1627         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1628     }
1629 
1630     client->service_index = service_index;
1631     client->handle = client->services[client->service_index].control_point_value_handle;
1632     client->value = value;
1633 
1634     client->state = HIDS_CLIENT_W2_WRITE_VALUE_OF_CHARACTERISTIC_WITHOUT_RESPONSE;
1635     hids_run_for_client(client);
1636     return ERROR_CODE_SUCCESS;
1637 }
1638 
hids_client_send_suspend(uint16_t hids_cid,uint8_t service_index)1639 uint8_t hids_client_send_suspend(uint16_t hids_cid, uint8_t service_index){
1640     return hids_client_send_control_point_cmd(hids_cid, service_index, 0);
1641 }
1642 
hids_client_send_exit_suspend(uint16_t hids_cid,uint8_t service_index)1643 uint8_t hids_client_send_exit_suspend(uint16_t hids_cid, uint8_t service_index){
1644     return hids_client_send_control_point_cmd(hids_cid, service_index, 1);
1645 }
1646 
hids_client_enable_notifications(uint16_t hids_cid)1647 uint8_t hids_client_enable_notifications(uint16_t hids_cid){
1648      hids_client_t * client = hids_get_client_for_cid(hids_cid);
1649     if (client == NULL){
1650         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1651     }
1652 
1653     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1654         return ERROR_CODE_COMMAND_DISALLOWED;
1655     }
1656     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION;
1657     if (hids_client_notifications_configuration_init(client)){
1658         hids_run_for_client(client);
1659         return ERROR_CODE_SUCCESS;
1660     }
1661     hids_emit_notifications_configuration(client);
1662     return ERROR_CODE_SUCCESS;
1663 }
1664 
hids_client_disable_notifications(uint16_t hids_cid)1665 uint8_t hids_client_disable_notifications(uint16_t hids_cid){
1666          hids_client_t * client = hids_get_client_for_cid(hids_cid);
1667     if (client == NULL){
1668         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1669     }
1670 
1671     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1672         return ERROR_CODE_COMMAND_DISALLOWED;
1673     }
1674 
1675     client->value = GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE;
1676     if (hids_client_notifications_configuration_init(client)){
1677         hids_run_for_client(client);
1678         return ERROR_CODE_SUCCESS;
1679     }
1680     hids_emit_notifications_configuration(client);
1681     return ERROR_CODE_SUCCESS;
1682 }
1683 
hids_client_init(uint8_t * hid_descriptor_storage,uint16_t hid_descriptor_storage_len)1684 void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1685     hids_client_descriptor_storage = hid_descriptor_storage;
1686     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1687 
1688     hci_event_callback_registration.callback = &handle_hci_event;
1689     hci_add_event_handler(&hci_event_callback_registration);
1690 }
1691 
hids_client_deinit(void)1692 void hids_client_deinit(void){}
1693