xref: /btstack/src/ble/gatt-service/hids_client.c (revision da142a6fe2d1df30db7daaef1157daecdc8f4111)
1fc975d0eSMilanka Ringwald /*
2fc975d0eSMilanka Ringwald  * Copyright (C) 2021 BlueKitchen GmbH
3fc975d0eSMilanka Ringwald  *
4fc975d0eSMilanka Ringwald  * Redistribution and use in source and binary forms, with or without
5fc975d0eSMilanka Ringwald  * modification, are permitted provided that the following conditions
6fc975d0eSMilanka Ringwald  * are met:
7fc975d0eSMilanka Ringwald  *
8fc975d0eSMilanka Ringwald  * 1. Redistributions of source code must retain the above copyright
9fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer.
10fc975d0eSMilanka Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
11fc975d0eSMilanka Ringwald  *    notice, this list of conditions and the following disclaimer in the
12fc975d0eSMilanka Ringwald  *    documentation and/or other materials provided with the distribution.
13fc975d0eSMilanka Ringwald  * 3. Neither the name of the copyright holders nor the names of
14fc975d0eSMilanka Ringwald  *    contributors may be used to endorse or promote products derived
15fc975d0eSMilanka Ringwald  *    from this software without specific prior written permission.
16fc975d0eSMilanka Ringwald  * 4. Any redistribution, use, or modification is done solely for
17fc975d0eSMilanka Ringwald  *    personal benefit and not for any commercial purpose or for
18fc975d0eSMilanka Ringwald  *    monetary gain.
19fc975d0eSMilanka Ringwald  *
20fc975d0eSMilanka Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21fc975d0eSMilanka Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22fc975d0eSMilanka Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23fc975d0eSMilanka Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24fc975d0eSMilanka Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25fc975d0eSMilanka Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26fc975d0eSMilanka Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27fc975d0eSMilanka Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28fc975d0eSMilanka Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29fc975d0eSMilanka Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30fc975d0eSMilanka Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31fc975d0eSMilanka Ringwald  * SUCH DAMAGE.
32fc975d0eSMilanka Ringwald  *
33fc975d0eSMilanka Ringwald  * Please inquire about commercial licensing options at
34fc975d0eSMilanka Ringwald  * [email protected]
35fc975d0eSMilanka Ringwald  *
36fc975d0eSMilanka Ringwald  */
37fc975d0eSMilanka Ringwald 
38fc975d0eSMilanka Ringwald #define BTSTACK_FILE__ "hids_client.c"
39fc975d0eSMilanka Ringwald 
40fc975d0eSMilanka Ringwald #include "btstack_config.h"
41fc975d0eSMilanka Ringwald 
42fc975d0eSMilanka Ringwald #include <stdint.h>
43fc975d0eSMilanka Ringwald #include <string.h>
44fc975d0eSMilanka Ringwald 
45cf26c8fbSMilanka Ringwald #include "ble/gatt-service/hids_client.h"
46fc975d0eSMilanka Ringwald 
47cf26c8fbSMilanka Ringwald #include "btstack_memory.h"
48fc975d0eSMilanka Ringwald #include "ble/att_db.h"
49fc975d0eSMilanka Ringwald #include "ble/core.h"
50fc975d0eSMilanka Ringwald #include "ble/gatt_client.h"
51fc975d0eSMilanka Ringwald #include "ble/sm.h"
52cf26c8fbSMilanka Ringwald #include "bluetooth_gatt.h"
53fc975d0eSMilanka Ringwald #include "btstack_debug.h"
54fc975d0eSMilanka Ringwald #include "btstack_event.h"
55fc975d0eSMilanka Ringwald #include "btstack_run_loop.h"
56fc975d0eSMilanka Ringwald #include "gap.h"
57fc975d0eSMilanka Ringwald 
5870093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
5970093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6070093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6170093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6270093cf5SMilanka Ringwald 
63*da142a6fSMilanka Ringwald static btstack_linked_list_t clients;
64*da142a6fSMilanka Ringwald static uint16_t hids_cid_counter = 0;
65*da142a6fSMilanka Ringwald 
66*da142a6fSMilanka Ringwald static uint8_t * hids_client_descriptor_storage;
67*da142a6fSMilanka Ringwald static uint16_t  hids_client_descriptor_storage_len;
68*da142a6fSMilanka Ringwald 
69cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
70cf26c8fbSMilanka Ringwald 
71*da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
72*da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
73*da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
74*da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
75*da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
76*da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
77*da142a6fSMilanka Ringwald         return client;
78*da142a6fSMilanka Ringwald     }
79*da142a6fSMilanka Ringwald     return NULL;
80*da142a6fSMilanka Ringwald }
81*da142a6fSMilanka Ringwald 
82*da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
83*da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
84*da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
85*da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
86*da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
87*da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
88*da142a6fSMilanka Ringwald         return client;
89*da142a6fSMilanka Ringwald     }
90*da142a6fSMilanka Ringwald     return NULL;
91*da142a6fSMilanka Ringwald }
92*da142a6fSMilanka Ringwald 
93*da142a6fSMilanka Ringwald 
94*da142a6fSMilanka Ringwald // START Descriptor Storage Util
95*da142a6fSMilanka Ringwald 
96*da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
97*da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
98*da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
99*da142a6fSMilanka Ringwald     uint8_t i;
100*da142a6fSMilanka Ringwald 
101*da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
102*da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
103*da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
104*da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
105*da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
106*da142a6fSMilanka Ringwald             free_space -= client->services[i].hid_descriptor_len;
107*da142a6fSMilanka Ringwald         }
108*da142a6fSMilanka Ringwald     }
109*da142a6fSMilanka Ringwald     return free_space;
110*da142a6fSMilanka Ringwald }
111*da142a6fSMilanka Ringwald 
112*da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
113*da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
114*da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_max_len = hids_client_descriptor_storage_get_available_space();
115*da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - client->services[service_index].hid_descriptor_max_len;
116*da142a6fSMilanka Ringwald }
117*da142a6fSMilanka Ringwald 
118*da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
119*da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
120*da142a6fSMilanka Ringwald 
121*da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
122*da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
123*da142a6fSMilanka Ringwald     return true;
124*da142a6fSMilanka Ringwald }
125*da142a6fSMilanka Ringwald 
126*da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
127*da142a6fSMilanka Ringwald     uint8_t service_index = 0;
128*da142a6fSMilanka Ringwald     uint16_t next_offset = 0;
129*da142a6fSMilanka Ringwald 
130*da142a6fSMilanka Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
131*da142a6fSMilanka Ringwald         next_offset += client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len;
132*da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_len = 0;
133*da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_offset = 0;
134*da142a6fSMilanka Ringwald     }
135*da142a6fSMilanka Ringwald 
136*da142a6fSMilanka Ringwald     memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
137*da142a6fSMilanka Ringwald             &hids_client_descriptor_storage[next_offset],
138*da142a6fSMilanka Ringwald             hids_client_descriptor_storage_len - next_offset);
139*da142a6fSMilanka Ringwald 
140*da142a6fSMilanka Ringwald     uint8_t i;
141*da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
142*da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
143*da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
144*da142a6fSMilanka Ringwald         hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
145*da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
146*da142a6fSMilanka Ringwald             if (conn->services[i].hid_descriptor_offset >= next_offset){
147*da142a6fSMilanka Ringwald                 conn->services[i].hid_descriptor_offset = next_offset;
148*da142a6fSMilanka Ringwald                 next_offset += conn->services[service_index].hid_descriptor_len;
149*da142a6fSMilanka Ringwald             }
150*da142a6fSMilanka Ringwald         }
151*da142a6fSMilanka Ringwald     }
152*da142a6fSMilanka Ringwald }
153*da142a6fSMilanka Ringwald 
154*da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
155*da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
156*da142a6fSMilanka Ringwald     if (!client){
157*da142a6fSMilanka Ringwald         return NULL;
158*da142a6fSMilanka Ringwald     }
159*da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
160*da142a6fSMilanka Ringwald }
161*da142a6fSMilanka Ringwald 
162*da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
163*da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
164*da142a6fSMilanka Ringwald     if (!client){
165*da142a6fSMilanka Ringwald         return 0;
166*da142a6fSMilanka Ringwald     }
167*da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
168*da142a6fSMilanka Ringwald }
169*da142a6fSMilanka Ringwald 
170*da142a6fSMilanka Ringwald // END Descriptor Storage Util
171*da142a6fSMilanka Ringwald 
172cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
173cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
174cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
175cf26c8fbSMilanka Ringwald     } else {
176cf26c8fbSMilanka Ringwald         hids_cid_counter++;
177cf26c8fbSMilanka Ringwald     }
178cf26c8fbSMilanka Ringwald     return hids_cid_counter;
179fc975d0eSMilanka Ringwald }
180fc975d0eSMilanka Ringwald 
181ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
182ab116b1cSMilanka Ringwald     uint8_t i;
183a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
184ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
185ab116b1cSMilanka Ringwald             return i;
186ab116b1cSMilanka Ringwald         }
187ab116b1cSMilanka Ringwald     }
188ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
189ab116b1cSMilanka Ringwald }
190ab116b1cSMilanka Ringwald 
191ab116b1cSMilanka Ringwald 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){
192ab116b1cSMilanka Ringwald     uint8_t i;
193a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
194ab116b1cSMilanka Ringwald         if ( (client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
195ab116b1cSMilanka Ringwald             return i;
196ab116b1cSMilanka Ringwald         }
197ab116b1cSMilanka Ringwald     }
198ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
199ab116b1cSMilanka Ringwald }
200ab116b1cSMilanka Ringwald 
201ab116b1cSMilanka Ringwald 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){
202ab116b1cSMilanka Ringwald     uint8_t i;
203a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
204ab116b1cSMilanka Ringwald         if ( (client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
205ab116b1cSMilanka Ringwald             return &client->reports[i];
206ab116b1cSMilanka Ringwald         }
207ab116b1cSMilanka Ringwald     }
208ab116b1cSMilanka Ringwald     return NULL;
209ab116b1cSMilanka Ringwald }
210ab116b1cSMilanka Ringwald 
211ab116b1cSMilanka Ringwald static hids_client_report_t * get_boot_mouse_input_report(hids_client_t * client){
212ab116b1cSMilanka Ringwald     return find_report_for_report_id_and_type(client, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT);
213ab116b1cSMilanka Ringwald }
214ab116b1cSMilanka Ringwald static hids_client_report_t * get_boot_keyboard_input_report(hids_client_t * client){
215ab116b1cSMilanka Ringwald     return find_report_for_report_id_and_type(client, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT);
216ab116b1cSMilanka Ringwald }
217ab116b1cSMilanka Ringwald 
2183322b222SMilanka Ringwald 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){
219ab116b1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, characteristic->value_handle);
220ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
2213322b222SMilanka Ringwald         return report_index;
222ab116b1cSMilanka Ringwald     }
22328da36a6SMilanka Ringwald     report_index = client->num_reports;
224ab116b1cSMilanka Ringwald 
22528da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
22628da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
22728da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
22828da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
229ab116b1cSMilanka Ringwald 
23028da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
23128da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
23228da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
23370093cf5SMilanka Ringwald 
2343322b222SMilanka Ringwald         // log_info("add index %d, id %d, type %d, value handle 0x%02x", report_index, report_id, report_type, characteristic->value_handle);
23528da36a6SMilanka Ringwald         log_info("add index %d, id %d, type %d, value handle 0x%02x", report_index, report_id, report_type, characteristic->value_handle);
236ab116b1cSMilanka Ringwald         client->num_reports++;
2373322b222SMilanka Ringwald         return report_index;
238ab116b1cSMilanka Ringwald     } else {
239ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
2403322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
241ab116b1cSMilanka Ringwald     }
242ab116b1cSMilanka Ringwald }
243ab116b1cSMilanka Ringwald 
24470093cf5SMilanka Ringwald static void hids_client_get_characteristic_for_report_index(hids_client_t * client, uint8_t report_index, gatt_client_characteristic_t * characteristic){
24570093cf5SMilanka Ringwald     characteristic->value_handle = client->reports[report_index].value_handle;
24670093cf5SMilanka Ringwald     characteristic->end_handle = client->reports[report_index].end_handle;
24770093cf5SMilanka Ringwald     characteristic->properties = client->reports[report_index].properties;
24870093cf5SMilanka Ringwald }
24970093cf5SMilanka Ringwald 
250ab116b1cSMilanka Ringwald 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){
251ab116b1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, report_type);
252ab116b1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
253ab116b1cSMilanka Ringwald         return report_index;
254ab116b1cSMilanka Ringwald     }
25570093cf5SMilanka Ringwald     hids_client_get_characteristic_for_report_index(client, report_index, characteristic);
256ab116b1cSMilanka Ringwald     return report_index;
257ab116b1cSMilanka Ringwald }
258ab116b1cSMilanka Ringwald 
2597e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
2607e1e6e7dSMilanka Ringwald     uint8_t i;
2617e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
2623322b222SMilanka Ringwald 
2633322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
2643322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
2653322b222SMilanka Ringwald             index = i;
2663322b222SMilanka Ringwald             break;
2673322b222SMilanka Ringwald         }
2683322b222SMilanka Ringwald     }
2693322b222SMilanka Ringwald     client->service_index = index;
2703322b222SMilanka Ringwald     return index;
2713322b222SMilanka Ringwald }
2723322b222SMilanka Ringwald 
2733322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
2743322b222SMilanka Ringwald     client->service_index++;
2753322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
2763322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
2773322b222SMilanka Ringwald         return true;
2783322b222SMilanka Ringwald     }
2793322b222SMilanka Ringwald     return false;
2803322b222SMilanka Ringwald }
2813322b222SMilanka Ringwald 
2823322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
2833322b222SMilanka Ringwald     client->service_index = 0;
2843322b222SMilanka Ringwald 
2853322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
2863322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
2873322b222SMilanka Ringwald         return true;
2883322b222SMilanka Ringwald     }
2893322b222SMilanka Ringwald     return false;
2903322b222SMilanka Ringwald }
2913322b222SMilanka Ringwald 
2923322b222SMilanka Ringwald 
2933322b222SMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_uuid_index(hids_client_t * client){
2943322b222SMilanka Ringwald     uint8_t i;
2953322b222SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
2963322b222SMilanka Ringwald 
297ef102a9eSMilanka Ringwald     for (i = client->active_index; i < client->num_reports; i++){
298e7bd2dbeSMilanka Ringwald         hids_client_report_t report = client->reports[i];
299e7bd2dbeSMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_MAP_ID){
3007e1e6e7dSMilanka Ringwald             index = i;
3017e1e6e7dSMilanka Ringwald             break;
302e7bd2dbeSMilanka Ringwald         }
3037e1e6e7dSMilanka Ringwald     }
3047e1e6e7dSMilanka Ringwald     client->active_index = index;
3057e1e6e7dSMilanka Ringwald     return index;
3067e1e6e7dSMilanka Ringwald }
3077e1e6e7dSMilanka Ringwald 
3083322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
3097e1e6e7dSMilanka Ringwald     client->active_index++;
3103322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_uuid_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3113322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
312e7bd2dbeSMilanka Ringwald         return true;
313e7bd2dbeSMilanka Ringwald     }
314e7bd2dbeSMilanka Ringwald     return false;
315e7bd2dbeSMilanka Ringwald }
316e7bd2dbeSMilanka Ringwald 
3173322b222SMilanka Ringwald 
3183322b222SMilanka Ringwald 
3193322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
3207e1e6e7dSMilanka Ringwald     client->active_index = 0;
32170093cf5SMilanka Ringwald 
3223322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_uuid_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3233322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
3247e1e6e7dSMilanka Ringwald         return true;
3257e1e6e7dSMilanka Ringwald     }
3267e1e6e7dSMilanka Ringwald     return false;
3277e1e6e7dSMilanka Ringwald }
3287e1e6e7dSMilanka Ringwald 
3297e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
3307e1e6e7dSMilanka Ringwald     uint8_t i;
3317e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
332ef102a9eSMilanka Ringwald     for (i = client->active_index; i < client->num_reports; i++){
33370093cf5SMilanka Ringwald         hids_client_report_t report = client->reports[i];
33470093cf5SMilanka Ringwald         if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
3357e1e6e7dSMilanka Ringwald             index = i;
3367e1e6e7dSMilanka Ringwald             break;
33770093cf5SMilanka Ringwald         }
3387e1e6e7dSMilanka Ringwald     }
3397e1e6e7dSMilanka Ringwald     client->active_index = index;
3407e1e6e7dSMilanka Ringwald     return index;
3417e1e6e7dSMilanka Ringwald }
3427e1e6e7dSMilanka Ringwald 
3437e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
3447e1e6e7dSMilanka Ringwald     client->active_index++;
3457e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
34628da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
347e7bd2dbeSMilanka Ringwald         return true;
34870093cf5SMilanka Ringwald     }
3497e1e6e7dSMilanka Ringwald     return false;
3507e1e6e7dSMilanka Ringwald }
3517e1e6e7dSMilanka Ringwald 
3527e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
3537e1e6e7dSMilanka Ringwald     client->service_index = 0;
3547e1e6e7dSMilanka Ringwald     client->active_index = 0;
3557e1e6e7dSMilanka Ringwald 
3567e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
35728da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
3587e1e6e7dSMilanka Ringwald         return true;
3597e1e6e7dSMilanka Ringwald     }
360e7bd2dbeSMilanka Ringwald     return false;
36170093cf5SMilanka Ringwald }
362ab116b1cSMilanka Ringwald 
363cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
364cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
365cf26c8fbSMilanka Ringwald     if (!client){
366cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
367cf26c8fbSMilanka Ringwald         return NULL;
368cf26c8fbSMilanka Ringwald     }
369cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
370cf26c8fbSMilanka Ringwald     client->cid = cid;
371cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
372fc975d0eSMilanka Ringwald 
373cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
374cf26c8fbSMilanka Ringwald     return client;
375fc975d0eSMilanka Ringwald }
376fc975d0eSMilanka Ringwald 
377cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
378*da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
379cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
380cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
381fc975d0eSMilanka Ringwald }
382cf26c8fbSMilanka Ringwald 
383cf26c8fbSMilanka Ringwald 
384cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
3856bcfb631SMilanka Ringwald     uint8_t event[8];
386cf26c8fbSMilanka Ringwald     int pos = 0;
387cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
388cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
389cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
390cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
391cf26c8fbSMilanka Ringwald     pos += 2;
392cf26c8fbSMilanka Ringwald     event[pos++] = status;
3936bcfb631SMilanka Ringwald     event[pos++] = client->protocol_mode;
394cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
395cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
396cf26c8fbSMilanka Ringwald }
397cf26c8fbSMilanka Ringwald 
398cf26c8fbSMilanka Ringwald 
399cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
400cf26c8fbSMilanka Ringwald     uint8_t att_status;
4016bcfb631SMilanka Ringwald     gatt_client_service_t service;
4026bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
403ab116b1cSMilanka Ringwald     uint8_t report_index;
404cf26c8fbSMilanka Ringwald 
405cf26c8fbSMilanka Ringwald     switch (client->state){
406cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
407cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
408cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
409cf26c8fbSMilanka Ringwald             // TODO handle status
410cf26c8fbSMilanka Ringwald             UNUSED(att_status);
411cf26c8fbSMilanka Ringwald             break;
412cf26c8fbSMilanka Ringwald 
413cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
4146bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
4156bcfb631SMilanka Ringwald 
4166bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
4176bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
4186bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
4196bcfb631SMilanka Ringwald 
4206bcfb631SMilanka Ringwald             UNUSED(att_status);
4216bcfb631SMilanka Ringwald             break;
4226bcfb631SMilanka Ringwald 
4232901a9b7SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD:
4242901a9b7SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED;
4252901a9b7SMilanka Ringwald 
426ab116b1cSMilanka Ringwald             report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, &characteristic);
427ab116b1cSMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
428ab116b1cSMilanka Ringwald                 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
429ab116b1cSMilanka Ringwald             }
430ab116b1cSMilanka Ringwald 
431ab116b1cSMilanka Ringwald             if ((report_index == HIDS_CLIENT_INVALID_REPORT_INDEX) || (att_status != ERROR_CODE_SUCCESS)){
432ab116b1cSMilanka Ringwald                 client->reports[report_index].value_handle = 0;
433ab116b1cSMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
4342901a9b7SMilanka Ringwald             }
4356bcfb631SMilanka Ringwald             break;
4366bcfb631SMilanka Ringwald 
4372901a9b7SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_MOUSE:
4382901a9b7SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_MOUSE_ENABLED;
4392901a9b7SMilanka Ringwald 
440ab116b1cSMilanka Ringwald             report_index = hids_client_get_characteristic(client, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, &characteristic);
441ab116b1cSMilanka Ringwald 
442ab116b1cSMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
443ab116b1cSMilanka Ringwald                 att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
4442901a9b7SMilanka Ringwald             }
445ab116b1cSMilanka Ringwald 
446ab116b1cSMilanka Ringwald             if ((report_index == HIDS_CLIENT_INVALID_REPORT_INDEX) || (att_status != ERROR_CODE_SUCCESS)){
447ab116b1cSMilanka Ringwald                 client->reports[report_index].value_handle = 0;
448ab116b1cSMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
449ab116b1cSMilanka Ringwald             }
450ab116b1cSMilanka Ringwald 
4516bcfb631SMilanka Ringwald             break;
4526bcfb631SMilanka Ringwald 
4532901a9b7SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE:
4542901a9b7SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SET_PROTOCOL_MODE;
4552901a9b7SMilanka Ringwald             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);
4566bcfb631SMilanka Ringwald             UNUSED(att_status);
4572901a9b7SMilanka Ringwald 
4582901a9b7SMilanka Ringwald             client->protocol_mode = client->required_protocol_mode;
4596bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
4606bcfb631SMilanka Ringwald             hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
461cf26c8fbSMilanka Ringwald             break;
462cf26c8fbSMilanka Ringwald 
4631624214bSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_REPORT:{
4641624214bSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
4651624214bSMilanka Ringwald 
4661624214bSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
467ef102a9eSMilanka Ringwald                 client->reports[client->active_index].value_handle,
4681624214bSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
4691624214bSMilanka Ringwald             UNUSED(att_status);
4701624214bSMilanka Ringwald             break;
4711624214bSMilanka Ringwald         }
47270093cf5SMilanka Ringwald 
47328da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
47428da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
475*da142a6fSMilanka Ringwald             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);
476e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
477e7bd2dbeSMilanka Ringwald             break;
478e7bd2dbeSMilanka Ringwald 
479e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
480e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
481e7bd2dbeSMilanka Ringwald 
4823322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
4833322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
4843322b222SMilanka Ringwald 
485e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
486e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
487e7bd2dbeSMilanka Ringwald             break;
488e7bd2dbeSMilanka Ringwald 
48928da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
49028da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
491e7bd2dbeSMilanka Ringwald 
4923322b222SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->reports[client->active_index].value_handle);
493e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
494e7bd2dbeSMilanka Ringwald             break;
495e7bd2dbeSMilanka Ringwald 
4963322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
4973322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
498e7bd2dbeSMilanka Ringwald 
4993322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
5003322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
5013322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
502e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
503e7bd2dbeSMilanka Ringwald             break;
504e7bd2dbeSMilanka Ringwald 
50528da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
50628da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
50770093cf5SMilanka Ringwald             client->descriptor_handle = 0;
508ef102a9eSMilanka Ringwald             hids_client_get_characteristic_for_report_index(client, client->active_index, &characteristic);
50970093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
51070093cf5SMilanka Ringwald             UNUSED(att_status);
51170093cf5SMilanka Ringwald             break;
51270093cf5SMilanka Ringwald 
51328da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
51428da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
51570093cf5SMilanka Ringwald 
51670093cf5SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
51770093cf5SMilanka Ringwald             client->descriptor_handle = 0;
51870093cf5SMilanka Ringwald             UNUSED(att_status);
51970093cf5SMilanka Ringwald             break;
52070093cf5SMilanka Ringwald 
5216bcfb631SMilanka Ringwald         default:
5226bcfb631SMilanka Ringwald             break;
5236bcfb631SMilanka Ringwald     }
5246bcfb631SMilanka Ringwald }
5256bcfb631SMilanka Ringwald 
52672a8858fSMilanka Ringwald static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_id, uint8_t *buffer, uint16_t report_len){
52772a8858fSMilanka Ringwald     uint16_t pos = 0;
52872a8858fSMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
52972a8858fSMilanka Ringwald     pos++;  // skip len
53072a8858fSMilanka Ringwald     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
53172a8858fSMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
53272a8858fSMilanka Ringwald     pos += 2;
53372a8858fSMilanka Ringwald     buffer[pos++] = report_id;
53472a8858fSMilanka Ringwald     little_endian_store_16(buffer, pos, report_len);
53572a8858fSMilanka Ringwald     pos += 2;
53672a8858fSMilanka Ringwald     buffer[1] = pos + report_len - 2;
53772a8858fSMilanka Ringwald }
53872a8858fSMilanka Ringwald 
53972a8858fSMilanka Ringwald static void handle_boot_keyboard_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
5406bcfb631SMilanka Ringwald     UNUSED(packet_type);
5416bcfb631SMilanka Ringwald     UNUSED(channel);
5426bcfb631SMilanka Ringwald     UNUSED(size);
54372a8858fSMilanka Ringwald 
54472a8858fSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
54572a8858fSMilanka Ringwald 
54672a8858fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
54772a8858fSMilanka Ringwald     btstack_assert(client != NULL);
54872a8858fSMilanka Ringwald 
54972a8858fSMilanka Ringwald     uint8_t * in_place_event = packet;
55072a8858fSMilanka Ringwald     hids_client_setup_report_event(client, HID_BOOT_MODE_KEYBOARD_ID, in_place_event, gatt_event_notification_get_value_length(packet));
55172a8858fSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size);
55272a8858fSMilanka Ringwald }
55372a8858fSMilanka Ringwald 
55472a8858fSMilanka Ringwald static void handle_boot_mouse_hid_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
55572a8858fSMilanka Ringwald     UNUSED(packet_type);
55672a8858fSMilanka Ringwald     UNUSED(channel);
55772a8858fSMilanka Ringwald     UNUSED(size);
55872a8858fSMilanka Ringwald 
55972a8858fSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
56072a8858fSMilanka Ringwald 
56172a8858fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
56272a8858fSMilanka Ringwald     btstack_assert(client != NULL);
56372a8858fSMilanka Ringwald 
56472a8858fSMilanka Ringwald     uint8_t * in_place_event = packet;
56572a8858fSMilanka Ringwald     hids_client_setup_report_event(client, HID_BOOT_MODE_MOUSE_ID, in_place_event, gatt_event_notification_get_value_length(packet));
56672a8858fSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, client->cid, in_place_event, size);
567cf26c8fbSMilanka Ringwald }
568cf26c8fbSMilanka Ringwald 
5691624214bSMilanka Ringwald 
570cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
571cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
572cf26c8fbSMilanka Ringwald     UNUSED(channel);
573cf26c8fbSMilanka Ringwald     UNUSED(size);
574cf26c8fbSMilanka Ringwald 
5756bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
576cf26c8fbSMilanka Ringwald     uint8_t att_status;
5776bcfb631SMilanka Ringwald     gatt_client_service_t service;
5786bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
57970093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
580ab116b1cSMilanka Ringwald 
581ab116b1cSMilanka Ringwald     hids_client_report_t * boot_keyboard_report;
582ab116b1cSMilanka Ringwald     hids_client_report_t * boot_mouse_report;
583e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
5843322b222SMilanka Ringwald     uint8_t i;
5853322b222SMilanka Ringwald     uint8_t report_index;
586*da142a6fSMilanka Ringwald     uint8_t next_service_index;
587*da142a6fSMilanka Ringwald 
588*da142a6fSMilanka Ringwald     const uint8_t * descriptor;
589*da142a6fSMilanka Ringwald     uint16_t descriptor_len;
590cf26c8fbSMilanka Ringwald 
591cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
592cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
593cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
594cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
595cf26c8fbSMilanka Ringwald 
5966bcfb631SMilanka Ringwald             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
5976bcfb631SMilanka Ringwald                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
5986bcfb631SMilanka Ringwald                 hids_finalize_client(client);
5996bcfb631SMilanka Ringwald                 break;
6006bcfb631SMilanka Ringwald             }
6016bcfb631SMilanka Ringwald 
602*da142a6fSMilanka Ringwald             next_service_index = client->num_instances;
603*da142a6fSMilanka Ringwald             if (next_service_index < MAX_NUM_HID_SERVICES){
6046bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
605*da142a6fSMilanka Ringwald                 client->services[next_service_index].start_handle = service.start_group_handle;
606*da142a6fSMilanka Ringwald                 client->services[next_service_index].end_handle = service.end_group_handle;
607*da142a6fSMilanka Ringwald 
608*da142a6fSMilanka Ringwald                 hids_client_descriptor_storage_init(client, next_service_index);
6096bcfb631SMilanka Ringwald                 client->num_instances++;
610*da142a6fSMilanka Ringwald             }
6116bcfb631SMilanka Ringwald             break;
6126bcfb631SMilanka Ringwald 
6136bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
6146bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
6156bcfb631SMilanka Ringwald             btstack_assert(client != NULL);
6166bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
6173322b222SMilanka Ringwald 
6183322b222SMilanka Ringwald             switch (client->state){
6193322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
6203322b222SMilanka Ringwald                     // update value handle od external reports, set type to reserved so that will be inlcuded in ID,TYPE update in hte next step
6213322b222SMilanka Ringwald                     for (i = 0; i < client->num_reports; i++){
6223322b222SMilanka Ringwald                         if (client->reports[i].external_report_reference_uuid == characteristic.uuid16){
6233322b222SMilanka Ringwald                             client->reports[i].report_id = HID_REPORT_MODE_REPORT_ID;
6243322b222SMilanka Ringwald                             client->reports[i].report_type = HID_REPORT_TYPE_RESERVED;
6253322b222SMilanka Ringwald                             client->reports[i].properties = characteristic.properties;
6263322b222SMilanka Ringwald                             client->reports[i].value_handle = characteristic.value_handle;
6273322b222SMilanka Ringwald                             client->reports[i].end_handle = characteristic.end_handle;
6283322b222SMilanka Ringwald                         }
6293322b222SMilanka Ringwald                     }
6303322b222SMilanka Ringwald                     break;
6313322b222SMilanka Ringwald 
6323322b222SMilanka Ringwald                 default:
6336bcfb631SMilanka Ringwald                     switch (characteristic.uuid16){
63470093cf5SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
63570093cf5SMilanka Ringwald                             client->protocol_mode_value_handle = characteristic.value_handle;
63670093cf5SMilanka Ringwald                             break;
63770093cf5SMilanka Ringwald 
6386bcfb631SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
639ab116b1cSMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT);
6406bcfb631SMilanka Ringwald                             break;
6411624214bSMilanka Ringwald 
6426bcfb631SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
643ab116b1cSMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT);
6446bcfb631SMilanka Ringwald                             break;
6451624214bSMilanka Ringwald 
6461624214bSMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
647ab116b1cSMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT);
6481624214bSMilanka Ringwald                             break;
6491624214bSMilanka Ringwald 
65070093cf5SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
65170093cf5SMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED);
65270093cf5SMilanka Ringwald                             break;
65370093cf5SMilanka Ringwald 
65470093cf5SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
6553322b222SMilanka Ringwald                             client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
6563322b222SMilanka Ringwald                             client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
65770093cf5SMilanka Ringwald                             break;
65870093cf5SMilanka Ringwald 
65970093cf5SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
66070093cf5SMilanka Ringwald                             // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION\n");
66170093cf5SMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_INFORMATION_ID, HID_REPORT_TYPE_INPUT);
66270093cf5SMilanka Ringwald                             break;
66370093cf5SMilanka Ringwald 
66470093cf5SMilanka Ringwald                         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
66570093cf5SMilanka Ringwald                             // printf("ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT\n");
66670093cf5SMilanka Ringwald                             hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_HID_CONTROL_POINT_ID, HID_REPORT_TYPE_INPUT);
66770093cf5SMilanka Ringwald                             break;
6686bcfb631SMilanka Ringwald                         default:
6696bcfb631SMilanka Ringwald                             break;
6706bcfb631SMilanka Ringwald                 }
6713322b222SMilanka Ringwald             }
6723322b222SMilanka Ringwald 
673cf26c8fbSMilanka Ringwald             break;
674cf26c8fbSMilanka Ringwald 
675e7bd2dbeSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
676e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
677e7bd2dbeSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
678e7bd2dbeSMilanka Ringwald             btstack_assert(client != NULL);
679*da142a6fSMilanka Ringwald 
6803322b222SMilanka Ringwald             // printf("HID descriptor found\n");
681*da142a6fSMilanka Ringwald             descriptor_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
682*da142a6fSMilanka Ringwald             descriptor = gatt_event_characteristic_value_query_result_get_value(packet);
683*da142a6fSMilanka Ringwald 
684*da142a6fSMilanka Ringwald             for (i = 0; i < descriptor_len; i++){
685*da142a6fSMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, descriptor[i]);
686*da142a6fSMilanka Ringwald                 if (!stored){
687*da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
688*da142a6fSMilanka Ringwald                     break;
689*da142a6fSMilanka Ringwald                 }
690*da142a6fSMilanka Ringwald             }
691e7bd2dbeSMilanka Ringwald             break;
692e7bd2dbeSMilanka Ringwald 
69370093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
69470093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
69570093cf5SMilanka Ringwald             btstack_assert(client != NULL);
69670093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
69770093cf5SMilanka Ringwald 
698e7bd2dbeSMilanka Ringwald             switch (client->state) {
699e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
700e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
701e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
7023322b222SMilanka Ringwald                         report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_MAP_ID, HID_REPORT_TYPE_RESERVED);
7033322b222SMilanka Ringwald 
7043322b222SMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
7053322b222SMilanka Ringwald                             client->reports[report_index].value_handle = characteristic_descriptor.handle;
7063322b222SMilanka Ringwald                             client->reports[report_index].service_index = client->service_index;
7073322b222SMilanka Ringwald                         }
7083322b222SMilanka Ringwald 
709e7bd2dbeSMilanka Ringwald                     }
710e7bd2dbeSMilanka Ringwald                     break;
71128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
71270093cf5SMilanka Ringwald                     // setup for descriptor value query
71370093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
71470093cf5SMilanka Ringwald                         client->descriptor_handle = characteristic_descriptor.handle;
71570093cf5SMilanka Ringwald                     }
71670093cf5SMilanka Ringwald                     break;
717e7bd2dbeSMilanka Ringwald                 default:
718e7bd2dbeSMilanka Ringwald                     break;
719e7bd2dbeSMilanka Ringwald             }
720e7bd2dbeSMilanka Ringwald             break;
72170093cf5SMilanka Ringwald 
72270093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
72370093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
72470093cf5SMilanka Ringwald             btstack_assert(client != NULL);
725e7bd2dbeSMilanka Ringwald 
726e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
727e7bd2dbeSMilanka Ringwald                 break;
728e7bd2dbeSMilanka Ringwald             }
7297e1e6e7dSMilanka Ringwald 
730e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
731e7bd2dbeSMilanka Ringwald             switch (client->state) {
73228da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
733e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
7343322b222SMilanka Ringwald                     report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
7353322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
7363322b222SMilanka Ringwald                         client->reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
7373322b222SMilanka Ringwald                     }
738e7bd2dbeSMilanka Ringwald                     break;
739e7bd2dbeSMilanka Ringwald 
74028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
741ef102a9eSMilanka Ringwald                     client->reports[client->active_index].report_id = characteristic_descriptor_value[0];
742ef102a9eSMilanka Ringwald                     client->reports[client->active_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
74328da36a6SMilanka Ringwald                     log_info("update index %d, id %d, type %d, value handle 0x%02x",
74428da36a6SMilanka Ringwald                         client->active_index,
74528da36a6SMilanka Ringwald                         client->reports[client->active_index].report_id,
74628da36a6SMilanka Ringwald                         client->reports[client->active_index].report_type,
74728da36a6SMilanka Ringwald                         client->reports[client->active_index].value_handle);
7487e1e6e7dSMilanka Ringwald 
749e7bd2dbeSMilanka Ringwald                     break;
750e7bd2dbeSMilanka Ringwald 
751e7bd2dbeSMilanka Ringwald                 default:
752e7bd2dbeSMilanka Ringwald                     break;
75370093cf5SMilanka Ringwald             }
75470093cf5SMilanka Ringwald             break;
75570093cf5SMilanka Ringwald 
756cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
757cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
758cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
759cf26c8fbSMilanka Ringwald 
760cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
761cf26c8fbSMilanka Ringwald 
762cf26c8fbSMilanka Ringwald             switch (client->state){
763cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
7646bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
7656bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
7666bcfb631SMilanka Ringwald                         hids_finalize_client(client);
767cf26c8fbSMilanka Ringwald                         break;
7686bcfb631SMilanka Ringwald                     }
7696bcfb631SMilanka Ringwald 
7706bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
7716bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
7726bcfb631SMilanka Ringwald                         hids_finalize_client(client);
7736bcfb631SMilanka Ringwald                         break;
7746bcfb631SMilanka Ringwald                     }
7756bcfb631SMilanka Ringwald 
7766bcfb631SMilanka Ringwald                     if (client->num_instances > MAX_NUM_HID_SERVICES) {
7776bcfb631SMilanka Ringwald                         log_info("%d hid services found, only first %d can be stored, increase MAX_NUM_HID_SERVICES", client->num_instances, MAX_NUM_HID_SERVICES);
7786bcfb631SMilanka Ringwald                         client->num_instances = MAX_NUM_HID_SERVICES;
7796bcfb631SMilanka Ringwald                     }
7806bcfb631SMilanka Ringwald 
7816bcfb631SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
7826bcfb631SMilanka Ringwald                     client->service_index = 0;
7836bcfb631SMilanka Ringwald                     break;
7846bcfb631SMilanka Ringwald 
7856bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
7866bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
7876bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
7886bcfb631SMilanka Ringwald                         hids_finalize_client(client);
7896bcfb631SMilanka Ringwald                         break;
7906bcfb631SMilanka Ringwald                     }
7916bcfb631SMilanka Ringwald 
7926bcfb631SMilanka Ringwald                     switch (client->required_protocol_mode){
7936bcfb631SMilanka Ringwald                         case HID_PROTOCOL_MODE_BOOT:
794ab116b1cSMilanka Ringwald                             if (get_boot_keyboard_input_report(client) != NULL){
7952901a9b7SMilanka Ringwald                                 client->state = HIDS_CLIENT_STATE_W2_ENABLE_KEYBOARD;
7966bcfb631SMilanka Ringwald                                 break;
7976bcfb631SMilanka Ringwald                             }
798ab116b1cSMilanka Ringwald                             if (get_boot_mouse_input_report(client) != NULL){
7992901a9b7SMilanka Ringwald                                 client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE;
8006bcfb631SMilanka Ringwald                                 break;
8016bcfb631SMilanka Ringwald                             }
8026bcfb631SMilanka Ringwald                             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
8036bcfb631SMilanka Ringwald                             hids_finalize_client(client);
8046bcfb631SMilanka Ringwald                             break;
8057e1e6e7dSMilanka Ringwald 
80670093cf5SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
80770093cf5SMilanka Ringwald                             if ((client->service_index + 1) < client->num_instances){
80870093cf5SMilanka Ringwald                                 // discover characteristics of next service
80970093cf5SMilanka Ringwald                                 client->service_index++;
81070093cf5SMilanka Ringwald                                 client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
81170093cf5SMilanka Ringwald                             } else {
8123322b222SMilanka Ringwald 
8137e1e6e7dSMilanka Ringwald                                 // 1. we need to get HID Descriptor and
8147e1e6e7dSMilanka Ringwald                                 // 2. get external Report characteristics if referenced from Report Map
8157e1e6e7dSMilanka Ringwald                                 if (hids_client_report_map_query_init(client)){
81670093cf5SMilanka Ringwald                                     break;
81770093cf5SMilanka Ringwald                                 }
81870093cf5SMilanka Ringwald                                 hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
81970093cf5SMilanka Ringwald                                 hids_finalize_client(client);
82070093cf5SMilanka Ringwald                             }
82170093cf5SMilanka Ringwald                             break;
8226bcfb631SMilanka Ringwald                         default:
8236bcfb631SMilanka Ringwald                             break;
8246bcfb631SMilanka Ringwald                     }
8256bcfb631SMilanka Ringwald                     break;
8266bcfb631SMilanka Ringwald 
8273322b222SMilanka Ringwald 
82828da36a6SMilanka Ringwald                 // HID descriptor found
82928da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
830e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
831e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
832e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
833e7bd2dbeSMilanka Ringwald                         break;
834e7bd2dbeSMilanka Ringwald                     }
835e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
836e7bd2dbeSMilanka Ringwald                     break;
837e7bd2dbeSMilanka Ringwald 
83828da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
839e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
8403322b222SMilanka Ringwald                     // go for next report map
8413322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
842e7bd2dbeSMilanka Ringwald                         break;
843e7bd2dbeSMilanka Ringwald                     }
844e7bd2dbeSMilanka Ringwald 
8453322b222SMilanka Ringwald                     // read UUIDS for external characteristics
8463322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
847e7bd2dbeSMilanka Ringwald                         break;
848e7bd2dbeSMilanka Ringwald                     }
849e7bd2dbeSMilanka Ringwald 
850e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
851e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
8527e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
853e7bd2dbeSMilanka Ringwald                         break;
854e7bd2dbeSMilanka Ringwald                     }
855e7bd2dbeSMilanka Ringwald 
856e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
857e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
858e7bd2dbeSMilanka Ringwald                     break;
859e7bd2dbeSMilanka Ringwald 
86028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
861e7bd2dbeSMilanka Ringwald                     // go for next map report
8623322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
863e7bd2dbeSMilanka Ringwald                         break;
864e7bd2dbeSMilanka Ringwald                     }
865e7bd2dbeSMilanka Ringwald 
8663322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
8673322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
868e7bd2dbeSMilanka Ringwald                     break;
869e7bd2dbeSMilanka Ringwald 
8703322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
871e7bd2dbeSMilanka Ringwald 
872e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
873e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
8747e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
875e7bd2dbeSMilanka Ringwald                         break;
876e7bd2dbeSMilanka Ringwald                     }
877e7bd2dbeSMilanka Ringwald 
878e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
879e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
880e7bd2dbeSMilanka Ringwald                     break;
881e7bd2dbeSMilanka Ringwald 
88228da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
88370093cf5SMilanka Ringwald                     if (client->descriptor_handle != 0){
88470093cf5SMilanka Ringwald                         // descriptor found
88528da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
88670093cf5SMilanka Ringwald                         break;
88770093cf5SMilanka Ringwald                     }
88870093cf5SMilanka Ringwald 
88970093cf5SMilanka Ringwald                     // go for next report
8907e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
89170093cf5SMilanka Ringwald                         break;
89270093cf5SMilanka Ringwald                     }
89370093cf5SMilanka Ringwald 
89470093cf5SMilanka Ringwald                     // TODO continue with report mode
89570093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
89670093cf5SMilanka Ringwald                     break;
89770093cf5SMilanka Ringwald 
89828da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
89970093cf5SMilanka Ringwald                     // go for next report
9007e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
90170093cf5SMilanka Ringwald                         break;
90270093cf5SMilanka Ringwald                     }
90370093cf5SMilanka Ringwald 
90470093cf5SMilanka Ringwald                     // TODO continue with report mode
90570093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
90670093cf5SMilanka Ringwald                     break;
90770093cf5SMilanka Ringwald 
9086bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_KEYBOARD_ENABLED:
909ab116b1cSMilanka Ringwald                     boot_keyboard_report = get_boot_keyboard_input_report(client);
910ab116b1cSMilanka Ringwald                     if (boot_keyboard_report != NULL){
9116bcfb631SMilanka Ringwald                         // setup listener
912ab116b1cSMilanka Ringwald                         characteristic.value_handle = boot_keyboard_report->value_handle;
913ab116b1cSMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(&boot_keyboard_report->notifications, &handle_boot_keyboard_hid_event, client->con_handle, &characteristic);
9142901a9b7SMilanka Ringwald                     }
9152901a9b7SMilanka Ringwald 
916ab116b1cSMilanka Ringwald                     // check if there is mouse input report
917ab116b1cSMilanka Ringwald                     boot_mouse_report = get_boot_mouse_input_report(client);
918ab116b1cSMilanka Ringwald                     if (boot_mouse_report != NULL){
9192901a9b7SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_ENABLE_MOUSE;
9202901a9b7SMilanka Ringwald                         break;
9212901a9b7SMilanka Ringwald                     }
9222901a9b7SMilanka Ringwald 
923ab116b1cSMilanka Ringwald                     // if none of the reports is found bail out
924ab116b1cSMilanka Ringwald                     if (!boot_mouse_report && !boot_keyboard_report){
925ab116b1cSMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
926ab116b1cSMilanka Ringwald                         hids_finalize_client(client);
927ab116b1cSMilanka Ringwald                         break;
928ab116b1cSMilanka Ringwald                     }
929ab116b1cSMilanka Ringwald 
9306bcfb631SMilanka Ringwald                     // set protocol
9316bcfb631SMilanka Ringwald                     if (client->protocol_mode_value_handle != 0){
9322901a9b7SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE;
9336bcfb631SMilanka Ringwald                     } else {
9342901a9b7SMilanka Ringwald                         client->protocol_mode = HID_PROTOCOL_MODE_BOOT;
9356bcfb631SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
9366bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
9376bcfb631SMilanka Ringwald                     }
9382901a9b7SMilanka Ringwald                     hids_run_for_client(client);
9396bcfb631SMilanka Ringwald                     break;
9406bcfb631SMilanka Ringwald 
9416bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_MOUSE_ENABLED:
942ab116b1cSMilanka Ringwald                     boot_mouse_report = get_boot_mouse_input_report(client);
943ab116b1cSMilanka Ringwald                     if (boot_mouse_report != NULL){
9446bcfb631SMilanka Ringwald                         // setup listener
945ab116b1cSMilanka Ringwald                         characteristic.value_handle = boot_mouse_report->value_handle;
946ab116b1cSMilanka Ringwald                         gatt_client_listen_for_characteristic_value_updates(&boot_mouse_report->notifications, &handle_boot_mouse_hid_event, client->con_handle, &characteristic);
947ab116b1cSMilanka Ringwald                     }
948ab116b1cSMilanka Ringwald 
949ab116b1cSMilanka Ringwald                     boot_keyboard_report = get_boot_keyboard_input_report(client);
950ab116b1cSMilanka Ringwald                     if (!boot_mouse_report && !boot_keyboard_report){
9512901a9b7SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
9522901a9b7SMilanka Ringwald                         hids_finalize_client(client);
9532901a9b7SMilanka Ringwald                         break;
9542901a9b7SMilanka Ringwald                     }
9556bcfb631SMilanka Ringwald 
9566bcfb631SMilanka Ringwald                     if (client->protocol_mode_value_handle != 0){
9572901a9b7SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_SET_PROTOCOL_MODE;
9586bcfb631SMilanka Ringwald                     } else {
9592901a9b7SMilanka Ringwald                         client->protocol_mode = HID_PROTOCOL_MODE_BOOT;
9606bcfb631SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_CONNECTED;
9616bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
9626bcfb631SMilanka Ringwald                     }
9636bcfb631SMilanka Ringwald                     break;
964cf26c8fbSMilanka Ringwald                 default:
965cf26c8fbSMilanka Ringwald                     break;
966cf26c8fbSMilanka Ringwald             }
967cf26c8fbSMilanka Ringwald             break;
968cf26c8fbSMilanka Ringwald 
969cf26c8fbSMilanka Ringwald         default:
970cf26c8fbSMilanka Ringwald             break;
971cf26c8fbSMilanka Ringwald     }
9726bcfb631SMilanka Ringwald 
9736bcfb631SMilanka Ringwald     if (client != NULL){
9746bcfb631SMilanka Ringwald         hids_run_for_client(client);
9756bcfb631SMilanka Ringwald     }
976cf26c8fbSMilanka Ringwald }
977cf26c8fbSMilanka Ringwald 
9786bcfb631SMilanka Ringwald 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){
979cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
980cf26c8fbSMilanka Ringwald 
981cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
982cf26c8fbSMilanka Ringwald     if (client != NULL){
983cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
984cf26c8fbSMilanka Ringwald     }
985cf26c8fbSMilanka Ringwald 
986cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
987cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
988cf26c8fbSMilanka Ringwald         *hids_cid = cid;
989cf26c8fbSMilanka Ringwald     }
990cf26c8fbSMilanka Ringwald 
991cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
992cf26c8fbSMilanka Ringwald     if (client == NULL) {
993cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
994cf26c8fbSMilanka Ringwald     }
995cf26c8fbSMilanka Ringwald 
9966bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
997cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
998cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
999cf26c8fbSMilanka Ringwald 
1000cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1001cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1002cf26c8fbSMilanka Ringwald }
1003cf26c8fbSMilanka Ringwald 
1004cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1005cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1006cf26c8fbSMilanka Ringwald     if (client == NULL){
1007cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1008cf26c8fbSMilanka Ringwald     }
1009cf26c8fbSMilanka Ringwald     // finalize connection
1010cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1011cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1012cf26c8fbSMilanka Ringwald }
1013cf26c8fbSMilanka Ringwald 
10141624214bSMilanka Ringwald uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){
10151624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
10161624214bSMilanka Ringwald     if (client == NULL){
10171624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
10181624214bSMilanka Ringwald     }
10191624214bSMilanka Ringwald 
10201624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
10211624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
10221624214bSMilanka Ringwald     }
10231624214bSMilanka Ringwald 
10242fe59e00SMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT);
10251624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
10261624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
10271624214bSMilanka Ringwald     }
10281624214bSMilanka Ringwald 
10291624214bSMilanka Ringwald     uint16_t mtu;
10301624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
10311624214bSMilanka Ringwald 
10321624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
10331624214bSMilanka Ringwald         return status;
10341624214bSMilanka Ringwald     }
10351624214bSMilanka Ringwald 
10361624214bSMilanka Ringwald     if (mtu - 2 < report_len){
10371624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
10381624214bSMilanka Ringwald     }
10391624214bSMilanka Ringwald 
10401624214bSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_REPORT;
1041ef102a9eSMilanka Ringwald     client->active_index = report_index;
10421624214bSMilanka Ringwald     client->report = report;
10431624214bSMilanka Ringwald     client->report_len = report_len;
10441624214bSMilanka Ringwald 
10451624214bSMilanka Ringwald     hids_run_for_client(client);
10461624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
10471624214bSMilanka Ringwald }
10481624214bSMilanka Ringwald 
1049cf26c8fbSMilanka Ringwald void hids_client_init(void){}
1050cf26c8fbSMilanka Ringwald 
1051cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1052