xref: /btstack/src/ble/gatt-service/hids_client.c (revision 83d7ed1cf6ef92272c4ca557f257d0c6e597db60)
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 
42708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
43708c69d2SMilanka Ringwald #include <stdio.h>
44708c69d2SMilanka Ringwald #endif
45708c69d2SMilanka Ringwald 
46fc975d0eSMilanka Ringwald #include <stdint.h>
47fc975d0eSMilanka Ringwald #include <string.h>
48fc975d0eSMilanka Ringwald 
49cf26c8fbSMilanka Ringwald #include "ble/gatt-service/hids_client.h"
50fc975d0eSMilanka Ringwald 
51cf26c8fbSMilanka Ringwald #include "btstack_memory.h"
52fc975d0eSMilanka Ringwald #include "ble/att_db.h"
53fc975d0eSMilanka Ringwald #include "ble/core.h"
54fc975d0eSMilanka Ringwald #include "ble/gatt_client.h"
55fc975d0eSMilanka Ringwald #include "ble/sm.h"
56cf26c8fbSMilanka Ringwald #include "bluetooth_gatt.h"
57fc975d0eSMilanka Ringwald #include "btstack_debug.h"
58fc975d0eSMilanka Ringwald #include "btstack_event.h"
59fc975d0eSMilanka Ringwald #include "btstack_run_loop.h"
60fc975d0eSMilanka Ringwald #include "gap.h"
61fc975d0eSMilanka Ringwald 
6270093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_ID               3
6370093cf5SMilanka Ringwald #define HID_REPORT_MODE_REPORT_MAP_ID           4
6470093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_INFORMATION_ID      5
6570093cf5SMilanka Ringwald #define HID_REPORT_MODE_HID_CONTROL_POINT_ID    6
6670093cf5SMilanka Ringwald 
67da142a6fSMilanka Ringwald static btstack_linked_list_t clients;
68da142a6fSMilanka Ringwald static uint16_t hids_cid_counter = 0;
69da142a6fSMilanka Ringwald 
70da142a6fSMilanka Ringwald static uint8_t * hids_client_descriptor_storage;
71da142a6fSMilanka Ringwald static uint16_t  hids_client_descriptor_storage_len;
72da142a6fSMilanka Ringwald 
73cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
74cf26c8fbSMilanka Ringwald 
75556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
76556456ccSMilanka Ringwald static char * hid_characteristic_name(uint16_t uuid){
77556456ccSMilanka Ringwald     switch (uuid){
78556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
79556456ccSMilanka Ringwald             return "PROTOCOL_MODE";
80556456ccSMilanka Ringwald 
81556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
82556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_INPUT_REPORT";
83556456ccSMilanka Ringwald 
84556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
85556456ccSMilanka Ringwald             return "BOOT_MOUSE_INPUT_REPORT";
86556456ccSMilanka Ringwald 
87556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
88556456ccSMilanka Ringwald             return "BOOT_KEYBOARD_OUTPUT_REPORT";
89556456ccSMilanka Ringwald 
90556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
91556456ccSMilanka Ringwald             return "REPORT";
92556456ccSMilanka Ringwald 
93556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
94556456ccSMilanka Ringwald             return "REPORT_MAP";
95556456ccSMilanka Ringwald 
96556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
97556456ccSMilanka Ringwald             return "HID_INFORMATION";
98556456ccSMilanka Ringwald 
99556456ccSMilanka Ringwald         case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
100556456ccSMilanka Ringwald             return "HID_CONTROL_POINT";
101556456ccSMilanka Ringwald         default:
102556456ccSMilanka Ringwald             return "UKNOWN";
103556456ccSMilanka Ringwald     }
104556456ccSMilanka Ringwald }
105556456ccSMilanka Ringwald #endif
106556456ccSMilanka Ringwald 
107da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_con_handle(hci_con_handle_t con_handle){
108da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
109da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
110da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
111da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
112da142a6fSMilanka Ringwald         if (client->con_handle != con_handle) continue;
113da142a6fSMilanka Ringwald         return client;
114da142a6fSMilanka Ringwald     }
115da142a6fSMilanka Ringwald     return NULL;
116da142a6fSMilanka Ringwald }
117da142a6fSMilanka Ringwald 
118da142a6fSMilanka Ringwald static hids_client_t * hids_get_client_for_cid(uint16_t hids_cid){
119da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
120da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
121da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
122da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
123da142a6fSMilanka Ringwald         if (client->cid != hids_cid) continue;
124da142a6fSMilanka Ringwald         return client;
125da142a6fSMilanka Ringwald     }
126da142a6fSMilanka Ringwald     return NULL;
127da142a6fSMilanka Ringwald }
128da142a6fSMilanka Ringwald 
129da142a6fSMilanka Ringwald 
130da142a6fSMilanka Ringwald // START Descriptor Storage Util
131da142a6fSMilanka Ringwald 
132da142a6fSMilanka Ringwald static uint16_t hids_client_descriptor_storage_get_available_space(void){
133da142a6fSMilanka Ringwald     // assumes all descriptors are back to back
134da142a6fSMilanka Ringwald     uint16_t free_space = hids_client_descriptor_storage_len;
135da142a6fSMilanka Ringwald     uint8_t i;
136da142a6fSMilanka Ringwald 
137da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
138da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
139da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
140da142a6fSMilanka Ringwald         hids_client_t * client = (hids_client_t *)btstack_linked_list_iterator_next(&it);
141da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
142da142a6fSMilanka Ringwald             free_space -= client->services[i].hid_descriptor_len;
143da142a6fSMilanka Ringwald         }
144da142a6fSMilanka Ringwald     }
145da142a6fSMilanka Ringwald     return free_space;
146da142a6fSMilanka Ringwald }
147da142a6fSMilanka Ringwald 
148da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_init(hids_client_t * client, uint8_t service_index){
149da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len = 0;
150da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_max_len = hids_client_descriptor_storage_get_available_space();
151da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_offset = hids_client_descriptor_storage_len - client->services[service_index].hid_descriptor_max_len;
152da142a6fSMilanka Ringwald }
153da142a6fSMilanka Ringwald 
154da142a6fSMilanka Ringwald static bool hids_client_descriptor_storage_store(hids_client_t * client, uint8_t service_index, uint8_t byte){
155da142a6fSMilanka Ringwald     if (client->services[service_index].hid_descriptor_len >= client->services[service_index].hid_descriptor_max_len) return false;
156da142a6fSMilanka Ringwald 
157da142a6fSMilanka Ringwald     hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len] = byte;
158da142a6fSMilanka Ringwald     client->services[service_index].hid_descriptor_len++;
159da142a6fSMilanka Ringwald     return true;
160da142a6fSMilanka Ringwald }
161da142a6fSMilanka Ringwald 
162da142a6fSMilanka Ringwald static void hids_client_descriptor_storage_delete(hids_client_t * client){
163da142a6fSMilanka Ringwald     uint8_t service_index = 0;
164da142a6fSMilanka Ringwald     uint16_t next_offset = 0;
165da142a6fSMilanka Ringwald 
166da142a6fSMilanka Ringwald     for (service_index = 0; service_index < client->num_instances; service_index++){
167da142a6fSMilanka Ringwald         next_offset += client->services[service_index].hid_descriptor_offset + client->services[service_index].hid_descriptor_len;
168da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_len = 0;
169da142a6fSMilanka Ringwald         client->services[service_index].hid_descriptor_offset = 0;
170da142a6fSMilanka Ringwald     }
171da142a6fSMilanka Ringwald 
172da142a6fSMilanka Ringwald     memmove(&hids_client_descriptor_storage[client->services[0].hid_descriptor_offset],
173da142a6fSMilanka Ringwald             &hids_client_descriptor_storage[next_offset],
174da142a6fSMilanka Ringwald             hids_client_descriptor_storage_len - next_offset);
175da142a6fSMilanka Ringwald 
176da142a6fSMilanka Ringwald     uint8_t i;
177da142a6fSMilanka Ringwald     btstack_linked_list_iterator_t it;
178da142a6fSMilanka Ringwald     btstack_linked_list_iterator_init(&it, &clients);
179da142a6fSMilanka Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
180da142a6fSMilanka Ringwald         hids_client_t * conn = (hids_client_t *)btstack_linked_list_iterator_next(&it);
181da142a6fSMilanka Ringwald         for (i = 0; i < client->num_instances; i++){
182da142a6fSMilanka Ringwald             if (conn->services[i].hid_descriptor_offset >= next_offset){
183da142a6fSMilanka Ringwald                 conn->services[i].hid_descriptor_offset = next_offset;
184da142a6fSMilanka Ringwald                 next_offset += conn->services[service_index].hid_descriptor_len;
185da142a6fSMilanka Ringwald             }
186da142a6fSMilanka Ringwald         }
187da142a6fSMilanka Ringwald     }
188da142a6fSMilanka Ringwald }
189da142a6fSMilanka Ringwald 
190da142a6fSMilanka Ringwald const uint8_t * hids_client_descriptor_storage_get_descriptor_data(uint16_t hids_cid, uint8_t service_index){
191da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
192021192e1SMilanka Ringwald     if (client == NULL){
193021192e1SMilanka Ringwald         return NULL;
194021192e1SMilanka Ringwald     }
195021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
196da142a6fSMilanka Ringwald         return NULL;
197da142a6fSMilanka Ringwald     }
198da142a6fSMilanka Ringwald     return &hids_client_descriptor_storage[client->services[service_index].hid_descriptor_offset];
199da142a6fSMilanka Ringwald }
200da142a6fSMilanka Ringwald 
201da142a6fSMilanka Ringwald uint16_t hids_client_descriptor_storage_get_descriptor_len(uint16_t hids_cid, uint8_t service_index){
202da142a6fSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
203021192e1SMilanka Ringwald     if (client == NULL){
204021192e1SMilanka Ringwald         return 0;
205021192e1SMilanka Ringwald     }
206021192e1SMilanka Ringwald     if (service_index >= client->num_instances){
207da142a6fSMilanka Ringwald         return 0;
208da142a6fSMilanka Ringwald     }
209da142a6fSMilanka Ringwald     return client->services[service_index].hid_descriptor_len;
210da142a6fSMilanka Ringwald }
211da142a6fSMilanka Ringwald 
212da142a6fSMilanka Ringwald // END Descriptor Storage Util
213da142a6fSMilanka Ringwald 
214cf26c8fbSMilanka Ringwald static uint16_t hids_get_next_cid(void){
215cf26c8fbSMilanka Ringwald     if (hids_cid_counter == 0xffff) {
216cf26c8fbSMilanka Ringwald         hids_cid_counter = 1;
217cf26c8fbSMilanka Ringwald     } else {
218cf26c8fbSMilanka Ringwald         hids_cid_counter++;
219cf26c8fbSMilanka Ringwald     }
220cf26c8fbSMilanka Ringwald     return hids_cid_counter;
221fc975d0eSMilanka Ringwald }
222fc975d0eSMilanka Ringwald 
223ab116b1cSMilanka Ringwald static uint8_t find_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
224ab116b1cSMilanka Ringwald     uint8_t i;
225a381a464SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
226ab116b1cSMilanka Ringwald         if (client->reports[i].value_handle == value_handle){
227ab116b1cSMilanka Ringwald             return i;
228ab116b1cSMilanka Ringwald         }
229ab116b1cSMilanka Ringwald     }
230ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
231ab116b1cSMilanka Ringwald }
232ab116b1cSMilanka Ringwald 
23319146789SMilanka Ringwald static uint8_t find_external_report_index_for_value_handle(hids_client_t * client, uint16_t value_handle){
23419146789SMilanka Ringwald     uint8_t i;
23519146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
23619146789SMilanka Ringwald         if (client->external_reports[i].value_handle == value_handle){
23719146789SMilanka Ringwald             return i;
23819146789SMilanka Ringwald         }
23919146789SMilanka Ringwald     }
24019146789SMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
24119146789SMilanka Ringwald }
24219146789SMilanka Ringwald 
24319146789SMilanka Ringwald static bool external_report_index_for_uuid_exists(hids_client_t * client, uint16_t uuid16){
24419146789SMilanka Ringwald     uint8_t i;
24519146789SMilanka Ringwald     for (i = 0; i < client->num_external_reports; i++){
24619146789SMilanka Ringwald         if (client->external_reports[i].external_report_reference_uuid == uuid16){
24719146789SMilanka Ringwald             return true;
24819146789SMilanka Ringwald         }
24919146789SMilanka Ringwald     }
25019146789SMilanka Ringwald     return false;
25119146789SMilanka Ringwald }
25219146789SMilanka Ringwald 
253*83d7ed1cSMilanka Ringwald static uint8_t find_input_report_index_for_report_id(hids_client_t * client, uint8_t report_id){
254*83d7ed1cSMilanka Ringwald     uint8_t i;
255*83d7ed1cSMilanka Ringwald     switch (client->protocol_mode){
256*83d7ed1cSMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
257*83d7ed1cSMilanka Ringwald             for (i = 0; i < client->num_reports; i++){
258*83d7ed1cSMilanka Ringwald                 if (!client->reports[i].boot_report){
259*83d7ed1cSMilanka Ringwald                     continue;
260*83d7ed1cSMilanka Ringwald                 }
261*83d7ed1cSMilanka Ringwald                 if ((client->reports[i].report_id == report_id) && (client->reports[i].report_type != HID_REPORT_TYPE_OUTPUT)){
262*83d7ed1cSMilanka Ringwald                     return i;
263*83d7ed1cSMilanka Ringwald                 }
264*83d7ed1cSMilanka Ringwald             }
265*83d7ed1cSMilanka Ringwald             break;
266*83d7ed1cSMilanka Ringwald 
267*83d7ed1cSMilanka Ringwald         default:
268*83d7ed1cSMilanka Ringwald             for (i = 0; i < client->num_reports; i++){
269*83d7ed1cSMilanka Ringwald                 if (client->reports[i].boot_report){
270*83d7ed1cSMilanka Ringwald                     continue;
271*83d7ed1cSMilanka Ringwald                 }
272*83d7ed1cSMilanka Ringwald                 if ((client->reports[i].report_id == report_id) && (client->reports[i].report_type != HID_REPORT_TYPE_OUTPUT)){
273*83d7ed1cSMilanka Ringwald                     return i;
274*83d7ed1cSMilanka Ringwald                 }
275*83d7ed1cSMilanka Ringwald             }
276*83d7ed1cSMilanka Ringwald             break;
277*83d7ed1cSMilanka Ringwald     }
278*83d7ed1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
279*83d7ed1cSMilanka Ringwald }
28019146789SMilanka Ringwald 
28119146789SMilanka Ringwald 
282ab116b1cSMilanka 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){
283ab116b1cSMilanka Ringwald     uint8_t i;
284*83d7ed1cSMilanka Ringwald     switch (client->protocol_mode){
285*83d7ed1cSMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
286a381a464SMilanka Ringwald             for (i = 0; i < client->num_reports; i++){
287*83d7ed1cSMilanka Ringwald                 if (!client->reports[i].boot_report){
288*83d7ed1cSMilanka Ringwald                     continue;
289*83d7ed1cSMilanka Ringwald                 }
290ab116b1cSMilanka Ringwald                 if ((client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
291ab116b1cSMilanka Ringwald                     return i;
292ab116b1cSMilanka Ringwald                 }
293ab116b1cSMilanka Ringwald             }
294*83d7ed1cSMilanka Ringwald             break;
295*83d7ed1cSMilanka Ringwald 
296*83d7ed1cSMilanka Ringwald         default:
297*83d7ed1cSMilanka Ringwald             for (i = 0; i < client->num_reports; i++){
298*83d7ed1cSMilanka Ringwald                 if (client->reports[i].boot_report){
299*83d7ed1cSMilanka Ringwald                     continue;
300*83d7ed1cSMilanka Ringwald                 }
301*83d7ed1cSMilanka Ringwald                 if ((client->reports[i].report_id == report_id) && (client->reports[i].report_type == report_type)){
302*83d7ed1cSMilanka Ringwald                     return i;
303*83d7ed1cSMilanka Ringwald                 }
304*83d7ed1cSMilanka Ringwald             }
305*83d7ed1cSMilanka Ringwald             break;
306*83d7ed1cSMilanka Ringwald     }
307ab116b1cSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
308ab116b1cSMilanka Ringwald }
309ab116b1cSMilanka Ringwald 
310021192e1SMilanka 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, bool boot_report){
311ab116b1cSMilanka Ringwald 
31219146789SMilanka Ringwald     uint8_t report_index = find_external_report_index_for_value_handle(client, characteristic->value_handle);
313ab116b1cSMilanka Ringwald     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
3143322b222SMilanka Ringwald         return report_index;
315ab116b1cSMilanka Ringwald     }
31628da36a6SMilanka Ringwald     report_index = client->num_reports;
317ab116b1cSMilanka Ringwald 
31828da36a6SMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
31928da36a6SMilanka Ringwald         client->reports[report_index].value_handle = characteristic->value_handle;
32028da36a6SMilanka Ringwald         client->reports[report_index].end_handle = characteristic->end_handle;
32128da36a6SMilanka Ringwald         client->reports[report_index].properties = characteristic->properties;
322ab116b1cSMilanka Ringwald 
32328da36a6SMilanka Ringwald         client->reports[report_index].service_index = client->service_index;
32428da36a6SMilanka Ringwald         client->reports[report_index].report_id = report_id;
32528da36a6SMilanka Ringwald         client->reports[report_index].report_type = report_type;
326021192e1SMilanka Ringwald         client->reports[report_index].boot_report = boot_report;
32770093cf5SMilanka Ringwald 
328021192e1SMilanka Ringwald         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);
329ab116b1cSMilanka Ringwald         client->num_reports++;
3303322b222SMilanka Ringwald         return report_index;
331ab116b1cSMilanka Ringwald     } else {
332ab116b1cSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
3333322b222SMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
334ab116b1cSMilanka Ringwald     }
335ab116b1cSMilanka Ringwald }
336ab116b1cSMilanka Ringwald 
33719146789SMilanka Ringwald static uint8_t hids_client_add_external_report(hids_client_t * client, gatt_client_characteristic_descriptor_t * characteristic_descriptor){
338556456ccSMilanka Ringwald     uint8_t report_index = client->num_external_reports;
339556456ccSMilanka Ringwald 
340556456ccSMilanka Ringwald     if (report_index < HIDS_CLIENT_NUM_REPORTS) {
34119146789SMilanka Ringwald         client->external_reports[report_index].value_handle = characteristic_descriptor->handle;
342556456ccSMilanka Ringwald         client->external_reports[report_index].service_index = client->service_index;
34319146789SMilanka Ringwald 
344556456ccSMilanka Ringwald         client->num_external_reports++;
34519146789SMilanka Ringwald         log_info("add external index %d [%d], value handle 0x%02x", report_index, client->num_external_reports, characteristic_descriptor->handle);
346556456ccSMilanka Ringwald         return report_index;
347556456ccSMilanka Ringwald     } else {
348556456ccSMilanka Ringwald         log_info("not enough storage, increase HIDS_CLIENT_NUM_REPORTS");
349556456ccSMilanka Ringwald         return HIDS_CLIENT_INVALID_REPORT_INDEX;
35070093cf5SMilanka Ringwald     }
351556456ccSMilanka Ringwald }
352556456ccSMilanka Ringwald 
35370093cf5SMilanka Ringwald 
354021192e1SMilanka Ringwald static bool hid_clients_has_reports_in_report_mode(hids_client_t * client){
355021192e1SMilanka Ringwald     uint8_t i;
356021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
357021192e1SMilanka Ringwald         if (!client->reports[i].boot_report){
358021192e1SMilanka Ringwald             return true;
359ab116b1cSMilanka Ringwald         }
360021192e1SMilanka Ringwald     }
361021192e1SMilanka Ringwald     return false;
362021192e1SMilanka Ringwald }
363021192e1SMilanka Ringwald 
364021192e1SMilanka Ringwald static bool hid_clients_has_reports_in_boot_mode(hids_client_t * client){
365021192e1SMilanka Ringwald     uint8_t i;
366021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
367021192e1SMilanka Ringwald         if (client->reports[i].boot_report){
368021192e1SMilanka Ringwald             return true;
369021192e1SMilanka Ringwald         }
370021192e1SMilanka Ringwald     }
371021192e1SMilanka Ringwald     return false;
372ab116b1cSMilanka Ringwald }
373ab116b1cSMilanka Ringwald 
3747e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_active_report_map_index(hids_client_t * client){
3757e1e6e7dSMilanka Ringwald     uint8_t i;
3763322b222SMilanka Ringwald     for (i = client->service_index; i < client->num_instances; i++){
3773322b222SMilanka Ringwald         if (client->services[i].report_map_value_handle != 0){
378556456ccSMilanka Ringwald             return i;
3793322b222SMilanka Ringwald         }
3803322b222SMilanka Ringwald     }
381556456ccSMilanka Ringwald     client->service_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
382556456ccSMilanka Ringwald     return HIDS_CLIENT_INVALID_REPORT_INDEX;
3833322b222SMilanka Ringwald }
3843322b222SMilanka Ringwald 
3853322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map(hids_client_t * client){
3863322b222SMilanka Ringwald     client->service_index++;
3873322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
388556456ccSMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3893322b222SMilanka Ringwald         return true;
3903322b222SMilanka Ringwald     }
3913322b222SMilanka Ringwald     return false;
3923322b222SMilanka Ringwald }
3933322b222SMilanka Ringwald 
3943322b222SMilanka Ringwald static bool hids_client_report_map_query_init(hids_client_t * client){
3953322b222SMilanka Ringwald     client->service_index = 0;
3963322b222SMilanka Ringwald 
3973322b222SMilanka Ringwald     if (hids_client_get_next_active_report_map_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
3983322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR;
3993322b222SMilanka Ringwald         return true;
4003322b222SMilanka Ringwald     }
4013322b222SMilanka Ringwald     return false;
4023322b222SMilanka Ringwald }
4033322b222SMilanka Ringwald 
4043322b222SMilanka Ringwald static bool hids_client_report_query_next_report_map_uuid(hids_client_t * client){
405021192e1SMilanka Ringwald     client->report_index++;
40619146789SMilanka Ringwald     if (client->report_index < client->num_external_reports){
4073322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
408e7bd2dbeSMilanka Ringwald         return true;
409e7bd2dbeSMilanka Ringwald     }
410e7bd2dbeSMilanka Ringwald     return false;
411e7bd2dbeSMilanka Ringwald }
412e7bd2dbeSMilanka Ringwald 
4133322b222SMilanka Ringwald static bool hids_client_report_map_uuid_query_init(hids_client_t * client){
414021192e1SMilanka Ringwald     client->report_index = 0;
41519146789SMilanka Ringwald     if (client->num_external_reports > 0){
4163322b222SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID;
4177e1e6e7dSMilanka Ringwald         return true;
4187e1e6e7dSMilanka Ringwald     }
4197e1e6e7dSMilanka Ringwald     return false;
4207e1e6e7dSMilanka Ringwald }
4217e1e6e7dSMilanka Ringwald 
4227e1e6e7dSMilanka Ringwald static uint8_t hids_client_get_next_report_index(hids_client_t * client){
4237e1e6e7dSMilanka Ringwald     uint8_t i;
4247e1e6e7dSMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
425021192e1SMilanka Ringwald     switch (client->protocol_mode){
426021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_REPORT:
427021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
42870093cf5SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
42970093cf5SMilanka Ringwald                 if (report.report_type == HID_REPORT_TYPE_RESERVED && report.report_id == HID_REPORT_MODE_REPORT_ID){
4307e1e6e7dSMilanka Ringwald                     index = i;
431021192e1SMilanka Ringwald                     client->service_index = report.service_index;
432021192e1SMilanka Ringwald                 }
433021192e1SMilanka Ringwald             }
434021192e1SMilanka Ringwald             break;
435021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
436021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
437021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
438021192e1SMilanka Ringwald                 if (report.boot_report){
439021192e1SMilanka Ringwald                     index = i;
440021192e1SMilanka Ringwald                     client->service_index = report.service_index;
441021192e1SMilanka Ringwald                 }
442021192e1SMilanka Ringwald             }
443021192e1SMilanka Ringwald             break;
444021192e1SMilanka Ringwald         default:
4457e1e6e7dSMilanka Ringwald             break;
44670093cf5SMilanka Ringwald     }
447021192e1SMilanka Ringwald 
448021192e1SMilanka Ringwald     client->report_index = index;
4497e1e6e7dSMilanka Ringwald     return index;
4507e1e6e7dSMilanka Ringwald }
4517e1e6e7dSMilanka Ringwald 
4527e1e6e7dSMilanka Ringwald static bool hids_client_report_query_next_report(hids_client_t * client){
453021192e1SMilanka Ringwald     client->report_index++;
4547e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
45528da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
456e7bd2dbeSMilanka Ringwald         return true;
45770093cf5SMilanka Ringwald     }
4587e1e6e7dSMilanka Ringwald     return false;
4597e1e6e7dSMilanka Ringwald }
4607e1e6e7dSMilanka Ringwald 
4617e1e6e7dSMilanka Ringwald static bool hids_client_report_query_init(hids_client_t * client){
462021192e1SMilanka Ringwald     client->report_index = 0;
4637e1e6e7dSMilanka Ringwald 
4647e1e6e7dSMilanka Ringwald     if (hids_client_get_next_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
46528da36a6SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_FIND_REPORT;
4667e1e6e7dSMilanka Ringwald         return true;
4677e1e6e7dSMilanka Ringwald     }
468e7bd2dbeSMilanka Ringwald     return false;
46970093cf5SMilanka Ringwald }
470ab116b1cSMilanka Ringwald 
471021192e1SMilanka Ringwald static uint8_t hids_client_get_next_notification_report_index(hids_client_t * client){
472021192e1SMilanka Ringwald     uint8_t i;
473021192e1SMilanka Ringwald     uint8_t index = HIDS_CLIENT_INVALID_REPORT_INDEX;
474021192e1SMilanka Ringwald 
475021192e1SMilanka Ringwald     switch (client->protocol_mode){
476021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_REPORT:
477021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
478021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
479021192e1SMilanka Ringwald                 if (report.report_type != HID_REPORT_TYPE_INPUT){
480021192e1SMilanka Ringwald                     continue;
481021192e1SMilanka Ringwald                 }
482021192e1SMilanka Ringwald                 if (!report.boot_report){
483021192e1SMilanka Ringwald                     index = i;
484021192e1SMilanka Ringwald                 }
485021192e1SMilanka Ringwald             }
486021192e1SMilanka Ringwald             break;
487021192e1SMilanka Ringwald 
488021192e1SMilanka Ringwald         case HID_PROTOCOL_MODE_BOOT:
489021192e1SMilanka Ringwald             for (i = client->report_index; i < client->num_reports && (index == HIDS_CLIENT_INVALID_REPORT_INDEX); i++){
490021192e1SMilanka Ringwald                 hids_client_report_t report = client->reports[i];
491021192e1SMilanka Ringwald                 if (report.report_type != HID_REPORT_TYPE_INPUT){
492021192e1SMilanka Ringwald                     continue;
493021192e1SMilanka Ringwald                 }
494021192e1SMilanka Ringwald                 if (report.boot_report){
495021192e1SMilanka Ringwald                     index = i;
496021192e1SMilanka Ringwald                 }
497021192e1SMilanka Ringwald             }
498021192e1SMilanka Ringwald             break;
499021192e1SMilanka Ringwald 
500021192e1SMilanka Ringwald         default:
501021192e1SMilanka Ringwald             break;
502021192e1SMilanka Ringwald     }
503021192e1SMilanka Ringwald 
504021192e1SMilanka Ringwald     client->report_index = index;
505021192e1SMilanka Ringwald     return index;
506021192e1SMilanka Ringwald }
507021192e1SMilanka Ringwald 
508021192e1SMilanka Ringwald static bool hids_client_report_next_notification_report_index(hids_client_t * client){
509021192e1SMilanka Ringwald     client->report_index++;
510021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
511021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
512021192e1SMilanka Ringwald         return true;
513021192e1SMilanka Ringwald     }
514021192e1SMilanka Ringwald     return false;
515021192e1SMilanka Ringwald }
516021192e1SMilanka Ringwald 
517021192e1SMilanka Ringwald static bool hids_client_report_notifications_init(hids_client_t * client){
518021192e1SMilanka Ringwald     client->report_index = 0;
519021192e1SMilanka Ringwald 
520021192e1SMilanka Ringwald     if (hids_client_get_next_notification_report_index(client) != HIDS_CLIENT_INVALID_REPORT_INDEX){
521021192e1SMilanka Ringwald         client->state = HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS;
522021192e1SMilanka Ringwald         return true;
523021192e1SMilanka Ringwald     }
524021192e1SMilanka Ringwald     return false;
525021192e1SMilanka Ringwald }
526021192e1SMilanka Ringwald 
527cf26c8fbSMilanka Ringwald static hids_client_t * hids_create_client(hci_con_handle_t con_handle, uint16_t cid){
528cf26c8fbSMilanka Ringwald     hids_client_t * client = btstack_memory_hids_client_get();
529cf26c8fbSMilanka Ringwald     if (!client){
530cf26c8fbSMilanka Ringwald         log_error("Not enough memory to create client");
531cf26c8fbSMilanka Ringwald         return NULL;
532cf26c8fbSMilanka Ringwald     }
533cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_IDLE;
534cf26c8fbSMilanka Ringwald     client->cid = cid;
535cf26c8fbSMilanka Ringwald     client->con_handle = con_handle;
536fc975d0eSMilanka Ringwald 
537cf26c8fbSMilanka Ringwald     btstack_linked_list_add(&clients, (btstack_linked_item_t *) client);
538cf26c8fbSMilanka Ringwald     return client;
539fc975d0eSMilanka Ringwald }
540fc975d0eSMilanka Ringwald 
541cf26c8fbSMilanka Ringwald static void hids_finalize_client(hids_client_t * client){
542021192e1SMilanka Ringwald     // stop listening
543021192e1SMilanka Ringwald     uint8_t i;
544021192e1SMilanka Ringwald     for (i = 0; i < client->num_reports; i++){
545021192e1SMilanka Ringwald         gatt_client_stop_listening_for_characteristic_value_updates(&client->reports[i].notification_listener);
546021192e1SMilanka Ringwald     }
547021192e1SMilanka Ringwald 
548da142a6fSMilanka Ringwald     hids_client_descriptor_storage_delete(client);
549cf26c8fbSMilanka Ringwald     btstack_linked_list_remove(&clients, (btstack_linked_item_t *) client);
550cf26c8fbSMilanka Ringwald     btstack_memory_hids_client_free(client);
551fc975d0eSMilanka Ringwald }
552cf26c8fbSMilanka Ringwald 
553cf26c8fbSMilanka Ringwald 
554cf26c8fbSMilanka Ringwald static void hids_emit_connection_established(hids_client_t * client, uint8_t status){
5556bcfb631SMilanka Ringwald     uint8_t event[8];
556cf26c8fbSMilanka Ringwald     int pos = 0;
557cf26c8fbSMilanka Ringwald     event[pos++] = HCI_EVENT_GATTSERVICE_META;
558cf26c8fbSMilanka Ringwald     event[pos++] = sizeof(event) - 2;
559cf26c8fbSMilanka Ringwald     event[pos++] = GATTSERVICE_SUBEVENT_HID_SERVICE_CONNECTED;
560cf26c8fbSMilanka Ringwald     little_endian_store_16(event, pos, client->cid);
561cf26c8fbSMilanka Ringwald     pos += 2;
562cf26c8fbSMilanka Ringwald     event[pos++] = status;
5636bcfb631SMilanka Ringwald     event[pos++] = client->protocol_mode;
564cf26c8fbSMilanka Ringwald     event[pos++] = client->num_instances;
565cf26c8fbSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
566cf26c8fbSMilanka Ringwald }
567cf26c8fbSMilanka Ringwald 
568021192e1SMilanka Ringwald static void hids_client_setup_report_event(hids_client_t * client, uint8_t report_index, uint8_t *buffer, uint16_t report_len){
569021192e1SMilanka Ringwald     uint16_t pos = 0;
570021192e1SMilanka Ringwald     buffer[pos++] = HCI_EVENT_GATTSERVICE_META;
571021192e1SMilanka Ringwald     pos++;  // skip len
572021192e1SMilanka Ringwald     buffer[pos++] = GATTSERVICE_SUBEVENT_HID_REPORT;
573021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, client->cid);
574021192e1SMilanka Ringwald     pos += 2;
575021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].service_index;
576021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
577021192e1SMilanka Ringwald     little_endian_store_16(buffer, pos, report_len + 1);
578021192e1SMilanka Ringwald     pos += 2;
579021192e1SMilanka Ringwald     buffer[pos++] = client->reports[report_index].report_id;
580*83d7ed1cSMilanka Ringwald     buffer[1] = pos + (report_len + 1) - 2;
581*83d7ed1cSMilanka Ringwald 
582021192e1SMilanka Ringwald }
583021192e1SMilanka Ringwald 
584*83d7ed1cSMilanka Ringwald static void handle_notification_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
585021192e1SMilanka Ringwald     UNUSED(packet_type);
586021192e1SMilanka Ringwald     UNUSED(channel);
587*83d7ed1cSMilanka Ringwald 
588021192e1SMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_NOTIFICATION) return;
589021192e1SMilanka Ringwald 
590021192e1SMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_notification_get_handle(packet));
591021192e1SMilanka Ringwald     btstack_assert(client != NULL);
592021192e1SMilanka Ringwald 
593021192e1SMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_notification_get_value_handle(packet));
594021192e1SMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
595021192e1SMilanka Ringwald         return;
596021192e1SMilanka Ringwald     }
597021192e1SMilanka Ringwald 
598*83d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
599021192e1SMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_notification_get_value_length(packet));
600*83d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
601021192e1SMilanka Ringwald }
602021192e1SMilanka Ringwald 
603*83d7ed1cSMilanka Ringwald static void handle_report_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
604*83d7ed1cSMilanka Ringwald     UNUSED(packet_type);
605*83d7ed1cSMilanka Ringwald     UNUSED(channel);
606*83d7ed1cSMilanka Ringwald 
607*83d7ed1cSMilanka Ringwald     if (hci_event_packet_get_type(packet) != GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) return;
608*83d7ed1cSMilanka Ringwald 
609*83d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
610*83d7ed1cSMilanka Ringwald     btstack_assert(client != NULL);
611*83d7ed1cSMilanka Ringwald 
612*83d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_W4_GET_REPORT_RESULT){
613*83d7ed1cSMilanka Ringwald         return;
614*83d7ed1cSMilanka Ringwald     }
615*83d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_CONNECTED;
616*83d7ed1cSMilanka Ringwald 
617*83d7ed1cSMilanka Ringwald     uint8_t report_index = find_report_index_for_value_handle(client, gatt_event_characteristic_value_query_result_get_value_handle(packet));
618*83d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
619*83d7ed1cSMilanka Ringwald         return;
620*83d7ed1cSMilanka Ringwald     }
621*83d7ed1cSMilanka Ringwald 
622*83d7ed1cSMilanka Ringwald     uint8_t * in_place_event = &packet[-2];
623*83d7ed1cSMilanka Ringwald     hids_client_setup_report_event(client, report_index, in_place_event, gatt_event_characteristic_value_query_result_get_value_length(packet));
624*83d7ed1cSMilanka Ringwald     (*client->client_handler)(HCI_EVENT_GATTSERVICE_META, client->cid, in_place_event, size + 2);
625*83d7ed1cSMilanka Ringwald }
626cf26c8fbSMilanka Ringwald 
627cf26c8fbSMilanka Ringwald static void hids_run_for_client(hids_client_t * client){
628cf26c8fbSMilanka Ringwald     uint8_t att_status;
6296bcfb631SMilanka Ringwald     gatt_client_service_t service;
6306bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
631cf26c8fbSMilanka Ringwald 
632cf26c8fbSMilanka Ringwald     switch (client->state){
633cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_SERVICE:
634556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
635556456ccSMilanka Ringwald             printf("\n\nQuery Services:\n");
636556456ccSMilanka Ringwald #endif
637cf26c8fbSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_SERVICE_RESULT;
63819146789SMilanka Ringwald 
63919146789SMilanka Ringwald             // result in GATT_EVENT_SERVICE_QUERY_RESULT
640cf26c8fbSMilanka Ringwald             att_status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, client->con_handle, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE);
641cf26c8fbSMilanka Ringwald             UNUSED(att_status);
642cf26c8fbSMilanka Ringwald             break;
643cf26c8fbSMilanka Ringwald 
644cf26c8fbSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC:
645556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
646556456ccSMilanka Ringwald             printf("\n\nQuery Characteristics of service %d:\n", client->service_index);
647556456ccSMilanka Ringwald #endif
6486bcfb631SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT;
6496bcfb631SMilanka Ringwald 
6506bcfb631SMilanka Ringwald             service.start_group_handle = client->services[client->service_index].start_handle;
6516bcfb631SMilanka Ringwald             service.end_group_handle = client->services[client->service_index].end_handle;
65219146789SMilanka Ringwald 
65319146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
6546bcfb631SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
6556bcfb631SMilanka Ringwald 
6566bcfb631SMilanka Ringwald             UNUSED(att_status);
6576bcfb631SMilanka Ringwald             break;
6586bcfb631SMilanka Ringwald 
659021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE:
6602901a9b7SMilanka 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);
6616bcfb631SMilanka Ringwald             UNUSED(att_status);
6622901a9b7SMilanka Ringwald 
6632901a9b7SMilanka Ringwald             client->protocol_mode = client->required_protocol_mode;
664021192e1SMilanka Ringwald             if (hids_client_report_query_init(client)){
665021192e1SMilanka Ringwald                 break;
666021192e1SMilanka Ringwald             }
667021192e1SMilanka Ringwald 
668021192e1SMilanka Ringwald             hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
669021192e1SMilanka Ringwald             hids_finalize_client(client);
670cf26c8fbSMilanka Ringwald             break;
671cf26c8fbSMilanka Ringwald 
67228da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_MAP_HID_DESCRIPTOR:
673556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
674556456ccSMilanka Ringwald             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);
675556456ccSMilanka Ringwald #endif
67628da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR;
677556456ccSMilanka Ringwald 
67819146789SMilanka Ringwald             // result in GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT
67919146789SMilanka 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);
680e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
681e7bd2dbeSMilanka Ringwald             break;
682e7bd2dbeSMilanka Ringwald 
683e7bd2dbeSMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS:
684556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
685556456ccSMilanka Ringwald             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);
686556456ccSMilanka Ringwald #endif
687e7bd2dbeSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT;
688e7bd2dbeSMilanka Ringwald 
6893322b222SMilanka Ringwald             characteristic.value_handle = client->services[client->service_index].report_map_value_handle;
6903322b222SMilanka Ringwald             characteristic.end_handle = client->services[client->service_index].report_map_end_handle;
6913322b222SMilanka Ringwald 
69219146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
693e7bd2dbeSMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
694e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
695e7bd2dbeSMilanka Ringwald             break;
696e7bd2dbeSMilanka Ringwald 
69728da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_REPORT_MAP_READ_EXTERNAL_REPORT_REFERENCE_UUID:
698556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
69919146789SMilanka Ringwald             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);
700556456ccSMilanka Ringwald #endif
70128da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID;
702e7bd2dbeSMilanka Ringwald 
70319146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
70419146789SMilanka Ringwald             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);
705e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
706e7bd2dbeSMilanka Ringwald             break;
707e7bd2dbeSMilanka Ringwald 
7083322b222SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC:
709556456ccSMilanka Ringwald  #ifdef ENABLE_TESTING_SUPPORT
71019146789SMilanka Ringwald             printf("\nDiscover External Report Characteristic:\n");
711556456ccSMilanka Ringwald #endif
7123322b222SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT;
713e7bd2dbeSMilanka Ringwald 
7143322b222SMilanka Ringwald             service.start_group_handle = 0x0001;
7153322b222SMilanka Ringwald             service.end_group_handle = 0xffff;
71619146789SMilanka Ringwald 
71719146789SMilanka Ringwald             // Result in GATT_EVENT_CHARACTERISTIC_QUERY_RESULT
7183322b222SMilanka Ringwald             att_status = gatt_client_discover_characteristics_for_service(&handle_gatt_client_event, client->con_handle, &service);
719e7bd2dbeSMilanka Ringwald             UNUSED(att_status);
720e7bd2dbeSMilanka Ringwald             break;
721e7bd2dbeSMilanka Ringwald 
72228da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_FIND_REPORT:
723556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
724*83d7ed1cSMilanka Ringwald             printf("\nQuery Report Characteristic Descriptors [%d, %d, 0x%04X]:\n",
725556456ccSMilanka Ringwald                 client->report_index,
726*83d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index,
727*83d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
728556456ccSMilanka Ringwald #endif
72928da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_FOUND;
73070093cf5SMilanka Ringwald             client->descriptor_handle = 0;
731556456ccSMilanka Ringwald 
732556456ccSMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
733556456ccSMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
734556456ccSMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
735556456ccSMilanka Ringwald 
73619146789SMilanka Ringwald             // result in GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT
73770093cf5SMilanka Ringwald             att_status = gatt_client_discover_characteristic_descriptors(&handle_gatt_client_event, client->con_handle, &characteristic);
73870093cf5SMilanka Ringwald             UNUSED(att_status);
73970093cf5SMilanka Ringwald             break;
74070093cf5SMilanka Ringwald 
74128da36a6SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE:
74228da36a6SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE;
74370093cf5SMilanka Ringwald 
74419146789SMilanka Ringwald             // result in GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT
74570093cf5SMilanka Ringwald             att_status = gatt_client_read_characteristic_descriptor_using_descriptor_handle(&handle_gatt_client_event, client->con_handle, client->descriptor_handle);
74670093cf5SMilanka Ringwald             client->descriptor_handle = 0;
74770093cf5SMilanka Ringwald             UNUSED(att_status);
74870093cf5SMilanka Ringwald             break;
74970093cf5SMilanka Ringwald 
750021192e1SMilanka Ringwald         case HIDS_CLIENT_STATE_W2_ENABLE_INPUT_REPORTS:
751021192e1SMilanka Ringwald             client->state = HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED;
752021192e1SMilanka Ringwald 
753021192e1SMilanka Ringwald             characteristic.value_handle = client->reports[client->report_index].value_handle;
754021192e1SMilanka Ringwald             characteristic.end_handle = client->reports[client->report_index].end_handle;
755021192e1SMilanka Ringwald             characteristic.properties = client->reports[client->report_index].properties;
756021192e1SMilanka Ringwald 
75719146789SMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
758021192e1SMilanka Ringwald             att_status = gatt_client_write_client_characteristic_configuration(&handle_gatt_client_event, client->con_handle, &characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
759021192e1SMilanka Ringwald 
760021192e1SMilanka Ringwald             if (att_status != ERROR_CODE_SUCCESS){
761021192e1SMilanka Ringwald                 if (hids_client_report_next_notification_report_index(client)){
762021192e1SMilanka Ringwald                     hids_run_for_client(client);
763021192e1SMilanka Ringwald                     break;
764021192e1SMilanka Ringwald                 }
765021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
766021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
767021192e1SMilanka Ringwald             } else {
768021192e1SMilanka Ringwald                 gatt_client_listen_for_characteristic_value_updates(
769021192e1SMilanka Ringwald                     &client->reports[client->report_index].notification_listener,
770*83d7ed1cSMilanka Ringwald                     &handle_notification_event, client->con_handle, &characteristic);
771021192e1SMilanka Ringwald 
772021192e1SMilanka Ringwald                 client->state = HIDS_CLIENT_STATE_CONNECTED;
773021192e1SMilanka Ringwald                 hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
774021192e1SMilanka Ringwald             }
775021192e1SMilanka Ringwald             UNUSED(att_status);
776021192e1SMilanka Ringwald             break;
777021192e1SMilanka Ringwald 
778*83d7ed1cSMilanka Ringwald 
779*83d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_REPORT:
780*83d7ed1cSMilanka Ringwald 
781*83d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
782*83d7ed1cSMilanka Ringwald             printf("    Send report [%d, %d, 0x%04X]:\n",
783*83d7ed1cSMilanka Ringwald                 client->report_index,
784*83d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
785*83d7ed1cSMilanka Ringwald #endif
786*83d7ed1cSMilanka Ringwald 
787*83d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_STATE_CONNECTED;
788*83d7ed1cSMilanka Ringwald 
789*83d7ed1cSMilanka Ringwald             att_status = gatt_client_write_value_of_characteristic_without_response(client->con_handle,
790*83d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle,
791*83d7ed1cSMilanka Ringwald                 client->report_len, (uint8_t *)client->report);
792*83d7ed1cSMilanka Ringwald             UNUSED(att_status);
793*83d7ed1cSMilanka Ringwald             break;
794*83d7ed1cSMilanka Ringwald 
795*83d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_SEND_GET_REPORT:
796*83d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
797*83d7ed1cSMilanka Ringwald             printf("    Get report [ID %d, Service %d, handle 0x%04X]:\n",
798*83d7ed1cSMilanka Ringwald                 client->reports[client->report_index].report_id,
799*83d7ed1cSMilanka Ringwald                 client->reports[client->report_index].service_index, client->reports[client->report_index].value_handle);
800*83d7ed1cSMilanka Ringwald #endif
801*83d7ed1cSMilanka Ringwald 
802*83d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_GET_REPORT_RESULT;
803*83d7ed1cSMilanka Ringwald 
804*83d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
805*83d7ed1cSMilanka Ringwald                 &handle_report_event,
806*83d7ed1cSMilanka Ringwald                 client->con_handle,
807*83d7ed1cSMilanka Ringwald                 client->reports[client->report_index].value_handle);
808*83d7ed1cSMilanka Ringwald             UNUSED(att_status);
809*83d7ed1cSMilanka Ringwald             break;
810*83d7ed1cSMilanka Ringwald 
811*83d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
812*83d7ed1cSMilanka Ringwald         case HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION:
813*83d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W4_CHARACTERISTIC_CONFIGURATION_RESULT;
814*83d7ed1cSMilanka Ringwald 
815*83d7ed1cSMilanka Ringwald             // end of write marked in GATT_EVENT_QUERY_COMPLETE
816*83d7ed1cSMilanka Ringwald             att_status = gatt_client_read_value_of_characteristic_using_value_handle(
817*83d7ed1cSMilanka Ringwald                 &handle_gatt_client_event,
818*83d7ed1cSMilanka Ringwald                 client->con_handle,
819*83d7ed1cSMilanka Ringwald                 client->reports[client->report_index].ccc_handle);
820*83d7ed1cSMilanka Ringwald 
821*83d7ed1cSMilanka Ringwald             break;
822*83d7ed1cSMilanka Ringwald #endif
823*83d7ed1cSMilanka Ringwald 
8246bcfb631SMilanka Ringwald         default:
8256bcfb631SMilanka Ringwald             break;
8266bcfb631SMilanka Ringwald     }
8276bcfb631SMilanka Ringwald }
8286bcfb631SMilanka Ringwald 
829cf26c8fbSMilanka Ringwald static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
830cf26c8fbSMilanka Ringwald     UNUSED(packet_type);
831cf26c8fbSMilanka Ringwald     UNUSED(channel);
832cf26c8fbSMilanka Ringwald     UNUSED(size);
833cf26c8fbSMilanka Ringwald 
8346bcfb631SMilanka Ringwald     hids_client_t * client = NULL;
835cf26c8fbSMilanka Ringwald     uint8_t att_status;
8366bcfb631SMilanka Ringwald     gatt_client_service_t service;
8376bcfb631SMilanka Ringwald     gatt_client_characteristic_t characteristic;
83870093cf5SMilanka Ringwald     gatt_client_characteristic_descriptor_t characteristic_descriptor;
839ab116b1cSMilanka Ringwald 
840021192e1SMilanka Ringwald     // hids_client_report_t * boot_keyboard_report;
841021192e1SMilanka Ringwald     // hids_client_report_t * boot_mouse_report;
842e7bd2dbeSMilanka Ringwald     const uint8_t * characteristic_descriptor_value;
8433322b222SMilanka Ringwald     uint8_t i;
8443322b222SMilanka Ringwald     uint8_t report_index;
845da142a6fSMilanka Ringwald 
8468cec2b74SMilanka Ringwald     const uint8_t * descriptor_value;
8478cec2b74SMilanka Ringwald     uint16_t descriptor_value_len;
848cf26c8fbSMilanka Ringwald 
849cf26c8fbSMilanka Ringwald     switch(hci_event_packet_get_type(packet)){
850cf26c8fbSMilanka Ringwald         case GATT_EVENT_SERVICE_QUERY_RESULT:
851cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_service_query_result_get_handle(packet));
852cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
853cf26c8fbSMilanka Ringwald 
8546bcfb631SMilanka Ringwald             if (client->state != HIDS_CLIENT_STATE_W4_SERVICE_RESULT) {
8556bcfb631SMilanka Ringwald                 hids_emit_connection_established(client, GATT_CLIENT_IN_WRONG_STATE);
8566bcfb631SMilanka Ringwald                 hids_finalize_client(client);
8576bcfb631SMilanka Ringwald                 break;
8586bcfb631SMilanka Ringwald             }
8596bcfb631SMilanka Ringwald 
860021192e1SMilanka Ringwald             if (client->num_instances < MAX_NUM_HID_SERVICES){
861021192e1SMilanka Ringwald                 uint8_t index = client->num_instances;
8626bcfb631SMilanka Ringwald                 gatt_event_service_query_result_get_service(packet, &service);
863021192e1SMilanka Ringwald                 client->services[index].start_handle = service.start_group_handle;
864021192e1SMilanka Ringwald                 client->services[index].end_handle = service.end_group_handle;
8656bcfb631SMilanka Ringwald                 client->num_instances++;
866021192e1SMilanka Ringwald 
867708c69d2SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
868708c69d2SMilanka Ringwald                 printf("HID Service: start handle 0x%04X, end handle 0x%04X\n", client->services[index].start_handle, client->services[index].end_handle);
869708c69d2SMilanka Ringwald #endif
870021192e1SMilanka Ringwald                 hids_client_descriptor_storage_init(client, index);
871021192e1SMilanka Ringwald             }  else {
872021192e1SMilanka Ringwald                 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);
873da142a6fSMilanka Ringwald             }
8746bcfb631SMilanka Ringwald             break;
8756bcfb631SMilanka Ringwald 
8766bcfb631SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
8776bcfb631SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_query_result_get_handle(packet));
8786bcfb631SMilanka Ringwald             btstack_assert(client != NULL);
8796bcfb631SMilanka Ringwald             gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
88019146789SMilanka Ringwald 
88119146789SMilanka Ringwald             report_index = HIDS_CLIENT_INVALID_REPORT_INDEX;
88219146789SMilanka Ringwald             if (client->state == HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT){
88319146789SMilanka Ringwald                 if (!external_report_index_for_uuid_exists(client, characteristic.uuid16)){
88419146789SMilanka Ringwald                     break;
88519146789SMilanka Ringwald                 }
88619146789SMilanka Ringwald             }
88719146789SMilanka Ringwald 
88819146789SMilanka Ringwald             switch (characteristic.uuid16){
88919146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE:
89019146789SMilanka Ringwald                     client->protocol_mode_value_handle = characteristic.value_handle;
89119146789SMilanka Ringwald                     break;
89219146789SMilanka Ringwald 
89319146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT:
89419146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_INPUT, true);
89519146789SMilanka Ringwald                     break;
89619146789SMilanka Ringwald 
89719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT:
89819146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_MOUSE_ID, HID_REPORT_TYPE_INPUT, true);
89919146789SMilanka Ringwald                     break;
90019146789SMilanka Ringwald 
90119146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT:
90219146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_BOOT_MODE_KEYBOARD_ID, HID_REPORT_TYPE_OUTPUT, false);
90319146789SMilanka Ringwald                     break;
90419146789SMilanka Ringwald 
90519146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT:
90619146789SMilanka Ringwald                     report_index = hids_client_add_characteristic(client, &characteristic, HID_REPORT_MODE_REPORT_ID, HID_REPORT_TYPE_RESERVED, false);
90719146789SMilanka Ringwald                     break;
90819146789SMilanka Ringwald 
90919146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP:
91019146789SMilanka Ringwald                     client->services[client->service_index].report_map_value_handle = characteristic.value_handle;
91119146789SMilanka Ringwald                     client->services[client->service_index].report_map_end_handle = characteristic.end_handle;
91219146789SMilanka Ringwald                     break;
91319146789SMilanka Ringwald 
91419146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION:
91519146789SMilanka Ringwald                     break;
91619146789SMilanka Ringwald 
91719146789SMilanka Ringwald                 case ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT:
91819146789SMilanka Ringwald                     break;
91919146789SMilanka Ringwald 
92019146789SMilanka Ringwald                 default:
92119146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
92219146789SMilanka Ringwald                     printf("    TODO: Found external characteristic 0x%04X\n", characteristic.uuid16);
92319146789SMilanka Ringwald #endif
92419146789SMilanka Ringwald                     return;
92519146789SMilanka Ringwald             }
92619146789SMilanka Ringwald 
92719146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
92819146789SMilanka Ringwald             printf("HID Characteristic %s:  \n    Attribute Handle 0x%04X, Properties 0x%02X, Handle 0x%04X, UUID 0x%04X, service %d",
92919146789SMilanka Ringwald                 hid_characteristic_name(characteristic.uuid16),
93019146789SMilanka Ringwald                 characteristic.start_handle,
93119146789SMilanka Ringwald                 characteristic.properties,
93219146789SMilanka Ringwald                 characteristic.value_handle, characteristic.uuid16,
93319146789SMilanka Ringwald                 client->service_index);
93419146789SMilanka Ringwald 
93519146789SMilanka Ringwald             if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
93619146789SMilanka Ringwald                 printf(", report index 0x%02X", report_index);
93719146789SMilanka Ringwald             }
93819146789SMilanka Ringwald             printf("\n");
93919146789SMilanka Ringwald #endif
940cf26c8fbSMilanka Ringwald             break;
941cf26c8fbSMilanka Ringwald 
9428cec2b74SMilanka Ringwald         case GATT_EVENT_LONG_CHARACTERISTIC_VALUE_QUERY_RESULT:
943e7bd2dbeSMilanka Ringwald             // Map Report characteristic value == HID Descriptor
9448cec2b74SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_long_characteristic_value_query_result_get_handle(packet));
945e7bd2dbeSMilanka Ringwald             btstack_assert(client != NULL);
946da142a6fSMilanka Ringwald 
9478cec2b74SMilanka Ringwald             descriptor_value = gatt_event_long_characteristic_value_query_result_get_value(packet);
9488cec2b74SMilanka Ringwald             descriptor_value_len = gatt_event_long_characteristic_value_query_result_get_value_length(packet);
9498cec2b74SMilanka Ringwald 
950556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
9518cec2b74SMilanka Ringwald             // printf("Report Map HID Desc [%d] for service %d\n", descriptor_len, client->service_index);
9528cec2b74SMilanka Ringwald             printf_hexdump(descriptor_value, descriptor_value_len);
953556456ccSMilanka Ringwald #endif
9548cec2b74SMilanka Ringwald             for (i = 0; i < descriptor_value_len; i++){
9558cec2b74SMilanka Ringwald                 bool stored = hids_client_descriptor_storage_store(client, client->service_index, descriptor_value[i]);
956da142a6fSMilanka Ringwald                 if (!stored){
957da142a6fSMilanka Ringwald                     client->services[client->service_index].hid_descriptor_status = ERROR_CODE_MEMORY_CAPACITY_EXCEEDED;
958da142a6fSMilanka Ringwald                     break;
959da142a6fSMilanka Ringwald                 }
960da142a6fSMilanka Ringwald             }
961e7bd2dbeSMilanka Ringwald             break;
962e7bd2dbeSMilanka Ringwald 
96370093cf5SMilanka Ringwald         case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
96470093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_all_characteristic_descriptors_query_result_get_handle(packet));
96570093cf5SMilanka Ringwald             btstack_assert(client != NULL);
96670093cf5SMilanka Ringwald             gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &characteristic_descriptor);
96770093cf5SMilanka Ringwald 
968e7bd2dbeSMilanka Ringwald             switch (client->state) {
969e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
970e7bd2dbeSMilanka Ringwald                     // setup for descriptor value query
971e7bd2dbeSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_EXTERNAL_REPORT_REFERENCE){
97219146789SMilanka Ringwald                         report_index = hids_client_add_external_report(client, &characteristic_descriptor);
9733322b222SMilanka Ringwald 
974556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
975556456ccSMilanka Ringwald                         if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
976556456ccSMilanka Ringwald                             printf("    External Report Reference Characteristic Descriptor: Handle 0x%04X, UUID 0x%04X, service %d, report index 0x%02X\n",
977556456ccSMilanka Ringwald                                 characteristic_descriptor.handle,
978556456ccSMilanka Ringwald                                 characteristic_descriptor.uuid16,
979556456ccSMilanka Ringwald                                 client->service_index, report_index);
980556456ccSMilanka Ringwald                         }
981556456ccSMilanka Ringwald #endif
982e7bd2dbeSMilanka Ringwald                     }
983e7bd2dbeSMilanka Ringwald                     break;
98428da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
98570093cf5SMilanka Ringwald                     // setup for descriptor value query
98670093cf5SMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_REPORT_REFERENCE){
98770093cf5SMilanka Ringwald                         client->descriptor_handle = characteristic_descriptor.handle;
988556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
989556456ccSMilanka Ringwald                         printf("    Report Characteristic Report Reference Characteristic Descriptor:  Handle 0x%04X, UUID 0x%04X\n",
990556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
991556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
99219146789SMilanka Ringwald #endif
993556456ccSMilanka Ringwald                     }
994556456ccSMilanka Ringwald 
99519146789SMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
996556456ccSMilanka Ringwald                     if (characteristic_descriptor.uuid16 == ORG_BLUETOOTH_DESCRIPTOR_GATT_CLIENT_CHARACTERISTIC_CONFIGURATION){
997*83d7ed1cSMilanka Ringwald                         client->reports[client->report_index].ccc_handle = characteristic_descriptor.handle;
998556456ccSMilanka Ringwald                         printf("    Report Client Characteristic Configuration Descriptor: Handle 0x%04X, UUID 0x%04X\n",
999556456ccSMilanka Ringwald                             characteristic_descriptor.handle,
1000556456ccSMilanka Ringwald                             characteristic_descriptor.uuid16);
1001556456ccSMilanka Ringwald                     }
1002556456ccSMilanka Ringwald #endif
100370093cf5SMilanka Ringwald                     break;
1004556456ccSMilanka Ringwald 
1005e7bd2dbeSMilanka Ringwald                 default:
1006e7bd2dbeSMilanka Ringwald                     break;
1007e7bd2dbeSMilanka Ringwald             }
1008e7bd2dbeSMilanka Ringwald             break;
100970093cf5SMilanka Ringwald 
1010*83d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1011*83d7ed1cSMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
1012*83d7ed1cSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_value_query_result_get_handle(packet));
1013*83d7ed1cSMilanka Ringwald             btstack_assert(client != NULL);
1014*83d7ed1cSMilanka Ringwald 
1015*83d7ed1cSMilanka Ringwald             printf("    Received CCC value: ");
1016*83d7ed1cSMilanka Ringwald             printf_hexdump(gatt_event_characteristic_value_query_result_get_value(packet),  gatt_event_characteristic_value_query_result_get_value_length(packet));
1017*83d7ed1cSMilanka Ringwald             client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1018*83d7ed1cSMilanka Ringwald             break;
1019*83d7ed1cSMilanka Ringwald #endif
1020*83d7ed1cSMilanka Ringwald 
102170093cf5SMilanka Ringwald         case GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT:
102270093cf5SMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_characteristic_descriptor_query_result_get_handle(packet));
102370093cf5SMilanka Ringwald             btstack_assert(client != NULL);
1024e7bd2dbeSMilanka Ringwald 
1025e7bd2dbeSMilanka Ringwald             if (gatt_event_characteristic_descriptor_query_result_get_descriptor_length(packet) != 2){
1026e7bd2dbeSMilanka Ringwald                 break;
1027e7bd2dbeSMilanka Ringwald             }
10287e1e6e7dSMilanka Ringwald 
1029e7bd2dbeSMilanka Ringwald             characteristic_descriptor_value = gatt_event_characteristic_descriptor_query_result_get_descriptor(packet);
1030e7bd2dbeSMilanka Ringwald             switch (client->state) {
103128da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1032e7bd2dbeSMilanka Ringwald                     // get external report characteristic uuid
103319146789SMilanka Ringwald                     report_index = find_external_report_index_for_value_handle(client, gatt_event_characteristic_descriptor_query_result_get_descriptor_handle(packet));
10343322b222SMilanka Ringwald                     if (report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
103519146789SMilanka Ringwald                         client->external_reports[report_index].external_report_reference_uuid = little_endian_read_16(characteristic_descriptor_value, 0);
1036556456ccSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
103719146789SMilanka Ringwald                         printf("    Update external_report_reference_uuid of report index 0x%02X, service index 0x%02X, UUID 0x%02X\n",
103819146789SMilanka Ringwald                             report_index, client->service_index, client->external_reports[report_index].external_report_reference_uuid);
1039556456ccSMilanka Ringwald #endif
10403322b222SMilanka Ringwald                     }
1041e7bd2dbeSMilanka Ringwald                     break;
1042e7bd2dbeSMilanka Ringwald 
104328da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
104419146789SMilanka Ringwald 
104519146789SMilanka Ringwald                     if (client->report_index != HIDS_CLIENT_INVALID_REPORT_INDEX){
1046021192e1SMilanka Ringwald                         client->reports[client->report_index].report_id = characteristic_descriptor_value[0];
1047021192e1SMilanka Ringwald                         client->reports[client->report_index].report_type = (hid_report_type_t)characteristic_descriptor_value[1];
104819146789SMilanka Ringwald     #ifdef ENABLE_TESTING_SUPPORT
1049*83d7ed1cSMilanka Ringwald                         printf("    Update report ID and type [%d, %d] of report index 0x%02X, service index 0x%02X\n",
1050021192e1SMilanka Ringwald                             client->reports[client->report_index].report_id,
1051021192e1SMilanka Ringwald                             client->reports[client->report_index].report_type,
1052*83d7ed1cSMilanka Ringwald                             client->report_index, client->service_index);
105319146789SMilanka Ringwald     #endif
105419146789SMilanka Ringwald                     }
1055e7bd2dbeSMilanka Ringwald                     break;
1056e7bd2dbeSMilanka Ringwald 
1057e7bd2dbeSMilanka Ringwald                 default:
1058e7bd2dbeSMilanka Ringwald                     break;
105970093cf5SMilanka Ringwald             }
106070093cf5SMilanka Ringwald             break;
106170093cf5SMilanka Ringwald 
1062cf26c8fbSMilanka Ringwald         case GATT_EVENT_QUERY_COMPLETE:
1063cf26c8fbSMilanka Ringwald             client = hids_get_client_for_con_handle(gatt_event_query_complete_get_handle(packet));
1064cf26c8fbSMilanka Ringwald             btstack_assert(client != NULL);
1065cf26c8fbSMilanka Ringwald 
1066cf26c8fbSMilanka Ringwald             att_status = gatt_event_query_complete_get_att_status(packet);
1067cf26c8fbSMilanka Ringwald 
1068cf26c8fbSMilanka Ringwald             switch (client->state){
1069cf26c8fbSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_SERVICE_RESULT:
10706bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
10716bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
10726bcfb631SMilanka Ringwald                         hids_finalize_client(client);
1073cf26c8fbSMilanka Ringwald                         break;
10746bcfb631SMilanka Ringwald                     }
10756bcfb631SMilanka Ringwald 
10766bcfb631SMilanka Ringwald                     if (client->num_instances == 0){
10776bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
10786bcfb631SMilanka Ringwald                         hids_finalize_client(client);
10796bcfb631SMilanka Ringwald                         break;
10806bcfb631SMilanka Ringwald                     }
10816bcfb631SMilanka Ringwald 
10826bcfb631SMilanka Ringwald                     client->service_index = 0;
108319146789SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
10846bcfb631SMilanka Ringwald                     break;
10856bcfb631SMilanka Ringwald 
10866bcfb631SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_CHARACTERISTIC_RESULT:
10876bcfb631SMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
10886bcfb631SMilanka Ringwald                         hids_emit_connection_established(client, att_status);
10896bcfb631SMilanka Ringwald                         hids_finalize_client(client);
10906bcfb631SMilanka Ringwald                         break;
10916bcfb631SMilanka Ringwald                     }
10926bcfb631SMilanka Ringwald 
109370093cf5SMilanka Ringwald                     if ((client->service_index + 1) < client->num_instances){
109470093cf5SMilanka Ringwald                         // discover characteristics of next service
109570093cf5SMilanka Ringwald                         client->service_index++;
109670093cf5SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_QUERY_CHARACTERISTIC;
1097021192e1SMilanka Ringwald                         break;
1098021192e1SMilanka Ringwald                     }
1099021192e1SMilanka Ringwald 
1100021192e1SMilanka Ringwald                     switch (client->required_protocol_mode){
1101021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT:
1102021192e1SMilanka Ringwald                             client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1103021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_report_mode(client)){
1104021192e1SMilanka Ringwald                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1105021192e1SMilanka Ringwald                                 break;
1106021192e1SMilanka Ringwald                             }
1107021192e1SMilanka Ringwald                             hids_emit_connection_established(client, att_status);
1108021192e1SMilanka Ringwald                             hids_finalize_client(client);
1109021192e1SMilanka Ringwald                             return;
1110021192e1SMilanka Ringwald 
1111021192e1SMilanka Ringwald                         case HID_PROTOCOL_MODE_REPORT_WITH_FALLBACK_TO_BOOT:
1112021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_report_mode(client)){
1113021192e1SMilanka Ringwald                                 client->protocol_mode = HID_PROTOCOL_MODE_REPORT;
1114021192e1SMilanka Ringwald                                 break;
1115021192e1SMilanka Ringwald                             }
1116021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_boot_mode(client)){
1117021192e1SMilanka Ringwald                                 if (client->protocol_mode_value_handle != 0){
1118021192e1SMilanka Ringwald                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
1119021192e1SMilanka Ringwald                                     break;
1120021192e1SMilanka Ringwald                                 }
1121021192e1SMilanka Ringwald                                 hids_emit_connection_established(client, att_status);
1122021192e1SMilanka Ringwald                                 hids_finalize_client(client);
1123021192e1SMilanka Ringwald                                 return;
1124021192e1SMilanka Ringwald                             }
1125021192e1SMilanka Ringwald                             break;
1126021192e1SMilanka Ringwald                         default:
1127021192e1SMilanka Ringwald                             if (hid_clients_has_reports_in_boot_mode(client)){
1128021192e1SMilanka Ringwald                                 if (client->protocol_mode_value_handle != 0){
1129021192e1SMilanka Ringwald                                     client->state = HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE;
1130021192e1SMilanka Ringwald                                     break;
1131021192e1SMilanka Ringwald                                 }
1132021192e1SMilanka Ringwald                                 hids_emit_connection_established(client, att_status);
1133021192e1SMilanka Ringwald                                 hids_finalize_client(client);
1134021192e1SMilanka Ringwald                                 return;
1135021192e1SMilanka Ringwald                             }
1136021192e1SMilanka Ringwald                             break;
1137021192e1SMilanka Ringwald                     }
1138021192e1SMilanka Ringwald 
1139021192e1SMilanka Ringwald                     if (client->state == HIDS_CLIENT_STATE_W2_SET_BOOT_PROTOCOL_MODE){
1140021192e1SMilanka Ringwald                         break;
1141021192e1SMilanka Ringwald                     }
11423322b222SMilanka Ringwald 
11437e1e6e7dSMilanka Ringwald                     // 1. we need to get HID Descriptor and
11447e1e6e7dSMilanka Ringwald                     // 2. get external Report characteristics if referenced from Report Map
11457e1e6e7dSMilanka Ringwald                     if (hids_client_report_map_query_init(client)){
114670093cf5SMilanka Ringwald                         break;
114770093cf5SMilanka Ringwald                     }
114870093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
114970093cf5SMilanka Ringwald                     hids_finalize_client(client);
11506bcfb631SMilanka Ringwald                     break;
11516bcfb631SMilanka Ringwald 
11523322b222SMilanka Ringwald 
115328da36a6SMilanka Ringwald                 // HID descriptor found
115428da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_HID_DESCRIPTOR:
1155e7bd2dbeSMilanka Ringwald                     if (att_status != ATT_ERROR_SUCCESS){
1156e7bd2dbeSMilanka Ringwald                         hids_emit_connection_established(client, att_status);
1157e7bd2dbeSMilanka Ringwald                         hids_finalize_client(client);
1158e7bd2dbeSMilanka Ringwald                         break;
1159e7bd2dbeSMilanka Ringwald                     }
1160e7bd2dbeSMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_REPORT_MAP_DISCOVER_CHARACTERISTIC_DESCRIPTORS;
1161e7bd2dbeSMilanka Ringwald                     break;
1162e7bd2dbeSMilanka Ringwald 
116328da36a6SMilanka Ringwald                 // found all descriptors, check if there is one with EXTERNAL_REPORT_REFERENCE
1164e7bd2dbeSMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_CHARACTERISTIC_DESCRIPTORS_RESULT:
11653322b222SMilanka Ringwald                     // go for next report map
11663322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map(client)){
1167e7bd2dbeSMilanka Ringwald                         break;
1168e7bd2dbeSMilanka Ringwald                     }
1169e7bd2dbeSMilanka Ringwald 
11703322b222SMilanka Ringwald                     // read UUIDS for external characteristics
11713322b222SMilanka Ringwald                     if (hids_client_report_map_uuid_query_init(client)){
1172e7bd2dbeSMilanka Ringwald                         break;
1173e7bd2dbeSMilanka Ringwald                     }
1174e7bd2dbeSMilanka Ringwald 
1175e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1176e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
11777e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1178e7bd2dbeSMilanka Ringwald                         break;
1179e7bd2dbeSMilanka Ringwald                     }
1180e7bd2dbeSMilanka Ringwald 
1181e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1182e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1183e7bd2dbeSMilanka Ringwald                     break;
1184e7bd2dbeSMilanka Ringwald 
118528da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_MAP_EXTERNAL_REPORT_REFERENCE_UUID:
1186e7bd2dbeSMilanka Ringwald                     // go for next map report
11873322b222SMilanka Ringwald                     if (hids_client_report_query_next_report_map_uuid(client)){
1188e7bd2dbeSMilanka Ringwald                         break;
1189e7bd2dbeSMilanka Ringwald                     }
1190e7bd2dbeSMilanka Ringwald 
11913322b222SMilanka Ringwald                     // update external characteristics with correct value handle and end handle
11923322b222SMilanka Ringwald                     client->state = HIDS_CLIENT_STATE_W2_DISCOVER_EXTERNAL_REPORT_CHARACTERISTIC;
1193e7bd2dbeSMilanka Ringwald                     break;
1194e7bd2dbeSMilanka Ringwald 
11953322b222SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_EXTERNAL_REPORT_CHARACTERISTIC_RESULT:
1196e7bd2dbeSMilanka Ringwald                     // discover characteristic descriptor for all Report characteristics,
1197e7bd2dbeSMilanka Ringwald                     // then read value of characteristic descriptor to get Report ID
11987e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_init(client)){
1199e7bd2dbeSMilanka Ringwald                         break;
1200e7bd2dbeSMilanka Ringwald                     }
1201e7bd2dbeSMilanka Ringwald 
1202e7bd2dbeSMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
1203e7bd2dbeSMilanka Ringwald                     hids_finalize_client(client);
1204e7bd2dbeSMilanka Ringwald                     break;
1205e7bd2dbeSMilanka Ringwald 
120628da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_FOUND:
120770093cf5SMilanka Ringwald                     if (client->descriptor_handle != 0){
120828da36a6SMilanka Ringwald                         client->state = HIDS_CLIENT_STATE_W2_READ_REPORT_ID_AND_TYPE;
120970093cf5SMilanka Ringwald                         break;
121070093cf5SMilanka Ringwald                     }
121170093cf5SMilanka Ringwald 
121270093cf5SMilanka Ringwald                     // go for next report
12137e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
121470093cf5SMilanka Ringwald                         break;
121570093cf5SMilanka Ringwald                     }
121670093cf5SMilanka Ringwald 
121770093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
121870093cf5SMilanka Ringwald                     break;
121970093cf5SMilanka Ringwald 
122028da36a6SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_REPORT_ID_AND_TYPE:
122170093cf5SMilanka Ringwald                     // go for next report
12227e1e6e7dSMilanka Ringwald                     if (hids_client_report_query_next_report(client)){
122370093cf5SMilanka Ringwald                         break;
122470093cf5SMilanka Ringwald                     }
122570093cf5SMilanka Ringwald 
1226021192e1SMilanka Ringwald                     if (hids_client_report_notifications_init(client)){
1227021192e1SMilanka Ringwald                         break;
1228021192e1SMilanka Ringwald                     }
122970093cf5SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
123070093cf5SMilanka Ringwald                     break;
123170093cf5SMilanka Ringwald 
1232021192e1SMilanka Ringwald                 case HIDS_CLIENT_STATE_W4_INPUT_REPORTS_ENABLED:
1233021192e1SMilanka Ringwald                     if (hids_client_report_next_notification_report_index(client)){
12342901a9b7SMilanka Ringwald                         break;
12352901a9b7SMilanka Ringwald                     }
12366bcfb631SMilanka Ringwald                     hids_emit_connection_established(client, ERROR_CODE_SUCCESS);
12376bcfb631SMilanka Ringwald                     break;
12386bcfb631SMilanka Ringwald 
1239cf26c8fbSMilanka Ringwald                 default:
1240cf26c8fbSMilanka Ringwald                     break;
1241cf26c8fbSMilanka Ringwald             }
1242cf26c8fbSMilanka Ringwald             break;
1243cf26c8fbSMilanka Ringwald 
1244cf26c8fbSMilanka Ringwald         default:
1245cf26c8fbSMilanka Ringwald             break;
1246cf26c8fbSMilanka Ringwald     }
12476bcfb631SMilanka Ringwald 
12486bcfb631SMilanka Ringwald     if (client != NULL){
12496bcfb631SMilanka Ringwald         hids_run_for_client(client);
12506bcfb631SMilanka Ringwald     }
1251cf26c8fbSMilanka Ringwald }
1252cf26c8fbSMilanka Ringwald 
12536bcfb631SMilanka 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){
1254cf26c8fbSMilanka Ringwald     btstack_assert(packet_handler != NULL);
1255cf26c8fbSMilanka Ringwald 
1256cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_con_handle(con_handle);
1257cf26c8fbSMilanka Ringwald     if (client != NULL){
1258cf26c8fbSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1259cf26c8fbSMilanka Ringwald     }
1260cf26c8fbSMilanka Ringwald 
1261cf26c8fbSMilanka Ringwald     uint16_t cid = hids_get_next_cid();
1262cf26c8fbSMilanka Ringwald     if (hids_cid != NULL) {
1263cf26c8fbSMilanka Ringwald         *hids_cid = cid;
1264cf26c8fbSMilanka Ringwald     }
1265cf26c8fbSMilanka Ringwald 
1266cf26c8fbSMilanka Ringwald     client = hids_create_client(con_handle, cid);
1267cf26c8fbSMilanka Ringwald     if (client == NULL) {
1268cf26c8fbSMilanka Ringwald         return BTSTACK_MEMORY_ALLOC_FAILED;
1269cf26c8fbSMilanka Ringwald     }
1270cf26c8fbSMilanka Ringwald 
12716bcfb631SMilanka Ringwald     client->required_protocol_mode = protocol_mode;
1272cf26c8fbSMilanka Ringwald     client->client_handler = packet_handler;
1273cf26c8fbSMilanka Ringwald     client->state = HIDS_CLIENT_STATE_W2_QUERY_SERVICE;
1274cf26c8fbSMilanka Ringwald 
1275cf26c8fbSMilanka Ringwald     hids_run_for_client(client);
1276cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1277cf26c8fbSMilanka Ringwald }
1278cf26c8fbSMilanka Ringwald 
1279cf26c8fbSMilanka Ringwald uint8_t hids_client_disconnect(uint16_t hids_cid){
1280cf26c8fbSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1281cf26c8fbSMilanka Ringwald     if (client == NULL){
1282cf26c8fbSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1283cf26c8fbSMilanka Ringwald     }
1284cf26c8fbSMilanka Ringwald     // finalize connection
1285cf26c8fbSMilanka Ringwald     hids_finalize_client(client);
1286cf26c8fbSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1287cf26c8fbSMilanka Ringwald }
1288cf26c8fbSMilanka Ringwald 
12891624214bSMilanka Ringwald uint8_t hids_client_send_report(uint16_t hids_cid, uint8_t report_id, const uint8_t * report, uint8_t report_len){
12901624214bSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
12911624214bSMilanka Ringwald     if (client == NULL){
12921624214bSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
12931624214bSMilanka Ringwald     }
12941624214bSMilanka Ringwald 
12951624214bSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
12961624214bSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
12971624214bSMilanka Ringwald     }
12981624214bSMilanka Ringwald 
12992fe59e00SMilanka Ringwald     uint8_t report_index = find_report_index_for_report_id_and_type(client, report_id, HID_REPORT_TYPE_OUTPUT);
1300*83d7ed1cSMilanka Ringwald 
13011624214bSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
13021624214bSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
13031624214bSMilanka Ringwald     }
13041624214bSMilanka Ringwald 
13051624214bSMilanka Ringwald     uint16_t mtu;
13061624214bSMilanka Ringwald     uint8_t status = gatt_client_get_mtu(client->con_handle, &mtu);
13071624214bSMilanka Ringwald 
13081624214bSMilanka Ringwald     if (status != ERROR_CODE_SUCCESS){
13091624214bSMilanka Ringwald         return status;
13101624214bSMilanka Ringwald     }
13111624214bSMilanka Ringwald 
13121624214bSMilanka Ringwald     if (mtu - 2 < report_len){
13131624214bSMilanka Ringwald         return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE;
13141624214bSMilanka Ringwald     }
13151624214bSMilanka Ringwald 
13161624214bSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_REPORT;
1317021192e1SMilanka Ringwald     client->report_index = report_index;
13181624214bSMilanka Ringwald     client->report = report;
13191624214bSMilanka Ringwald     client->report_len = report_len;
13201624214bSMilanka Ringwald 
13211624214bSMilanka Ringwald     hids_run_for_client(client);
13221624214bSMilanka Ringwald     return ERROR_CODE_SUCCESS;
13231624214bSMilanka Ringwald }
13241624214bSMilanka Ringwald 
1325*83d7ed1cSMilanka Ringwald uint8_t hids_client_send_get_report(uint16_t hids_cid, uint8_t report_id){
1326*83d7ed1cSMilanka Ringwald     hids_client_t * client = hids_get_client_for_cid(hids_cid);
1327*83d7ed1cSMilanka Ringwald     if (client == NULL){
1328*83d7ed1cSMilanka Ringwald         return ERROR_CODE_UNKNOWN_CONNECTION_IDENTIFIER;
1329*83d7ed1cSMilanka Ringwald     }
1330*83d7ed1cSMilanka Ringwald 
1331*83d7ed1cSMilanka Ringwald     if (client->state != HIDS_CLIENT_STATE_CONNECTED) {
1332*83d7ed1cSMilanka Ringwald         return ERROR_CODE_COMMAND_DISALLOWED;
1333*83d7ed1cSMilanka Ringwald     }
1334*83d7ed1cSMilanka Ringwald 
1335*83d7ed1cSMilanka Ringwald     uint8_t report_index = find_input_report_index_for_report_id(client, report_id);
1336*83d7ed1cSMilanka Ringwald     if (report_index == HIDS_CLIENT_INVALID_REPORT_INDEX){
1337*83d7ed1cSMilanka Ringwald         return ERROR_CODE_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
1338*83d7ed1cSMilanka Ringwald     }
1339*83d7ed1cSMilanka Ringwald 
1340*83d7ed1cSMilanka Ringwald     client->report_index = report_index;
1341*83d7ed1cSMilanka Ringwald 
1342*83d7ed1cSMilanka Ringwald #ifdef ENABLE_TESTING_SUPPORT
1343*83d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_READ_CHARACTERISTIC_CONFIGURATION;
1344*83d7ed1cSMilanka Ringwald #else
1345*83d7ed1cSMilanka Ringwald     client->state = HIDS_CLIENT_W2_SEND_GET_REPORT;
1346*83d7ed1cSMilanka Ringwald #endif
1347*83d7ed1cSMilanka Ringwald     hids_run_for_client(client);
1348*83d7ed1cSMilanka Ringwald     return ERROR_CODE_SUCCESS;
1349*83d7ed1cSMilanka Ringwald }
1350*83d7ed1cSMilanka Ringwald 
1351*83d7ed1cSMilanka Ringwald 
1352021192e1SMilanka Ringwald void hids_client_init(uint8_t * hid_descriptor_storage, uint16_t hid_descriptor_storage_len){
1353021192e1SMilanka Ringwald     hids_client_descriptor_storage = hid_descriptor_storage;
1354021192e1SMilanka Ringwald     hids_client_descriptor_storage_len = hid_descriptor_storage_len;
1355021192e1SMilanka Ringwald }
1356cf26c8fbSMilanka Ringwald 
1357cf26c8fbSMilanka Ringwald void hids_client_deinit(void){}
1358